Skip to content

Commit

Permalink
- Implemented ZScript abstract functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Player701 authored and coelckers committed Oct 19, 2020
1 parent 628d23a commit 761dea8
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/common/objects/dobjtype.cpp
Expand Up @@ -435,7 +435,7 @@ DObject *PClass::CreateNew()
else
memset (mem, 0, Size);

if (ConstructNative == nullptr)
if (ConstructNative == nullptr || bAbstract)
{
M_Free(mem);
I_Error("Attempt to instantiate abstract class %s.", TypeName.GetChars());
Expand Down
15 changes: 11 additions & 4 deletions src/common/scripting/backend/vmbuilder.cpp
Expand Up @@ -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)
{
auto func = code->GetDirectFunction(functype, ver);
if (func != nullptr)
if (code != nullptr)
{
delete code;
auto func = code->GetDirectFunction(functype, ver);
if (func != nullptr)
{
delete code;


return func;
return func;
}
}

//Printf("Adding %s\n", name.GetChars());
Expand Down Expand Up @@ -815,6 +818,10 @@ void FFunctionBuildList::Build()

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);

// We don't know the return type in advance for anonymous functions.
Expand Down
1 change: 1 addition & 0 deletions src/common/scripting/core/types.h
Expand Up @@ -36,6 +36,7 @@ enum
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_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 ------------------------------------
Expand Down
1 change: 1 addition & 0 deletions src/common/scripting/frontend/zcc-parse.lemon
Expand Up @@ -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) ::= 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) ::= 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) ::= UI(T). { X.Int = ZCC_UIFlag; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= PLAY(T). { X.Int = ZCC_Play; X.SourceLoc = T.SourceLoc; }
Expand Down
48 changes: 37 additions & 11 deletions src/common/scripting/frontend/zcc_compile.cpp
Expand Up @@ -2042,7 +2042,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
} 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)
{
Expand Down Expand Up @@ -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_Virtual) varflags |= VARF_Virtual;
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_FuncConst) varflags |= VARF_ReadOnly; // FuncConst method is internally marked as VARF_ReadOnly
if (mVersion >= MakeVersion(2, 4, 0))
Expand Down Expand Up @@ -2131,7 +2132,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool

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 int exclude[] = { ZCC_Abstract, ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static };
int excludeflags = 0;
int fc = 0;
for (size_t i = 0; i < countof(exclude); i++)
Expand All @@ -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());
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.
if ((varflags&(VARF_ReadOnly | VARF_Method)) == VARF_ReadOnly) // non-method const function
Expand Down Expand Up @@ -2180,6 +2181,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool

if (f->Flags & ZCC_Native)
{
if (varflags & VARF_Abstract)
{
Error(f, "Native functions cannot be abstract");
return;
}

varflags |= VARF_Native;
afd = FindFunction(c->Type(), FName(f->Name).GetChars());
if (afd == nullptr)
Expand Down Expand Up @@ -2371,19 +2378,20 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool

if (!(f->Flags & ZCC_Native))
{
if (f->Body == nullptr)
if (f->Body != nullptr && (varflags & VARF_Abstract))
{
Error(f, "Empty function %s", FName(f->Name).GetChars());
Error(f, "Abstract function %s cannot have a body", FName(f->Name).GetChars());
return;
}
else

if (f->Body == nullptr && !(varflags & VARF_Abstract))
{
auto code = ConvertAST(c->Type(), f->Body);
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);
}
Error(f, "Empty function %s", FName(f->Name).GetChars());
return;
}

FxExpression* code = f->Body != nullptr ? ConvertAST(c->Type(), f->Body) : 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.
{
Expand All @@ -2408,6 +2416,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool

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;

int vindex = clstype->FindVirtualIndex(sym->SymbolName, &sym->Variants[0], parentfunc, exactReturnType);
Expand Down Expand Up @@ -2526,6 +2540,18 @@ void ZCCCompiler::InitFunctions()
{
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());
}
}
}
}
}

Expand Down
13 changes: 12 additions & 1 deletion src/common/scripting/vm/vmframe.cpp
Expand Up @@ -284,6 +284,13 @@ static bool CanJit(VMScriptFunction *func)

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
if (vm_jit && CanJit(static_cast<VMScriptFunction*>(func)))
{
Expand Down Expand Up @@ -654,7 +661,11 @@ CVMAbortException::CVMAbortException(EVMAbortException reason, const char *morei
}
if (moreinfo != nullptr)
{
AppendMessage(" ");
// [Player701] avoid double space
if (reason != X_OTHER)
{
AppendMessage(" ");
}
size_t len = strlen(m_Message);
myvsnprintf(m_Message + len, MAX_ERRORTEXT - len, moreinfo, ap);
}
Expand Down
5 changes: 0 additions & 5 deletions src/gamedata/info.cpp
Expand Up @@ -777,11 +777,6 @@ static void SummonActor (int command, int command2, FCommandLine argv)
Printf ("Unknown actor '%s'\n", argv[1]);
return;
}
if (type->bAbstract)
{
Printf("Cannot instantiate abstract class %s\n", argv[1]);
return;
}
Net_WriteByte (argv.argc() > 2 ? command2 : command);
Net_WriteString (type->TypeName.GetChars());

Expand Down
6 changes: 6 additions & 0 deletions src/playsim/p_mobj.cpp
Expand Up @@ -4626,6 +4626,12 @@ AActor *AActor::StaticSpawn(FLevelLocals *Level, PClassActor *type, const DVecto
{
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)
{
Expand Down
13 changes: 13 additions & 0 deletions src/scripting/decorate/thingdef_parse.cpp
Expand Up @@ -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());
}
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;
PClassActor *type = static_cast<PClassActor *>(parent->CreateDerivedClass(typeName, parent->Size, &newlycreated));
if (type == nullptr)
Expand Down

0 comments on commit 761dea8

Please sign in to comment.