Using separate keyword 'nonew' as reverse abstract; nonew is inherited, and nonew class can only be created from the first nonew class in the hierarchy

This commit is contained in:
ZZYZX 2017-02-16 12:40:09 +02:00
parent 0803faf596
commit 5e5d0d3e57
9 changed files with 40 additions and 7 deletions

View File

@ -205,7 +205,8 @@ enum EObjectFlags
OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk)
OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning)
OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function
OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function
OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function at all
OF_NoNew = 1 << 15, // Marks a class that can only be created with new() in the exact class that has this keyword
};
template<class T> class TObjPtr;

View File

@ -170,6 +170,7 @@ std2:
'virtual' { RET(TK_Virtual); }
'override' { RET(TK_Override); }
'vararg' { RET(TK_VarArg); }
'nonew' { RET(TK_NoNew); }
'super' { RET(TK_Super); }
'global' { RET(TK_Global); }
'stop' { RET(TK_Stop); }

View File

@ -112,6 +112,7 @@ xx(TK_Optional, "'optional'")
xx(TK_Export, "'expert'")
xx(TK_Virtual, "'virtual'")
xx(TK_VarArg, "'vararg'")
xx(TK_NoNew, "'nonew'")
xx(TK_Override, "'override'")
xx(TK_Super, "'super'")
xx(TK_Null, "'null'")

View File

@ -5058,13 +5058,26 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx)
if (val->isConstant())
{
auto cls = static_cast<PClass *>(static_cast<FxConstant*>(val)->GetValue().GetPointer());
if ((cls->ObjectFlags & OF_Abstract) && cls != ctx.Class)
if (cls->ObjectFlags & OF_Abstract)
{
ScriptPosition.Message(MSG_ERROR, "Cannot instantiate abstract class %s outside of that class", cls->TypeName.GetChars());
ScriptPosition.Message(MSG_ERROR, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
delete this;
return nullptr;
}
if (cls->ObjectFlags & OF_NoNew)
{
PClass* pcls = cls;
while (pcls && pcls->ParentClass && (pcls->ParentClass->ObjectFlags & OF_NoNew))
pcls = pcls->ParentClass;
if (pcls != ctx.Class)
{
ScriptPosition.Message(MSG_ERROR, "Cannot instantiate class %s directly", cls->TypeName.GetChars());
delete this;
return nullptr;
}
}
ValueType = NewPointer(cls);
}

View File

@ -788,7 +788,19 @@ begin:
b = B;
PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[b] : konsta[b].v);
PFunction *callingfunc = (PFunction*)konsta[C].o; // [ZZ] due to how this is set, it's always const
if ((cls->ObjectFlags & OF_Abstract) && (!callingfunc || callingfunc->OwningClass != cls)) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s outside of that class", cls->TypeName.GetChars());
if (cls->ObjectFlags & OF_Abstract) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
if (cls->ObjectFlags & OF_NoNew)
{
// trace to the first nonew class in the hierarchy.
// compare that class to the context class.
// if not matching, disallow creation.
// this ensures that only the root class can have a static factory method that can also create instances of subclasses via new().
PClass* pcls = cls;
while (pcls && pcls->ParentClass && (pcls->ParentClass->ObjectFlags & OF_NoNew))
pcls = pcls->ParentClass;
if (!callingfunc || pcls != callingfunc->OwningClass)
ThrowAbortException(X_OTHER, "Cannot instantiate class %s directly", cls->TypeName.GetChars());
}
reg.a[a] = cls->CreateNew();
reg.atag[a] = ATAG_OBJECT;
NEXTOP;

View File

@ -212,6 +212,7 @@ class_ancestry(X) ::= COLON dottable_id(A). { X = A; /*X-overwrites-A*/ }
%type class_flags{ClassFlagsBlock}
class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; }
class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; }
class_flags(X) ::= class_flags(A) NONEW. { X.Flags = A.Flags | ZCC_NoNew; X.Replaces = A.Replaces; }
class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; }
class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; }

View File

@ -599,9 +599,11 @@ void ZCCCompiler::CreateClassTypes()
}
}
if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName());
// [ZZ] if parent class is abstract, this one should be abstract as well - otherwise we can subclass Actor and be able to new() our subclass
if ((c->cls->Flags & ZCC_Abstract) || (parent && parent->ObjectFlags & OF_Abstract))
if (c->cls->Flags & ZCC_Abstract)
c->Type()->ObjectFlags |= OF_Abstract;
// [ZZ] inherit nonew keyword
if (c->cls->Flags & ZCC_NoNew || (parent && parent->ObjectFlags & OF_NoNew))
c->Type()->ObjectFlags |= OF_NoNew;
c->Type()->bExported = true; // this class is accessible to script side type casts. (The reason for this flag is that types like PInt need to be skipped.)
c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type());
OutNamespace->Symbols.AddSymbol(c->cls->Symbol);

View File

@ -137,6 +137,7 @@ static void InitTokenMap()
TOKENDEF (TK_Latent, ZCC_LATENT);
TOKENDEF (TK_Virtual, ZCC_VIRTUAL);
TOKENDEF (TK_VarArg, ZCC_VARARG);
TOKENDEF (TK_NoNew, ZCC_NONEW);
TOKENDEF (TK_Override, ZCC_OVERRIDE);
TOKENDEF (TK_Final, ZCC_FINAL);
TOKENDEF (TK_Meta, ZCC_META);

View File

@ -36,7 +36,8 @@ enum
ZCC_Virtual = 1 << 13,
ZCC_Override = 1 << 14,
ZCC_Transient = 1 << 15,
ZCC_VarArg = 1 << 16
ZCC_VarArg = 1 << 16,
ZCC_NoNew = 1 << 17,
};
// Function parameter modifiers