From cbb990a79e625357dee848bbc98d653f6fc9e09b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Oct 2016 17:40:27 +0200 Subject: [PATCH] - pass the current function to the compile context instead of just the containing class. --- src/dobjtype.cpp | 4 +- src/dobjtype.h | 1 + src/scripting/codegeneration/codegen.cpp | 116 ++++++++++++++++------- src/scripting/codegeneration/codegen.h | 9 +- src/scripting/thingdef.cpp | 6 +- src/scripting/vm/vmbuilder.cpp | 8 +- src/scripting/zscript/zcc_compile.cpp | 5 + 7 files changed, 101 insertions(+), 48 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index c43abf9a7..966663f12 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2509,7 +2509,9 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray &argflags, TArra if (flags & VARF_Method) { assert(proto->ArgumentTypes.Size() > 0); - variant.SelfClass = dyn_cast(proto->ArgumentTypes[0]); + auto selftypeptr = dyn_cast(proto->ArgumentTypes[0]); + assert(selftypeptr != nullptr); + variant.SelfClass = dyn_cast(selftypeptr->PointedType); assert(variant.SelfClass != nullptr); } diff --git a/src/dobjtype.h b/src/dobjtype.h index 07d7ae10f..a5da177a4 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -29,6 +29,7 @@ enum VARF_In = (1<<10), VARF_Out = (1<<11), VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare when checking function signatures) + VARF_Static = (1<<13), // static class data (by necessity read only.) }; // Symbol information ------------------------------------------------------- diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 801df8c65..4ed712834 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -49,7 +49,7 @@ #include "thingdef.h" #include "p_lnspec.h" #include "doomstat.h" -#include "thingdef_exp.h" +#include "codegen.h" #include "m_fixed.h" #include "vmbuilder.h" #include "v_text.h" @@ -91,13 +91,25 @@ static const FLOP FxFlops[] = // //========================================================================== -FCompileContext::FCompileContext(PClass *cls, PPrototype *ret) : Class(cls), ReturnProto(ret) +FCompileContext::FCompileContext(PFunction *fnc, PPrototype *ret) : ReturnProto(ret), Function(fnc), Class(nullptr) +{ + if (fnc != nullptr) Class = fnc->OwningClass; +} + +FCompileContext::FCompileContext(PClass *cls) : ReturnProto(nullptr), Function(nullptr), Class(cls) { } -PSymbol *FCompileContext::FindInClass(FName identifier) +PSymbol *FCompileContext::FindInClass(FName identifier, PSymbolTable *&symt) { - return Class ? Class->Symbols.FindSymbol(identifier, true) : nullptr; + return Class != nullptr? Class->Symbols.FindSymbolInTable(identifier, symt) : nullptr; +} + +PSymbol *FCompileContext::FindInSelfClass(FName identifier, PSymbolTable *&symt) +{ + // If we have no self we cannot retrieve any values from it. + if (Function == nullptr || Function->Variants[0].SelfClass == nullptr) return nullptr; + return Function->Variants[0].SelfClass->Symbols.FindSymbolInTable(identifier, symt); } PSymbol *FCompileContext::FindGlobal(FName identifier) { @@ -3178,15 +3190,16 @@ FxIdentifier::FxIdentifier(FName name, const FScriptPosition &pos) FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) { PSymbol * sym; - FxExpression *newex = NULL; - //FBaseCVar * cv = NULL; - //FString s; + FxExpression *newex = nullptr; int num; - //const PClass *Class; CHECKRESOLVED(); + + // Ugh, the horror. Constants need to be taken from the owning class, but members from the self class to catch invalid accesses here... + // see if the current class (if valid) defines something with this name. - if ((sym = ctx.FindInClass(Identifier)) != NULL) + PSymbolTable *symtbl; + if ((sym = ctx.FindInClass(Identifier, symtbl)) != nullptr) { if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) { @@ -3195,15 +3208,64 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) } else if (sym->IsKindOf(RUNTIME_CLASS(PField))) { + ABORT(ctx.Function); // only valid when resolving a function. PField *vsym = static_cast(sym); - ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as member variable, index %d\n", Identifier.GetChars(), vsym->Offset); - newex = new FxClassMember((new FxSelf(ScriptPosition))->Resolve(ctx), vsym, ScriptPosition); + + // We have 4 cases to consider here: + // 1. The symbol is a static/meta member (not implemented yet) which is always accessible. + // 2. This is a static function + // 3. This is an action function with a restricted self pointer + // 4. This is a normal member or unrestricted action function. + if (vsym->Flags & VARF_Deprecated) + { + ScriptPosition.Message(MSG_WARNING, "Accessing deprecated member variable %s", vsym->SymbolName.GetChars()); + } + if ((vsym->Flags & VARF_Private) && symtbl != &ctx.Class->Symbols) + { + ScriptPosition.Message(MSG_ERROR, "Private member %s not accessible", vsym->SymbolName.GetChars()); + delete this; + return nullptr; + } + + if (vsym->Flags & VARF_Static) + { + // todo. For now these cannot be defined so let's just exit. + delete this; + return nullptr; + } + + if (ctx.Function->Variants[0].SelfClass == nullptr) + { + ScriptPosition.Message(MSG_ERROR, "Unable to access class member from static function"); + delete this; + return nullptr; + } + else + { + if (ctx.Function->Variants[0].SelfClass != ctx.Class) + { + // Check if the restricted class can access it. + PSymbol *sym2; + if ((sym2 = ctx.FindInSelfClass(Identifier, symtbl)) != nullptr) + { + if (sym != sym2) + { + ScriptPosition.Message(MSG_ERROR, "Member variable of %s not accessible through restricted self pointer", ctx.Class->TypeName.GetChars()); + delete this; + return nullptr; + } + } + } + ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as member variable, index %d\n", Identifier.GetChars(), vsym->Offset); + newex = new FxClassMember((new FxSelf(ScriptPosition))->Resolve(ctx), vsym, ScriptPosition); + } } else { ScriptPosition.Message(MSG_ERROR, "Invalid member identifier '%s'\n", Identifier.GetChars()); } } + // now check the global identifiers. else if ((sym = ctx.FindGlobal(Identifier)) != NULL) { @@ -3217,23 +3279,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) ScriptPosition.Message(MSG_ERROR, "Invalid global identifier '%s'\n", Identifier.GetChars()); } } - /* - else if ((Class = PClass::FindClass(Identifier))) - { - pos.Message(MSG_DEBUGLOG, "Resolving name '%s' as class name\n", Identifier.GetChars()); - newex = new FxClassType(Class, ScriptPosition); - } - } - */ - // also check for CVars - /* - else if ((cv = FindCVar(Identifier, NULL)) != NULL) - { - CLOG(CL_RESOLVE, LPrintf("Resolving name '%s' as cvar\n", Identifier.GetChars())); - newex = new FxCVar(cv, ScriptPosition); - } - */ // and line specials else if ((num = P_FindLineSpecial(Identifier, NULL, NULL))) { @@ -3270,15 +3316,13 @@ FxSelf::FxSelf(const FScriptPosition &pos) FxExpression *FxSelf::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); - if (!ctx.Class) + if (ctx.Function == nullptr || ctx.Function->Variants[0].SelfClass == nullptr) { - // can't really happen with DECORATE's expression evaluator. ScriptPosition.Message(MSG_ERROR, "self used outside of a member function"); delete this; - return NULL; + return nullptr; } - ValueType = ctx.Class; - ValueType = NewPointer(RUNTIME_CLASS(DObject)); + ValueType = NewPointer(ctx.Function->Variants[0].SelfClass); return this; } @@ -3614,8 +3658,10 @@ FxFunctionCall::~FxFunctionCall() FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) { - // This part is mostly a kludge, it really needs to get the class type from Self. + ABORT(ctx.Class); + PFunction *afd = dyn_cast(ctx.Class->Symbols.FindSymbol(MethodName, true)); + // Todo: More checks are needed here to make it work as expected. if (afd != nullptr) { auto x = new FxVMFunctionCall(afd, ArgList, ScriptPosition, false); @@ -5172,9 +5218,9 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx) delete this; return NULL; } - else if (!scope->IsDescendantOf(ctx.Class)) + else if (!scope->IsAncestorOf(ctx.Class)) { - ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(),ctx.Class->TypeName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars()); delete this; return NULL; } diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index 3c52547c5..d69323ce0 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -67,11 +67,14 @@ struct FCompileContext { TArray Jumps; PPrototype *ReturnProto; - PClass *Class; + PFunction *Function; // The function that is currently being compiled (or nullptr for constant evaluation.) + PClass *Class; // The type of the owning class. - FCompileContext(PClass *cls = nullptr, PPrototype *ret = nullptr); + FCompileContext(PFunction *func = nullptr, PPrototype *ret = nullptr); + FCompileContext(PClass *cls); // only to be used to resolve constants! - PSymbol *FindInClass(FName identifier); + PSymbol *FindInClass(FName identifier, PSymbolTable *&symt); + PSymbol *FindInSelfClass(FName identifier, PSymbolTable *&symt); PSymbol *FindGlobal(FName identifier); void HandleJumps(int token, FxExpression *handler); diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index f80a8f87a..413999ddc 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -89,7 +89,7 @@ void SetImplicitArgs(TArray *args, TArray *argflags, TArrayPush(cls); + if (args != nullptr) args->Push(NewPointer(cls)); if (argflags != nullptr) argflags->Push(VARF_Implicit); if (argnames != nullptr) argnames->Push(NAME_self); } @@ -101,11 +101,11 @@ void SetImplicitArgs(TArray *args, TArray *argflags, TArrayIsDescendantOf(RUNTIME_CLASS(AStateProvider))) { - args->Insert(0, RUNTIME_CLASS(AActor)); // this must go in before the real pointer to the containing class. + args->Insert(0, NewPointer(RUNTIME_CLASS(AActor))); // this must go in before the real pointer to the containing class. } else { - args->Push(cls); + args->Push(NewPointer(cls)); } args->Push(TypeState/*Info*/); // fixme: TypeState is not the correct type here!!! } diff --git a/src/scripting/vm/vmbuilder.cpp b/src/scripting/vm/vmbuilder.cpp index f366ec650..7c5118467 100644 --- a/src/scripting/vm/vmbuilder.cpp +++ b/src/scripting/vm/vmbuilder.cpp @@ -686,9 +686,8 @@ void FFunctionBuildList::Build() // This needs to be fixed, so that the compile context receives the entire function symbol, including the containing class, the prototype and argument names, which will be needed to run the code generator // As a first step this just needs to get working so fetch the class type from the prototype's argument list. - auto cls = static_cast(item.Func->Variants[0].Proto->ArgumentTypes[!!(item.Func->Variants[0].Flags & VARF_Action)]); // We don't know the return type in advance for anonymous functions. - FCompileContext ctx(cls, nullptr); + FCompileContext ctx(item.Func, nullptr); item.Code = item.Code->Resolve(ctx); item.Proto = ctx.ReturnProto; @@ -715,10 +714,7 @@ void FFunctionBuildList::Build() if (dump != nullptr) { - char label[64]; - int labellen = mysnprintf(label, countof(label), item.DumpName, - cls->TypeName.GetChars()); - DumpFunction(dump, sfunc, label, labellen); + DumpFunction(dump, sfunc, item.DumpName.GetChars(), (int)item.DumpName.Len()); codesize += sfunc->CodeSize; } } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index ee40868d3..aee7e8b76 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -1888,6 +1888,11 @@ void ZCCCompiler::InitFunctions() do { auto type = DetermineType(c->Type(), f, f->Name, t, false); + if (type->IsKindOf(RUNTIME_CLASS(PStruct))) + { + // structs and classes only get passed by pointer. + type = NewPointer(type); + } // TBD: disallow certain types? For now, let everything pass that isn't an array. rets.Push(type); t = static_cast(t->SiblingNext);