- Implemented ZScript abstract functions

This commit is contained in:
Player701 2020-10-10 11:24:49 +03:00 committed by Christoph Oelckers
parent 628d23a688
commit 761dea8640
9 changed files with 83 additions and 23 deletions

View file

@ -435,7 +435,7 @@ DObject *PClass::CreateNew()
else else
memset (mem, 0, Size); memset (mem, 0, Size);
if (ConstructNative == nullptr) if (ConstructNative == nullptr || bAbstract)
{ {
M_Free(mem); M_Free(mem);
I_Error("Attempt to instantiate abstract class %s.", TypeName.GetChars()); I_Error("Attempt to instantiate abstract class %s.", TypeName.GetChars());

View file

@ -767,13 +767,16 @@ FFunctionBuildList FunctionBuildList;
VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, const VersionInfo &ver, 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(functype, ver); if (code != nullptr)
if (func != nullptr)
{ {
delete code; auto func = code->GetDirectFunction(functype, ver);
if (func != nullptr)
{
delete code;
return func; return func;
}
} }
//Printf("Adding %s\n", name.GetChars()); //Printf("Adding %s\n", name.GetChars());
@ -815,6 +818,10 @@ void FFunctionBuildList::Build()
for (auto &item : mItems) for (auto &item : mItems)
{ {
// [Player701] Do not emit code for abstract functions
bool isAbstract = item.Func->Variants[0].Implementation->VarFlags & VARF_Abstract;
if (isAbstract) continue;
assert(item.Code != NULL); assert(item.Code != NULL);
// We don't know the return type in advance for anonymous functions. // We don't know the return type in advance for anonymous functions.

View file

@ -36,6 +36,7 @@ enum
VARF_Play = (1<<21), // [ZZ] play: object is playsim-scope only (can't access ui) VARF_Play = (1<<21), // [ZZ] play: object is playsim-scope only (can't access ui)
VARF_VirtualScope = (1<<22), // [ZZ] virtualscope: object should use the scope of the particular class it's being used with (methods only) VARF_VirtualScope = (1<<22), // [ZZ] virtualscope: object should use the scope of the particular class it's being used with (methods only)
VARF_ClearScope = (1<<23), // [ZZ] clearscope: this method ignores the member access chain that leads to it and is always plain data. VARF_ClearScope = (1<<23), // [ZZ] clearscope: this method ignores the member access chain that leads to it and is always plain data.
VARF_Abstract = (1<<24), // [Player701] Function does not have a body and must be overridden in subclasses
}; };
// Basic information shared by all types ------------------------------------ // Basic information shared by all types ------------------------------------

View file

@ -1214,6 +1214,7 @@ decl_flag(X) ::= READONLY(T). { X.Int = ZCC_ReadOnly; X.SourceLoc = T.SourceLoc
decl_flag(X) ::= INTERNAL(T). { X.Int = ZCC_Internal; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= INTERNAL(T). { X.Int = ZCC_Internal; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; } 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) ::= OVERRIDE(T). { X.Int = ZCC_Override; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= ABSTRACT(T). { X.Int = ZCC_Abstract; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= VARARG(T). { X.Int = ZCC_VarArg; 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) ::= 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) ::= PLAY(T). { X.Int = ZCC_Play; X.SourceLoc = T.SourceLoc; }

View file

@ -2042,7 +2042,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
} while (t != f->Type); } while (t != f->Type);
} }
int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_Abstract | ZCC_Internal; int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_Internal;
if (f->Flags & notallowed) if (f->Flags & notallowed)
{ {
@ -2088,6 +2088,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated; if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual; if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual;
if (f->Flags & ZCC_Override) varflags |= VARF_Override; if (f->Flags & ZCC_Override) varflags |= VARF_Override;
if (f->Flags & ZCC_Abstract) varflags |= VARF_Abstract;
if (f->Flags & ZCC_VarArg) varflags |= VARF_VarArg; 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 (f->Flags & ZCC_FuncConst) varflags |= VARF_ReadOnly; // FuncConst method is internally marked as VARF_ReadOnly
if (mVersion >= MakeVersion(2, 4, 0)) if (mVersion >= MakeVersion(2, 4, 0))
@ -2131,7 +2132,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'. if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'.
// Only one of these flags may be used. // Only one of these flags may be used.
static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static }; static int exclude[] = { ZCC_Abstract, ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static };
int excludeflags = 0; int excludeflags = 0;
int fc = 0; int fc = 0;
for (size_t i = 0; i < countof(exclude); i++) for (size_t i = 0; i < countof(exclude); i++)
@ -2147,7 +2148,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
Error(f, "Invalid combination of qualifiers %s on function %s", FlagsToString(excludeflags).GetChars(), FName(f->Name).GetChars()); Error(f, "Invalid combination of qualifiers %s on function %s", FlagsToString(excludeflags).GetChars(), FName(f->Name).GetChars());
varflags |= VARF_Method; varflags |= VARF_Method;
} }
if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well. if (varflags & (VARF_Override | VARF_Abstract)) varflags |= VARF_Virtual; // Now that the flags are checked, make all override and abstract functions virtual as well.
// [ZZ] this doesn't make sense either. // [ZZ] this doesn't make sense either.
if ((varflags&(VARF_ReadOnly | VARF_Method)) == VARF_ReadOnly) // non-method const function if ((varflags&(VARF_ReadOnly | VARF_Method)) == VARF_ReadOnly) // non-method const function
@ -2180,6 +2181,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
if (f->Flags & ZCC_Native) if (f->Flags & ZCC_Native)
{ {
if (varflags & VARF_Abstract)
{
Error(f, "Native functions cannot be abstract");
return;
}
varflags |= VARF_Native; varflags |= VARF_Native;
afd = FindFunction(c->Type(), FName(f->Name).GetChars()); afd = FindFunction(c->Type(), FName(f->Name).GetChars());
if (afd == nullptr) if (afd == nullptr)
@ -2371,19 +2378,20 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
if (!(f->Flags & ZCC_Native)) if (!(f->Flags & ZCC_Native))
{ {
if (f->Body == nullptr) if (f->Body != nullptr && (varflags & VARF_Abstract))
{
Error(f, "Abstract function %s cannot have a body", FName(f->Name).GetChars());
return;
}
if (f->Body == nullptr && !(varflags & VARF_Abstract))
{ {
Error(f, "Empty function %s", FName(f->Name).GetChars()); Error(f, "Empty function %s", FName(f->Name).GetChars());
return; return;
} }
else
{ FxExpression* code = f->Body != nullptr ? ConvertAST(c->Type(), f->Body) : nullptr;
auto code = ConvertAST(c->Type(), f->Body); newfunc = FunctionBuildList.AddFunction(OutNamespace, mVersion, sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
if (code != nullptr)
{
newfunc = FunctionBuildList.AddFunction(OutNamespace, mVersion, sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
}
}
} }
if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time. if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time.
{ {
@ -2408,6 +2416,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
if (forclass) if (forclass)
{ {
if ((varflags & VARF_Abstract) && !clstype->bAbstract)
{
Error(f, "Abstract functions can only be defined in abstract classes");
return;
}
auto parentfunc = clstype->ParentClass? dyn_cast<PFunction>(clstype->ParentClass->VMType->Symbols.FindSymbol(sym->SymbolName, true)) : nullptr; auto parentfunc = clstype->ParentClass? dyn_cast<PFunction>(clstype->ParentClass->VMType->Symbols.FindSymbol(sym->SymbolName, true)) : nullptr;
int vindex = clstype->FindVirtualIndex(sym->SymbolName, &sym->Variants[0], parentfunc, exactReturnType); int vindex = clstype->FindVirtualIndex(sym->SymbolName, &sym->Variants[0], parentfunc, exactReturnType);
@ -2526,6 +2540,18 @@ void ZCCCompiler::InitFunctions()
{ {
CompileFunction(c, f, true); CompileFunction(c, f, true);
} }
// [Player701] Make sure all abstract functions are overridden
if (!c->ClassType()->bAbstract)
{
for (auto v : c->ClassType()->Virtuals)
{
if (v->VarFlags & VARF_Abstract)
{
Error(c->cls, "Non-abstract class %s must override abstract function %s", c->Type()->TypeName.GetChars(), v->PrintableName.GetChars());
}
}
}
} }
} }

View file

@ -284,6 +284,13 @@ static bool CanJit(VMScriptFunction *func)
int VMScriptFunction::FirstScriptCall(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) int VMScriptFunction::FirstScriptCall(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret)
{ {
// [Player701] Check that we aren't trying to call an abstract function.
// This shouldn't happen normally, but if it does, let's catch this explicitly
// rather than let GZDoom crash.
if (func->VarFlags & VARF_Abstract)
{
ThrowAbortException(X_OTHER, "attempt to call abstract function %s.", func->PrintableName.GetChars());
}
#ifdef HAVE_VM_JIT #ifdef HAVE_VM_JIT
if (vm_jit && CanJit(static_cast<VMScriptFunction*>(func))) if (vm_jit && CanJit(static_cast<VMScriptFunction*>(func)))
{ {
@ -654,7 +661,11 @@ CVMAbortException::CVMAbortException(EVMAbortException reason, const char *morei
} }
if (moreinfo != nullptr) if (moreinfo != nullptr)
{ {
AppendMessage(" "); // [Player701] avoid double space
if (reason != X_OTHER)
{
AppendMessage(" ");
}
size_t len = strlen(m_Message); size_t len = strlen(m_Message);
myvsnprintf(m_Message + len, MAX_ERRORTEXT - len, moreinfo, ap); myvsnprintf(m_Message + len, MAX_ERRORTEXT - len, moreinfo, ap);
} }

View file

@ -777,11 +777,6 @@ static void SummonActor (int command, int command2, FCommandLine argv)
Printf ("Unknown actor '%s'\n", argv[1]); Printf ("Unknown actor '%s'\n", argv[1]);
return; return;
} }
if (type->bAbstract)
{
Printf("Cannot instantiate abstract class %s\n", argv[1]);
return;
}
Net_WriteByte (argv.argc() > 2 ? command2 : command); Net_WriteByte (argv.argc() > 2 ? command2 : command);
Net_WriteString (type->TypeName.GetChars()); Net_WriteString (type->TypeName.GetChars());

View file

@ -4626,6 +4626,12 @@ AActor *AActor::StaticSpawn(FLevelLocals *Level, PClassActor *type, const DVecto
{ {
I_Error("Tried to spawn a class-less actor\n"); I_Error("Tried to spawn a class-less actor\n");
} }
else if (type->bAbstract)
{
// [Player701] Abstract actors cannot be spawned by any means
Printf("Attempt to spawn an instance of abstract actor class %s\n", type->TypeName.GetChars());
return nullptr;
}
if (allowreplacement) if (allowreplacement)
{ {

View file

@ -78,6 +78,19 @@ PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FN
{ {
sc.Message(MSG_ERROR, "Parent class %s of %s not accessible to DECORATE", parent->TypeName.GetChars(), typeName.GetChars()); sc.Message(MSG_ERROR, "Parent class %s of %s not accessible to DECORATE", parent->TypeName.GetChars(), typeName.GetChars());
} }
else
{
// [Player701] Parent class must not have abstract functions
for (auto v : parent->Virtuals)
{
if (v->VarFlags & VARF_Abstract)
{
sc.Message(MSG_ERROR, "Parent class %s of %s cannot have abstract functions.", parent->TypeName.GetChars(), typeName.GetChars());
break;
}
}
}
bool newlycreated; bool newlycreated;
PClassActor *type = static_cast<PClassActor *>(parent->CreateDerivedClass(typeName, parent->Size, &newlycreated)); PClassActor *type = static_cast<PClassActor *>(parent->CreateDerivedClass(typeName, parent->Size, &newlycreated));
if (type == nullptr) if (type == nullptr)