mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-12-01 16:41:22 +00:00
Dynamic virtualscope checking. May yet be buggy.
This commit is contained in:
parent
b5ab011bb9
commit
e0ae0fdb2e
6 changed files with 92 additions and 20 deletions
|
@ -89,6 +89,34 @@ static const FLOP FxFlops[] =
|
|||
{ NAME_TanH, FLOP_TANH, [](double v) { return g_tanh(v); } },
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// [ZZ] Magic methods to be used in vmexec.h for runtime checking of scope
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
// this can be imported in vmexec.h
|
||||
void FScopeBarrier_ValidateNew(PClass* cls, PFunction* callingfunc)
|
||||
{
|
||||
int outerside = FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags);
|
||||
int innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags);
|
||||
if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) // "cannot construct ui class ... from data context"
|
||||
ThrowAbortException(X_OTHER, "Cannot construct %s class %s from %s context", FScopeBarrier::StringFromSide(innerside), cls->TypeName.GetChars(), FScopeBarrier::StringFromSide(outerside));
|
||||
}
|
||||
// this can be imported in vmexec.h
|
||||
void FScopeBarrier_ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype)
|
||||
{
|
||||
int outerside = FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags);
|
||||
if (outerside == FScopeBarrier::Side_Virtual)
|
||||
outerside = FScopeBarrier::Side_PlainData;
|
||||
int innerside = FScopeBarrier::SideFromFlags(calledfunc->Variants[0].Flags);
|
||||
if (innerside == FScopeBarrier::Side_Virtual)
|
||||
innerside = FScopeBarrier::SideFromObjectFlags(selftype->ObjectFlags);
|
||||
if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData))
|
||||
ThrowAbortException(X_OTHER, "Cannot call %s function %s from %s context", FScopeBarrier::StringFromSide(innerside), calledfunc->SymbolName.GetChars(), FScopeBarrier::StringFromSide(outerside));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FCompileContext
|
||||
|
@ -8300,6 +8328,7 @@ FxVMFunctionCall::FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumen
|
|||
ArgList = std::move(args);
|
||||
EmitTail = false;
|
||||
NoVirtual = novirtual;
|
||||
CallingFunction = nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -8373,6 +8402,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
CallingFunction = ctx.Function;
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
bool foundvarargs = false;
|
||||
|
@ -8580,6 +8610,15 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
|||
ExpEmit selfemit;
|
||||
if (Function->Variants[0].Flags & VARF_Method)
|
||||
{
|
||||
// [ZZ]
|
||||
if (Function->Variants[0].Implementation && Function->Variants[0].Implementation->BarrierSide == FScopeBarrier::Side_Virtual)
|
||||
{
|
||||
// pass this even before Self, because otherwise we can't silently advance the arguments.
|
||||
// this is not even implicit arguments.
|
||||
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(Function, ATAG_OBJECT));
|
||||
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(CallingFunction, ATAG_OBJECT));
|
||||
count += 2;
|
||||
}
|
||||
assert(Self != nullptr);
|
||||
selfemit = Self->Emit(build);
|
||||
assert((selfemit.RegType == REGT_POINTER) || (selfemit.Fixed && selfemit.Target));
|
||||
|
|
|
@ -70,7 +70,8 @@ class FxCompoundStatement;
|
|||
class FxLocalVariableDeclaration;
|
||||
typedef TDeletingArray<FxExpression*> FArgumentList;
|
||||
|
||||
// [ZZ] this is kind of related to compile context as well
|
||||
//
|
||||
// [ZZ] this really should be in codegen.h, but vmexec needs to access it
|
||||
struct FScopeBarrier
|
||||
{
|
||||
bool callable;
|
||||
|
@ -86,9 +87,9 @@ struct FScopeBarrier
|
|||
enum Side
|
||||
{
|
||||
Side_PlainData = 0,
|
||||
Side_UI,
|
||||
Side_Play,
|
||||
Side_Virtual
|
||||
Side_UI = 1,
|
||||
Side_Play = 2,
|
||||
Side_Virtual = 3 // do NOT change the value
|
||||
};
|
||||
int sidefrom;
|
||||
int sidelast;
|
||||
|
@ -152,7 +153,7 @@ struct FScopeBarrier
|
|||
// this modifies VARF_ flags and sets the side properly.
|
||||
static int ChangeSideInFlags(int flags, int side)
|
||||
{
|
||||
flags &= ~(VARF_UI | VARF_Play);
|
||||
flags &= ~(VARF_UI | VARF_Play | VARF_VirtualScope);
|
||||
flags |= FlagsFromSide(side);
|
||||
return flags;
|
||||
}
|
||||
|
@ -208,28 +209,31 @@ struct FScopeBarrier
|
|||
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), name, StringFromSide(sidefrom));
|
||||
if (name) readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom));
|
||||
}
|
||||
|
||||
if (!readable)
|
||||
{
|
||||
writable = false;
|
||||
callable = false;
|
||||
if (name)
|
||||
{
|
||||
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 (name) 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 function %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom));
|
||||
if (name) callerror.Format("Can't call %s function %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1873,6 +1877,7 @@ class FxVMFunctionCall : public FxExpression
|
|||
// for multi assignment
|
||||
int AssignCount = 0;
|
||||
TArray<ExpEmit> ReturnRegs;
|
||||
PFunction *CallingFunction;
|
||||
|
||||
public:
|
||||
FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual);
|
||||
|
|
|
@ -163,6 +163,10 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i
|
|||
// Functions that only get flagged for actors do not need the additional two context parameters.
|
||||
int fflags = (flags& (SUF_OVERLAY | SUF_WEAPON | SUF_ITEM)) ? VARF_Action | VARF_Method : VARF_Method;
|
||||
|
||||
// [ZZ] give anonymous functions the scope of their class
|
||||
// (just give them VARF_Play, whatever)
|
||||
fflags |= VARF_Play;
|
||||
|
||||
rets[0] = returntype != nullptr? returntype : TypeError; // Use TypeError as placeholder if we do not know the return type yet.
|
||||
SetImplicitArgs(&args, &argflags, &argnames, containingclass, fflags, flags);
|
||||
|
||||
|
|
|
@ -8,6 +8,11 @@
|
|||
#include "doomerrors.h"
|
||||
#include "memarena.h"
|
||||
|
||||
// [ZZ] there are serious circular references between this and the rest of ZScript code, so it needs to be done like this
|
||||
// these are used in vmexec.h
|
||||
void FScopeBarrier_ValidateNew(PClass* cls, PFunction* callingfunc);
|
||||
void FScopeBarrier_ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype);
|
||||
|
||||
extern FMemArena ClassDataAllocator;
|
||||
|
||||
#define MAX_RETURNS 8 // Maximum number of results a function called by script code can return
|
||||
|
|
|
@ -648,13 +648,25 @@ begin:
|
|||
VMReturn returns[MAX_RETURNS];
|
||||
int numret;
|
||||
|
||||
// [ZZ] hax!
|
||||
b = B;
|
||||
if (call->BarrierSide == 3) // :( - this is Side_Virtual. Side_Virtual should receive special arguments.
|
||||
{
|
||||
PFunction* calledfunc = (PFunction*)(reg.param + f->NumParam - b)[0].a;
|
||||
PFunction* callingfunc = (PFunction*)(reg.param + f->NumParam - b)[1].a;
|
||||
DObject* dobj = (DObject*)(reg.param + f->NumParam - b)[2].a; // this is the self pointer. it should be in, since Side_Virtual functions are always non-static methods.
|
||||
PClass* selftype = dobj->GetClass();
|
||||
FScopeBarrier_ValidateCall(calledfunc, callingfunc, selftype);
|
||||
b -= 2;
|
||||
}
|
||||
|
||||
FillReturns(reg, f, returns, pc+1, C);
|
||||
if (call->Native)
|
||||
{
|
||||
try
|
||||
{
|
||||
VMCycles[0].Unclock();
|
||||
numret = static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, returns, C);
|
||||
numret = static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - b, call->DefaultArgs, b, returns, C);
|
||||
VMCycles[0].Clock();
|
||||
}
|
||||
catch (CVMAbortException &err)
|
||||
|
@ -670,7 +682,7 @@ begin:
|
|||
VMCalls[0]++;
|
||||
VMScriptFunction *script = static_cast<VMScriptFunction *>(call);
|
||||
VMFrame *newf = stack->AllocFrame(script);
|
||||
VMFillParams(reg.param + f->NumParam - B, newf, B);
|
||||
VMFillParams(reg.param + f->NumParam - b, newf, b);
|
||||
try
|
||||
{
|
||||
numret = Exec(stack, script->Code, returns, C);
|
||||
|
@ -801,6 +813,9 @@ begin:
|
|||
if (!callingfunc || pcls != callingfunc->OwningClass)
|
||||
ThrowAbortException(X_OTHER, "Cannot instantiate class %s directly", cls->TypeName.GetChars());
|
||||
}
|
||||
// [ZZ] validate readonly and between scope construction
|
||||
if (callingfunc)
|
||||
FScopeBarrier_ValidateNew(cls, callingfunc);
|
||||
reg.a[a] = cls->CreateNew();
|
||||
reg.atag[a] = ATAG_OBJECT;
|
||||
NEXTOP;
|
||||
|
|
|
@ -2401,6 +2401,17 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
sym->Variants[0].Implementation->DefaultArgs = std::move(argdefaults);
|
||||
}
|
||||
|
||||
if (sym->Variants[0].Implementation != nullptr)
|
||||
{
|
||||
// [ZZ] unspecified virtual function inherits old scope. virtual function scope can't be changed.
|
||||
if (varflags & VARF_UI)
|
||||
sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_UI;
|
||||
if (varflags & VARF_Play)
|
||||
sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Play;
|
||||
if (varflags & VARF_VirtualScope)
|
||||
sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Virtual;
|
||||
}
|
||||
|
||||
PClass *clstype = static_cast<PClass *>(c->Type());
|
||||
if (varflags & VARF_Virtual)
|
||||
{
|
||||
|
@ -2412,13 +2423,6 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
|
||||
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 (varflags & VARF_UI)
|
||||
sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_UI;
|
||||
if (varflags & VARF_Play)
|
||||
sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Play;
|
||||
if (varflags & VARF_VirtualScope)
|
||||
sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Virtual;
|
||||
if (varflags & VARF_ReadOnly)
|
||||
sym->Variants[0].Implementation->FuncConst = true;
|
||||
|
||||
|
|
Loading…
Reference in a new issue