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;
|
||||
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));
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue