mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-01-18 23:21:41 +00:00
- Implemented ZScript abstract functions
This commit is contained in:
parent
628d23a688
commit
761dea8640
9 changed files with 83 additions and 23 deletions
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
@ -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.
|
||||
|
|
|
@ -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 ------------------------------------
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -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))
|
||||
|
@ -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++)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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, "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());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)))
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue