mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-23 04:22:34 +00:00
- implemented version checks for compile-time type determination and for class inheritance.
Note that in this system, DECORATE counts as version 2.0, so any data that should be available to DECORATE should not use version restriction.
This commit is contained in:
parent
7df698dad8
commit
456ac64723
19 changed files with 220 additions and 125 deletions
|
@ -3146,6 +3146,9 @@ void PClass::Derive(PClass *newclass, FName name)
|
|||
newclass->Symbols.SetParentTable(&this->Symbols);
|
||||
newclass->TypeName = name;
|
||||
newclass->mDescriptiveName.Format("Class<%s>", name.GetChars());
|
||||
newclass->mVersion = mVersion;
|
||||
newclass->MetaSize = MetaSize;
|
||||
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -3292,7 +3295,6 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
|
|||
type->bRuntimeClass = true;
|
||||
Derive(type, name);
|
||||
type->Size = size;
|
||||
type->MetaSize = MetaSize;
|
||||
if (size != TentativeClass)
|
||||
{
|
||||
type->InitializeDefaults();
|
||||
|
|
|
@ -91,6 +91,7 @@ public:
|
|||
PSymbolTable Symbols;
|
||||
bool MemberOnly = false; // type may only be used as a struct/class member but not as a local variable or function argument.
|
||||
FString mDescriptiveName;
|
||||
VersionInfo mVersion = { 0,0,0 };
|
||||
BYTE loadOp, storeOp, moveOp, RegType, RegCount;
|
||||
|
||||
PType(unsigned int size = 1, unsigned int align = 1);
|
||||
|
|
|
@ -193,6 +193,38 @@ public:
|
|||
};
|
||||
|
||||
|
||||
struct VersionInfo
|
||||
{
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint32_t revision;
|
||||
|
||||
bool operator <=(const VersionInfo &o) const
|
||||
{
|
||||
return o.major > this->major || (o.major == this->major && o.minor > this->minor) || (o.major == this->major && o.minor == this->minor && o.revision >= this->revision);
|
||||
}
|
||||
bool operator >=(const VersionInfo &o) const
|
||||
{
|
||||
return o.major < this->major || (o.major == this->major && o.minor < this->minor) || (o.major == this->major && o.minor == this->minor && o.revision <= this->revision);
|
||||
}
|
||||
bool operator > (const VersionInfo &o) const
|
||||
{
|
||||
return o.major < this->major || (o.major == this->major && o.minor < this->minor) || (o.major == this->major && o.minor == this->minor && o.revision < this->revision);
|
||||
}
|
||||
bool operator < (const VersionInfo &o) const
|
||||
{
|
||||
return o.major > this->major || (o.major == this->major && o.minor > this->minor) || (o.major == this->major && o.minor == this->minor && o.revision > this->revision);
|
||||
}
|
||||
void operator=(const char *string);
|
||||
};
|
||||
|
||||
// Cannot be a constructor because Lemon would puke on it.
|
||||
inline VersionInfo MakeVersion(unsigned int ma, unsigned int mi, unsigned int re = 0)
|
||||
{
|
||||
return{ (uint16_t)ma, (uint16_t)mi, (uint32_t)re };
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Screenshot buffer image data types
|
||||
enum ESSType
|
||||
|
|
19
src/sc_man.h
19
src/sc_man.h
|
@ -1,23 +1,6 @@
|
|||
#ifndef __SC_MAN_H__
|
||||
#define __SC_MAN_H__
|
||||
|
||||
struct VersionInfo
|
||||
{
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint32_t revision;
|
||||
|
||||
bool Check(unsigned int major, unsigned int minor)
|
||||
{
|
||||
return major < this->major || (major == this->major && minor <= this->minor);
|
||||
}
|
||||
bool Check(unsigned int major, unsigned int minor, unsigned int revision)
|
||||
{
|
||||
return major < this->major || (major == this->major && minor < this->minor) || (major == this->major && minor == this->minor && revision <= this->revision);
|
||||
}
|
||||
void operator=(const char *string);
|
||||
};
|
||||
|
||||
class FScanner
|
||||
{
|
||||
public:
|
||||
|
@ -123,7 +106,7 @@ protected:
|
|||
BYTE StateMode;
|
||||
bool StateOptions;
|
||||
bool Escape;
|
||||
VersionInfo ParseVersion = { 0, 0 }; // no ZScript extensions by default
|
||||
VersionInfo ParseVersion = { 0, 0, 0 }; // no ZScript extensions by default
|
||||
};
|
||||
|
||||
enum
|
||||
|
|
|
@ -146,35 +146,35 @@ std2:
|
|||
'property' { RET(TK_Property); }
|
||||
'native' { RET(TK_Native); }
|
||||
'var' { RET(TK_Var); }
|
||||
'out' { RET(ParseVersion.Check(1,0)? TK_Out : TK_Identifier); }
|
||||
'out' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Out : TK_Identifier); }
|
||||
'static' { RET(TK_Static); }
|
||||
'transient' { RET(ParseVersion.Check(1,0)? TK_Transient : TK_Identifier); }
|
||||
'final' { RET(ParseVersion.Check(1,0)? TK_Final : TK_Identifier); }
|
||||
'extend' { RET(ParseVersion.Check(1,0)? TK_Extend : TK_Identifier); }
|
||||
'protected' { RET(ParseVersion.Check(1,0)? TK_Protected : TK_Identifier); }
|
||||
'private' { RET(ParseVersion.Check(1,0)? TK_Private : TK_Identifier); }
|
||||
'transient' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Transient : TK_Identifier); }
|
||||
'final' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Final : TK_Identifier); }
|
||||
'extend' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Extend : TK_Identifier); }
|
||||
'protected' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Protected : TK_Identifier); }
|
||||
'private' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Private : TK_Identifier); }
|
||||
'dot' { RET(TK_Dot); }
|
||||
'cross' { RET(TK_Cross); }
|
||||
'virtual' { RET(ParseVersion.Check(1,0)? TK_Virtual : TK_Identifier); }
|
||||
'override' { RET(ParseVersion.Check(1,0)? TK_Override : TK_Identifier); }
|
||||
'vararg' { RET(ParseVersion.Check(1,0)? TK_VarArg : TK_Identifier); }
|
||||
'ui' { RET(ParseVersion.Check(2,4)? TK_UI : TK_Identifier); }
|
||||
'play' { RET(ParseVersion.Check(2,4)? TK_Play : TK_Identifier); }
|
||||
'clearscope' { RET(ParseVersion.Check(2,4)? TK_ClearScope : TK_Identifier); }
|
||||
'virtualscope' { RET(ParseVersion.Check(2,4)? TK_VirtualScope : TK_Identifier); }
|
||||
'super' { RET(ParseVersion.Check(1,0)? TK_Super : TK_Identifier); }
|
||||
'virtual' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Virtual : TK_Identifier); }
|
||||
'override' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Override : TK_Identifier); }
|
||||
'vararg' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_VarArg : TK_Identifier); }
|
||||
'ui' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_UI : TK_Identifier); }
|
||||
'play' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_Play : TK_Identifier); }
|
||||
'clearscope' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_ClearScope : TK_Identifier); }
|
||||
'virtualscope' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_VirtualScope : TK_Identifier); }
|
||||
'super' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Super : TK_Identifier); }
|
||||
'stop' { RET(TK_Stop); }
|
||||
'null' { RET(TK_Null); }
|
||||
|
||||
'is' { RET(ParseVersion.Check(1,0)? TK_Is : TK_Identifier); }
|
||||
'replaces' { RET(ParseVersion.Check(1,0)? TK_Replaces : TK_Identifier); }
|
||||
'is' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Is : TK_Identifier); }
|
||||
'replaces' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Replaces : TK_Identifier); }
|
||||
'states' { RET(TK_States); }
|
||||
'meta' { RET(ParseVersion.Check(1,0)? TK_Meta : TK_Identifier); }
|
||||
'deprecated' { RET(ParseVersion.Check(1,0)? TK_Deprecated : TK_Identifier); }
|
||||
'version' { RET(ParseVersion.Check(2,4)? TK_Version : TK_Identifier); }
|
||||
'action' { RET(ParseVersion.Check(1,0)? TK_Action : TK_Identifier); }
|
||||
'readonly' { RET(ParseVersion.Check(1,0)? TK_ReadOnly : TK_Identifier); }
|
||||
'let' { RET(ParseVersion.Check(1,0)? TK_Let : TK_Identifier); }
|
||||
'meta' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Meta : TK_Identifier); }
|
||||
'deprecated' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Deprecated : TK_Identifier); }
|
||||
'version' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_Version : TK_Identifier); }
|
||||
'action' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Action : TK_Identifier); }
|
||||
'readonly' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_ReadOnly : TK_Identifier); }
|
||||
'let' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Let : TK_Identifier); }
|
||||
|
||||
/* Actor state options */
|
||||
'bright' { RET(StateOptions ? TK_Bright : TK_Identifier); }
|
||||
|
|
|
@ -802,7 +802,7 @@ void VMFunctionBuilder::BackpatchListToHere(TArray<size_t> &locs)
|
|||
//==========================================================================
|
||||
FFunctionBuildList FunctionBuildList;
|
||||
|
||||
VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, PFunction *functype, FxExpression *code, const FString &name, bool fromdecorate, int stateindex, int statecount, int lumpnum)
|
||||
VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, const VersionInfo &ver, PFunction *functype, FxExpression *code, const FString &name, bool fromdecorate, int stateindex, int statecount, int lumpnum)
|
||||
{
|
||||
auto func = code->GetDirectFunction();
|
||||
if (func != nullptr)
|
||||
|
@ -828,6 +828,7 @@ VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, PFunction *functy
|
|||
it.StateIndex = stateindex;
|
||||
it.StateCount = statecount;
|
||||
it.Lump = lumpnum;
|
||||
it.Version = ver;
|
||||
assert(it.Func->Variants.Size() == 1);
|
||||
it.Func->Variants[0].Implementation = it.Function;
|
||||
|
||||
|
|
|
@ -146,13 +146,14 @@ class FFunctionBuildList
|
|||
int StateIndex;
|
||||
int StateCount;
|
||||
int Lump;
|
||||
VersionInfo Version;
|
||||
bool FromDecorate;
|
||||
};
|
||||
|
||||
TArray<Item> mItems;
|
||||
|
||||
public:
|
||||
VMFunction *AddFunction(PNamespace *curglobals, PFunction *func, FxExpression *code, const FString &name, bool fromdecorate, int currentstate, int statecnt, int lumpnum);
|
||||
VMFunction *AddFunction(PNamespace *curglobals, const VersionInfo &ver, PFunction *func, FxExpression *code, const FString &name, bool fromdecorate, int currentstate, int statecnt, int lumpnum);
|
||||
void Build();
|
||||
};
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns)
|
|||
bag.Namespace = ns;
|
||||
bag.Info = type;
|
||||
bag.fromDecorate = true;
|
||||
bag.Version = { 2, 0, 0 };
|
||||
#ifdef _DEBUG
|
||||
bag.ClassName = type->TypeName;
|
||||
#endif
|
||||
|
|
|
@ -69,6 +69,10 @@ EXTERN_CVAR(Bool, strictdecorate);
|
|||
|
||||
PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FName typeName)
|
||||
{
|
||||
if (parent->mVersion > MakeVersion(2, 0))
|
||||
{
|
||||
sc.Message(MSG_ERROR, "Parent class %s of %s not accessible to DECORATE", parent->GetClass()->TypeName.GetChars(), typeName.GetChars());
|
||||
}
|
||||
PClassActor *type = static_cast<PClassActor *>(parent->CreateDerivedClass(typeName, parent->Size));
|
||||
if (type == nullptr)
|
||||
{
|
||||
|
@ -1125,6 +1129,7 @@ static void ParseActor(FScanner &sc, PNamespace *ns)
|
|||
Baggage bag;
|
||||
|
||||
bag.Namespace = ns;
|
||||
bag.Version = { 2, 0, 0 };
|
||||
bag.fromDecorate = true;
|
||||
info = ParseActorHeader(sc, &bag);
|
||||
sc.MustGetToken('{');
|
||||
|
|
|
@ -340,7 +340,7 @@ endofstate:
|
|||
if (ScriptCode != nullptr)
|
||||
{
|
||||
auto funcsym = CreateAnonymousFunction(actor, nullptr, state.UseFlags);
|
||||
state.ActionFunc = FunctionBuildList.AddFunction(bag.Namespace, funcsym, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true, bag.statedef.GetStateCount(), int(statestring.Len()), sc.LumpNum);
|
||||
state.ActionFunc = FunctionBuildList.AddFunction(bag.Namespace, bag.Version, funcsym, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true, bag.statedef.GetStateCount(), int(statestring.Len()), sc.LumpNum);
|
||||
}
|
||||
int count = bag.statedef.AddStates(&state, statestring, scp);
|
||||
if (count < 0)
|
||||
|
|
|
@ -26,6 +26,7 @@ class PSymbol : public DObject
|
|||
DECLARE_ABSTRACT_CLASS(PSymbol, DObject);
|
||||
public:
|
||||
FName SymbolName;
|
||||
VersionInfo mVersion = { 0,0,0 };
|
||||
|
||||
protected:
|
||||
PSymbol(FName name) { SymbolName = name; }
|
||||
|
|
|
@ -228,7 +228,7 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void CreateDamageFunction(PNamespace *OutNamespace, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum)
|
||||
void CreateDamageFunction(PNamespace *OutNamespace, const VersionInfo &ver, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum)
|
||||
{
|
||||
if (id == nullptr)
|
||||
{
|
||||
|
@ -238,7 +238,7 @@ void CreateDamageFunction(PNamespace *OutNamespace, PClassActor *info, AActor *d
|
|||
{
|
||||
auto dmg = new FxReturnStatement(new FxIntCast(id, true), id->ScriptPosition);
|
||||
auto funcsym = CreateAnonymousFunction(info, TypeSInt32, 0);
|
||||
defaults->DamageFunc = FunctionBuildList.AddFunction(OutNamespace, funcsym, dmg, FStringf("%s.DamageFunction", info->TypeName.GetChars()), fromDecorate, -1, 0, lumpnum);
|
||||
defaults->DamageFunc = FunctionBuildList.AddFunction(OutNamespace, ver, funcsym, dmg, FStringf("%s.DamageFunction", info->TypeName.GetChars()), fromDecorate, -1, 0, lumpnum);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,7 @@ struct Baggage
|
|||
bool fromDecorate;
|
||||
int CurrentState;
|
||||
int Lumpnum;
|
||||
VersionInfo Version;
|
||||
FStateDefinitions statedef;
|
||||
|
||||
FDropItem *DropItemList;
|
||||
|
@ -161,7 +162,7 @@ FName CheckCastKludges(FName in);
|
|||
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PStruct *cls, DWORD funcflags, int useflags);
|
||||
PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags);
|
||||
PFunction *FindClassMemberFunction(PStruct *cls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error);
|
||||
void CreateDamageFunction(PNamespace *ns, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum);
|
||||
void CreateDamageFunction(PNamespace *ns, const VersionInfo &ver, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
|
@ -679,7 +679,7 @@ DEFINE_PROPERTY(damage, X, Actor)
|
|||
|
||||
defaults->DamageVal = dmgval;
|
||||
// Only DECORATE can get here with a valid expression.
|
||||
CreateDamageFunction(bag.Namespace, bag.Info, defaults, id, true, bag.Lumpnum);
|
||||
CreateDamageFunction(bag.Namespace, bag.Version, bag.Info, defaults, id, true, bag.Lumpnum);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -873,6 +873,9 @@ declarator(X) ::= decl_flags(A) type_list_or_void(B) variables_or_function(C).
|
|||
decl->Name = C.FuncName;
|
||||
decl->UseFlags = A == nullptr? nullptr : A->Id;
|
||||
decl->Flags = (A == nullptr? 0 : A->Flags) | C.FuncFlags;
|
||||
if (A == nullptr) decl->Version = {0,0,0};
|
||||
else decl->Version = A->Version;
|
||||
|
||||
decl->Body = C.FuncBody;
|
||||
X = decl;
|
||||
}
|
||||
|
@ -881,7 +884,16 @@ declarator(X) ::= decl_flags(A) type_list_or_void(B) variables_or_function(C).
|
|||
NEW_AST_NODE(VarDeclarator, decl, A == nullptr? B->SourceLoc : A->SourceLoc);
|
||||
decl->Type = B;
|
||||
decl->Names = C.VarNames;
|
||||
decl->Flags = (A == nullptr? 0 : A->Flags);
|
||||
if (A == nullptr)
|
||||
{
|
||||
decl->Flags = 0;
|
||||
decl->Version = {0,0,0};
|
||||
}
|
||||
else
|
||||
{
|
||||
decl->Flags = A->Flags;
|
||||
decl->Version = A->Version;
|
||||
}
|
||||
X = decl;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -275,8 +275,8 @@ void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZC
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols, PNamespace *_outnamespc, int lumpnum)
|
||||
: Outer(_outer), GlobalTreeNodes(&_symbols), OutNamespace(_outnamespc), AST(ast), Lump(lumpnum)
|
||||
ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols, PNamespace *_outnamespc, int lumpnum, const VersionInfo &ver)
|
||||
: Outer(_outer), GlobalTreeNodes(&_symbols), OutNamespace(_outnamespc), AST(ast), Lump(lumpnum), mVersion(ver)
|
||||
{
|
||||
FScriptPosition::ResetErrorCounter();
|
||||
// Group top-level nodes by type
|
||||
|
@ -495,20 +495,32 @@ void ZCCCompiler::CreateStructTypes()
|
|||
{
|
||||
s->strct->Type = NewStruct(s->NodeName(), outer);
|
||||
}
|
||||
|
||||
if ((s->strct->Flags & (ZCC_UIFlag | ZCC_Play)) == (ZCC_UIFlag | ZCC_Play))
|
||||
if (s->strct->Flags & ZCC_Version)
|
||||
{
|
||||
Error(s->strct, "Struct %s has incompatible flags", s->NodeName().GetChars());
|
||||
s->strct->Type->mVersion = s->strct->Version;
|
||||
}
|
||||
|
||||
if (outer != OutNamespace) s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::SideFromObjectFlags(outer->ObjectFlags));
|
||||
else if (s->strct->Flags & ZCC_ClearScope) Warn(s->strct, "Useless 'ClearScope' on struct %s not inside a class", s->NodeName().GetChars());
|
||||
if (s->strct->Flags & ZCC_UIFlag)
|
||||
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_UI);
|
||||
if (s->strct->Flags & ZCC_Play)
|
||||
if (mVersion >= MakeVersion(2, 4, 0))
|
||||
{
|
||||
if ((s->strct->Flags & (ZCC_UIFlag | ZCC_Play)) == (ZCC_UIFlag | ZCC_Play))
|
||||
{
|
||||
Error(s->strct, "Struct %s has incompatible flags", s->NodeName().GetChars());
|
||||
}
|
||||
|
||||
if (outer != OutNamespace) s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::SideFromObjectFlags(outer->ObjectFlags));
|
||||
else if (s->strct->Flags & ZCC_ClearScope) Warn(s->strct, "Useless 'ClearScope' on struct %s not inside a class", s->NodeName().GetChars());
|
||||
if (s->strct->Flags & ZCC_UIFlag)
|
||||
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_UI);
|
||||
if (s->strct->Flags & ZCC_Play)
|
||||
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_Play);
|
||||
if (s->strct->Flags & ZCC_ClearScope)
|
||||
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_PlainData); // don't inherit the scope from the outer class
|
||||
}
|
||||
else
|
||||
{
|
||||
// old versions force 'play'.
|
||||
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_Play);
|
||||
if (s->strct->Flags & ZCC_ClearScope)
|
||||
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_PlainData); // don't inherit the scope from the outer class
|
||||
}
|
||||
s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type());
|
||||
syms->AddSymbol(s->strct->Symbol);
|
||||
|
||||
|
@ -597,6 +609,10 @@ void ZCCCompiler::CreateClassTypes()
|
|||
// We will never get here if the name is a duplicate, so we can just do the assignment.
|
||||
try
|
||||
{
|
||||
if (parent->mVersion > mVersion)
|
||||
{
|
||||
Error(c->cls, "Parent class %s of %s not accessible to ZScript version %d.%d.%d", parent->GetClass()->TypeName.GetChars(), c->NodeName().GetChars(), mVersion.major, mVersion.minor, mVersion.revision);
|
||||
}
|
||||
c->cls->Type = parent->CreateDerivedClass(c->NodeName(), TentativeClass);
|
||||
if (c->Type() == nullptr)
|
||||
{
|
||||
|
@ -614,28 +630,40 @@ void ZCCCompiler::CreateClassTypes()
|
|||
if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName());
|
||||
if (c->cls->Flags & ZCC_Abstract)
|
||||
c->Type()->ObjectFlags |= OF_Abstract;
|
||||
//
|
||||
static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope };
|
||||
int incompatiblecnt = 0;
|
||||
for (size_t k = 0; k < countof(incompatible); k++)
|
||||
if (incompatible[k] & c->cls->Flags) incompatiblecnt++;
|
||||
|
||||
if (incompatiblecnt > 1)
|
||||
|
||||
if (c->cls->Flags & ZCC_Version)
|
||||
{
|
||||
Error(c->cls, "Class %s has incompatible flags", c->NodeName().GetChars());
|
||||
c->Type()->mVersion = c->cls->Version;
|
||||
}
|
||||
|
||||
if (c->cls->Flags & ZCC_UIFlag)
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_Play) | OF_UI;
|
||||
if (c->cls->Flags & ZCC_Play)
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_UI) | OF_Play;
|
||||
if (parent->ObjectFlags & (OF_UI | OF_Play)) // parent is either ui or play
|
||||
//
|
||||
if (mVersion >= MakeVersion(2, 4, 0))
|
||||
{
|
||||
if (c->cls->Flags & (ZCC_UIFlag | ZCC_Play))
|
||||
static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope };
|
||||
int incompatiblecnt = 0;
|
||||
for (size_t k = 0; k < countof(incompatible); k++)
|
||||
if (incompatible[k] & c->cls->Flags) incompatiblecnt++;
|
||||
|
||||
if (incompatiblecnt > 1)
|
||||
{
|
||||
Error(c->cls, "Can't change class scope in class %s", c->NodeName().GetChars());
|
||||
Error(c->cls, "Class %s has incompatible flags", c->NodeName().GetChars());
|
||||
}
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags & ~(OF_UI | OF_Play)) | (parent->ObjectFlags & (OF_UI | OF_Play));
|
||||
|
||||
if (c->cls->Flags & ZCC_UIFlag)
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_Play) | OF_UI;
|
||||
if (c->cls->Flags & ZCC_Play)
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_UI) | OF_Play;
|
||||
if (parent->ObjectFlags & (OF_UI | OF_Play)) // parent is either ui or play
|
||||
{
|
||||
if (c->cls->Flags & (ZCC_UIFlag | ZCC_Play))
|
||||
{
|
||||
Error(c->cls, "Can't change class scope in class %s", c->NodeName().GetChars());
|
||||
}
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags & ~(OF_UI | OF_Play)) | (parent->ObjectFlags & (OF_UI | OF_Play));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_UI) | OF_Play;
|
||||
}
|
||||
|
||||
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.)
|
||||
|
@ -1102,16 +1130,23 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
|||
if (field->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
|
||||
if (field->Flags & ZCC_ReadOnly) varflags |= VARF_ReadOnly;
|
||||
if (field->Flags & ZCC_Transient) varflags |= VARF_Transient;
|
||||
if (type->ObjectFlags & OF_UI)
|
||||
varflags |= VARF_UI;
|
||||
if (type->ObjectFlags & OF_Play)
|
||||
if (mVersion >= MakeVersion(2, 4, 0))
|
||||
{
|
||||
if (type->ObjectFlags & OF_UI)
|
||||
varflags |= VARF_UI;
|
||||
if (type->ObjectFlags & OF_Play)
|
||||
varflags |= VARF_Play;
|
||||
if (field->Flags & ZCC_UIFlag)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
|
||||
if (field->Flags & ZCC_Play)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play);
|
||||
if (field->Flags & ZCC_ClearScope)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData);
|
||||
}
|
||||
else
|
||||
{
|
||||
varflags |= VARF_Play;
|
||||
if (field->Flags & ZCC_UIFlag)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
|
||||
if (field->Flags & ZCC_Play)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play);
|
||||
if (field->Flags & ZCC_ClearScope)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData);
|
||||
}
|
||||
|
||||
if (field->Flags & ZCC_Native)
|
||||
{
|
||||
|
@ -1178,7 +1213,8 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
|||
{
|
||||
// for bit fields the type must point to the source variable.
|
||||
if (fd->BitValue != 0) thisfieldtype = fd->FieldSize == 1 ? TypeUInt8 : fd->FieldSize == 2 ? TypeUInt16 : TypeUInt32;
|
||||
type->AddNativeField(name->Name, thisfieldtype, fd->FieldOffset, varflags, fd->BitValue);
|
||||
auto f = type->AddNativeField(name->Name, thisfieldtype, fd->FieldOffset, varflags, fd->BitValue);
|
||||
if (field->Flags & (ZCC_Version | ZCC_Deprecated)) f->mVersion = field->Version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1188,7 +1224,8 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
|||
}
|
||||
else
|
||||
{
|
||||
type->AddField(name->Name, thisfieldtype, varflags);
|
||||
auto f = type->AddField(name->Name, thisfieldtype, varflags);
|
||||
if (field->Flags & (ZCC_Version | ZCC_Deprecated)) f->mVersion = field->Version;
|
||||
}
|
||||
}
|
||||
name = static_cast<ZCC_VarName*>(name->SiblingNext);
|
||||
|
@ -1471,6 +1508,11 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
|
|||
Error(field, "%s does not represent a class type", FName(ctype->Restriction->Id).GetChars());
|
||||
return TypeError;
|
||||
}
|
||||
if (typesym->Type->mVersion > mVersion)
|
||||
{
|
||||
Error(field, "Class %s not accessible to ZScript version %d.%d.%d", FName(ctype->Restriction->Id).GetChars(), mVersion.major, mVersion.minor, mVersion.revision);
|
||||
return TypeError;
|
||||
}
|
||||
retval = NewClassPointer(static_cast<PClass *>(typesym->Type));
|
||||
}
|
||||
break;
|
||||
|
@ -1504,6 +1546,12 @@ PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt)
|
|||
if (sym != nullptr && sym->IsKindOf(RUNTIME_CLASS(PSymbolType)))
|
||||
{
|
||||
auto ptype = static_cast<PSymbolType *>(sym)->Type;
|
||||
if (ptype->mVersion > mVersion)
|
||||
{
|
||||
Error(type, "Type %s not accessible to ZScript version %d.%d.%d", FName(type->UserType->Id).GetChars(), mVersion.major, mVersion.minor, mVersion.revision);
|
||||
return TypeError;
|
||||
}
|
||||
|
||||
if (ptype->IsKindOf(RUNTIME_CLASS(PEnum)))
|
||||
{
|
||||
return TypeSInt32; // hack this to an integer until we can resolve the enum mess.
|
||||
|
@ -1850,7 +1898,7 @@ void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *pro
|
|||
if (namenode->Id == NAME_DamageFunction)
|
||||
{
|
||||
auto x = ConvertNode(prop->Values);
|
||||
CreateDamageFunction(OutNamespace, cls, (AActor *)bag.Info->Defaults, x, false, Lump);
|
||||
CreateDamageFunction(OutNamespace, mVersion, cls, (AActor *)bag.Info->Defaults, x, false, Lump);
|
||||
((AActor *)bag.Info->Defaults)->DamageVal = -1;
|
||||
return;
|
||||
}
|
||||
|
@ -2005,6 +2053,7 @@ void ZCCCompiler::InitDefaults()
|
|||
#ifdef _DEBUG
|
||||
bag.ClassName = c->Type()->TypeName;
|
||||
#endif
|
||||
bag.Version = mVersion;
|
||||
bag.Namespace = OutNamespace;
|
||||
bag.Info = ti;
|
||||
bag.DropItemSet = false;
|
||||
|
@ -2128,31 +2177,27 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
if (f->Flags & ZCC_Override) varflags |= VARF_Override;
|
||||
if (f->Flags & ZCC_VarArg) varflags |= VARF_VarArg;
|
||||
if (f->Flags & ZCC_FuncConst) varflags |= VARF_ReadOnly; // FuncConst method is internally marked as VARF_ReadOnly
|
||||
if (c->Type()->ObjectFlags & OF_UI)
|
||||
varflags |= VARF_UI;
|
||||
if (c->Type()->ObjectFlags & OF_Play)
|
||||
varflags |= VARF_Play;
|
||||
//if (f->Flags & ZCC_FuncConst)
|
||||
// varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData); // const implies clearscope. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag.
|
||||
if (f->Flags & ZCC_UIFlag)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
|
||||
if (f->Flags & ZCC_Play)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play);
|
||||
if (f->Flags & ZCC_ClearScope)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Clear);
|
||||
if (f->Flags & ZCC_VirtualScope)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Virtual);
|
||||
|
||||
// [ZZ] supporting const self for actors is quite a cumbersome task because there's no concept of a const pointer (?)
|
||||
// either way, it doesn't make sense, because you can call any method on a readonly class instance.
|
||||
|
||||
// The above is nonsense. This needs to work and needs to be implemented properly.
|
||||
/*
|
||||
if ((f->Flags & ZCC_FuncConst) && (c->Type()->IsKindOf(RUNTIME_CLASS(PClass))))
|
||||
if (mVersion >= MakeVersion(2, 4, 0))
|
||||
{
|
||||
Error(f, "'Const' on a method can only be used in structs");
|
||||
if (c->Type()->ObjectFlags & OF_UI)
|
||||
varflags |= VARF_UI;
|
||||
if (c->Type()->ObjectFlags & OF_Play)
|
||||
varflags |= VARF_Play;
|
||||
//if (f->Flags & ZCC_FuncConst)
|
||||
// varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData); // const implies clearscope. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag.
|
||||
if (f->Flags & ZCC_UIFlag)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
|
||||
if (f->Flags & ZCC_Play)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play);
|
||||
if (f->Flags & ZCC_ClearScope)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Clear);
|
||||
if (f->Flags & ZCC_VirtualScope)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Virtual);
|
||||
}
|
||||
else
|
||||
{
|
||||
varflags |= VARF_Play;
|
||||
}
|
||||
*/
|
||||
|
||||
if ((f->Flags & ZCC_VarArg) && !(f->Flags & ZCC_Native))
|
||||
{
|
||||
|
@ -2390,6 +2435,14 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
if (imp != nullptr) vindex = imp->VirtualIndex;
|
||||
else Error(f, "Virtual base function %s not found in %s", FName(f->Name).GetChars(), cls->ParentClass->TypeName.GetChars());
|
||||
}
|
||||
if (f->Flags & (ZCC_Version | ZCC_Deprecated))
|
||||
{
|
||||
sym->mVersion = f->Version;
|
||||
if (varflags & VARF_Override)
|
||||
{
|
||||
Error(f, "Overridden function %s may not alter version restriction in %s", FName(f->Name).GetChars(), cls->ParentClass->TypeName.GetChars());
|
||||
}
|
||||
}
|
||||
|
||||
if (!(f->Flags & ZCC_Native))
|
||||
{
|
||||
|
@ -2403,7 +2456,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
auto code = ConvertAST(c->Type(), f->Body);
|
||||
if (code != nullptr)
|
||||
{
|
||||
FunctionBuildList.AddFunction(OutNamespace, sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
|
||||
FunctionBuildList.AddFunction(OutNamespace, mVersion, sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2739,7 +2792,7 @@ void ZCCCompiler::CompileStates()
|
|||
if (code != nullptr)
|
||||
{
|
||||
auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, state.UseFlags);
|
||||
state.ActionFunc = FunctionBuildList.AddFunction(OutNamespace, funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false, statedef.GetStateCount(), (int)sl->Frames->Len(), Lump);
|
||||
state.ActionFunc = FunctionBuildList.AddFunction(OutNamespace, mVersion, funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false, statedef.GetStateCount(), (int)sl->Frames->Len(), Lump);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ struct ZCC_ConstantWork
|
|||
class ZCCCompiler
|
||||
{
|
||||
public:
|
||||
ZCCCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PNamespace *outnamespace, int lumpnum);
|
||||
ZCCCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PNamespace *outnamespace, int lumpnum, const VersionInfo & ver);
|
||||
~ZCCCompiler();
|
||||
int Compile();
|
||||
|
||||
|
@ -127,6 +127,7 @@ private:
|
|||
TArray<ZCC_StructWork *> Structs;
|
||||
TArray<ZCC_ClassWork *> Classes;
|
||||
TArray<ZCC_PropertyWork *> Properties;
|
||||
VersionInfo mVersion;
|
||||
|
||||
PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false);
|
||||
|
||||
|
|
|
@ -360,34 +360,34 @@ static void DoParse(int lumpnum)
|
|||
{
|
||||
char *endp;
|
||||
sc.MustGetString();
|
||||
state.ParseVersion.major = (int16_t)clamp<long long>(strtoll(sc.String, &endp, 10), -1, USHRT_MAX);
|
||||
state.ParseVersion.major = (int16_t)clamp<unsigned long long>(strtoull(sc.String, &endp, 10), 0, USHRT_MAX);
|
||||
if (*endp != '.')
|
||||
{
|
||||
sc.ScriptError("Bad version directive");
|
||||
}
|
||||
state.ParseVersion.minor = (int16_t)clamp<long long>(strtoll(endp + 1, &endp, 10), -1, USHRT_MAX);
|
||||
state.ParseVersion.minor = (int16_t)clamp<unsigned long long>(strtoll(endp + 1, &endp, 10), 0, USHRT_MAX);
|
||||
if (*endp == '.')
|
||||
{
|
||||
state.ParseVersion.revision = (int16_t)clamp<long long>(strtoll(endp + 1, &endp, 10), -1, USHRT_MAX);
|
||||
state.ParseVersion.revision = (int16_t)clamp<unsigned long long>(strtoll(endp + 1, &endp, 10), 0, USHRT_MAX);
|
||||
}
|
||||
else state.ParseVersion.revision = 0;
|
||||
if (*endp != 0)
|
||||
{
|
||||
sc.ScriptError("Bad version directive");
|
||||
}
|
||||
if (state.ParseVersion.major < 0 || state.ParseVersion.minor < 0 || state.ParseVersion.revision < 0)
|
||||
if (state.ParseVersion.major == USHRT_MAX || state.ParseVersion.minor == USHRT_MAX || state.ParseVersion.revision == USHRT_MAX)
|
||||
{
|
||||
sc.ScriptError("Bad version directive");
|
||||
}
|
||||
if (!state.ParseVersion.Check(VER_MAJOR, VER_MINOR, VER_REVISION))
|
||||
if (state.ParseVersion > MakeVersion(VER_MAJOR, VER_MINOR, VER_REVISION))
|
||||
{
|
||||
sc.ScriptError("Version mismatch. %d.%d expected but only %d.%d supported", state.ParseVersion.major, state.ParseVersion.minor, VER_MAJOR, VER_MINOR);
|
||||
sc.ScriptError("Version mismatch. %d.%d.%d expected but only %d.%d.%d supported", state.ParseVersion.major, state.ParseVersion.minor, state.ParseVersion.revision, VER_MAJOR, VER_MINOR, VER_REVISION);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.ParseVersion.major = 1; // 0 excludes all ZSCRIPT keywords from the parser.
|
||||
state.ParseVersion.minor = 0;
|
||||
state.ParseVersion.major = 2; // 2.3 is the first version of ZScript.
|
||||
state.ParseVersion.minor = 3;
|
||||
sc.RestorePos(saved);
|
||||
}
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ static void DoParse(int lumpnum)
|
|||
|
||||
PSymbolTable symtable;
|
||||
auto newns = Wads.GetLumpFile(lumpnum) == 0 ? Namespaces.GlobalNamespace : Namespaces.NewNamespace(Wads.GetLumpFile(lumpnum));
|
||||
ZCCCompiler cc(state, NULL, symtable, newns, lumpnum);
|
||||
ZCCCompiler cc(state, NULL, symtable, newns, lumpnum, state.ParseVersion);
|
||||
cc.Compile();
|
||||
|
||||
if (FScriptPosition::ErrorCounter > 0)
|
||||
|
|
|
@ -500,6 +500,7 @@ struct ZCC_Declarator : ZCC_TreeNode
|
|||
{
|
||||
ZCC_Type *Type;
|
||||
int Flags;
|
||||
VersionInfo Version;
|
||||
};
|
||||
|
||||
// A variable in a class or struct.
|
||||
|
|
Loading…
Reference in a new issue