mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-10 23:02:08 +00:00
Added parsing of ui, play, allowui and const qualifiers
This commit is contained in:
parent
5e5d0d3e57
commit
4fe9c7d8c8
10 changed files with 287 additions and 19 deletions
|
@ -207,6 +207,8 @@ enum EObjectFlags
|
|||
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 at all
|
||||
OF_NoNew = 1 << 15, // Marks a class that can only be created with new() in the exact class that has this keyword
|
||||
OF_UI = 1 << 16, // Marks a class that defaults to VARF_UI for it's fields/methods
|
||||
OF_Play = 1 << 17, // Marks a class that defaults to VARF_Play for it's fields/methods
|
||||
};
|
||||
|
||||
template<class T> class TObjPtr;
|
||||
|
|
|
@ -37,6 +37,8 @@ enum
|
|||
VARF_Transient = (1<<17), // don't auto serialize field.
|
||||
VARF_Meta = (1<<18), // static class data (by necessity read only.)
|
||||
VARF_VarArg = (1<<19), // [ZZ] vararg: don't typecheck values after ... in function signature
|
||||
VARF_UI = (1<<20), // [ZZ] ui: object is ui-scope only (can't modify playsim)
|
||||
VARF_Play = (1<<21), // [ZZ] play: object is playsim-scope only (can't access ui)
|
||||
};
|
||||
|
||||
// An action function -------------------------------------------------------
|
||||
|
|
|
@ -171,6 +171,9 @@ std2:
|
|||
'override' { RET(TK_Override); }
|
||||
'vararg' { RET(TK_VarArg); }
|
||||
'nonew' { RET(TK_NoNew); }
|
||||
'ui' { RET(TK_UI); }
|
||||
'play' { RET(TK_Play); }
|
||||
'allowui' { RET(TK_AllowUI); }
|
||||
'super' { RET(TK_Super); }
|
||||
'global' { RET(TK_Global); }
|
||||
'stop' { RET(TK_Stop); }
|
||||
|
|
|
@ -113,6 +113,9 @@ xx(TK_Export, "'expert'")
|
|||
xx(TK_Virtual, "'virtual'")
|
||||
xx(TK_VarArg, "'vararg'")
|
||||
xx(TK_NoNew, "'nonew'")
|
||||
xx(TK_UI, "'ui'")
|
||||
xx(TK_Play, "'play'")
|
||||
xx(TK_AllowUI, "'allowui'")
|
||||
xx(TK_Override, "'override'")
|
||||
xx(TK_Super, "'super'")
|
||||
xx(TK_Null, "'null'")
|
||||
|
|
|
@ -70,6 +70,130 @@ class FxCompoundStatement;
|
|||
class FxLocalVariableDeclaration;
|
||||
typedef TDeletingArray<FxExpression*> FArgumentList;
|
||||
|
||||
// [ZZ] this is kind of related to compile context as well
|
||||
struct FPlayUIBarrier
|
||||
{
|
||||
bool callable;
|
||||
bool readable;
|
||||
bool writable;
|
||||
|
||||
// this is the error message
|
||||
FString callerror;
|
||||
FString readerror;
|
||||
FString writeerror;
|
||||
|
||||
// this is used to make the error message.
|
||||
enum Side
|
||||
{
|
||||
Side_PlainData = 0,
|
||||
Side_UI,
|
||||
Side_Play
|
||||
};
|
||||
int sidefrom;
|
||||
int sidelast;
|
||||
|
||||
// Note: the same object can't be both UI and Play. This is checked explicitly in the field construction and will cause esoteric errors here if found.
|
||||
int SideFromFlags(int flags)
|
||||
{
|
||||
if (flags & VARF_UI)
|
||||
return Side_UI;
|
||||
if (flags & VARF_Play)
|
||||
return Side_Play;
|
||||
return Side_PlainData;
|
||||
}
|
||||
|
||||
// used for errors
|
||||
const char* StringFromSide(int side)
|
||||
{
|
||||
switch (side)
|
||||
{
|
||||
case Side_PlainData:
|
||||
return "data";
|
||||
case Side_UI:
|
||||
return "ui";
|
||||
case Side_Play:
|
||||
return "play";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
FPlayUIBarrier()
|
||||
{
|
||||
sidefrom = -1;
|
||||
sidelast = -1;
|
||||
callable = true;
|
||||
readable = true;
|
||||
writable = true;
|
||||
}
|
||||
|
||||
FPlayUIBarrier(int flags1, int flags2, const char* name)
|
||||
{
|
||||
sidefrom = -1;
|
||||
sidelast = -1;
|
||||
callable = true;
|
||||
readable = true;
|
||||
writable = true;
|
||||
|
||||
AddFlags(flags1, flags2, name);
|
||||
}
|
||||
|
||||
// AddFlags modifies ALLOWED actions by flags1->flags2.
|
||||
// This is used for comparing a.b.c.d access - if non-allowed field is seen anywhere in the chain, anything after it is non-allowed.
|
||||
// This struct is used so that the logic is in a single place.
|
||||
void AddFlags(int flags1, int flags2, const char* name)
|
||||
{
|
||||
// note: if it's already non-readable, don't even try advancing
|
||||
if (!readable)
|
||||
return;
|
||||
|
||||
// we aren't interested in any other flags
|
||||
flags1 &= VARF_UI | VARF_Play;
|
||||
flags2 &= VARF_UI | VARF_Play | VARF_ReadOnly;
|
||||
|
||||
if (sidefrom < 0) sidefrom = SideFromFlags(flags1);
|
||||
if (sidelast < 0) sidelast = sidefrom;
|
||||
|
||||
// flags1 = what's trying to access
|
||||
// flags2 = what's being accessed
|
||||
|
||||
int sideto = SideFromFlags(flags2);
|
||||
|
||||
// plain data inherits whatever scope modifiers that context or field container has.
|
||||
// i.e. play String bla; is play, and all non-specified methods/fields inside it are play as well.
|
||||
if (sideto != Side_PlainData)
|
||||
sidelast = sideto;
|
||||
else sideto = sidelast;
|
||||
|
||||
if ((sideto == Side_UI) != (sidefrom == Side_UI)) // only ui -> ui is readable
|
||||
{
|
||||
readable = false;
|
||||
readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), StringFromSide(sidefrom));
|
||||
}
|
||||
|
||||
if (!readable)
|
||||
{
|
||||
writable = false;
|
||||
callable = false;
|
||||
writeerror.Format("Can't write %s field %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom));
|
||||
callerror.Format("Can't call %s function %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom));
|
||||
return;
|
||||
}
|
||||
|
||||
if (writable && (sidefrom != sideto)) // only matching types are writable (plain data implicitly takes context type by default, unless overridden)
|
||||
{
|
||||
writable = false;
|
||||
writeerror.Format("Can't write %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom));
|
||||
}
|
||||
|
||||
if (callable && (sidefrom != sideto) && !(flags2 & VARF_ReadOnly)) // readonly on methods is used for plain data stuff that can be called from ui/play context.
|
||||
{
|
||||
callable = false;
|
||||
callerror.Format("Can't call %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FCompileContext
|
||||
{
|
||||
FxExpression *ControlStmt = nullptr;
|
||||
|
|
|
@ -702,6 +702,9 @@ public:
|
|||
bool Native;
|
||||
bool Final = false; // cannot be overridden
|
||||
bool Unsafe = false; // Contains references to class fields that are unsafe for psp and item state calls.
|
||||
bool ScopeUI = false; // [ZZ] 'ui' method
|
||||
bool ScopePlay = false; // [ZZ] 'play' method
|
||||
bool FuncConst = false; // [ZZ] const qualifier for methods - these can be called on readonly
|
||||
BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action
|
||||
unsigned VirtualIndex = ~0u;
|
||||
FName Name;
|
||||
|
|
|
@ -214,6 +214,9 @@ 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) UI. { X.Flags = A.Flags | ZCC_UIFlag; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) ALLOWUI. { X.Flags = A.Flags | ZCC_AllowUI; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; }
|
||||
|
||||
/*----- Dottable Identifier -----*/
|
||||
|
@ -327,7 +330,10 @@ struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body
|
|||
|
||||
%type struct_flags{ClassFlagsBlock}
|
||||
struct_flags(X) ::= . { X.Flags = 0; }
|
||||
struct_flags(X) ::= NATIVE. { X.Flags = ZCC_Native; }
|
||||
struct_flags(X) ::= struct_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; }
|
||||
struct_flags(X) ::= struct_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; }
|
||||
struct_flags(X) ::= struct_flags(A) ALLOWUI. { X.Flags = A.Flags | ZCC_AllowUI; }
|
||||
struct_flags(X) ::= struct_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; }
|
||||
|
||||
opt_struct_body(X) ::= . { X = NULL; }
|
||||
opt_struct_body(X) ::= struct_body(X).
|
||||
|
@ -1000,6 +1006,9 @@ decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.Sourc
|
|||
decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; }
|
||||
decl_flag(X) ::= OVERRIDE(T). { X.Int = ZCC_Override; X.SourceLoc = T.SourceLoc; }
|
||||
decl_flag(X) ::= VARARG(T). { X.Int = ZCC_VarArg; X.SourceLoc = T.SourceLoc; }
|
||||
decl_flag(X) ::= UI(T). { X.Int = ZCC_UIFlag; X.SourceLoc = T.SourceLoc; }
|
||||
decl_flag(X) ::= PLAY(T). { X.Int = ZCC_Play; X.SourceLoc = T.SourceLoc; }
|
||||
decl_flag(X) ::= ALLOWUI(T). { X.Int = ZCC_AllowUI; X.SourceLoc = T.SourceLoc; }
|
||||
|
||||
func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); }
|
||||
func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; }
|
||||
|
|
|
@ -496,6 +496,16 @@ void ZCCCompiler::CreateStructTypes()
|
|||
{
|
||||
s->strct->Type = NewStruct(s->NodeName(), outer);
|
||||
}
|
||||
|
||||
if ((s->strct->Flags & (ZCC_UIFlag | ZCC_Play)) == (ZCC_UIFlag | ZCC_Play))
|
||||
{
|
||||
Error(s->strct, "Struct %s has incompatible flags", s->NodeName().GetChars());
|
||||
}
|
||||
|
||||
if (s->strct->Flags & ZCC_UIFlag)
|
||||
s->Type()->ObjectFlags |= OF_UI;
|
||||
if (s->strct->Flags & ZCC_Play)
|
||||
s->Type()->ObjectFlags |= OF_Play;
|
||||
s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type());
|
||||
syms->AddSymbol(s->strct->Symbol);
|
||||
|
||||
|
@ -602,8 +612,24 @@ void ZCCCompiler::CreateClassTypes()
|
|||
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))
|
||||
if (c->cls->Flags & ZCC_NoNew || (parent->ObjectFlags & OF_NoNew))
|
||||
c->Type()->ObjectFlags |= OF_NoNew;
|
||||
//
|
||||
static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_AllowUI };
|
||||
int incompatiblecnt = 0;
|
||||
for (int k = 0; k < countof(incompatible); k++)
|
||||
if (incompatible[k] & c->cls->Flags) incompatiblecnt++;
|
||||
|
||||
if (incompatiblecnt > 1)
|
||||
{
|
||||
Error(c->cls, "Class %s has incompatible flags", c->NodeName().GetChars());
|
||||
}
|
||||
|
||||
if (c->cls->Flags & ZCC_UIFlag || ((parent->ObjectFlags & OF_UI) && !(c->cls->Flags & ZCC_AllowUI)))
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_Play) | OF_UI;
|
||||
if (c->cls->Flags & ZCC_Play || ((parent->ObjectFlags & OF_Play) && !(c->cls->Flags & ZCC_AllowUI)))
|
||||
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.)
|
||||
c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type());
|
||||
OutNamespace->Symbols.AddSymbol(c->cls->Symbol);
|
||||
|
@ -1063,12 +1089,39 @@ 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)
|
||||
varflags |= VARF_Play;
|
||||
if (field->Flags & ZCC_UIFlag)
|
||||
varflags = (varflags&~VARF_Play) | VARF_UI;
|
||||
if (field->Flags & ZCC_Play)
|
||||
varflags = (varflags&~VARF_UI) | VARF_Play;
|
||||
if (field->Flags & ZCC_AllowUI)
|
||||
varflags = (varflags&~(VARF_UI | VARF_Play));
|
||||
|
||||
if (field->Flags & ZCC_Native)
|
||||
{
|
||||
varflags |= VARF_Native | VARF_Transient;
|
||||
}
|
||||
|
||||
static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_AllowUI };
|
||||
int excludeflags = 0;
|
||||
int fc = 0;
|
||||
for (int i = 0; i < countof(excludescope); i++)
|
||||
{
|
||||
if (field->Flags & excludescope[i])
|
||||
{
|
||||
fc++;
|
||||
excludeflags |= excludescope[i];
|
||||
}
|
||||
}
|
||||
if (fc > 1)
|
||||
{
|
||||
Error(field, "Invalid combination of scope qualifiers %s on field %s", FlagsToString(excludeflags).GetChars(), FName(field->Names->Name).GetChars());
|
||||
varflags &= ~(VARF_UI | VARF_Play); // make plain data
|
||||
}
|
||||
|
||||
if (field->Flags & ZCC_Meta)
|
||||
{
|
||||
varflags |= VARF_Meta | VARF_Static | VARF_ReadOnly; // metadata implies readonly
|
||||
|
@ -1101,11 +1154,11 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
|||
fd = FindField(querytype, FName(name->Name).GetChars());
|
||||
if (fd == nullptr)
|
||||
{
|
||||
Error(field, "The member variable '%s.%s' has not been exported from the executable.", type->TypeName.GetChars(), FName(name->Name).GetChars());
|
||||
Error(field, "The member variable '%s.%s' has not been exported from the executable", type->TypeName.GetChars(), FName(name->Name).GetChars());
|
||||
}
|
||||
else if (thisfieldtype->Size != fd->FieldSize && fd->BitValue == 0)
|
||||
{
|
||||
Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration. (Internal = %d, External = %d)", type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size);
|
||||
Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration (Internal = %d, External = %d)", type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size);
|
||||
}
|
||||
// Q: Should we check alignment, too? A mismatch may be an indicator for bad assumptions.
|
||||
else
|
||||
|
@ -1117,7 +1170,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
|||
}
|
||||
else if (hasnativechildren)
|
||||
{
|
||||
Error(field, "Cannot add field %s to %s. %s has native children which means it size may not change.", FName(name->Name).GetChars(), type->TypeName.GetChars(), type->TypeName.GetChars());
|
||||
Error(field, "Cannot add field %s to %s. %s has native children which means it size may not change", FName(name->Name).GetChars(), type->TypeName.GetChars(), type->TypeName.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1212,7 +1265,7 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray<ZCC_Property *> &Proper
|
|||
FString ZCCCompiler::FlagsToString(uint32_t flags)
|
||||
{
|
||||
|
||||
const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "funcconst", "abstract", "extension", "virtual", "override", "transient", "vararg" };
|
||||
const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "const", "abstract", "extend", "virtual", "override", "transient", "vararg", "nonew", "ui", "play", "allowui" };
|
||||
FString build;
|
||||
|
||||
for (size_t i = 0; i < countof(flagnames); i++)
|
||||
|
@ -2014,7 +2067,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
} while (t != f->Type);
|
||||
}
|
||||
|
||||
int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_FuncConst | ZCC_Abstract;
|
||||
int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_Abstract;
|
||||
|
||||
if (f->Flags & notallowed)
|
||||
{
|
||||
|
@ -2061,6 +2114,20 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual;
|
||||
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 = (varflags&~(VARF_Play | VARF_UI)); // const implies allowui. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag.
|
||||
if (f->Flags & ZCC_UIFlag)
|
||||
varflags = (varflags&~VARF_Play) | VARF_UI;
|
||||
if (f->Flags & ZCC_Play)
|
||||
varflags = (varflags&~VARF_UI) | VARF_Play;
|
||||
if (f->Flags & ZCC_AllowUI)
|
||||
varflags = (varflags&~(VARF_Play | VARF_UI));
|
||||
|
||||
if ((f->Flags & ZCC_VarArg) && !(f->Flags & ZCC_Native))
|
||||
{
|
||||
Error(f, "'VarArg' can only be used with native methods");
|
||||
|
@ -2086,36 +2153,57 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
}
|
||||
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final.
|
||||
|
||||
|
||||
if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'.
|
||||
// Only one of these flags may be used.
|
||||
static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static };
|
||||
static const char * print[] = { "virtual", "override", "action", "static" };
|
||||
int excludeflags = 0;
|
||||
int fc = 0;
|
||||
FString build;
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int i = 0; i < countof(exclude); i++)
|
||||
{
|
||||
if (f->Flags & exclude[i])
|
||||
{
|
||||
fc++;
|
||||
if (build.Len() > 0) build += ", ";
|
||||
build += print[i];
|
||||
excludeflags |= exclude[i];
|
||||
}
|
||||
}
|
||||
if (fc > 1)
|
||||
{
|
||||
Error(f, "Invalid combination of qualifiers %s on function %s.", FName(f->Name).GetChars(), build.GetChars());
|
||||
Error(f, "Invalid combination of qualifiers %s on function %s", FlagsToString(excludeflags).GetChars(), FName(f->Name).GetChars());
|
||||
varflags |= VARF_Method;
|
||||
}
|
||||
if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well.
|
||||
|
||||
// you can't have a const function belonging to either ui or play.
|
||||
// const is intended for plain data to signify that you can call a method on readonly variable.
|
||||
if ((f->Flags & ZCC_FuncConst) && (f->Flags & (ZCC_UIFlag | ZCC_Play)))
|
||||
{
|
||||
Error(f, "Invalid combination of qualifiers %s on function %s", FlagsToString(f->Flags&(ZCC_FuncConst | ZCC_UIFlag | ZCC_Play)).GetChars(), FName(f->Name).GetChars());
|
||||
}
|
||||
|
||||
static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_AllowUI };
|
||||
excludeflags = 0;
|
||||
fc = 0;
|
||||
for (int i = 0; i < countof(excludescope); i++)
|
||||
{
|
||||
if (f->Flags & excludescope[i])
|
||||
{
|
||||
fc++;
|
||||
excludeflags |= excludescope[i];
|
||||
}
|
||||
}
|
||||
if (fc > 1)
|
||||
{
|
||||
Error(f, "Invalid combination of scope qualifiers %s on function %s", FlagsToString(excludeflags).GetChars(), FName(f->Name).GetChars());
|
||||
varflags &= ~(VARF_UI | VARF_Play); // make plain data
|
||||
}
|
||||
|
||||
if (f->Flags & ZCC_Native)
|
||||
{
|
||||
varflags |= VARF_Native;
|
||||
afd = FindFunction(c->Type(), FName(f->Name).GetChars());
|
||||
if (afd == nullptr)
|
||||
{
|
||||
Error(f, "The function '%s.%s' has not been exported from the executable.", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars());
|
||||
Error(f, "The function '%s.%s' has not been exported from the executable", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2229,7 +2317,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
}
|
||||
else if (hasoptionals)
|
||||
{
|
||||
Error(p, "All arguments after the first optional one need also be optional.");
|
||||
Error(p, "All arguments after the first optional one need also be optional");
|
||||
}
|
||||
// TBD: disallow certain types? For now, let everything pass that isn't an array.
|
||||
args.Push(type);
|
||||
|
@ -2289,13 +2377,20 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
{
|
||||
if (sym->Variants[0].Implementation == nullptr)
|
||||
{
|
||||
Error(f, "Virtual function %s.%s not present.", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars());
|
||||
Error(f, "Virtual function %s.%s not present", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars());
|
||||
return;
|
||||
}
|
||||
|
||||
if (varflags & VARF_Final)
|
||||
{
|
||||
sym->Variants[0].Implementation->Final = true;
|
||||
}
|
||||
// [ZZ] unspecified virtual function inherits old scope. virtual function scope can't be changed.
|
||||
if (f->Flags & ZCC_UIFlag) // only direct specification here (varflags can also have owning class scope applied, we don't want that)
|
||||
sym->Variants[0].Implementation->ScopeUI = true;
|
||||
if (f->Flags & ZCC_Play) // only direct specification here
|
||||
sym->Variants[0].Implementation->ScopePlay = true;
|
||||
if (varflags & VARF_ReadOnly)
|
||||
sym->Variants[0].Implementation->FuncConst = true;
|
||||
|
||||
if (forclass)
|
||||
{
|
||||
int vindex = clstype->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto);
|
||||
|
@ -2313,6 +2408,27 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
{
|
||||
Error(f, "Attempt to override final function %s", FName(f->Name).GetChars());
|
||||
}
|
||||
// you can't change ui/play/allowui for a virtual method.
|
||||
if ((oldfunc->ScopePlay != sym->Variants[0].Implementation->ScopePlay) ||
|
||||
(oldfunc->ScopeUI != sym->Variants[0].Implementation->ScopeUI))
|
||||
{
|
||||
Error(f, "Attempt to change scope for virtual function %s", FName(f->Name).GetChars());
|
||||
}
|
||||
// you can't change const qualifier for a virtual method
|
||||
if (oldfunc->FuncConst != (varflags & VARF_ReadOnly))
|
||||
{
|
||||
Error(f, "Attempt to change const qualifier for virtual function %s", FName(f->Name).GetChars());
|
||||
}
|
||||
// inherit scope of original function
|
||||
if (sym->Variants[0].Implementation->ScopeUI = oldfunc->ScopeUI)
|
||||
sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_Play)) | VARF_UI;
|
||||
else if (sym->Variants[0].Implementation->ScopePlay = oldfunc->ScopePlay)
|
||||
sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_UI)) | VARF_Play;
|
||||
else sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_UI | VARF_Play));
|
||||
// inherit const from original function
|
||||
if (sym->Variants[0].Implementation->FuncConst = oldfunc->FuncConst)
|
||||
sym->Variants[0].Flags |= VARF_ReadOnly;
|
||||
|
||||
clstype->Virtuals[vindex] = sym->Variants[0].Implementation;
|
||||
sym->Variants[0].Implementation->VirtualIndex = vindex;
|
||||
}
|
||||
|
|
|
@ -137,6 +137,9 @@ static void InitTokenMap()
|
|||
TOKENDEF (TK_Latent, ZCC_LATENT);
|
||||
TOKENDEF (TK_Virtual, ZCC_VIRTUAL);
|
||||
TOKENDEF (TK_VarArg, ZCC_VARARG);
|
||||
TOKENDEF (TK_UI, ZCC_UI);
|
||||
TOKENDEF (TK_Play, ZCC_PLAY);
|
||||
TOKENDEF (TK_AllowUI, ZCC_ALLOWUI);
|
||||
TOKENDEF (TK_NoNew, ZCC_NONEW);
|
||||
TOKENDEF (TK_Override, ZCC_OVERRIDE);
|
||||
TOKENDEF (TK_Final, ZCC_FINAL);
|
||||
|
|
|
@ -38,6 +38,9 @@ enum
|
|||
ZCC_Transient = 1 << 15,
|
||||
ZCC_VarArg = 1 << 16,
|
||||
ZCC_NoNew = 1 << 17,
|
||||
ZCC_UIFlag = 1 << 18, // there's also token called ZCC_UI
|
||||
ZCC_Play = 1 << 19,
|
||||
ZCC_AllowUI = 1 << 20,
|
||||
};
|
||||
|
||||
// Function parameter modifiers
|
||||
|
|
Loading…
Reference in a new issue