Map<K,V> and MapIterator<K,V> for ZScript

This commit is contained in:
Ricardo Luís Vaz Silva 2022-11-25 20:39:12 -03:00 committed by Christoph Oelckers
commit 8b6a714d41
23 changed files with 2354 additions and 28 deletions

View file

@ -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<PMap*>(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<FxConstant*>(a)->GetValue().GetPointer() != nullptr)
{
if (!a->ValueType->isObjectPointer() ||
!static_cast<PObjectPointer*>(mapValueType)->PointedClass()->IsAncestorOf(static_cast<PObjectPointer*>(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<PMap*>(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<FxMemberBase*>(Self);
auto newfield = Create<PField>(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<PMapIterator*>(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<FxConstant*>(a)->GetValue().GetPointer() != nullptr)
{
if (!a->ValueType->isObjectPointer() ||
!static_cast<PObjectPointer*>(mapValueType)->PointedClass()->IsAncestorOf(static_cast<PObjectPointer*>(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<PMapIterator*>(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<FxMemberBase*>(Self);
auto newfield = Create<PField>(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;

View file

@ -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<PStruct*>(ValueType)->isNative); }

View file

@ -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<typename M::KeyType, DObject*> it(*x);\
typename M::Pair * p;\
while(it.NextPair(p)){\
GC::WriteBarrier(p->Value);\
}\
}
//==========================================================================
//
// MapCopy
//
//==========================================================================
template<typename M> void MapCopy(M * self, M * other)
{
if constexpr(std::is_same_v<typename M::ValueType,DObject*>)
{
MAP_GC_WRITE_BARRIER(other);
}
*self = *other; // how does this handle self->info? TODO double check.
self->info->rev++;
}
//==========================================================================
//
// MapMove
//
//==========================================================================
template<typename M> void MapMove(M * self, M * other)
{
if constexpr(std::is_same_v<typename M::ValueType,DObject*>)
{
MAP_GC_WRITE_BARRIER(other);
}
self->TransferFrom(*other);
self->info->rev++;
other->info->rev++;
}
//==========================================================================
//
// MapSwap
//
//==========================================================================
template<typename M> void MapSwap(M * self, M * other)
{
if constexpr(std::is_same_v<typename M::ValueType,DObject*>)
{
MAP_GC_WRITE_BARRIER(other);
}
self->Swap(*other);
self->info->rev++;
other->info->rev++;
}
//==========================================================================
//
// MapClear
//
//==========================================================================
template<typename M> void MapClear(M * self)
{
if constexpr(std::is_same_v<typename M::ValueType,DObject*>)
{
MAP_GC_WRITE_BARRIER(self);
}
self->Clear();
self->info->rev++;
}
//==========================================================================
//
//
//
//==========================================================================
template<typename T>
using expand_types_vm =
std::conditional_t<std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t>, uint32_t , /* expand 8/16-bit to 32-bit */
std::conditional_t<std::is_same_v<T, float> , double , /* expand float to double */
std::conditional_t<std::is_same_v<T, FString> , const FString & , T>>>; /* change String to String ref */
template<typename M> unsigned int MapCountUsed(M * self)
{
return self->CountUsed();
}
template<typename M> expand_types_vm<typename M::ValueType> MapGet(M * self,expand_types_vm<typename M::KeyType> key)
{
typename M::ValueType * v = self->CheckKey(key);
if (v) {
return *v;
}
else
{
typename M::ValueType n {};
self->Insert(key,n);
return n;
}
}
template<typename M> void MapGetString(M * self,expand_types_vm<typename M::KeyType> key, FString &out)
{
FString * v = self->CheckKey(key);
if (v) {
out = *v;
}
else
{
out = FString();
self->Insert(key,out);
}
}
template<typename M> int MapCheckKey(M * self, expand_types_vm<typename M::KeyType> key)
{
return self->CheckKey(key) != nullptr;
}
//==========================================================================
//
// MapInsert
//
//==========================================================================
template<typename M> void MapInsert(M * self, expand_types_vm<typename M::KeyType> key, expand_types_vm<typename M::ValueType> value)
{
if constexpr(std::is_same_v<typename M::ValueType, DObject*>)
{
MAP_GC_WRITE_BARRIER(self);
GC::WriteBarrier(value);
}
self->Insert(key, value);
self->info->rev++; // invalidate iterators
}
//==========================================================================
//
//
//
//==========================================================================
template<typename M> void MapInsertNew(M * self, expand_types_vm<typename M::KeyType> key)
{
if constexpr(std::is_same_v<typename M::ValueType, DObject*>)
{
MAP_GC_WRITE_BARRIER(self);
}
self->Insert(key,{});
self->info->rev++; // invalidate iterators
}
template<typename M> void MapRemove(M * self, expand_types_vm<typename M::KeyType> key)
{
self->Remove(key);
self->info->rev++; // invalidate iterators
}
//==========================================================================
//
//
//
//==========================================================================
template<typename I, typename M> int MapIteratorInit(I * self, M * other)
{
return self->Init(*other);
}
template<typename I> int MapIteratorReInit(I * self)
{
return self->ReInit();
}
template<typename I> int MapIteratorValid(I * self)
{
return self->Valid();
}
template<typename I> int MapIteratorNext(I * self)
{
return self->Next();
}
template<typename I> expand_types_vm<typename I::KeyType> MapIteratorGetKey(I * self)
{
return self->GetKey();
}
template<typename I> void MapIteratorGetKeyString(I * self, FString &out)
{
out = self->GetKey();
}
template<typename I> expand_types_vm<typename I::ValueType> MapIteratorGetValue(I * self)
{
return self->GetValue();
}
template<typename I> void MapIteratorGetValueString(I * self, FString &out)
{
out = self->GetValue();
}
template<typename I> void MapIteratorSetValue(I * self, expand_types_vm<typename I::ValueType> value)
{
auto & val = self->GetValue();
if constexpr(std::is_same_v<typename I::ValueType, DObject*>)
{
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<key_type , value_type> 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<key_type , value_type> 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();

View file

@ -0,0 +1,110 @@
#pragma once
#include <memory>
#include "tarray.h"
#include "refcounted.h"
class ZSMapInfo : public RefCountedBase
{
public:
void * self = nullptr;
int rev = 0;
};
struct ZSFMap : FMap {
RefCountedPtr<RefCountedBase> info;
};
template<class KT, class VT>
class ZSMap : public TMap<KT,VT>
{
public:
RefCountedPtr<ZSMapInfo> info;
ZSMap() :
TMap<KT,VT>(), info(new ZSMapInfo)
{
info->self = this;
}
~ZSMap()
{
info->self = nullptr;
}
};
template<class KT, class VT>
struct ZSMapIterator
{
RefCountedPtr<ZSMapInfo> info;
TMapIterator<KT,VT> *it = nullptr;
typename ZSMap<KT,VT>::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<KT,VT>(*static_cast<ZSMap<KT,VT>*>(info->self));
rev = info->rev;
p = nullptr;
return true;
}
return false;
}
bool Init(ZSMap<KT,VT> &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");
}
}
};

View file

@ -33,11 +33,14 @@
**
*/
#include <cinttypes>
#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<FTypeAndOffset>
//==========================================================================
//
// PType :: SetDefaultValue
// PType :: SetPointer*
//
//==========================================================================
@ -195,6 +198,10 @@ void PType::SetPointerArray(void *base, unsigned offset, TArray<size_t> *stroffs
{
}
void PType::SetPointerMap(void *base, unsigned offset, TArray<std::pair<size_t,PType *>> *ptrofs)
{
}
//==========================================================================
//
// PType :: InitializeValue
@ -1871,6 +1878,23 @@ void PArray::SetPointerArray(void *base, unsigned offset, TArray<size_t> *specia
}
}
//==========================================================================
//
// PArray :: SetPointerMap
//
//==========================================================================
void PArray::SetPointerMap(void *base, unsigned offset, TArray<std::pair<size_t,PType *>> *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<class MT, OverrideFunctionRetType RetType, OverrideFunctionArgType ArgType >
void CreateOverrideFunction(MT *self, FName name)
{
auto Fn = Create<PFunction>(self->BackingType, name);
auto NativeFn = FindFunction(self->BackingType, name.GetChars());
assert(NativeFn);
assert(NativeFn->VMPointer);
TArray<PType*> ret;
TArray<PType*> args;
TArray<uint32_t> argflags;
TArray<FName> 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<PMap, OFN_RET_VAL, OFN_ARG_KEY>(this, NAME_Get);
CreateOverrideFunction<PMap, OFN_RET_BOOL, OFN_ARG_KEY>(this, NAME_CheckKey);
CreateOverrideFunction<PMap, OFN_RET_VOID, OFN_ARG_KEY_VAL>(this, NAME_Insert);
CreateOverrideFunction<PMap, OFN_RET_VOID, OFN_ARG_KEY>(this, NAME_InsertNew);
CreateOverrideFunction<PMap, OFN_RET_VOID, OFN_ARG_KEY>(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<uint32_t, uint8_t>();
break;
case MAP_I32_I16:
new(addr) ZSMap<uint32_t, uint16_t>();
break;
case MAP_I32_I32:
new(addr) ZSMap<uint32_t, uint32_t>();
break;
case MAP_I32_F32:
new(addr) ZSMap<uint32_t, float>();
break;
case MAP_I32_F64:
new(addr) ZSMap<uint32_t, double>();
break;
case MAP_I32_OBJ:
new(addr) ZSMap<uint32_t, DObject*>();
break;
case MAP_I32_PTR:
new(addr) ZSMap<uint32_t, void*>();
break;
case MAP_I32_STR:
new(addr) ZSMap<uint32_t, FString>();
break;
case MAP_STR_I8:
new(addr) ZSMap<FString, uint8_t>();
break;
case MAP_STR_I16:
new(addr) ZSMap<FString, uint16_t>();
break;
case MAP_STR_I32:
new(addr) ZSMap<FString, uint32_t>();
break;
case MAP_STR_F32:
new(addr) ZSMap<FString, float>();
break;
case MAP_STR_F64:
new(addr) ZSMap<FString, double>();
break;
case MAP_STR_OBJ:
new(addr) ZSMap<FString, DObject*>();
break;
case MAP_STR_PTR:
new(addr) ZSMap<FString, void*>();
break;
case MAP_STR_STR:
new(addr) ZSMap<FString, FString>();
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<ZSMap<uint32_t, uint8_t>*>(addr)->~ZSMap();
break;
case MAP_I32_I16:
static_cast<ZSMap<uint32_t, uint16_t>*>(addr)->~ZSMap();
break;
case MAP_I32_I32:
static_cast<ZSMap<uint32_t, uint32_t>*>(addr)->~ZSMap();
break;
case MAP_I32_F32:
static_cast<ZSMap<uint32_t, float>*>(addr)->~ZSMap();
break;
case MAP_I32_F64:
static_cast<ZSMap<uint32_t, double>*>(addr)->~ZSMap();
break;
case MAP_I32_OBJ:
static_cast<ZSMap<uint32_t, DObject*>*>(addr)->~ZSMap();
break;
case MAP_I32_PTR:
static_cast<ZSMap<uint32_t, void*>*>(addr)->~ZSMap();
break;
case MAP_I32_STR:
static_cast<ZSMap<uint32_t, FString>*>(addr)->~ZSMap();
break;
case MAP_STR_I8:
static_cast<ZSMap<FString, uint8_t>*>(addr)->~ZSMap();
break;
case MAP_STR_I16:
static_cast<ZSMap<FString, uint16_t>*>(addr)->~ZSMap();
break;
case MAP_STR_I32:
static_cast<ZSMap<FString, uint32_t>*>(addr)->~ZSMap();
break;
case MAP_STR_F32:
static_cast<ZSMap<FString, float>*>(addr)->~ZSMap();
break;
case MAP_STR_F64:
static_cast<ZSMap<FString, double>*>(addr)->~ZSMap();
break;
case MAP_STR_OBJ:
static_cast<ZSMap<FString, DObject*>*>(addr)->~ZSMap();
break;
case MAP_STR_PTR:
static_cast<ZSMap<FString, void*>*>(addr)->~ZSMap();
break;
case MAP_STR_STR:
static_cast<ZSMap<FString, FString>*>(addr)->~ZSMap();
break;
}
}
//==========================================================================
//
// PMap :: SetDefaultValue
//
//==========================================================================
void PMap::SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *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<std::pair<size_t,PType *>> *special)
{
if (ValueType->isObjectPointer())
{
// Add to the list of pointer arrays for this class.
special->Push(std::make_pair(offset,KeyType));
}
}
//==========================================================================
//
// PMap :: WriteValue
//
//==========================================================================
template<typename M>
static void PMapValueWriter(FSerializer &ar, const M *map, const PMap *m)
{
TMapConstIterator<typename M::KeyType, typename M::ValueType> it(*map);
const typename M::Pair * p;
while(it.NextPair(p))
{
if constexpr(std::is_same_v<typename M::KeyType,FString>)
{
m->ValueType->WriteValue(ar,p->Key.GetChars(),static_cast<const void *>(&p->Value));
}
else if constexpr(std::is_same_v<typename M::KeyType,uint32_t>)
{
FString key;
key.Format("%u",p->Key);
m->ValueType->WriteValue(ar,key.GetChars(),static_cast<const void *>(&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<const ZSMap<uint32_t, uint8_t>*>(addr), this);
break;
case MAP_I32_I16:
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, uint16_t>*>(addr), this);
break;
case MAP_I32_I32:
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, uint32_t>*>(addr), this);
break;
case MAP_I32_F32:
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, float>*>(addr), this);
break;
case MAP_I32_F64:
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, double>*>(addr), this);
break;
case MAP_I32_OBJ:
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, DObject*>*>(addr), this);
break;
case MAP_I32_PTR:
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, void*>*>(addr), this);
break;
case MAP_I32_STR:
PMapValueWriter(ar, static_cast<const ZSMap<uint32_t, FString>*>(addr), this);
break;
case MAP_STR_I8:
PMapValueWriter(ar, static_cast<const ZSMap<FString, uint8_t>*>(addr), this);
break;
case MAP_STR_I16:
PMapValueWriter(ar, static_cast<const ZSMap<FString, uint16_t>*>(addr), this);
break;
case MAP_STR_I32:
PMapValueWriter(ar, static_cast<const ZSMap<FString, uint32_t>*>(addr), this);
break;
case MAP_STR_F32:
PMapValueWriter(ar, static_cast<const ZSMap<FString, float>*>(addr), this);
break;
case MAP_STR_F64:
PMapValueWriter(ar, static_cast<const ZSMap<FString, double>*>(addr), this);
break;
case MAP_STR_OBJ:
PMapValueWriter(ar, static_cast<const ZSMap<FString, DObject*>*>(addr), this);
break;
case MAP_STR_PTR:
PMapValueWriter(ar, static_cast<const ZSMap<FString, void*>*>(addr), this);
break;
case MAP_STR_STR:
PMapValueWriter(ar, static_cast<const ZSMap<FString, FString>*>(addr), this);
break;
}
ar.EndObject();
}
}
//==========================================================================
//
// PMap :: ReadValue
//
//==========================================================================
template<typename M>
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<typename M::KeyType,FString>)
{
val = &map->InsertNew(k);
}
else if constexpr(std::is_same_v<typename M::KeyType,uint32_t>)
{
FString s(k);
if(!s.IsInt())
{
ar.EndObject();
return false;
}
val = &map->InsertNew(static_cast<uint32_t>(s.ToULong()));
}
if (!m->ValueType->ReadValue(ar,nullptr,static_cast<void*>(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<ZSMap<uint32_t, uint8_t>*>(addr), this);
case MAP_I32_I16:
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, uint16_t>*>(addr), this);
case MAP_I32_I32:
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, uint32_t>*>(addr), this);
case MAP_I32_F32:
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, float>*>(addr), this);
case MAP_I32_F64:
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, double>*>(addr), this);
case MAP_I32_OBJ:
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, DObject*>*>(addr), this);
case MAP_I32_PTR:
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, void*>*>(addr), this);
case MAP_I32_STR:
return PMapValueReader(ar, static_cast<ZSMap<uint32_t, FString>*>(addr), this);
case MAP_STR_I8:
return PMapValueReader(ar, static_cast<ZSMap<FString, uint8_t>*>(addr), this);
case MAP_STR_I16:
return PMapValueReader(ar, static_cast<ZSMap<FString, uint16_t>*>(addr), this);
case MAP_STR_I32:
return PMapValueReader(ar, static_cast<ZSMap<FString, uint32_t>*>(addr), this);
case MAP_STR_F32:
return PMapValueReader(ar, static_cast<ZSMap<FString, float>*>(addr), this);
case MAP_STR_F64:
return PMapValueReader(ar, static_cast<ZSMap<FString, double>*>(addr), this);
case MAP_STR_OBJ:
return PMapValueReader(ar, static_cast<ZSMap<FString, DObject*>*>(addr), this);
case MAP_STR_PTR:
return PMapValueReader(ar, static_cast<ZSMap<FString, void*>*>(addr), this);
case MAP_STR_STR:
return PMapValueReader(ar, static_cast<ZSMap<FString, FString>*>(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<PMapIterator, OFN_RET_KEY, OFN_ARG_VOID>(this, NAME_GetKey);
CreateOverrideFunction<PMapIterator, OFN_RET_VAL, OFN_ARG_VOID>(this, NAME_GetValue);
CreateOverrideFunction<PMapIterator, OFN_RET_VOID, OFN_ARG_VAL>(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<uint32_t, uint8_t>();
break;
case PMap::MAP_I32_I16:
new(addr) ZSMapIterator<uint32_t, uint16_t>();
break;
case PMap::MAP_I32_I32:
new(addr) ZSMapIterator<uint32_t, uint32_t>();
break;
case PMap::MAP_I32_F32:
new(addr) ZSMapIterator<uint32_t, float>();
break;
case PMap::MAP_I32_F64:
new(addr) ZSMapIterator<uint32_t, double>();
break;
case PMap::MAP_I32_OBJ:
new(addr) ZSMapIterator<uint32_t, DObject*>();
break;
case PMap::MAP_I32_PTR:
new(addr) ZSMapIterator<uint32_t, void*>();
break;
case PMap::MAP_I32_STR:
new(addr) ZSMapIterator<uint32_t, FString>();
break;
case PMap::MAP_STR_I8:
new(addr) ZSMapIterator<FString, uint8_t>();
break;
case PMap::MAP_STR_I16:
new(addr) ZSMapIterator<FString, uint16_t>();
break;
case PMap::MAP_STR_I32:
new(addr) ZSMapIterator<FString, uint32_t>();
break;
case PMap::MAP_STR_F32:
new(addr) ZSMapIterator<FString, float>();
break;
case PMap::MAP_STR_F64:
new(addr) ZSMapIterator<FString, double>();
break;
case PMap::MAP_STR_OBJ:
new(addr) ZSMapIterator<FString, DObject*>();
break;
case PMap::MAP_STR_PTR:
new(addr) ZSMapIterator<FString, void*>();
break;
case PMap::MAP_STR_STR:
new(addr) ZSMapIterator<FString, FString>();
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<ZSMapIterator<uint32_t, uint8_t>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_I32_I16:
static_cast<ZSMapIterator<uint32_t, uint16_t>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_I32_I32:
static_cast<ZSMapIterator<uint32_t, uint32_t>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_I32_F32:
static_cast<ZSMapIterator<uint32_t, float>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_I32_F64:
static_cast<ZSMapIterator<uint32_t, double>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_I32_OBJ:
static_cast<ZSMapIterator<uint32_t, DObject*>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_I32_PTR:
static_cast<ZSMapIterator<uint32_t, void*>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_I32_STR:
static_cast<ZSMapIterator<uint32_t, FString>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_STR_I8:
static_cast<ZSMapIterator<FString, uint8_t>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_STR_I16:
static_cast<ZSMapIterator<FString, uint16_t>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_STR_I32:
static_cast<ZSMapIterator<FString, uint32_t>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_STR_F32:
static_cast<ZSMapIterator<FString, float>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_STR_F64:
static_cast<ZSMapIterator<FString, double>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_STR_OBJ:
static_cast<ZSMapIterator<FString, DObject*>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_STR_PTR:
static_cast<ZSMapIterator<FString, void*>*>(addr)->~ZSMapIterator();
break;
case PMap::MAP_STR_STR:
static_cast<ZSMapIterator<FString, FString>*>(addr)->~ZSMapIterator();
break;
}
}
//==========================================================================
//
// PMapIterator :: SetDefaultValue
//
//==========================================================================
void PMapIterator::SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *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<size_t> *speci
}
}
//==========================================================================
//
// PStruct :: SetPointerMap
//
//==========================================================================
void PStruct::SetPointerMap(void *base, unsigned offset, TArray<std::pair<size_t,PType *>> *special)
{
auto it = Symbols.GetIterator();
PSymbolTable::MapType::Pair *pair;
while (it.NextPair(pair))
{
auto field = dyn_cast<PField>(pair->Value);
if (field && !(field->Flags & VARF_Transient))
{
field->Type->SetPointerMap(base, unsigned(offset + field->Offset), special);
}
}
}
//==========================================================================
//
// PStruct :: WriteValue

View file

@ -129,6 +129,7 @@ public:
virtual void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special=NULL);
virtual void SetPointer(void *base, unsigned offset, TArray<size_t> *ptrofs = NULL);
virtual void SetPointerArray(void *base, unsigned offset, TArray<size_t> *ptrofs = NULL);
virtual void SetPointerMap(void *base, unsigned offset, TArray<std::pair<size_t,PType *>> *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<FTypeAndOffset> *special) override;
void SetPointer(void *base, unsigned offset, TArray<size_t> *special) override;
void SetPointerArray(void *base, unsigned offset, TArray<size_t> *ptrofs = NULL) override;
void SetPointerMap(void *base, unsigned offset, TArray<std::pair<size_t,PType *>> *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<FName,PFunction*> 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<FTypeAndOffset> *specials) override;
void InitializeValue(void *addr, const void *def) const override;
void DestroyValue(void *addr) const override;
void SetPointerMap(void *base, unsigned offset, TArray<std::pair<size_t,PType *>> *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<FName,PFunction*> 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<FTypeAndOffset> *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<FTypeAndOffset> *specials) override;
void SetPointer(void *base, unsigned offset, TArray<size_t> *specials) override;
void SetPointerArray(void *base, unsigned offset, TArray<size_t> *special) override;
void SetPointerMap(void *base, unsigned offset, TArray<std::pair<size_t,PType *>> *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);

View file

@ -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,

View file

@ -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<K, V> */
{
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<K, V> */
{
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<type> */
{
NEW_AST_NODE(DynArrayType,arr,T);

View file

@ -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<ZCC_MapType *>(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<ZCC_MapType *>(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<ZCC_MapIteratorType *>(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<ZCC_DynArrayType *>(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)*/);

View file

@ -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<ZCC_Expression *>(TreeNodeDeepCopy_Internal(ast, origCasted->ArraySize, true, copiedNodesList));
// AST_MapIteratorType
copy->KeyType = static_cast<ZCC_Type *>(TreeNodeDeepCopy_Internal(ast, origCasted->KeyType, true, copiedNodesList));
copy->ValueType = static_cast<ZCC_Type *>(TreeNodeDeepCopy_Internal(ast, origCasted->ValueType, true, copiedNodesList));
break;
}
case AST_DynArrayType:
{
TreeNodeDeepCopy_Start(DynArrayType);

View file

@ -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;