mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-23 20:32:51 +00:00
- major work on dynamic array support. Mostly working, some issues still exist with Move and Copy methods and with assignments in stack variables.
This commit is contained in:
parent
12477216ef
commit
ca48a687f8
6 changed files with 175 additions and 55 deletions
|
@ -2012,8 +2012,8 @@ PDynArray::PDynArray()
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
PDynArray::PDynArray(PType *etype)
|
PDynArray::PDynArray(PType *etype,PStruct *backing)
|
||||||
: ElementType(etype)
|
: ElementType(etype), BackingType(backing)
|
||||||
{
|
{
|
||||||
mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName());
|
mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName());
|
||||||
Size = sizeof(FArray);
|
Size = sizeof(FArray);
|
||||||
|
@ -2061,7 +2061,33 @@ PDynArray *NewDynArray(PType *type)
|
||||||
PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket);
|
PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket);
|
||||||
if (atype == NULL)
|
if (atype == NULL)
|
||||||
{
|
{
|
||||||
atype = new PDynArray(type);
|
FString backingname;
|
||||||
|
|
||||||
|
switch (type->GetRegType())
|
||||||
|
{
|
||||||
|
case REGT_INT:
|
||||||
|
backingname.Format("DynArray_I%d", type->Size * 8);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REGT_FLOAT:
|
||||||
|
backingname.Format("DynArray_F%d", type->Size * 8);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REGT_STRING:
|
||||||
|
backingname = "DynArray_String";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REGT_POINTER:
|
||||||
|
backingname = "DynArray_Ptr";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
I_Error("Unsupported dynamic array requested");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto backing = NewNativeStruct(backingname, nullptr);
|
||||||
|
atype = new PDynArray(type, backing);
|
||||||
TypeTable.AddType(atype, RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, bucket);
|
TypeTable.AddType(atype, RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, bucket);
|
||||||
}
|
}
|
||||||
return (PDynArray *)atype;
|
return (PDynArray *)atype;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef std::pair<const class PType *, unsigned> FTypeAndOffset;
|
typedef std::pair<const class PType *, unsigned> FTypeAndOffset;
|
||||||
|
class PStruct;
|
||||||
|
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
|
||||||
|
@ -649,9 +650,10 @@ class PDynArray : public PCompoundType
|
||||||
DECLARE_CLASS(PDynArray, PCompoundType);
|
DECLARE_CLASS(PDynArray, PCompoundType);
|
||||||
HAS_OBJECT_POINTERS;
|
HAS_OBJECT_POINTERS;
|
||||||
public:
|
public:
|
||||||
PDynArray(PType *etype);
|
PDynArray(PType *etype, PStruct *backing);
|
||||||
|
|
||||||
PType *ElementType;
|
PType *ElementType;
|
||||||
|
PStruct *BackingType;
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -821,6 +821,8 @@ xx(DamageFunction)
|
||||||
xx(Length)
|
xx(Length)
|
||||||
xx(Unit)
|
xx(Unit)
|
||||||
xx(Size)
|
xx(Size)
|
||||||
|
xx(Copy)
|
||||||
|
xx(Move)
|
||||||
xx(Voidptr)
|
xx(Voidptr)
|
||||||
xx(StateLabel)
|
xx(StateLabel)
|
||||||
xx(SpriteID)
|
xx(SpriteID)
|
||||||
|
|
|
@ -6946,20 +6946,33 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
PArray *arraytype = dyn_cast<PArray>(Array->ValueType);
|
PArray *arraytype = nullptr;
|
||||||
if (arraytype == nullptr)
|
PType *elementtype = nullptr;
|
||||||
|
if (Array->IsDynamicArray())
|
||||||
{
|
{
|
||||||
// Check if we got a pointer to an array. Some native data structures (like the line list in sectors) use this.
|
PDynArray *darraytype = static_cast<PDynArray*>(Array->ValueType);
|
||||||
PPointer *ptype = dyn_cast<PPointer>(Array->ValueType);
|
elementtype = darraytype->ElementType;
|
||||||
if (ptype == nullptr || !ptype->PointedType->IsKindOf(RUNTIME_CLASS(PArray)))
|
Array->ValueType = NewPointer(NewResizableArray(elementtype)); // change type so that this can use the code for resizable arrays unchanged.
|
||||||
{
|
|
||||||
ScriptPosition.Message(MSG_ERROR, "'[]' can only be used with arrays.");
|
|
||||||
delete this;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
arraytype = static_cast<PArray*>(ptype->PointedType);
|
|
||||||
arrayispointer = true;
|
arrayispointer = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arraytype = dyn_cast<PArray>(Array->ValueType);
|
||||||
|
if (arraytype == nullptr)
|
||||||
|
{
|
||||||
|
// Check if we got a pointer to an array. Some native data structures (like the line list in sectors) use this.
|
||||||
|
PPointer *ptype = dyn_cast<PPointer>(Array->ValueType);
|
||||||
|
if (ptype == nullptr || !ptype->PointedType->IsKindOf(RUNTIME_CLASS(PArray)))
|
||||||
|
{
|
||||||
|
ScriptPosition.Message(MSG_ERROR, "'[]' can only be used with arrays.");
|
||||||
|
delete this;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
arraytype = static_cast<PArray*>(ptype->PointedType);
|
||||||
|
arrayispointer = true;
|
||||||
|
}
|
||||||
|
elementtype = arraytype->ElementType;
|
||||||
|
}
|
||||||
|
|
||||||
if (Array->IsResizableArray())
|
if (Array->IsResizableArray())
|
||||||
{
|
{
|
||||||
|
@ -6967,12 +6980,17 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
|
||||||
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
|
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
|
||||||
{
|
{
|
||||||
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
|
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
|
||||||
SizeAddr = parentfield->Offset + parentfield->Type->Align;
|
SizeAddr = parentfield->Offset + sizeof(void*);
|
||||||
}
|
}
|
||||||
else if (Array->ExprType == EFX_GlobalVariable)
|
else if (Array->ExprType == EFX_GlobalVariable)
|
||||||
{
|
{
|
||||||
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
|
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
|
||||||
SizeAddr = parentfield->Offset + parentfield->Type->Align;
|
SizeAddr = parentfield->Offset + sizeof(void*);
|
||||||
|
}
|
||||||
|
else if (Array->ExprType == EFX_StackVariable)
|
||||||
|
{
|
||||||
|
auto parentfield = static_cast<FxStackVariable *>(Array)->membervar;
|
||||||
|
SizeAddr = parentfield->Offset + sizeof(void*);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -6981,54 +6999,52 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (index->isConstant())
|
// constant indices can only be resolved at compile time for statically sized arrays.
|
||||||
|
else if (index->isConstant() && arraytype != nullptr && !arrayispointer)
|
||||||
{
|
{
|
||||||
unsigned indexval = static_cast<FxConstant *>(index)->GetValue().GetInt();
|
unsigned indexval = static_cast<FxConstant *>(index)->GetValue().GetInt();
|
||||||
if (indexval >= arraytype->ElementCount && !Array->IsResizableArray())
|
if (indexval >= arraytype->ElementCount)
|
||||||
{
|
{
|
||||||
ScriptPosition.Message(MSG_ERROR, "Array index out of bounds");
|
ScriptPosition.Message(MSG_ERROR, "Array index out of bounds");
|
||||||
delete this;
|
delete this;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!arrayispointer)
|
// if this is an array within a class or another struct we can simplify the expression by creating a new PField with a cumulative offset.
|
||||||
|
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
|
||||||
{
|
{
|
||||||
// if this is an array within a class or another struct we can simplify the expression by creating a new PField with a cumulative offset.
|
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
|
||||||
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
|
// PFields are garbage collected so this will be automatically taken care of later.
|
||||||
{
|
auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
||||||
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
|
static_cast<FxStructMember *>(Array)->membervar = newfield;
|
||||||
// PFields are garbage collected so this will be automatically taken care of later.
|
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
||||||
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
auto x = Array->Resolve(ctx);
|
||||||
static_cast<FxStructMember *>(Array)->membervar = newfield;
|
Array = nullptr;
|
||||||
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
return x;
|
||||||
auto x = Array->Resolve(ctx);
|
}
|
||||||
Array = nullptr;
|
else if (Array->ExprType == EFX_GlobalVariable)
|
||||||
return x;
|
{
|
||||||
}
|
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
|
||||||
else if (Array->ExprType == EFX_GlobalVariable)
|
auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
||||||
{
|
static_cast<FxGlobalVariable *>(Array)->membervar = newfield;
|
||||||
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
|
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
||||||
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
auto x = Array->Resolve(ctx);
|
||||||
static_cast<FxGlobalVariable *>(Array)->membervar = newfield;
|
Array = nullptr;
|
||||||
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
return x;
|
||||||
auto x = Array->Resolve(ctx);
|
}
|
||||||
Array = nullptr;
|
else if (Array->ExprType == EFX_StackVariable)
|
||||||
return x;
|
{
|
||||||
}
|
auto parentfield = static_cast<FxStackVariable *>(Array)->membervar;
|
||||||
else if (Array->ExprType == EFX_StackVariable)
|
auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
||||||
{
|
static_cast<FxStackVariable *>(Array)->ReplaceField(newfield);
|
||||||
auto parentfield = static_cast<FxStackVariable *>(Array)->membervar;
|
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
||||||
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
auto x = Array->Resolve(ctx);
|
||||||
static_cast<FxStackVariable *>(Array)->ReplaceField(newfield);
|
Array = nullptr;
|
||||||
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
return x;
|
||||||
auto x = Array->Resolve(ctx);
|
|
||||||
Array = nullptr;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueType = arraytype->ElementType;
|
ValueType = elementtype;
|
||||||
if (!Array->RequestAddress(ctx, &AddressWritable))
|
if (!Array->RequestAddress(ctx, &AddressWritable))
|
||||||
{
|
{
|
||||||
ScriptPosition.Message(MSG_ERROR, "Unable to dereference array.");
|
ScriptPosition.Message(MSG_ERROR, "Unable to dereference array.");
|
||||||
|
@ -7804,6 +7820,72 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
||||||
// same for String methods. It also uses a hidden struct type to define them.
|
// same for String methods. It also uses a hidden struct type to define them.
|
||||||
Self->ValueType = TypeStringStruct;
|
Self->ValueType = TypeStringStruct;
|
||||||
}
|
}
|
||||||
|
else if (Self->IsDynamicArray())
|
||||||
|
{
|
||||||
|
if (MethodName == NAME_Size)
|
||||||
|
{
|
||||||
|
FxExpression *x = new FxMemberIdentifier(Self, NAME_Size, ScriptPosition); // todo: obfuscate the name to prevent direct access.
|
||||||
|
Self = nullptr;
|
||||||
|
delete this;
|
||||||
|
return x->Resolve(ctx);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto elementType = static_cast<PDynArray*>(Self->ValueType)->ElementType;
|
||||||
|
Self->ValueType = static_cast<PDynArray*>(Self->ValueType)->BackingType;
|
||||||
|
// this requires some added type checks for the passed types.
|
||||||
|
for (auto &a : ArgList)
|
||||||
|
{
|
||||||
|
a = a->Resolve(ctx);
|
||||||
|
if (a == nullptr)
|
||||||
|
{
|
||||||
|
delete this;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (a->IsDynamicArray())
|
||||||
|
{
|
||||||
|
// Copy and Move must turn their parameter into a pointer to the backing struct type.
|
||||||
|
auto backingtype = static_cast<PDynArray*>(a->ValueType)->BackingType;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
auto member = static_cast<FxStructMember*>(a);
|
||||||
|
auto newfield = new PField(NAME_None, backingtype, 0, member->membervar->Offset);
|
||||||
|
member->membervar = newfield;
|
||||||
|
Self = nullptr;
|
||||||
|
delete this;
|
||||||
|
member->ValueType = TypeUInt32;
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
else if (Self->ExprType == EFX_StackVariable)
|
||||||
|
{
|
||||||
|
auto member = static_cast<FxStackVariable*>(Self);
|
||||||
|
auto newfield = new PField(NAME_None, backingtype, 0, member->membervar->Offset);
|
||||||
|
member->membervar = newfield;
|
||||||
|
Self = nullptr;
|
||||||
|
delete this;
|
||||||
|
member->ValueType = TypeUInt32;
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (a->IsPointer() && Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)))
|
||||||
|
{
|
||||||
|
// 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 != elementType)
|
||||||
|
{
|
||||||
|
ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument. Got %s, expected %s", a->ValueType->DescriptiveName(), elementType->DescriptiveName());
|
||||||
|
delete this;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (Self->IsArray())
|
else if (Self->IsArray())
|
||||||
{
|
{
|
||||||
if (MethodName == NAME_Size)
|
if (MethodName == NAME_Size)
|
||||||
|
|
|
@ -333,6 +333,7 @@ public:
|
||||||
bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); }
|
bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); }
|
||||||
bool IsArray() const { return ValueType->IsKindOf(RUNTIME_CLASS(PArray)) || (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PArray))); }
|
bool IsArray() const { return ValueType->IsKindOf(RUNTIME_CLASS(PArray)) || (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PArray))); }
|
||||||
bool IsResizableArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PResizableArray))); } // can only exist in pointer form.
|
bool IsResizableArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PResizableArray))); } // can only exist in pointer form.
|
||||||
|
bool IsDynamicArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PDynArray))); }
|
||||||
|
|
||||||
virtual ExpEmit Emit(VMFunctionBuilder *build);
|
virtual ExpEmit Emit(VMFunctionBuilder *build);
|
||||||
void EmitStatement(VMFunctionBuilder *build);
|
void EmitStatement(VMFunctionBuilder *build);
|
||||||
|
|
|
@ -1358,9 +1358,16 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
|
||||||
case AST_DynArrayType:
|
case AST_DynArrayType:
|
||||||
if (allowarraytypes)
|
if (allowarraytypes)
|
||||||
{
|
{
|
||||||
Error(field, "%s: Dynamic array types not implemented yet", name.GetChars());
|
|
||||||
auto atype = static_cast<ZCC_DynArrayType *>(ztype);
|
auto atype = static_cast<ZCC_DynArrayType *>(ztype);
|
||||||
retval = NewDynArray(DetermineType(outertype, field, name, atype->ElementType, false, false));
|
auto ftype = DetermineType(outertype, field, name, atype->ElementType, false, true);
|
||||||
|
if (ftype->GetRegType() == REGT_NIL || ftype->GetRegCount() > 1)
|
||||||
|
{
|
||||||
|
Error(field, "%s: Base type for dynamic array types nust be integral, but got %s", name.GetChars(), ftype->DescriptiveName());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retval = NewDynArray(ftype);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue