mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2025-02-03 11:50:57 +00:00
- did a bit of cleanup.
- disabled the runtime check in OP_CALL because as implemented it doesn't clean up properly and is not fully implemented.
This commit is contained in:
parent
3338fb7f33
commit
fd4727e701
6 changed files with 210 additions and 175 deletions
|
@ -1168,6 +1168,7 @@ set (PCH_SOURCES
|
||||||
scripting/thingdef_data.cpp
|
scripting/thingdef_data.cpp
|
||||||
scripting/thingdef_properties.cpp
|
scripting/thingdef_properties.cpp
|
||||||
scripting/backend/codegen.cpp
|
scripting/backend/codegen.cpp
|
||||||
|
scripting/backend/scopebarrier.cpp
|
||||||
scripting/backend/dynarrays.cpp
|
scripting/backend/dynarrays.cpp
|
||||||
scripting/backend/vmbuilder.cpp
|
scripting/backend/vmbuilder.cpp
|
||||||
scripting/backend/vmdisasm.cpp
|
scripting/backend/vmdisasm.cpp
|
||||||
|
|
|
@ -8725,6 +8725,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
||||||
ExpEmit selfemit;
|
ExpEmit selfemit;
|
||||||
if (Function->Variants[0].Flags & VARF_Method)
|
if (Function->Variants[0].Flags & VARF_Method)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
// [ZZ]
|
// [ZZ]
|
||||||
if (Function->Variants[0].Implementation && Function->Variants[0].Implementation->BarrierSide == FScopeBarrier::Side_Virtual)
|
if (Function->Variants[0].Implementation && Function->Variants[0].Implementation->BarrierSide == FScopeBarrier::Side_Virtual)
|
||||||
{
|
{
|
||||||
|
@ -8734,6 +8735,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
||||||
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(CallingFunction, ATAG_OBJECT));
|
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(CallingFunction, ATAG_OBJECT));
|
||||||
count += 2;
|
count += 2;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
assert(Self != nullptr);
|
assert(Self != nullptr);
|
||||||
selfemit = Self->Emit(build);
|
selfemit = Self->Emit(build);
|
||||||
assert((selfemit.RegType == REGT_POINTER) || (selfemit.Fixed && selfemit.Target));
|
assert((selfemit.RegType == REGT_POINTER) || (selfemit.Fixed && selfemit.Target));
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "s_sound.h"
|
#include "s_sound.h"
|
||||||
#include "actor.h"
|
#include "actor.h"
|
||||||
#include "vmbuilder.h"
|
#include "vmbuilder.h"
|
||||||
|
#include "scopebarrier.h"
|
||||||
|
|
||||||
|
|
||||||
#define CHECKRESOLVED() if (isresolved) return this; isresolved=true;
|
#define CHECKRESOLVED() if (isresolved) return this; isresolved=true;
|
||||||
|
@ -70,181 +71,6 @@ class FxCompoundStatement;
|
||||||
class FxLocalVariableDeclaration;
|
class FxLocalVariableDeclaration;
|
||||||
typedef TDeletingArray<FxExpression*> FArgumentList;
|
typedef TDeletingArray<FxExpression*> FArgumentList;
|
||||||
|
|
||||||
//
|
|
||||||
// [ZZ] this really should be in codegen.h, but vmexec needs to access it
|
|
||||||
struct FScopeBarrier
|
|
||||||
{
|
|
||||||
bool callable;
|
|
||||||
bool readable;
|
|
||||||
bool writable;
|
|
||||||
|
|
||||||
// this is the error message
|
|
||||||
FString callerror;
|
|
||||||
FString readerror;
|
|
||||||
FString writeerror;
|
|
||||||
|
|
||||||
// this is used to make the error message.
|
|
||||||
enum Side
|
|
||||||
{
|
|
||||||
Side_PlainData = 0,
|
|
||||||
Side_UI = 1,
|
|
||||||
Side_Play = 2,
|
|
||||||
Side_Virtual = 3, // do NOT change the value
|
|
||||||
Side_Clear = 4
|
|
||||||
};
|
|
||||||
int sidefrom;
|
|
||||||
int sidelast;
|
|
||||||
|
|
||||||
// Note: the same object can't be both UI and Play. This is checked explicitly in the field construction and will cause esoteric errors here if found.
|
|
||||||
static int SideFromFlags(int flags)
|
|
||||||
{
|
|
||||||
if (flags & VARF_UI)
|
|
||||||
return Side_UI;
|
|
||||||
if (flags & VARF_Play)
|
|
||||||
return Side_Play;
|
|
||||||
if (flags & VARF_VirtualScope)
|
|
||||||
return Side_Virtual;
|
|
||||||
if (flags & VARF_ClearScope)
|
|
||||||
return Side_Clear;
|
|
||||||
return Side_PlainData;
|
|
||||||
}
|
|
||||||
|
|
||||||
// same as above, but from object flags
|
|
||||||
static int SideFromObjectFlags(int flags)
|
|
||||||
{
|
|
||||||
if (flags & OF_UI)
|
|
||||||
return Side_UI;
|
|
||||||
if (flags & OF_Play)
|
|
||||||
return Side_Play;
|
|
||||||
return Side_PlainData;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
static int FlagsFromSide(int side)
|
|
||||||
{
|
|
||||||
switch (side)
|
|
||||||
{
|
|
||||||
case Side_Play:
|
|
||||||
return VARF_Play;
|
|
||||||
case Side_UI:
|
|
||||||
return VARF_UI;
|
|
||||||
case Side_Virtual:
|
|
||||||
return VARF_VirtualScope;
|
|
||||||
case Side_Clear:
|
|
||||||
return VARF_ClearScope;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// used for errors
|
|
||||||
static const char* StringFromSide(int side)
|
|
||||||
{
|
|
||||||
switch (side)
|
|
||||||
{
|
|
||||||
case Side_PlainData:
|
|
||||||
return "data";
|
|
||||||
case Side_UI:
|
|
||||||
return "ui";
|
|
||||||
case Side_Play:
|
|
||||||
return "play";
|
|
||||||
case Side_Virtual:
|
|
||||||
return "virtualscope"; // should not happen!
|
|
||||||
case Side_Clear:
|
|
||||||
return "clearscope"; // should not happen!
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this modifies VARF_ flags and sets the side properly.
|
|
||||||
static int ChangeSideInFlags(int flags, int side)
|
|
||||||
{
|
|
||||||
flags &= ~(VARF_UI | VARF_Play | VARF_VirtualScope | VARF_ClearScope);
|
|
||||||
flags |= FlagsFromSide(side);
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
FScopeBarrier()
|
|
||||||
{
|
|
||||||
sidefrom = -1;
|
|
||||||
sidelast = -1;
|
|
||||||
callable = true;
|
|
||||||
readable = true;
|
|
||||||
writable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
FScopeBarrier(int flags1, int flags2, const char* name)
|
|
||||||
{
|
|
||||||
sidefrom = -1;
|
|
||||||
sidelast = -1;
|
|
||||||
callable = true;
|
|
||||||
readable = true;
|
|
||||||
writable = true;
|
|
||||||
|
|
||||||
AddFlags(flags1, flags2, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFlags modifies ALLOWED actions by flags1->flags2.
|
|
||||||
// This is used for comparing a.b.c.d access - if non-allowed field is seen anywhere in the chain, anything after it is non-allowed.
|
|
||||||
// This struct is used so that the logic is in a single place.
|
|
||||||
void AddFlags(int flags1, int flags2, const char* name)
|
|
||||||
{
|
|
||||||
// note: if it's already non-readable, don't even try advancing
|
|
||||||
if (!readable)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// we aren't interested in any other flags
|
|
||||||
// - update: including VARF_VirtualScope. inside the function itself, we treat it as if it's PlainData.
|
|
||||||
flags1 &= VARF_UI | VARF_Play;
|
|
||||||
flags2 &= VARF_UI | VARF_Play | VARF_ReadOnly;
|
|
||||||
|
|
||||||
if (sidefrom < 0) sidefrom = SideFromFlags(flags1);
|
|
||||||
if (sidelast < 0) sidelast = sidefrom;
|
|
||||||
|
|
||||||
// flags1 = what's trying to access
|
|
||||||
// flags2 = what's being accessed
|
|
||||||
|
|
||||||
int sideto = SideFromFlags(flags2);
|
|
||||||
|
|
||||||
// plain data inherits whatever scope modifiers that context or field container has.
|
|
||||||
// i.e. play String bla; is play, and all non-specified methods/fields inside it are play as well.
|
|
||||||
if (sideto != Side_PlainData)
|
|
||||||
sidelast = sideto;
|
|
||||||
else sideto = sidelast;
|
|
||||||
|
|
||||||
if ((sideto == Side_UI) && (sidefrom != Side_UI)) // only ui -> ui is readable
|
|
||||||
{
|
|
||||||
readable = false;
|
|
||||||
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;
|
|
||||||
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;
|
|
||||||
if (name) callerror.Format("Can't call %s function %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FCompileContext
|
struct FCompileContext
|
||||||
{
|
{
|
||||||
FxExpression *ControlStmt = nullptr;
|
FxExpression *ControlStmt = nullptr;
|
||||||
|
|
152
src/scripting/backend/scopebarrier.cpp
Normal file
152
src/scripting/backend/scopebarrier.cpp
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
#include "scopebarrier.h"
|
||||||
|
#include "dobject.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Note: the same object can't be both UI and Play. This is checked explicitly in the field construction and will cause esoteric errors here if found.
|
||||||
|
int FScopeBarrier::SideFromFlags(int flags)
|
||||||
|
{
|
||||||
|
if (flags & VARF_UI)
|
||||||
|
return Side_UI;
|
||||||
|
if (flags & VARF_Play)
|
||||||
|
return Side_Play;
|
||||||
|
if (flags & VARF_VirtualScope)
|
||||||
|
return Side_Virtual;
|
||||||
|
if (flags & VARF_ClearScope)
|
||||||
|
return Side_Clear;
|
||||||
|
return Side_PlainData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// same as above, but from object flags
|
||||||
|
int FScopeBarrier::SideFromObjectFlags(int flags)
|
||||||
|
{
|
||||||
|
if (flags & OF_UI)
|
||||||
|
return Side_UI;
|
||||||
|
if (flags & OF_Play)
|
||||||
|
return Side_Play;
|
||||||
|
return Side_PlainData;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
int FScopeBarrier::FlagsFromSide(int side)
|
||||||
|
{
|
||||||
|
switch (side)
|
||||||
|
{
|
||||||
|
case Side_Play:
|
||||||
|
return VARF_Play;
|
||||||
|
case Side_UI:
|
||||||
|
return VARF_UI;
|
||||||
|
case Side_Virtual:
|
||||||
|
return VARF_VirtualScope;
|
||||||
|
case Side_Clear:
|
||||||
|
return VARF_ClearScope;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// used for errors
|
||||||
|
const char* FScopeBarrier::StringFromSide(int side)
|
||||||
|
{
|
||||||
|
switch (side)
|
||||||
|
{
|
||||||
|
case Side_PlainData:
|
||||||
|
return "data";
|
||||||
|
case Side_UI:
|
||||||
|
return "ui";
|
||||||
|
case Side_Play:
|
||||||
|
return "play";
|
||||||
|
case Side_Virtual:
|
||||||
|
return "virtualscope"; // should not happen!
|
||||||
|
case Side_Clear:
|
||||||
|
return "clearscope"; // should not happen!
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this modifies VARF_ flags and sets the side properly.
|
||||||
|
int FScopeBarrier::ChangeSideInFlags(int flags, int side)
|
||||||
|
{
|
||||||
|
flags &= ~(VARF_UI | VARF_Play | VARF_VirtualScope | VARF_ClearScope);
|
||||||
|
flags |= FlagsFromSide(side);
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
FScopeBarrier::FScopeBarrier()
|
||||||
|
{
|
||||||
|
sidefrom = -1;
|
||||||
|
sidelast = -1;
|
||||||
|
callable = true;
|
||||||
|
readable = true;
|
||||||
|
writable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FScopeBarrier::FScopeBarrier(int flags1, int flags2, const char* name)
|
||||||
|
{
|
||||||
|
sidefrom = -1;
|
||||||
|
sidelast = -1;
|
||||||
|
callable = true;
|
||||||
|
readable = true;
|
||||||
|
writable = true;
|
||||||
|
|
||||||
|
AddFlags(flags1, flags2, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlags modifies ALLOWED actions by flags1->flags2.
|
||||||
|
// This is used for comparing a.b.c.d access - if non-allowed field is seen anywhere in the chain, anything after it is non-allowed.
|
||||||
|
// This struct is used so that the logic is in a single place.
|
||||||
|
void FScopeBarrier::AddFlags(int flags1, int flags2, const char* name)
|
||||||
|
{
|
||||||
|
// note: if it's already non-readable, don't even try advancing
|
||||||
|
if (!readable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// we aren't interested in any other flags
|
||||||
|
// - update: including VARF_VirtualScope. inside the function itself, we treat it as if it's PlainData.
|
||||||
|
flags1 &= VARF_UI | VARF_Play;
|
||||||
|
flags2 &= VARF_UI | VARF_Play | VARF_ReadOnly;
|
||||||
|
|
||||||
|
if (sidefrom < 0) sidefrom = SideFromFlags(flags1);
|
||||||
|
if (sidelast < 0) sidelast = sidefrom;
|
||||||
|
|
||||||
|
// flags1 = what's trying to access
|
||||||
|
// flags2 = what's being accessed
|
||||||
|
|
||||||
|
int sideto = SideFromFlags(flags2);
|
||||||
|
|
||||||
|
// plain data inherits whatever scope modifiers that context or field container has.
|
||||||
|
// i.e. play String bla; is play, and all non-specified methods/fields inside it are play as well.
|
||||||
|
if (sideto != Side_PlainData)
|
||||||
|
sidelast = sideto;
|
||||||
|
else sideto = sidelast;
|
||||||
|
|
||||||
|
if ((sideto == Side_UI) && (sidefrom != Side_UI)) // only ui -> ui is readable
|
||||||
|
{
|
||||||
|
readable = false;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
if (name) callerror.Format("Can't call %s function %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom));
|
||||||
|
}
|
||||||
|
}
|
52
src/scripting/backend/scopebarrier.h
Normal file
52
src/scripting/backend/scopebarrier.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "zstring.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// [ZZ] this really should be in codegen.h, but vmexec needs to access it
|
||||||
|
struct FScopeBarrier
|
||||||
|
{
|
||||||
|
bool callable;
|
||||||
|
bool readable;
|
||||||
|
bool writable;
|
||||||
|
|
||||||
|
// this is the error message
|
||||||
|
FString callerror;
|
||||||
|
FString readerror;
|
||||||
|
FString writeerror;
|
||||||
|
|
||||||
|
// this is used to make the error message.
|
||||||
|
enum Side
|
||||||
|
{
|
||||||
|
Side_PlainData = 0,
|
||||||
|
Side_UI = 1,
|
||||||
|
Side_Play = 2,
|
||||||
|
Side_Virtual = 3, // do NOT change the value
|
||||||
|
Side_Clear = 4
|
||||||
|
};
|
||||||
|
int sidefrom;
|
||||||
|
int sidelast;
|
||||||
|
|
||||||
|
// Note: the same object can't be both UI and Play. This is checked explicitly in the field construction and will cause esoteric errors here if found.
|
||||||
|
static int SideFromFlags(int flags);
|
||||||
|
|
||||||
|
// same as above, but from object flags
|
||||||
|
static int SideFromObjectFlags(int flags);
|
||||||
|
|
||||||
|
//
|
||||||
|
static int FlagsFromSide(int side);
|
||||||
|
|
||||||
|
// used for errors
|
||||||
|
static const char* StringFromSide(int side);
|
||||||
|
|
||||||
|
// this modifies VARF_ flags and sets the side properly.
|
||||||
|
static int ChangeSideInFlags(int flags, int side);
|
||||||
|
FScopeBarrier();
|
||||||
|
FScopeBarrier(int flags1, int flags2, const char* name);
|
||||||
|
|
||||||
|
// AddFlags modifies ALLOWED actions by flags1->flags2.
|
||||||
|
// This is used for comparing a.b.c.d access - if non-allowed field is seen anywhere in the chain, anything after it is non-allowed.
|
||||||
|
// This struct is used so that the logic is in a single place.
|
||||||
|
void AddFlags(int flags1, int flags2, const char* name);
|
||||||
|
};
|
||||||
|
|
|
@ -664,6 +664,7 @@ begin:
|
||||||
VMReturn returns[MAX_RETURNS];
|
VMReturn returns[MAX_RETURNS];
|
||||||
int numret;
|
int numret;
|
||||||
|
|
||||||
|
#if 0
|
||||||
// [ZZ] hax!
|
// [ZZ] hax!
|
||||||
b = B;
|
b = B;
|
||||||
if (call->BarrierSide == 3) // :( - this is Side_Virtual. Side_Virtual should receive special arguments.
|
if (call->BarrierSide == 3) // :( - this is Side_Virtual. Side_Virtual should receive special arguments.
|
||||||
|
@ -675,6 +676,7 @@ begin:
|
||||||
FScopeBarrier_ValidateCall(calledfunc, callingfunc, selftype);
|
FScopeBarrier_ValidateCall(calledfunc, callingfunc, selftype);
|
||||||
b -= 2;
|
b -= 2;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
FillReturns(reg, f, returns, pc+1, C);
|
FillReturns(reg, f, returns, pc+1, C);
|
||||||
if (call->Native)
|
if (call->Native)
|
||||||
|
|
Loading…
Reference in a new issue