mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-24 13:01:47 +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)
|
||||
: ElementType(etype)
|
||||
PDynArray::PDynArray(PType *etype,PStruct *backing)
|
||||
: ElementType(etype), BackingType(backing)
|
||||
{
|
||||
mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName());
|
||||
Size = sizeof(FArray);
|
||||
|
@ -2061,7 +2061,33 @@ PDynArray *NewDynArray(PType *type)
|
|||
PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket);
|
||||
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);
|
||||
}
|
||||
return (PDynArray *)atype;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#endif
|
||||
|
||||
typedef std::pair<const class PType *, unsigned> FTypeAndOffset;
|
||||
class PStruct;
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
|
@ -649,9 +650,10 @@ class PDynArray : public PCompoundType
|
|||
DECLARE_CLASS(PDynArray, PCompoundType);
|
||||
HAS_OBJECT_POINTERS;
|
||||
public:
|
||||
PDynArray(PType *etype);
|
||||
PDynArray(PType *etype, PStruct *backing);
|
||||
|
||||
PType *ElementType;
|
||||
PStruct *BackingType;
|
||||
|
||||
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
|
||||
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
|
||||
|
|
|
@ -821,6 +821,8 @@ xx(DamageFunction)
|
|||
xx(Length)
|
||||
xx(Unit)
|
||||
xx(Size)
|
||||
xx(Copy)
|
||||
xx(Move)
|
||||
xx(Voidptr)
|
||||
xx(StateLabel)
|
||||
xx(SpriteID)
|
||||
|
|
|
@ -6946,20 +6946,33 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
PArray *arraytype = dyn_cast<PArray>(Array->ValueType);
|
||||
if (arraytype == nullptr)
|
||||
PArray *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.
|
||||
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);
|
||||
PDynArray *darraytype = static_cast<PDynArray*>(Array->ValueType);
|
||||
elementtype = darraytype->ElementType;
|
||||
Array->ValueType = NewPointer(NewResizableArray(elementtype)); // change type so that this can use the code for resizable arrays unchanged.
|
||||
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())
|
||||
{
|
||||
|
@ -6967,12 +6980,17 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
|
|||
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
|
||||
{
|
||||
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
|
||||
SizeAddr = parentfield->Offset + parentfield->Type->Align;
|
||||
SizeAddr = parentfield->Offset + sizeof(void*);
|
||||
}
|
||||
else if (Array->ExprType == EFX_GlobalVariable)
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@ -6981,54 +6999,52 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
|
|||
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();
|
||||
if (indexval >= arraytype->ElementCount && !Array->IsResizableArray())
|
||||
if (indexval >= arraytype->ElementCount)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Array index out of bounds");
|
||||
delete this;
|
||||
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.
|
||||
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
|
||||
{
|
||||
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
|
||||
// PFields are garbage collected so this will be automatically taken care of later.
|
||||
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
||||
static_cast<FxStructMember *>(Array)->membervar = newfield;
|
||||
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
||||
auto x = Array->Resolve(ctx);
|
||||
Array = nullptr;
|
||||
return x;
|
||||
}
|
||||
else if (Array->ExprType == EFX_GlobalVariable)
|
||||
{
|
||||
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
|
||||
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
||||
static_cast<FxGlobalVariable *>(Array)->membervar = newfield;
|
||||
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
||||
auto x = Array->Resolve(ctx);
|
||||
Array = nullptr;
|
||||
return x;
|
||||
}
|
||||
else if (Array->ExprType == EFX_StackVariable)
|
||||
{
|
||||
auto parentfield = static_cast<FxStackVariable *>(Array)->membervar;
|
||||
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
||||
static_cast<FxStackVariable *>(Array)->ReplaceField(newfield);
|
||||
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
||||
auto x = Array->Resolve(ctx);
|
||||
Array = nullptr;
|
||||
return x;
|
||||
}
|
||||
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
|
||||
// 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);
|
||||
static_cast<FxStructMember *>(Array)->membervar = newfield;
|
||||
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
||||
auto x = Array->Resolve(ctx);
|
||||
Array = nullptr;
|
||||
return x;
|
||||
}
|
||||
else if (Array->ExprType == EFX_GlobalVariable)
|
||||
{
|
||||
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
|
||||
auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
||||
static_cast<FxGlobalVariable *>(Array)->membervar = newfield;
|
||||
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
||||
auto x = Array->Resolve(ctx);
|
||||
Array = nullptr;
|
||||
return x;
|
||||
}
|
||||
else if (Array->ExprType == EFX_StackVariable)
|
||||
{
|
||||
auto parentfield = static_cast<FxStackVariable *>(Array)->membervar;
|
||||
auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
|
||||
static_cast<FxStackVariable *>(Array)->ReplaceField(newfield);
|
||||
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
||||
auto x = Array->Resolve(ctx);
|
||||
Array = nullptr;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
ValueType = arraytype->ElementType;
|
||||
ValueType = elementtype;
|
||||
if (!Array->RequestAddress(ctx, &AddressWritable))
|
||||
{
|
||||
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.
|
||||
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())
|
||||
{
|
||||
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 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 IsDynamicArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PDynArray))); }
|
||||
|
||||
virtual ExpEmit Emit(VMFunctionBuilder *build);
|
||||
void EmitStatement(VMFunctionBuilder *build);
|
||||
|
|
|
@ -1358,9 +1358,16 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
|
|||
case AST_DynArrayType:
|
||||
if (allowarraytypes)
|
||||
{
|
||||
Error(field, "%s: Dynamic array types not implemented yet", name.GetChars());
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue