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
parent 1e5e65546d
commit 8b6a714d41
23 changed files with 2355 additions and 29 deletions

View file

@ -1153,6 +1153,7 @@ set (PCH_SOURCES
common/rendering/gl/gl_samplers.cpp common/rendering/gl/gl_samplers.cpp
common/rendering/gl/gl_shader.cpp common/rendering/gl/gl_shader.cpp
common/rendering/gl/gl_shaderprogram.cpp common/rendering/gl/gl_shaderprogram.cpp
common/scripting/core/maps.cpp
common/scripting/core/dictionary.cpp common/scripting/core/dictionary.cpp
common/scripting/core/dynarrays.cpp common/scripting/core/dynarrays.cpp
common/scripting/core/symbols.cpp common/scripting/core/symbols.cpp

View file

@ -117,6 +117,7 @@ xx(Double)
xx(String) xx(String)
xx(Vector) xx(Vector)
xx(Map) xx(Map)
xx(MapIterator)
xx(Array) xx(Array)
xx(Include) xx(Include)
xx(Sound) xx(Sound)
@ -162,6 +163,14 @@ xx(ToVector)
xx(Size) xx(Size)
xx(Push) xx(Push)
xx(Insert) xx(Insert)
xx(InsertNew)
xx(Remove)
xx(Get)
xx(GetValue)
xx(GetKey)
xx(SetValue)
xx(CheckKey)
xx(Value)
xx(Copy) xx(Copy)
xx(Move) xx(Move)
xx(Voidptr) xx(Voidptr)

View file

@ -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))); int DPrintf (int level, const char *format, ...) ATTRIBUTE((format(printf,2,3)));
void I_DebugPrint(const char* cp); 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 // flag to silence non-error output
extern bool batchrun; extern bool batchrun;

View file

@ -167,6 +167,7 @@ std2:
'vector2' { RET(TK_Vector2); } 'vector2' { RET(TK_Vector2); }
'vector3' { RET(TK_Vector3); } 'vector3' { RET(TK_Vector3); }
'map' { RET(TK_Map); } 'map' { RET(TK_Map); }
'mapiterator' { RET(TK_MapIterator); }
'array' { RET(TK_Array); } 'array' { RET(TK_Array); }
'in' { RET(TK_In); } 'in' { RET(TK_In); }
'sizeof' { RET(TK_SizeOf); } 'sizeof' { RET(TK_SizeOf); }

View file

@ -128,6 +128,7 @@ xx(TK_Replaces, "'replaces'")
xx(TK_Vector2, "'vector2'") xx(TK_Vector2, "'vector2'")
xx(TK_Vector3, "'vector3'") xx(TK_Vector3, "'vector3'")
xx(TK_Map, "'map'") xx(TK_Map, "'map'")
xx(TK_MapIterator, "'mapiterator'")
xx(TK_Array, "'array'") xx(TK_Array, "'array'")
xx(TK_In, "'in'") xx(TK_In, "'in'")
xx(TK_SizeOf, "'sizeof'") xx(TK_SizeOf, "'sizeof'")

View file

@ -42,6 +42,7 @@
#include "types.h" #include "types.h"
#include "i_time.h" #include "i_time.h"
#include "printf.h" #include "printf.h"
#include "maps.h"
//========================================================================== //==========================================================================
// //
@ -342,6 +343,17 @@ DEFINE_ACTION_FUNCTION(DObject, Destroy)
// //
//========================================================================== //==========================================================================
template<typename M>
static void PropagateMarkMap(M *map)
{
TMapIterator<typename M::KeyType,DObject*> it(*map);
typename M::Pair * p;
while(it.NextPair(p))
{
GC::Mark(&p->Value);
}
}
size_t DObject::PropagateMark() size_t DObject::PropagateMark()
{ {
const PClass *info = GetClass(); const PClass *info = GetClass();
@ -375,6 +387,27 @@ size_t DObject::PropagateMark()
offsets++; offsets++;
} }
{
const std::pair<size_t,PType *> *maps = info->MapPointers;
if (maps == NULL)
{
const_cast<PClass *>(info)->BuildMapPointers();
maps = info->MapPointers;
}
while (maps->first != ~(size_t)0)
{
if(maps->second->RegType == REGT_STRING)
{ // FString,DObject*
PropagateMarkMap((ZSMap<FString,DObject*>*)((uint8_t *)this + maps->first));
}
else
{ // uint32_t,DObject*
PropagateMarkMap((ZSMap<uint32_t,DObject*>*)((uint8_t *)this + maps->first));
}
maps++;
}
}
return info->Size; return info->Size;
} }
return 0; return 0;

View file

@ -82,6 +82,8 @@ DEFINE_GLOBAL(WP_NOCHANGE);
// A harmless non-nullptr FlatPointer for classes without pointers. // A harmless non-nullptr FlatPointer for classes without pointers.
static const size_t TheEnd = ~(size_t)0; static const size_t TheEnd = ~(size_t)0;
static const std::pair<size_t,PType *> TheMapEnd = {~(size_t)0 , nullptr};
//========================================================================== //==========================================================================
// //
// PClass :: WriteValue // 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<std::pair<size_t,PType *>> 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<size_t,PType *> *flat = (std::pair<size_t,PType *>*)ClassDataAllocator.Alloc(sizeof(std::pair<size_t,PType *>) * (numSuperPointers + ScriptPointers.Size() + 1));
if (numSuperPointers > 0)
{
memcpy(flat, ParentClass->MapPointers, sizeof(std::pair<size_t,PType *>)*numSuperPointers);
}
if (ScriptPointers.Size() > 0)
{
memcpy(flat + numSuperPointers, &ScriptPointers[0], sizeof(std::pair<size_t,PType *>) * ScriptPointers.Size());
}
flat[numSuperPointers + ScriptPointers.Size()] = TheMapEnd;
MapPointers = flat;
}
}
}
//========================================================================== //==========================================================================
// //
// PClass :: NativeClass // PClass :: NativeClass

View file

@ -56,6 +56,7 @@ public:
const size_t *Pointers = nullptr; // object pointers defined by this class *only* 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 *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 size_t *ArrayPointers = nullptr; // dynamic arrays containing object pointers.
const std::pair<size_t,PType *> *MapPointers = nullptr; // maps containing object pointers.
uint8_t *Defaults = nullptr; uint8_t *Defaults = nullptr;
uint8_t *Meta = nullptr; // Per-class static script data uint8_t *Meta = nullptr; // Per-class static script data
unsigned Size = sizeof(DObject); unsigned Size = sizeof(DObject);
@ -84,6 +85,7 @@ public:
void InitializeActorInfo(); void InitializeActorInfo();
void BuildFlatPointers(); void BuildFlatPointers();
void BuildArrayPointers(); void BuildArrayPointers();
void BuildMapPointers();
void DestroySpecials(void *addr); void DestroySpecials(void *addr);
void DestroyMeta(void *addr); void DestroyMeta(void *addr);
const PClass *NativeClass() const; const PClass *NativeClass() const;

View file

@ -8205,6 +8205,8 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
PContainerType *ccls = nullptr; PContainerType *ccls = nullptr;
PFunction * afd_override = nullptr;
if (ctx.Class == nullptr) if (ctx.Class == nullptr)
{ {
// There's no way that a member function call can resolve to a constant so abort right away. // 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()) if (Self->ValueType->isRealPointer())
{ {
auto pointedType = Self->ValueType->toPointer()->PointedType; auto pointedType = Self->ValueType->toPointer()->PointedType;
if (pointedType && pointedType->isDynArray()) if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator()))
{ {
Self = new FxOutVarDereference(Self, Self->ScriptPosition); Self = new FxOutVarDereference(Self, Self->ScriptPosition);
SAFE_RESOLVE(Self, ctx); SAFE_RESOLVE(Self, ctx);
@ -8454,7 +8456,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
if (a->ValueType->isRealPointer()) if (a->ValueType->isRealPointer())
{ {
auto pointedType = a->ValueType->toPointer()->PointedType; auto pointedType = a->ValueType->toPointer()->PointedType;
if (pointedType && pointedType->isDynArray()) if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator()))
{ {
a = new FxOutVarDereference(a, a->ScriptPosition); a = new FxOutVarDereference(a, a->ScriptPosition);
SAFE_RESOLVE(a, ctx); 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 && if (MethodName == NAME_GetParentClass &&
@ -8635,7 +8824,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
isresolved: isresolved:
bool error = false; 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) if (error)
{ {
delete this; delete this;
@ -9034,7 +9223,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
if (ArgList[i] && ArgList[i]->ValueType->isRealPointer()) if (ArgList[i] && ArgList[i]->ValueType->isRealPointer())
{ {
auto pointedType = ArgList[i]->ValueType->toPointer()->PointedType; 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); ArgList[i] = new FxOutVarDereference(ArgList[i], ArgList[i]->ScriptPosition);
SAFE_RESOLVE(ArgList[i], ctx); SAFE_RESOLVE(ArgList[i], ctx);
@ -11806,7 +11995,7 @@ FxExpression *FxOutVarDereference::Resolve(FCompileContext &ctx)
SelfType = Self->ValueType->toPointer()->PointedType; SelfType = Self->ValueType->toPointer()->PointedType;
ValueType = SelfType; 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"); ScriptPosition.Message(MSG_ERROR, "Cannot dereference pointer");
delete this; delete this;
@ -11835,7 +12024,7 @@ ExpEmit FxOutVarDereference::Emit(VMFunctionBuilder *build)
regType = REGT_POINTER; regType = REGT_POINTER;
loadOp = OP_LP; loadOp = OP_LP;
} }
else if (SelfType->isDynArray()) else if (SelfType->isDynArray() || SelfType->isMap() || SelfType->isMapIterator())
{ {
regType = REGT_POINTER; regType = REGT_POINTER;
loadOp = OP_MOVEA; loadOp = OP_MOVEA;

View file

@ -350,6 +350,8 @@ public:
bool IsArray() const { return ValueType->isArray() || (ValueType->isPointer() && ValueType->toPointer()->PointedType->isArray()); } 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 isStaticArray() const { return (ValueType->isPointer() && ValueType->toPointer()->PointedType->isStaticArray()); } // can only exist in pointer form.
bool IsDynamicArray() const { return (ValueType->isDynArray()); } 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 IsStruct() const { return ValueType->isStruct(); }
bool IsNativeStruct() const { return (ValueType->isStruct() && static_cast<PStruct*>(ValueType)->isNative); } 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 "vmintern.h"
#include "s_soundinternal.h" #include "s_soundinternal.h"
#include "types.h" #include "types.h"
#include "printf.h" #include "printf.h"
#include "textureid.h" #include "textureid.h"
#include "maps.h"
FTypeTable TypeTable; 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 // 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 // NewArray
@ -2202,12 +2226,89 @@ PDynArray *NewDynArray(PType *type)
// //
//========================================================================== //==========================================================================
PMap::PMap(PType *keytype, PType *valtype) enum OverrideFunctionRetType {
: KeyType(keytype), ValueType(valtype) 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()); mDescriptiveName.Format("Map<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName());
Size = sizeof(FMap); Size = sizeof(ZSFMap);
Align = alignof(FMap); 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; 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 // 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; size_t bucket;
PType *maptype = TypeTable.FindType(NAME_Map, (intptr_t)keytype, (intptr_t)valuetype, &bucket); PType *mapType = TypeTable.FindType(NAME_Map, (intptr_t)keyType, (intptr_t)valueType, &bucket);
if (maptype == nullptr) if (mapType == nullptr)
{ {
maptype = new PMap(keytype, valuetype); FString backingName = "Map_";
TypeTable.AddType(maptype, NAME_Map, (intptr_t)keytype, (intptr_t)valuetype, bucket); 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 ****************************************************************/ /* 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 // PStruct :: WriteValue

View file

@ -129,6 +129,7 @@ public:
virtual void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special=NULL); 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 SetPointer(void *base, unsigned offset, TArray<size_t> *ptrofs = NULL);
virtual void SetPointerArray(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) // Initialize the value, if needed (e.g. strings)
virtual void InitializeValue(void *addr, const void *def) const; virtual void InitializeValue(void *addr, const void *def) const;
@ -200,6 +201,8 @@ public:
bool isArray() const { return !!(Flags & TYPE_Array); } bool isArray() const { return !!(Flags & TYPE_Array); }
bool isStaticArray() const { return TypeTableType == NAME_StaticArray; } bool isStaticArray() const { return TypeTableType == NAME_StaticArray; }
bool isDynArray() const { return TypeTableType == NAME_DynArray; } 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 isStruct() const { return TypeTableType == NAME_Struct; }
bool isClass() const { return TypeTableType == NAME_Object; } bool isClass() const { return TypeTableType == NAME_Object; }
bool isPrototype() const { return TypeTableType == NAME_Prototype; } bool isPrototype() const { return TypeTableType == NAME_Prototype; }
@ -489,6 +492,7 @@ public:
void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special) override; void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special) override;
void SetPointer(void *base, unsigned offset, TArray<size_t> *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 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 class PStaticArray : public PArray
@ -521,14 +525,72 @@ public:
class PMap : public PCompoundType class PMap : public PCompoundType
{ {
void Construct(void * addr) const;
public: public:
PMap(PType *keytype, PType *valtype); PMap(PType *keytype, PType *valtype, PStruct *backing, int backing_class);
PType *KeyType; PType *KeyType;
PType *ValueType; 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 bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(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 class PStruct : public PContainerType
@ -550,6 +612,7 @@ public:
void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *specials) override; void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *specials) override;
void SetPointer(void *base, unsigned offset, TArray<size_t> *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 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 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. // Returns a type from the TypeTable. Will create one if it isn't present.
PMap *NewMap(PType *keytype, PType *valuetype); PMap *NewMap(PType *keytype, PType *valuetype);
PMapIterator *NewMapIterator(PType *keytype, PType *valuetype);
PArray *NewArray(PType *type, unsigned int count); PArray *NewArray(PType *type, unsigned int count);
PStaticArray *NewStaticArray(PType *type); PStaticArray *NewStaticArray(PType *type);
PDynArray *NewDynArray(PType *type); PDynArray *NewDynArray(PType *type);

View file

@ -502,6 +502,16 @@ static void PrintMapType(FLispString &out, const ZCC_TreeNode *node)
out.Close(); 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) static void PrintDynArrayType(FLispString &out, const ZCC_TreeNode *node)
{ {
ZCC_DynArrayType *tnode = (ZCC_DynArrayType *)node; ZCC_DynArrayType *tnode = (ZCC_DynArrayType *)node;
@ -955,6 +965,7 @@ static const NodePrinterFunc TreeNodePrinter[] =
PrintType, PrintType,
PrintBasicType, PrintBasicType,
PrintMapType, PrintMapType,
PrintMapIteratorType,
PrintDynArrayType, PrintDynArrayType,
PrintClassType, PrintClassType,
PrintExpression, 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 /* Type names can also be used as identifiers in contexts where type names
* are not normally allowed. */ * are not normally allowed. */
%fallback IDENTIFIER %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 */ /* Aggregate types */
%type aggregate_type {ZCC_Type *} %type aggregate_type {ZCC_Type *}
@ -944,7 +944,7 @@ type_name(X) ::= DOT dottable_id(A).
%type array_size{ZCC_Expression *} %type array_size{ZCC_Expression *}
%type array_size_expr{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); NEW_AST_NODE(MapType,map,T);
map->KeyType = A; 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; 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> */ aggregate_type(X) ::= ARRAY(T) LT type_or_array(A) GT. /* TArray<type> */
{ {
NEW_AST_NODE(DynArrayType,arr,T); 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: case AST_MapType:
if (allowarraytypes) {
if(AST.ParseVersion < MakeVersion(4, 10, 0))
{ {
Error(field, "%s: Map types not implemented yet", name.GetChars()); Error(field, "Map not accessible to ZScript version %d.%d.%d", AST.ParseVersion.major, AST.ParseVersion.minor, AST.ParseVersion.revision);
// 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));
break; 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: case AST_DynArrayType:
{ {
auto atype = static_cast<ZCC_DynArrayType *>(ztype); 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); auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
int flags = 0; 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. // Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly.
type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/); 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_Vector3, ZCC_VECTOR3, NAME_Vector3);
TOKENDEF2(TK_Name, ZCC_NAME, NAME_Name); TOKENDEF2(TK_Name, ZCC_NAME, NAME_Name);
TOKENDEF2(TK_Map, ZCC_MAP, NAME_Map); TOKENDEF2(TK_Map, ZCC_MAP, NAME_Map);
TOKENDEF2(TK_MapIterator, ZCC_MAPITERATOR,NAME_MapIterator);
TOKENDEF2(TK_Array, ZCC_ARRAY, NAME_Array); TOKENDEF2(TK_Array, ZCC_ARRAY, NAME_Array);
TOKENDEF2(TK_Include, ZCC_INCLUDE, NAME_Include); TOKENDEF2(TK_Include, ZCC_INCLUDE, NAME_Include);
TOKENDEF (TK_Void, ZCC_VOID); TOKENDEF (TK_Void, ZCC_VOID);
@ -896,6 +897,19 @@ ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool c
break; 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: case AST_DynArrayType:
{ {
TreeNodeDeepCopy_Start(DynArrayType); TreeNodeDeepCopy_Start(DynArrayType);

View file

@ -98,6 +98,7 @@ enum EZCCTreeNodeType
AST_Type, AST_Type,
AST_BasicType, AST_BasicType,
AST_MapType, AST_MapType,
AST_MapIteratorType,
AST_DynArrayType, AST_DynArrayType,
AST_ClassType, AST_ClassType,
AST_Expression, AST_Expression,
@ -367,6 +368,12 @@ struct ZCC_MapType : ZCC_Type
ZCC_Type *ValueType; ZCC_Type *ValueType;
}; };
struct ZCC_MapIteratorType : ZCC_Type
{
ZCC_Type *KeyType;
ZCC_Type *ValueType;
};
struct ZCC_DynArrayType : ZCC_Type struct ZCC_DynArrayType : ZCC_Type
{ {
ZCC_Type *ElementType; ZCC_Type *ElementType;

View file

@ -36,6 +36,7 @@
bool gameisdead; bool gameisdead;
#ifdef _WIN32 #ifdef _WIN32
#include <cstdarg>
#include <windows.h> #include <windows.h>
#include "zstring.h" #include "zstring.h"
void I_DebugPrint(const char *cp) void I_DebugPrint(const char *cp)
@ -46,10 +47,31 @@ void I_DebugPrint(const char *cp)
OutputDebugStringW(wstr.c_str()); 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 #else
void I_DebugPrint(const char *cp) void I_DebugPrint(const char *cp)
{ {
} }
void I_DebugPrintf(const char *fmt,...)
{
}
#endif #endif
#include "engineerrors.h" #include "engineerrors.h"

View file

@ -916,6 +916,9 @@ public:
typedef struct { const KT Key; VT Value; } Pair; typedef struct { const KT Key; VT Value; } Pair;
typedef const Pair ConstPair; typedef const Pair ConstPair;
typedef KT KeyType;
typedef VT ValueType;
TMap() { NumUsed = 0; SetNodeVector(1); } TMap() { NumUsed = 0; SetNodeVector(1); }
TMap(hash_t size) { NumUsed = 0; SetNodeVector(size); } TMap(hash_t size) { NumUsed = 0; SetNodeVector(size); }
~TMap() { ClearNodeVector(); } ~TMap() { ClearNodeVector(); }

View file

@ -3,6 +3,7 @@ version "4.10"
// Generic engine code // Generic engine code
#include "zscript/engine/base.zs" #include "zscript/engine/base.zs"
#include "zscript/engine/dynarrays.zs" #include "zscript/engine/dynarrays.zs"
#include "zscript/engine/maps.zs"
#include "zscript/engine/dictionary.zs" #include "zscript/engine/dictionary.zs"
#include "zscript/engine/inputevents.zs" #include "zscript/engine/inputevents.zs"
#include "zscript/engine/service.zs" #include "zscript/engine/service.zs"

View file

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