mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-11 07:12:16 +00:00
This commit is contained in:
commit
b8b5360de1
31 changed files with 844 additions and 179 deletions
|
@ -1232,6 +1232,7 @@ set (PCH_SOURCES
|
|||
scripting/thingdef_data.cpp
|
||||
scripting/thingdef_properties.cpp
|
||||
scripting/backend/codegen.cpp
|
||||
scripting/backend/scopebarrier.cpp
|
||||
scripting/backend/dynarrays.cpp
|
||||
scripting/backend/vmbuilder.cpp
|
||||
scripting/backend/vmdisasm.cpp
|
||||
|
|
|
@ -207,7 +207,9 @@ enum EObjectFlags
|
|||
OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk)
|
||||
OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning)
|
||||
OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function
|
||||
OF_Abstract = 1 << 14, // Marks a class that cannot be created with CreateNew
|
||||
OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function at all
|
||||
OF_UI = 1 << 15, // Marks a class that defaults to VARF_UI for it's fields/methods
|
||||
OF_Play = 1 << 16, // Marks a class that defaults to VARF_Play for it's fields/methods
|
||||
};
|
||||
|
||||
template<class T> class TObjPtr;
|
||||
|
|
|
@ -37,6 +37,10 @@ enum
|
|||
VARF_Transient = (1<<17), // don't auto serialize field.
|
||||
VARF_Meta = (1<<18), // static class data (by necessity read only.)
|
||||
VARF_VarArg = (1<<19), // [ZZ] vararg: don't typecheck values after ... in function signature
|
||||
VARF_UI = (1<<20), // [ZZ] ui: object is ui-scope only (can't modify playsim)
|
||||
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.
|
||||
};
|
||||
|
||||
// An action function -------------------------------------------------------
|
||||
|
|
|
@ -117,6 +117,21 @@ bool E_UnregisterHandler(DStaticEventHandler* handler)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3)
|
||||
{
|
||||
if (gamestate != GS_LEVEL)
|
||||
return false;
|
||||
|
||||
Net_WriteByte(DEM_NETEVENT);
|
||||
Net_WriteString(name);
|
||||
Net_WriteByte(3);
|
||||
Net_WriteLong(arg1);
|
||||
Net_WriteLong(arg2);
|
||||
Net_WriteLong(arg3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool E_CheckHandler(DStaticEventHandler* handler)
|
||||
{
|
||||
for (DStaticEventHandler* lhandler = E_FirstEventHandler; lhandler; lhandler = lhandler->next)
|
||||
|
@ -521,6 +536,18 @@ DEFINE_ACTION_FUNCTION(DStaticEventHandler, SetOrder)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DEventHandler, SendNetworkEvent)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_STRING(name);
|
||||
PARAM_INT(arg1);
|
||||
PARAM_INT(arg2);
|
||||
PARAM_INT(arg3);
|
||||
//
|
||||
|
||||
ACTION_RETURN_BOOL(E_SendNetworkEvent(name, arg1, arg2, arg3));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DEventHandler, Create)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
|
@ -658,6 +685,7 @@ DEFINE_EMPTY_HANDLER(DStaticEventHandler, UiProcess);
|
|||
DEFINE_EMPTY_HANDLER(DStaticEventHandler, InputProcess);
|
||||
|
||||
DEFINE_EMPTY_HANDLER(DStaticEventHandler, ConsoleProcess);
|
||||
DEFINE_EMPTY_HANDLER(DStaticEventHandler, NetworkProcess);
|
||||
|
||||
// ===========================================
|
||||
//
|
||||
|
@ -1068,22 +1096,45 @@ static DConsoleEvent* E_SetupConsoleEvent()
|
|||
|
||||
void DStaticEventHandler::ConsoleProcess(int player, FString name, int arg1, int arg2, int arg3)
|
||||
{
|
||||
IFVIRTUAL(DStaticEventHandler, ConsoleProcess)
|
||||
if (player < 0)
|
||||
{
|
||||
// don't create excessive DObjects if not going to be processed anyway
|
||||
if (func == DStaticEventHandler_ConsoleProcess_VMPtr)
|
||||
return;
|
||||
DConsoleEvent* e = E_SetupConsoleEvent();
|
||||
IFVIRTUAL(DStaticEventHandler, ConsoleProcess)
|
||||
{
|
||||
// don't create excessive DObjects if not going to be processed anyway
|
||||
if (func == DStaticEventHandler_ConsoleProcess_VMPtr)
|
||||
return;
|
||||
DConsoleEvent* e = E_SetupConsoleEvent();
|
||||
|
||||
//
|
||||
e->Player = player;
|
||||
e->Name = name;
|
||||
e->Args[0] = arg1;
|
||||
e->Args[1] = arg2;
|
||||
e->Args[2] = arg3;
|
||||
//
|
||||
e->Player = player;
|
||||
e->Name = name;
|
||||
e->Args[0] = arg1;
|
||||
e->Args[1] = arg2;
|
||||
e->Args[2] = arg3;
|
||||
|
||||
VMValue params[2] = { (DStaticEventHandler*)this, e };
|
||||
GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr);
|
||||
VMValue params[2] = { (DStaticEventHandler*)this, e };
|
||||
GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IFVIRTUAL(DStaticEventHandler, NetworkProcess)
|
||||
{
|
||||
// don't create excessive DObjects if not going to be processed anyway
|
||||
if (func == DStaticEventHandler_NetworkProcess_VMPtr)
|
||||
return;
|
||||
DConsoleEvent* e = E_SetupConsoleEvent();
|
||||
|
||||
//
|
||||
e->Player = player;
|
||||
e->Name = name;
|
||||
e->Args[0] = arg1;
|
||||
e->Args[1] = arg2;
|
||||
e->Args[2] = arg3;
|
||||
|
||||
VMValue params[2] = { (DStaticEventHandler*)this, e };
|
||||
GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1136,10 +1187,6 @@ CCMD(netevent)
|
|||
for (int i = 0; i < argn; i++)
|
||||
arg[i] = atoi(argv[2 + i]);
|
||||
// call networked
|
||||
Net_WriteByte(DEM_NETEVENT);
|
||||
Net_WriteString(argv[1]);
|
||||
Net_WriteByte(argn);
|
||||
for (int i = 0; i < 3; i++)
|
||||
Net_WriteLong(arg[i]);
|
||||
E_SendNetworkEvent(argv[1], arg[0], arg[1], arg[2]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,9 @@ bool E_Responder(event_t* ev); // splits events into InputProcess and UiProcess
|
|||
// this executes on console/net events.
|
||||
void E_Console(int player, FString name, int arg1, int arg2, int arg3);
|
||||
|
||||
// send networked event. unified function.
|
||||
bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3);
|
||||
|
||||
// check if there is anything that should receive GUI events
|
||||
bool E_CheckUiProcessors();
|
||||
// check if we need native mouse due to UiProcessors
|
||||
|
|
|
@ -796,7 +796,16 @@ void M_ClearMenus()
|
|||
|
||||
void M_Init (void)
|
||||
{
|
||||
M_ParseMenuDefs();
|
||||
try
|
||||
{
|
||||
M_ParseMenuDefs();
|
||||
}
|
||||
catch (CVMAbortException &err)
|
||||
{
|
||||
err.MaybePrintMessage();
|
||||
Printf("%s", err.stacktrace.GetChars());
|
||||
I_FatalError("Failed to initialize menus");
|
||||
}
|
||||
M_CreateMenus();
|
||||
}
|
||||
|
||||
|
|
|
@ -4765,24 +4765,24 @@ static int ScriptCall(unsigned argc, int32_t *args)
|
|||
auto cls = PClass::FindClass(clsname);
|
||||
if (!cls)
|
||||
{
|
||||
I_Error("ACS call to unknown class in script function %s.%s", cls, funcname);
|
||||
I_Error("ACS call to unknown class in script function %s.%s", clsname, funcname);
|
||||
}
|
||||
auto funcsym = dyn_cast<PFunction>(cls->Symbols.FindSymbol(funcname, true));
|
||||
if (funcsym == nullptr)
|
||||
{
|
||||
I_Error("ACS call to unknown script function %s.%s", cls, funcname);
|
||||
I_Error("ACS call to unknown script function %s.%s", clsname, funcname);
|
||||
}
|
||||
auto func = funcsym->Variants[0].Implementation;
|
||||
if (func->ImplicitArgs > 0)
|
||||
{
|
||||
I_Error("ACS call to non-static script function %s.%s", cls, funcname);
|
||||
I_Error("ACS call to non-static script function %s.%s", clsname, funcname);
|
||||
}
|
||||
TArray<VMValue> params;
|
||||
for (unsigned i = 2; i < argc; i++)
|
||||
{
|
||||
if (func->Proto->ArgumentTypes.Size() < i - 1)
|
||||
{
|
||||
I_Error("Too many parameters in call to %s.%s", cls, funcname);
|
||||
I_Error("Too many parameters in call to %s.%s", clsname, funcname);
|
||||
}
|
||||
auto argtype = func->Proto->ArgumentTypes[i - 2];
|
||||
// The only types allowed are int, bool, double, Name, Sound, Color and String
|
||||
|
@ -4812,7 +4812,7 @@ static int ScriptCall(unsigned argc, int32_t *args)
|
|||
}
|
||||
else
|
||||
{
|
||||
I_Error("Invalid type %s in call to %s.%s", argtype->DescriptiveName(), cls, funcname);
|
||||
I_Error("Invalid type %s in call to %s.%s", argtype->DescriptiveName(), clsname, funcname);
|
||||
}
|
||||
}
|
||||
if (func->Proto->ArgumentTypes.Size() > params.Size())
|
||||
|
@ -4820,7 +4820,7 @@ static int ScriptCall(unsigned argc, int32_t *args)
|
|||
// Check if we got enough parameters. That means we either cover the full argument list of the function or the next argument is optional.
|
||||
if (!(funcsym->Variants[0].ArgFlags[params.Size()] & VARF_Optional))
|
||||
{
|
||||
I_Error("Insufficient parameters in call to %s.%s", cls, funcname);
|
||||
I_Error("Insufficient parameters in call to %s.%s", clsname, funcname);
|
||||
}
|
||||
}
|
||||
// The return value can be the same types as the parameter types, plus void
|
||||
|
|
|
@ -170,6 +170,10 @@ std2:
|
|||
'virtual' { RET(TK_Virtual); }
|
||||
'override' { RET(TK_Override); }
|
||||
'vararg' { RET(TK_VarArg); }
|
||||
'ui' { RET(TK_UI); }
|
||||
'play' { RET(TK_Play); }
|
||||
'clearscope' { RET(TK_ClearScope); }
|
||||
'virtualscope' { RET(TK_VirtualScope); }
|
||||
'super' { RET(TK_Super); }
|
||||
'global' { RET(TK_Global); }
|
||||
'stop' { RET(TK_Stop); }
|
||||
|
|
|
@ -112,6 +112,10 @@ xx(TK_Optional, "'optional'")
|
|||
xx(TK_Export, "'expert'")
|
||||
xx(TK_Virtual, "'virtual'")
|
||||
xx(TK_VarArg, "'vararg'")
|
||||
xx(TK_UI, "'ui'")
|
||||
xx(TK_Play, "'play'")
|
||||
xx(TK_ClearScope, "'clearscope'")
|
||||
xx(TK_VirtualScope, "'virtualscope'")
|
||||
xx(TK_Override, "'override'")
|
||||
xx(TK_Super, "'super'")
|
||||
xx(TK_Null, "'null'")
|
||||
|
|
|
@ -89,6 +89,37 @@ 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 = callingfunc->Variants.Size() ? FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags) : FScopeBarrier::Side_Virtual;
|
||||
if (outerside == FScopeBarrier::Side_Virtual)
|
||||
outerside = FScopeBarrier::SideFromObjectFlags(callingfunc->OwningClass->ObjectFlags);
|
||||
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)
|
||||
{
|
||||
// [ZZ] anonymous blocks have 0 variants, so give them Side_Virtual.
|
||||
int outerside = callingfunc->Variants.Size() ? FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags) : FScopeBarrier::Side_Virtual;
|
||||
if (outerside == FScopeBarrier::Side_Virtual)
|
||||
outerside = FScopeBarrier::SideFromObjectFlags(callingfunc->OwningClass->ObjectFlags);
|
||||
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
|
||||
|
@ -167,7 +198,8 @@ void FCompileContext::CheckReturn(PPrototype *proto, FScriptPosition &pos)
|
|||
}
|
||||
}
|
||||
|
||||
bool FCompileContext::CheckReadOnly(int flags)
|
||||
// [ZZ] I find it really dumb that something called CheckReadOnly returns false for readonly. renamed.
|
||||
bool FCompileContext::CheckWritable(int flags)
|
||||
{
|
||||
if (!(flags & VARF_ReadOnly)) return false;
|
||||
if (!(flags & VARF_InternalAccess)) return true;
|
||||
|
@ -5023,6 +5055,7 @@ FxNew::FxNew(FxExpression *v)
|
|||
{
|
||||
val = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), v, false);
|
||||
ValueType = NewPointer(RUNTIME_CLASS(DObject));
|
||||
CallingFunction = nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -5047,6 +5080,7 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx)
|
|||
CHECKRESOLVED();
|
||||
SAFE_RESOLVE(val, ctx);
|
||||
|
||||
CallingFunction = ctx.Function;
|
||||
if (!val->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Class type expected");
|
||||
|
@ -5056,8 +5090,28 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx)
|
|||
if (val->isConstant())
|
||||
{
|
||||
auto cls = static_cast<PClass *>(static_cast<FxConstant*>(val)->GetValue().GetPointer());
|
||||
if (cls->ObjectFlags & OF_Abstract)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//
|
||||
int outerside = ctx.Function && ctx.Function->Variants.Size() ? FScopeBarrier::SideFromFlags(ctx.Function->Variants[0].Flags) : FScopeBarrier::Side_Virtual;
|
||||
if (outerside == FScopeBarrier::Side_Virtual)
|
||||
outerside = FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags);
|
||||
int innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags);
|
||||
if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) // "cannot construct ui class ... from data context"
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Cannot construct %s class %s from %s context", FScopeBarrier::StringFromSide(innerside), cls->TypeName.GetChars(), FScopeBarrier::StringFromSide(outerside));
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ValueType = NewPointer(cls);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -5072,7 +5126,7 @@ ExpEmit FxNew::Emit(VMFunctionBuilder *build)
|
|||
ExpEmit from = val->Emit(build);
|
||||
from.Free(build);
|
||||
ExpEmit to(build, REGT_POINTER);
|
||||
build->Emit(from.Konst ? OP_NEW_K : OP_NEW, to.RegNum, from.RegNum);
|
||||
build->Emit(from.Konst ? OP_NEW_K : OP_NEW, to.RegNum, from.RegNum, build->GetConstantAddress(CallingFunction, ATAG_OBJECT));
|
||||
return to;
|
||||
}
|
||||
|
||||
|
@ -6245,7 +6299,7 @@ FxExpression *FxLocalVariable::Resolve(FCompileContext &ctx)
|
|||
bool FxLocalVariable::RequestAddress(FCompileContext &ctx, bool *writable)
|
||||
{
|
||||
AddressRequested = true;
|
||||
if (writable != nullptr) *writable = !ctx.CheckReadOnly(Variable->VarFlags);
|
||||
if (writable != nullptr) *writable = !ctx.CheckWritable(Variable->VarFlags);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6463,7 +6517,7 @@ FxGlobalVariable::FxGlobalVariable(PField* mem, const FScriptPosition &pos)
|
|||
bool FxGlobalVariable::RequestAddress(FCompileContext &ctx, bool *writable)
|
||||
{
|
||||
AddressRequested = true;
|
||||
if (writable != nullptr) *writable = AddressWritable && !ctx.CheckReadOnly(membervar->Flags);
|
||||
if (writable != nullptr) *writable = AddressWritable && !ctx.CheckWritable(membervar->Flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6653,7 +6707,7 @@ FxStackVariable::~FxStackVariable()
|
|||
bool FxStackVariable::RequestAddress(FCompileContext &ctx, bool *writable)
|
||||
{
|
||||
AddressRequested = true;
|
||||
if (writable != nullptr) *writable = AddressWritable && !ctx.CheckReadOnly(membervar->Flags);
|
||||
if (writable != nullptr) *writable = AddressWritable && !ctx.CheckWritable(membervar->Flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6751,8 +6805,34 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable)
|
|||
return false;
|
||||
}
|
||||
AddressRequested = true;
|
||||
if (writable != nullptr) *writable = (AddressWritable && !ctx.CheckReadOnly(membervar->Flags) &&
|
||||
(!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) || !static_cast<PPointer*>(classx->ValueType)->IsConst));
|
||||
if (writable != nullptr)
|
||||
{
|
||||
// [ZZ] original check.
|
||||
bool bWritable = (AddressWritable && !ctx.CheckWritable(membervar->Flags) &&
|
||||
(!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) || !static_cast<PPointer*>(classx->ValueType)->IsConst));
|
||||
// [ZZ] self in a const function is not writable.
|
||||
if (bWritable) // don't do complex checks on early fail
|
||||
{
|
||||
if ((classx->ExprType == EFX_Self) && (ctx.Function && (ctx.Function->Variants[0].Flags & VARF_ReadOnly)))
|
||||
bWritable = false;
|
||||
}
|
||||
// [ZZ] implement write barrier between different scopes
|
||||
if (bWritable)
|
||||
{
|
||||
int outerflags = 0;
|
||||
if (ctx.Function)
|
||||
{
|
||||
outerflags = ctx.Function->Variants[0].Flags;
|
||||
if (((outerflags & (VARF_VirtualScope | VARF_Virtual)) == (VARF_VirtualScope | VARF_Virtual)) && ctx.Class)
|
||||
outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags));
|
||||
}
|
||||
FScopeBarrier scopeBarrier(outerflags, FScopeBarrier::FlagsFromSide(BarrierSide), membervar->SymbolName.GetChars());
|
||||
if (!scopeBarrier.writable)
|
||||
bWritable = false;
|
||||
}
|
||||
|
||||
*writable = bWritable;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6772,7 +6852,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx)
|
|||
if (!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))
|
||||
|| !static_cast<PPointer *>(classx->ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(AActor)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type.");
|
||||
ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -6782,12 +6862,36 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx)
|
|||
return x->Resolve(ctx);
|
||||
}
|
||||
|
||||
// [ZZ] support magic
|
||||
int outerflags = 0;
|
||||
if (ctx.Function)
|
||||
{
|
||||
outerflags = ctx.Function->Variants[0].Flags;
|
||||
if (((outerflags & (VARF_VirtualScope | VARF_Virtual)) == (VARF_VirtualScope | VARF_Virtual)) && ctx.Class)
|
||||
outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags));
|
||||
}
|
||||
FScopeBarrier scopeBarrier(outerflags, membervar->Flags, membervar->SymbolName.GetChars());
|
||||
if (!scopeBarrier.readable)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "%s", scopeBarrier.readerror.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BarrierSide = scopeBarrier.sidelast;
|
||||
if (classx->ExprType == EFX_StructMember && ExprType == EFX_StructMember) // note: only do this for structs now
|
||||
{
|
||||
FxStructMember* pmember = (FxStructMember*)classx;
|
||||
if (BarrierSide == FScopeBarrier::Side_PlainData && pmember)
|
||||
BarrierSide = pmember->BarrierSide;
|
||||
}
|
||||
|
||||
if (classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)))
|
||||
{
|
||||
PPointer *ptrtype = dyn_cast<PPointer>(classx->ValueType);
|
||||
if (ptrtype == nullptr || !ptrtype->PointedType->IsKindOf(RUNTIME_CLASS(PStruct)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Member variable requires a struct or class object.");
|
||||
ScriptPosition.Message(MSG_ERROR, "Member variable requires a struct or class object");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -6799,7 +6903,8 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx)
|
|||
{
|
||||
auto parentfield = static_cast<FxMemberBase *>(classx)->membervar;
|
||||
// PFields are garbage collected so this will be automatically taken care of later.
|
||||
auto newfield = new PField(NAME_None, membervar->Type, membervar->Flags | parentfield->Flags, membervar->Offset + parentfield->Offset);
|
||||
// [ZZ] call ChangeSideInFlags to ensure that we don't get ui+play
|
||||
auto newfield = new PField(NAME_None, membervar->Type, FScopeBarrier::ChangeSideInFlags(membervar->Flags | parentfield->Flags, BarrierSide), membervar->Offset + parentfield->Offset);
|
||||
newfield->BitValue = membervar->BitValue;
|
||||
static_cast<FxMemberBase *>(classx)->membervar = newfield;
|
||||
classx->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
|
||||
|
@ -6841,7 +6946,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx)
|
|||
{
|
||||
if (!(classx->RequestAddress(ctx, &AddressWritable)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "unable to dereference left side of %s", membervar->SymbolName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Unable to dereference left side of %s", membervar->SymbolName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -7371,6 +7476,32 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// [ZZ] validate call
|
||||
PClass* cls = (PClass*)ctx.Class;
|
||||
int outerflags = 0;
|
||||
if (ctx.Function)
|
||||
{
|
||||
outerflags = ctx.Function->Variants[0].Flags;
|
||||
if (((outerflags & (VARF_VirtualScope | VARF_Virtual)) == (VARF_VirtualScope | VARF_Virtual)) && ctx.Class)
|
||||
outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags));
|
||||
}
|
||||
int innerflags = afd->Variants[0].Flags;
|
||||
int innerside = FScopeBarrier::SideFromFlags(innerflags);
|
||||
// [ZZ] check this at compile time. this would work for most legit cases.
|
||||
if (innerside == FScopeBarrier::Side_Virtual)
|
||||
{
|
||||
innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags);
|
||||
innerflags = FScopeBarrier::FlagsFromSide(innerside);
|
||||
}
|
||||
FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars());
|
||||
if (!scopeBarrier.callable)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "%s", scopeBarrier.callerror.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// [ZZ] this is only checked for VARF_Methods in the other place. bug?
|
||||
if (!CheckFunctionCompatiblity(ScriptPosition, ctx.Function, afd))
|
||||
{
|
||||
delete this;
|
||||
|
@ -7593,8 +7724,18 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
|||
break;
|
||||
|
||||
case NAME_New:
|
||||
if (CheckArgSize(MethodName, ArgList, 1, 1, ScriptPosition))
|
||||
if (CheckArgSize(MethodName, ArgList, 0, 1, ScriptPosition))
|
||||
{
|
||||
// [ZZ] allow implicit new() call to mean "create current class instance"
|
||||
if (!ArgList.Size() && !ctx.Class->IsKindOf(RUNTIME_CLASS(PClass)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Cannot use implicit new() in a struct");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (!ArgList.Size())
|
||||
ArgList.Push(new FxConstant((PClass*)ctx.Class, NewClassPointer((PClass*)ctx.Class), ScriptPosition));
|
||||
|
||||
func = new FxNew(ArgList[0]);
|
||||
ArgList[0] = nullptr;
|
||||
}
|
||||
|
@ -7651,13 +7792,14 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
PStruct *cls;
|
||||
bool staticonly = false;
|
||||
bool novirtual = false;
|
||||
bool isreadonly = false;
|
||||
|
||||
PStruct *ccls = nullptr;
|
||||
|
||||
if (ctx.Class == nullptr)
|
||||
{
|
||||
// There's no way that a member function call can resolve to a constant so abort right away.
|
||||
ScriptPosition.Message(MSG_ERROR, "Expression is not constant.");
|
||||
ScriptPosition.Message(MSG_ERROR, "Expression is not constant");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -7666,7 +7808,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
{
|
||||
if (a == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Empty function argument.");
|
||||
ScriptPosition.Message(MSG_ERROR, "Empty function argument");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -7677,6 +7819,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
auto id = static_cast<FxIdentifier *>(Self)->Identifier;
|
||||
// If the left side is a class name for a static member function call it needs to be resolved manually
|
||||
// because the resulting value type would cause problems in nearly every other place where identifiers are being used.
|
||||
// [ZZ] substitute ccls for String internal type.
|
||||
if (id == NAME_String) ccls = TypeStringStruct;
|
||||
else ccls = FindStructType(id, ctx);
|
||||
if (ccls != nullptr) static_cast<FxIdentifier *>(Self)->noglobal = true;
|
||||
|
@ -7688,7 +7831,6 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
{
|
||||
if (ccls != nullptr)
|
||||
{
|
||||
// [ZZ] substitute ccls for String internal type.
|
||||
if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast<PClass *>(ccls)->bExported)
|
||||
{
|
||||
cls = ccls;
|
||||
|
@ -7717,6 +7859,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
Self = new FxSelf(ScriptPosition);
|
||||
Self->ValueType = NewPointer(cls);
|
||||
}
|
||||
else novirtual = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7757,7 +7900,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
{
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -7801,7 +7944,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
{
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -7895,7 +8038,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
{
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -7945,7 +8088,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
auto x = new FxGetParentClass(Self);
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
|
||||
|
||||
if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !Self->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)))
|
||||
{
|
||||
auto ptype = static_cast<PPointer *>(Self->ValueType)->PointedType;
|
||||
|
@ -7955,7 +8098,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
{
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -7968,7 +8111,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
else
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Left hand side of %s must point to a class object\n", MethodName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Left hand side of %s must point to a class object", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -7976,22 +8119,15 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
else if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PStruct)))
|
||||
{
|
||||
bool writable;
|
||||
if (Self->RequestAddress(ctx, &writable) && writable)
|
||||
{
|
||||
cls = static_cast<PStruct*>(Self->ValueType);
|
||||
Self->ValueType = NewPointer(Self->ValueType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cannot be made writable so we cannot use its methods.
|
||||
ScriptPosition.Message(MSG_ERROR, "Invalid expression on left hand side of %s\n", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// [ZZ] allow const method to be called on a readonly struct
|
||||
isreadonly = !(Self->RequestAddress(ctx, &writable) && writable);
|
||||
cls = static_cast<PStruct*>(Self->ValueType);
|
||||
Self->ValueType = NewPointer(Self->ValueType);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Invalid expression on left hand side of %s\n", MethodName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Invalid expression on left hand side of %s", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -8009,7 +8145,50 @@ isresolved:
|
|||
|
||||
if (afd == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Unknown function %s\n", MethodName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Unknown function %s", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (isreadonly && !(afd->Variants[0].Flags & VARF_ReadOnly))
|
||||
{
|
||||
// Cannot be made writable so we cannot use its methods.
|
||||
// [ZZ] Why this esoteric message?
|
||||
ScriptPosition.Message(MSG_ERROR, "Readonly struct on left hand side of %s not allowed", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// [ZZ] if self is a struct or a class member, check if it's valid to call this function at all.
|
||||
// implement more magic
|
||||
int outerflags = 0;
|
||||
if (ctx.Function)
|
||||
{
|
||||
outerflags = ctx.Function->Variants[0].Flags;
|
||||
if (((outerflags & (VARF_VirtualScope | VARF_Virtual)) == (VARF_VirtualScope | VARF_Virtual)) && ctx.Class)
|
||||
outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags));
|
||||
}
|
||||
int innerflags = afd->Variants[0].Flags;
|
||||
int innerside = FScopeBarrier::SideFromFlags(innerflags);
|
||||
// [ZZ] check this at compile time. this would work for most legit cases.
|
||||
if (innerside == FScopeBarrier::Side_Virtual)
|
||||
{
|
||||
innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags);
|
||||
innerflags = FScopeBarrier::FlagsFromSide(innerside);
|
||||
}
|
||||
else if (innerside != FScopeBarrier::Side_Clear)
|
||||
{
|
||||
if (Self->ExprType == EFX_StructMember)
|
||||
{
|
||||
FxStructMember* pmember = (FxStructMember*)Self;
|
||||
if (innerside == FScopeBarrier::Side_PlainData)
|
||||
innerflags = FScopeBarrier::ChangeSideInFlags(innerflags, pmember->BarrierSide);
|
||||
}
|
||||
}
|
||||
FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars());
|
||||
if (!scopeBarrier.callable)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "%s", scopeBarrier.callerror.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -8022,14 +8201,14 @@ isresolved:
|
|||
auto ccls = dyn_cast<PClass>(cls);
|
||||
if (clstype == nullptr || ccls == nullptr || !clstype->IsDescendantOf(ccls))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here\n", cls->TypeName.GetChars(), MethodName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here", cls->TypeName.GetChars(), MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Todo: If this is a qualified call to a parent class function, let it through (but this needs to disable virtual calls later.)
|
||||
ScriptPosition.Message(MSG_ERROR, "Qualified member call to parent class %s::%s is not yet implemented\n", cls->TypeName.GetChars(), MethodName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Qualified member call to parent class %s::%s is not yet implemented", cls->TypeName.GetChars(), MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -8057,7 +8236,7 @@ isresolved:
|
|||
// Functions with no Actor usage may not be called through a pointer because they will lose their context.
|
||||
if (!(afd->Variants[0].UseFlags & SUF_ACTOR))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Function %s cannot be used with a non-self object\n", afd->SymbolName.GetChars());
|
||||
ScriptPosition.Message(MSG_ERROR, "Function %s cannot be used with a non-self object", afd->SymbolName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -8264,6 +8443,7 @@ FxVMFunctionCall::FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumen
|
|||
ArgList = std::move(args);
|
||||
EmitTail = false;
|
||||
NoVirtual = novirtual;
|
||||
CallingFunction = nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -8337,6 +8517,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
CallingFunction = ctx.Function;
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
bool foundvarargs = false;
|
||||
|
@ -8544,6 +8725,17 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
|||
ExpEmit selfemit;
|
||||
if (Function->Variants[0].Flags & VARF_Method)
|
||||
{
|
||||
#if 0
|
||||
// [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;
|
||||
}
|
||||
#endif
|
||||
assert(Self != nullptr);
|
||||
selfemit = Self->Emit(build);
|
||||
assert((selfemit.RegType == REGT_POINTER) || (selfemit.Fixed && selfemit.Target));
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "s_sound.h"
|
||||
#include "actor.h"
|
||||
#include "vmbuilder.h"
|
||||
#include "scopebarrier.h"
|
||||
|
||||
|
||||
#define CHECKRESOLVED() if (isresolved) return this; isresolved=true;
|
||||
|
@ -95,7 +96,7 @@ struct FCompileContext
|
|||
|
||||
void HandleJumps(int token, FxExpression *handler);
|
||||
void CheckReturn(PPrototype *proto, FScriptPosition &pos);
|
||||
bool CheckReadOnly(int flags);
|
||||
bool CheckWritable(int flags);
|
||||
FxLocalVariableDeclaration *FindLocalVariable(FName name);
|
||||
};
|
||||
|
||||
|
@ -1208,6 +1209,7 @@ private:
|
|||
class FxNew : public FxExpression
|
||||
{
|
||||
FxExpression *val;
|
||||
PFunction *CallingFunction;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -1326,6 +1328,7 @@ public:
|
|||
PField *membervar;
|
||||
bool AddressRequested = false;
|
||||
bool AddressWritable = true;
|
||||
int BarrierSide = -1; // [ZZ] some magic
|
||||
FxMemberBase(EFxType type, PField *f, const FScriptPosition &p);
|
||||
};
|
||||
|
||||
|
@ -1707,6 +1710,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);
|
||||
|
|
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);
|
||||
};
|
||||
|
|
@ -110,12 +110,13 @@ void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FNam
|
|||
if (funcflags & VARF_Method)
|
||||
{
|
||||
// implied self pointer
|
||||
if (args != nullptr) args->Push(NewPointer(cls));
|
||||
if (args != nullptr) args->Push(NewPointer(cls, !!(funcflags & VARF_ReadOnly)));
|
||||
if (argflags != nullptr) argflags->Push(VARF_Implicit | VARF_ReadOnly);
|
||||
if (argnames != nullptr) argnames->Push(NAME_self);
|
||||
}
|
||||
if (funcflags & VARF_Action)
|
||||
{
|
||||
assert(!(funcflags & VARF_ReadOnly));
|
||||
// implied caller and callingstate pointers
|
||||
if (args != nullptr)
|
||||
{
|
||||
|
@ -163,6 +164,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);
|
||||
|
||||
|
@ -194,7 +199,7 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam
|
|||
{
|
||||
sc.Message(MSG_ERROR, "%s is not a member function of %s", name.GetChars(), selfcls->TypeName.GetChars());
|
||||
}
|
||||
else if (funcsym->Variants[0].Flags & VARF_Private && symtable != &funccls->Symbols)
|
||||
else if ((funcsym->Variants[0].Flags & VARF_Private) && symtable != &funccls->Symbols)
|
||||
{
|
||||
// private access is only allowed if the symbol table belongs to the class in which the current function is being defined.
|
||||
sc.Message(MSG_ERROR, "%s is declared private and not accessible", symbol->SymbolName.GetChars());
|
||||
|
|
|
@ -858,8 +858,8 @@ void InitThingdef()
|
|||
// As a result, the size has to be set to something large and arbritrary because it can change between maps. This will need some serious improvement when things get cleaned up.
|
||||
sectorstruct->AddNativeField("lines", NewPointer(NewResizableArray(NewPointer(linestruct, false)), false), myoffsetof(sector_t, Lines), VARF_Native);
|
||||
|
||||
sectorstruct->AddNativeField("ceilingplane", secplanestruct, myoffsetof(sector_t, ceilingplane), VARF_Native);
|
||||
sectorstruct->AddNativeField("floorplane", secplanestruct, myoffsetof(sector_t, floorplane), VARF_Native);
|
||||
sectorstruct->AddNativeField("ceilingplane", secplanestruct, myoffsetof(sector_t, ceilingplane), VARF_Native | VARF_ReadOnly);
|
||||
sectorstruct->AddNativeField("floorplane", secplanestruct, myoffsetof(sector_t, floorplane), VARF_Native | VARF_ReadOnly);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
#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);
|
||||
class DObject;
|
||||
|
||||
extern FMemArena ClassDataAllocator;
|
||||
|
@ -704,6 +708,8 @@ public:
|
|||
bool Native;
|
||||
bool Final = false; // cannot be overridden
|
||||
bool Unsafe = false; // Contains references to class fields that are unsafe for psp and item state calls.
|
||||
bool FuncConst = false; // [ZZ] readonly function
|
||||
int BarrierSide = 0; // [ZZ] FScopeBarrier::Side
|
||||
BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action
|
||||
unsigned VirtualIndex = ~0u;
|
||||
FName Name;
|
||||
|
@ -712,7 +718,7 @@ public:
|
|||
|
||||
class PPrototype *Proto;
|
||||
|
||||
VMFunction(FName name = NAME_None) : Native(false), ImplicitArgs(0), Name(name), Proto(NULL)
|
||||
VMFunction(FName name = NAME_None) : Native(false), ImplicitArgs(0), Name(name), Proto(NULL)
|
||||
{
|
||||
AllFunctions.Push(this);
|
||||
}
|
||||
|
|
|
@ -664,13 +664,27 @@ begin:
|
|||
VMReturn returns[MAX_RETURNS];
|
||||
int numret;
|
||||
|
||||
b = B;
|
||||
#if 0
|
||||
// [ZZ] hax!
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
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)
|
||||
|
@ -686,7 +700,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);
|
||||
|
@ -803,7 +817,11 @@ begin:
|
|||
{
|
||||
b = B;
|
||||
PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[b] : konsta[b].v);
|
||||
PFunction *callingfunc = (PFunction*)konsta[C].o; // [ZZ] due to how this is set, it's always const
|
||||
if (cls->ObjectFlags & OF_Abstract) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", 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;
|
||||
|
|
|
@ -213,6 +213,8 @@ class_ancestry(X) ::= COLON dottable_id(A). { X = A; /*X-overwrites-A*/ }
|
|||
class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; }
|
||||
class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; }
|
||||
|
||||
/*----- Dottable Identifier -----*/
|
||||
|
@ -326,7 +328,9 @@ struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body
|
|||
|
||||
%type struct_flags{ClassFlagsBlock}
|
||||
struct_flags(X) ::= . { X.Flags = 0; }
|
||||
struct_flags(X) ::= NATIVE. { X.Flags = ZCC_Native; }
|
||||
struct_flags(X) ::= struct_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; }
|
||||
struct_flags(X) ::= struct_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; }
|
||||
struct_flags(X) ::= struct_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; }
|
||||
|
||||
opt_struct_body(X) ::= . { X = NULL; }
|
||||
opt_struct_body(X) ::= struct_body(X).
|
||||
|
@ -999,6 +1003,10 @@ decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.Sourc
|
|||
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) ::= 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; }
|
||||
decl_flag(X) ::= CLEARSCOPE(T). { X.Int = ZCC_ClearScope; X.SourceLoc = T.SourceLoc; }
|
||||
decl_flag(X) ::= VIRTUALSCOPE(T). { X.Int = ZCC_VirtualScope; X.SourceLoc = T.SourceLoc; }
|
||||
|
||||
func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); }
|
||||
func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; }
|
||||
|
|
|
@ -495,6 +495,16 @@ void ZCCCompiler::CreateStructTypes()
|
|||
{
|
||||
s->strct->Type = NewStruct(s->NodeName(), outer);
|
||||
}
|
||||
|
||||
if ((s->strct->Flags & (ZCC_UIFlag | ZCC_Play)) == (ZCC_UIFlag | ZCC_Play))
|
||||
{
|
||||
Error(s->strct, "Struct %s has incompatible flags", s->NodeName().GetChars());
|
||||
}
|
||||
|
||||
if (s->strct->Flags & ZCC_UIFlag)
|
||||
s->Type()->ObjectFlags |= OF_UI;
|
||||
if (s->strct->Flags & ZCC_Play)
|
||||
s->Type()->ObjectFlags |= OF_Play;
|
||||
s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type());
|
||||
syms->AddSymbol(s->strct->Symbol);
|
||||
|
||||
|
@ -597,11 +607,33 @@ void ZCCCompiler::CreateClassTypes()
|
|||
c->cls->Type = nullptr;
|
||||
}
|
||||
}
|
||||
if (c->cls->Flags & ZCC_Abstract)
|
||||
{
|
||||
c->Type()->ObjectFlags |= OF_Abstract;
|
||||
}
|
||||
if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName());
|
||||
if (c->cls->Flags & ZCC_Abstract)
|
||||
c->Type()->ObjectFlags |= OF_Abstract;
|
||||
//
|
||||
static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope };
|
||||
int incompatiblecnt = 0;
|
||||
for (size_t k = 0; k < countof(incompatible); k++)
|
||||
if (incompatible[k] & c->cls->Flags) incompatiblecnt++;
|
||||
|
||||
if (incompatiblecnt > 1)
|
||||
{
|
||||
Error(c->cls, "Class %s has incompatible flags", c->NodeName().GetChars());
|
||||
}
|
||||
|
||||
if (c->cls->Flags & ZCC_UIFlag)
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_Play) | OF_UI;
|
||||
if (c->cls->Flags & ZCC_Play)
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_UI) | OF_Play;
|
||||
if (parent->ObjectFlags & (OF_UI | OF_Play)) // parent is either ui or play
|
||||
{
|
||||
if (c->cls->Flags & (ZCC_UIFlag | ZCC_Play))
|
||||
{
|
||||
Error(c->cls, "Can't change class scope in class %s", c->NodeName().GetChars());
|
||||
}
|
||||
c->Type()->ObjectFlags = (c->Type()->ObjectFlags & ~(OF_UI | OF_Play)) | (parent->ObjectFlags & (OF_UI | OF_Play));
|
||||
}
|
||||
|
||||
c->Type()->bExported = true; // this class is accessible to script side type casts. (The reason for this flag is that types like PInt need to be skipped.)
|
||||
c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type());
|
||||
OutNamespace->Symbols.AddSymbol(c->cls->Symbol);
|
||||
|
@ -1050,8 +1082,8 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
|||
|
||||
// For structs only allow 'deprecated', for classes exclude function qualifiers.
|
||||
int notallowed = forstruct?
|
||||
ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Meta | ZCC_Extension :
|
||||
ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Extension;
|
||||
ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Meta | ZCC_Extension | ZCC_VirtualScope | ZCC_ClearScope :
|
||||
ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Extension | ZCC_VirtualScope | ZCC_ClearScope;
|
||||
|
||||
if (field->Flags & notallowed)
|
||||
{
|
||||
|
@ -1066,12 +1098,39 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
|||
if (field->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
|
||||
if (field->Flags & ZCC_ReadOnly) varflags |= VARF_ReadOnly;
|
||||
if (field->Flags & ZCC_Transient) varflags |= VARF_Transient;
|
||||
if (type->ObjectFlags & OF_UI)
|
||||
varflags |= VARF_UI;
|
||||
if (type->ObjectFlags & OF_Play)
|
||||
varflags |= VARF_Play;
|
||||
if (field->Flags & ZCC_UIFlag)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
|
||||
if (field->Flags & ZCC_Play)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play);
|
||||
if (field->Flags & ZCC_ClearScope)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData);
|
||||
|
||||
if (field->Flags & ZCC_Native)
|
||||
{
|
||||
varflags |= VARF_Native | VARF_Transient;
|
||||
}
|
||||
|
||||
static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope };
|
||||
int excludeflags = 0;
|
||||
int fc = 0;
|
||||
for (size_t i = 0; i < countof(excludescope); i++)
|
||||
{
|
||||
if (field->Flags & excludescope[i])
|
||||
{
|
||||
fc++;
|
||||
excludeflags |= excludescope[i];
|
||||
}
|
||||
}
|
||||
if (fc > 1)
|
||||
{
|
||||
Error(field, "Invalid combination of scope qualifiers %s on field %s", FlagsToString(excludeflags).GetChars(), FName(field->Names->Name).GetChars());
|
||||
varflags &= ~(VARF_UI | VARF_Play); // make plain data
|
||||
}
|
||||
|
||||
if (field->Flags & ZCC_Meta)
|
||||
{
|
||||
varflags |= VARF_Meta | VARF_Static | VARF_ReadOnly; // metadata implies readonly
|
||||
|
@ -1121,7 +1180,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
|||
}
|
||||
else if (hasnativechildren)
|
||||
{
|
||||
Error(field, "Cannot add field %s to %s. %s has native children which means it size may not change.", FName(name->Name).GetChars(), type->TypeName.GetChars(), type->TypeName.GetChars());
|
||||
Error(field, "Cannot add field %s to %s. %s has native children which means it size may not change", FName(name->Name).GetChars(), type->TypeName.GetChars(), type->TypeName.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1218,7 +1277,7 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray<ZCC_Property *> &Proper
|
|||
FString ZCCCompiler::FlagsToString(uint32_t flags)
|
||||
{
|
||||
|
||||
const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "funcconst", "abstract", "extension", "virtual", "override", "transient", "vararg" };
|
||||
const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "const", "abstract", "extend", "virtual", "override", "transient", "vararg", "ui", "play", "clearscope", "virtualscope" };
|
||||
FString build;
|
||||
|
||||
for (size_t i = 0; i < countof(flagnames); i++)
|
||||
|
@ -2017,7 +2076,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
} while (t != f->Type);
|
||||
}
|
||||
|
||||
int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_FuncConst | ZCC_Abstract;
|
||||
int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_Abstract;
|
||||
|
||||
if (f->Flags & notallowed)
|
||||
{
|
||||
|
@ -2064,12 +2123,48 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual;
|
||||
if (f->Flags & ZCC_Override) varflags |= VARF_Override;
|
||||
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 (c->Type()->ObjectFlags & OF_UI)
|
||||
varflags |= VARF_UI;
|
||||
if (c->Type()->ObjectFlags & OF_Play)
|
||||
varflags |= VARF_Play;
|
||||
if (f->Flags & ZCC_FuncConst)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData); // const implies clearscope. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag.
|
||||
if (f->Flags & ZCC_UIFlag)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
|
||||
if (f->Flags & ZCC_Play)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play);
|
||||
if (f->Flags & ZCC_ClearScope)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Clear);
|
||||
if (f->Flags & ZCC_VirtualScope)
|
||||
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Virtual);
|
||||
|
||||
// [ZZ] supporting const self for actors is quite a cumbersome task because there's no concept of a const pointer (?)
|
||||
// either way, it doesn't make sense, because you can call any method on a readonly class instance.
|
||||
|
||||
// The above is nonsense. This needs to work and needs to be implemented properly.
|
||||
/*
|
||||
if ((f->Flags & ZCC_FuncConst) && (c->Type()->IsKindOf(RUNTIME_CLASS(PClass))))
|
||||
{
|
||||
Error(f, "'Const' on a method can only be used in structs");
|
||||
}
|
||||
*/
|
||||
|
||||
if ((f->Flags & ZCC_VarArg) && !(f->Flags & ZCC_Native))
|
||||
{
|
||||
Error(f, "'VarArg' can only be used with native methods");
|
||||
}
|
||||
if (f->Flags & ZCC_Action)
|
||||
{
|
||||
if (varflags & VARF_ReadOnly)
|
||||
{
|
||||
Error(f, "Action functions cannot be declared const");
|
||||
varflags &= ~VARF_ReadOnly;
|
||||
}
|
||||
if (varflags & VARF_UI)
|
||||
{
|
||||
Error(f, "Action functions cannot be declared UI");
|
||||
}
|
||||
// Non-Actors cannot have action functions.
|
||||
if (!c->Type()->IsKindOf(RUNTIME_CLASS(PClassActor)))
|
||||
{
|
||||
|
@ -2089,36 +2184,69 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
}
|
||||
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final.
|
||||
|
||||
|
||||
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 const char * print[] = { "virtual", "override", "action", "static" };
|
||||
int excludeflags = 0;
|
||||
int fc = 0;
|
||||
FString build;
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (size_t i = 0; i < countof(exclude); i++)
|
||||
{
|
||||
if (f->Flags & exclude[i])
|
||||
{
|
||||
fc++;
|
||||
if (build.Len() > 0) build += ", ";
|
||||
build += print[i];
|
||||
excludeflags |= exclude[i];
|
||||
}
|
||||
}
|
||||
if (fc > 1)
|
||||
{
|
||||
Error(f, "Invalid combination of qualifiers %s on function %s.", FName(f->Name).GetChars(), build.GetChars());
|
||||
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.
|
||||
|
||||
// [ZZ] this doesn't make sense either.
|
||||
if ((varflags&(VARF_ReadOnly | VARF_Method)) == VARF_ReadOnly) // non-method const function
|
||||
{
|
||||
Error(f, "'Const' on a static method is not supported");
|
||||
}
|
||||
|
||||
// [ZZ] neither this
|
||||
if ((varflags&(VARF_VirtualScope | VARF_Method)) == VARF_VirtualScope) // non-method virtualscope function
|
||||
{
|
||||
Error(f, "'VirtualScope' on a static method is not supported");
|
||||
}
|
||||
|
||||
// you can't have a const function belonging to either ui or play.
|
||||
// const is intended for plain data to signify that you can call a method on readonly variable.
|
||||
if ((f->Flags & ZCC_FuncConst) && (f->Flags & (ZCC_UIFlag | ZCC_Play | ZCC_VirtualScope)))
|
||||
{
|
||||
Error(f, "Invalid combination of qualifiers %s on function %s", FlagsToString(f->Flags&(ZCC_FuncConst | ZCC_UIFlag | ZCC_Play | ZCC_VirtualScope)).GetChars(), FName(f->Name).GetChars());
|
||||
}
|
||||
|
||||
static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope, ZCC_VirtualScope };
|
||||
excludeflags = 0;
|
||||
fc = 0;
|
||||
for (size_t i = 0; i < countof(excludescope); i++)
|
||||
{
|
||||
if (f->Flags & excludescope[i])
|
||||
{
|
||||
fc++;
|
||||
excludeflags |= excludescope[i];
|
||||
}
|
||||
}
|
||||
if (fc > 1)
|
||||
{
|
||||
Error(f, "Invalid combination of scope qualifiers %s on function %s", FlagsToString(excludeflags).GetChars(), FName(f->Name).GetChars());
|
||||
varflags &= ~(VARF_UI | VARF_Play); // make plain data
|
||||
}
|
||||
|
||||
if (f->Flags & ZCC_Native)
|
||||
{
|
||||
varflags |= VARF_Native;
|
||||
afd = FindFunction(c->Type(), FName(f->Name).GetChars());
|
||||
if (afd == nullptr)
|
||||
{
|
||||
Error(f, "The function '%s.%s' has not been exported from the executable.", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars());
|
||||
Error(f, "The function '%s.%s' has not been exported from the executable", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2232,7 +2360,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
}
|
||||
else if (hasoptionals)
|
||||
{
|
||||
Error(p, "All arguments after the first optional one need also be optional.");
|
||||
Error(p, "All arguments after the first optional one need also be optional");
|
||||
}
|
||||
// TBD: disallow certain types? For now, let everything pass that isn't an array.
|
||||
args.Push(type);
|
||||
|
@ -2287,18 +2415,26 @@ 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.
|
||||
sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::SideFromFlags(varflags);
|
||||
}
|
||||
|
||||
PClass *clstype = static_cast<PClass *>(c->Type());
|
||||
if (varflags & VARF_Virtual)
|
||||
{
|
||||
if (sym->Variants[0].Implementation == nullptr)
|
||||
{
|
||||
Error(f, "Virtual function %s.%s not present.", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars());
|
||||
Error(f, "Virtual function %s.%s not present", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars());
|
||||
return;
|
||||
}
|
||||
|
||||
if (varflags & VARF_Final)
|
||||
{
|
||||
sym->Variants[0].Implementation->Final = true;
|
||||
}
|
||||
if (varflags & VARF_ReadOnly)
|
||||
sym->Variants[0].Implementation->FuncConst = true;
|
||||
|
||||
if (forclass)
|
||||
{
|
||||
int vindex = clstype->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto);
|
||||
|
@ -2316,6 +2452,23 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
|||
{
|
||||
Error(f, "Attempt to override final function %s", FName(f->Name).GetChars());
|
||||
}
|
||||
// you can't change ui/play/clearscope for a virtual method.
|
||||
if (f->Flags & (ZCC_UIFlag|ZCC_Play|ZCC_ClearScope|ZCC_VirtualScope))
|
||||
{
|
||||
Error(f, "Attempt to change scope for virtual function %s", FName(f->Name).GetChars());
|
||||
}
|
||||
// you can't change const qualifier for a virtual method
|
||||
if (oldfunc->FuncConst != sym->Variants[0].Implementation->FuncConst)
|
||||
{
|
||||
Error(f, "Attempt to change const qualifier for virtual function %s", FName(f->Name).GetChars());
|
||||
}
|
||||
// inherit scope of original function if override not specified
|
||||
sym->Variants[0].Implementation->BarrierSide = oldfunc->BarrierSide;
|
||||
sym->Variants[0].Flags = FScopeBarrier::ChangeSideInFlags(sym->Variants[0].Flags, oldfunc->BarrierSide);
|
||||
// inherit const from original function
|
||||
if ((sym->Variants[0].Implementation->FuncConst = oldfunc->FuncConst))
|
||||
sym->Variants[0].Flags |= VARF_ReadOnly;
|
||||
|
||||
clstype->Virtuals[vindex] = sym->Variants[0].Implementation;
|
||||
sym->Variants[0].Implementation->VirtualIndex = vindex;
|
||||
}
|
||||
|
|
|
@ -137,6 +137,10 @@ static void InitTokenMap()
|
|||
TOKENDEF (TK_Latent, ZCC_LATENT);
|
||||
TOKENDEF (TK_Virtual, ZCC_VIRTUAL);
|
||||
TOKENDEF (TK_VarArg, ZCC_VARARG);
|
||||
TOKENDEF (TK_UI, ZCC_UI);
|
||||
TOKENDEF (TK_Play, ZCC_PLAY);
|
||||
TOKENDEF (TK_ClearScope, ZCC_CLEARSCOPE);
|
||||
TOKENDEF (TK_VirtualScope, ZCC_VIRTUALSCOPE);
|
||||
TOKENDEF (TK_Override, ZCC_OVERRIDE);
|
||||
TOKENDEF (TK_Final, ZCC_FINAL);
|
||||
TOKENDEF (TK_Meta, ZCC_META);
|
||||
|
|
|
@ -36,7 +36,11 @@ enum
|
|||
ZCC_Virtual = 1 << 13,
|
||||
ZCC_Override = 1 << 14,
|
||||
ZCC_Transient = 1 << 15,
|
||||
ZCC_VarArg = 1 << 16
|
||||
ZCC_VarArg = 1 << 16,
|
||||
ZCC_UIFlag = 1 << 17, // there's also token called ZCC_UI
|
||||
ZCC_Play = 1 << 18,
|
||||
ZCC_ClearScope = 1 << 19,
|
||||
ZCC_VirtualScope = 1 << 20,
|
||||
};
|
||||
|
||||
// Function parameter modifiers
|
||||
|
|
|
@ -450,13 +450,4 @@ template<> struct THashTraits<FString>
|
|||
int Compare(const FString &left, const FString &right) { return left.Compare(right); }
|
||||
};
|
||||
|
||||
class FStringNoInit
|
||||
{
|
||||
char mem[sizeof(FString)];
|
||||
operator FString&()
|
||||
{
|
||||
return *reinterpret_cast<FString*>(&mem);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -450,13 +450,13 @@ class Actor : Thinker native
|
|||
native static int FindUniqueTid(int start = 0, int limit = 0);
|
||||
native void SetShade(color col);
|
||||
|
||||
native string GetTag(string defstr = "");
|
||||
native string GetTag(string defstr = "") const;
|
||||
native void SetTag(string defstr = "");
|
||||
native double GetBobOffset(double frac = 0);
|
||||
native void ClearCounters();
|
||||
native bool GiveBody (int num, int max=0);
|
||||
native bool HitFloor();
|
||||
native bool isTeammate(Actor other);
|
||||
native bool isTeammate(Actor other) const;
|
||||
native int PlayerNumber();
|
||||
native void SetFriendPlayer(PlayerInfo player);
|
||||
native void SoundAlert(Actor target, bool splash = false, double maxdist = 0);
|
||||
|
@ -468,15 +468,15 @@ class Actor : Thinker native
|
|||
native bool UpdateWaterLevel (bool splash = true);
|
||||
native bool IsZeroDamage();
|
||||
native void ClearInterpolation();
|
||||
native Vector3 PosRelative(sector sec);
|
||||
native Vector3 PosRelative(sector sec) const;
|
||||
|
||||
native void HandleSpawnFlags();
|
||||
native void ExplodeMissile(line lin = null, Actor target = null, bool onsky = false);
|
||||
native void RestoreDamage();
|
||||
native int SpawnHealth();
|
||||
native int SpawnHealth() const;
|
||||
native void SetDamage(int dmg);
|
||||
native double Distance2D(Actor other);
|
||||
native double Distance3D(Actor other);
|
||||
native double Distance2D(Actor other) const;
|
||||
native double Distance3D(Actor other) const;
|
||||
native void SetOrigin(vector3 newpos, bool moving);
|
||||
native void SetXYZ(vector3 newpos);
|
||||
native Actor GetPointer(int aaptr);
|
||||
|
@ -511,7 +511,7 @@ class Actor : Thinker native
|
|||
native void BloodSplatter (Vector3 pos, double hitangle, bool axe = false);
|
||||
native bool HitWater (sector sec, Vector3 pos, bool checkabove = false, bool alert = true, bool force = false);
|
||||
native void PlaySpawnSound(Actor missile);
|
||||
native bool CountsAsKill();
|
||||
native bool CountsAsKill() const;
|
||||
|
||||
native bool Teleport(Vector3 pos, double angle, int flags);
|
||||
native void TraceBleed(int damage, Actor missile);
|
||||
|
@ -559,21 +559,21 @@ class Actor : Thinker native
|
|||
native void LinkToWorld(LinkContext ctx = null);
|
||||
native void UnlinkFromWorld(out LinkContext ctx = null);
|
||||
native bool CanSeek(Actor target);
|
||||
native double AngleTo(Actor target, bool absolute = false);
|
||||
native double AngleTo(Actor target, bool absolute = false) const;
|
||||
native void AddZ(double zadd, bool moving = true);
|
||||
native void SetZ(double z);
|
||||
native vector2 Vec2To(Actor other);
|
||||
native vector3 Vec3To(Actor other);
|
||||
native vector3 Vec3Offset(double x, double y, double z, bool absolute = false);
|
||||
native vector3 Vec3Angle(double length, double angle, double z = 0, bool absolute = false);
|
||||
native vector2 Vec2Angle(double length, double angle, bool absolute = false);
|
||||
native vector2 Vec2Offset(double x, double y, bool absolute = false);
|
||||
native vector3 Vec2OffsetZ(double x, double y, double atz, bool absolute = false);
|
||||
native void VelFromAngle(double speed = 0, double angle = 0);
|
||||
native void Vel3DFromAngle(double speed, double angle, double pitch);
|
||||
native vector2 Vec2To(Actor other) const;
|
||||
native vector3 Vec3To(Actor other) const;
|
||||
native vector3 Vec3Offset(double x, double y, double z, bool absolute = false) const;
|
||||
native vector3 Vec3Angle(double length, double angle, double z = 0, bool absolute = false) const;
|
||||
native vector2 Vec2Angle(double length, double angle, bool absolute = false) const;
|
||||
native vector2 Vec2Offset(double x, double y, bool absolute = false) const;
|
||||
native vector3 Vec2OffsetZ(double x, double y, double atz, bool absolute = false) const;
|
||||
native void VelFromAngle(double speed = 0, double angle = 0) const;
|
||||
native void Vel3DFromAngle(double speed, double angle, double pitch) const;
|
||||
native void Thrust(double speed = 0, double angle = 0);
|
||||
native bool isFriend(Actor other);
|
||||
native bool isHostile(Actor other);
|
||||
native bool isFriend(Actor other) const;
|
||||
native bool isHostile(Actor other) const;
|
||||
native void AdjustFloorClip();
|
||||
native DropItem GetDropItems();
|
||||
native void CopyFriendliness (Actor other, bool changeTarget, bool resetHealth = true);
|
||||
|
@ -588,8 +588,8 @@ class Actor : Thinker native
|
|||
native void Howl();
|
||||
native void DrawSplash (int count, double angle, int kind);
|
||||
native void GiveSecret(bool printmsg = true, bool playsound = true);
|
||||
native double GetCameraHeight();
|
||||
native double GetGravity();
|
||||
native double GetCameraHeight() const;
|
||||
native double GetGravity() const;
|
||||
|
||||
native bool CheckClass(class<Actor> checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false);
|
||||
native void AddInventory(Inventory inv);
|
||||
|
@ -597,7 +597,7 @@ class Actor : Thinker native
|
|||
native void ClearInventory();
|
||||
native bool GiveInventory(class<Inventory> type, int amount, bool givecheat = false);
|
||||
native bool TakeInventory(class<Inventory> itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false);
|
||||
native Inventory FindInventory(class<Inventory> itemtype, bool subclass = false);
|
||||
native Inventory FindInventory(class<Inventory> itemtype, bool subclass = false) const;
|
||||
native Inventory GiveInventoryType(class<Inventory> itemtype);
|
||||
native Inventory DropInventory (Inventory item, int amt = -1);
|
||||
native bool UseInventory(Inventory item);
|
||||
|
@ -613,7 +613,7 @@ class Actor : Thinker native
|
|||
native double GetDistance(bool checkz, int ptr = AAPTR_TARGET);
|
||||
native double GetAngle(int flags, int ptr = AAPTR_TARGET);
|
||||
native double GetZAt(double px = 0, double py = 0, double angle = 0, int flags = 0, int pick_pointer = AAPTR_DEFAULT);
|
||||
native int GetSpawnHealth();
|
||||
native int GetSpawnHealth() const;
|
||||
native double GetCrouchFactor(int ptr = AAPTR_PLAYER1);
|
||||
native double GetCVar(string cvar);
|
||||
native int GetPlayerInput(int inputnum, int ptr = AAPTR_DEFAULT);
|
||||
|
|
|
@ -331,10 +331,10 @@ class Object native
|
|||
native static uint MSTime();
|
||||
|
||||
native Name GetClassName();
|
||||
native void Destroy();
|
||||
native virtualscope void Destroy();
|
||||
|
||||
// This does not call into the native method of the same name to avoid problems with objects that get garbage collected late on shutdown.
|
||||
virtual void OnDestroy() {}
|
||||
virtual virtualscope void OnDestroy() {}
|
||||
}
|
||||
|
||||
class BrokenLines : Object native
|
||||
|
@ -344,7 +344,7 @@ class BrokenLines : Object native
|
|||
native String StringAt(int line);
|
||||
}
|
||||
|
||||
class Thinker : Object native
|
||||
class Thinker : Object native play
|
||||
{
|
||||
enum EStatnums
|
||||
{
|
||||
|
@ -386,7 +386,6 @@ class Thinker : Object native
|
|||
|
||||
class ThinkerIterator : Object native
|
||||
{
|
||||
|
||||
native static ThinkerIterator Create(class<Object> type = "Actor", int statnum=Thinker.MAX_STATNUM+1);
|
||||
native Thinker Next(bool exact = false);
|
||||
native void Reinit();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class BaseEvent native { } // just a base class. it doesn't inherit from Object on the scripting side so you can't call Destroy() on it and break everything.
|
||||
|
||||
class RenderEvent : BaseEvent native
|
||||
class RenderEvent : BaseEvent native ui
|
||||
{
|
||||
native readonly Vector3 ViewPos;
|
||||
native readonly double ViewAngle;
|
||||
|
@ -10,7 +10,7 @@ class RenderEvent : BaseEvent native
|
|||
native readonly Actor Camera;
|
||||
}
|
||||
|
||||
class WorldEvent : BaseEvent native
|
||||
class WorldEvent : BaseEvent native play
|
||||
{
|
||||
// for loaded/unloaded
|
||||
native readonly bool IsSaveGame;
|
||||
|
@ -28,7 +28,7 @@ class WorldEvent : BaseEvent native
|
|||
native readonly double DamageAngle;
|
||||
}
|
||||
|
||||
class PlayerEvent : BaseEvent native
|
||||
class PlayerEvent : BaseEvent native play
|
||||
{
|
||||
// this is the player number that caused the event.
|
||||
// note: you can get player struct from this by using players[e.PlayerNumber]
|
||||
|
@ -37,7 +37,7 @@ class PlayerEvent : BaseEvent native
|
|||
native readonly bool IsReturn;
|
||||
}
|
||||
|
||||
class UiEvent : BaseEvent native
|
||||
class UiEvent : BaseEvent native ui
|
||||
{
|
||||
// d_gui.h
|
||||
enum EGUIEvent
|
||||
|
@ -121,7 +121,7 @@ class UiEvent : BaseEvent native
|
|||
native readonly bool IsAlt;
|
||||
}
|
||||
|
||||
class InputEvent : BaseEvent native
|
||||
class InputEvent : BaseEvent native play
|
||||
{
|
||||
enum EGenericEvent
|
||||
{
|
||||
|
@ -279,7 +279,7 @@ class ConsoleEvent : BaseEvent native
|
|||
native readonly int Args[3];
|
||||
}
|
||||
|
||||
class StaticEventHandler : Object native
|
||||
class StaticEventHandler : Object native play
|
||||
{
|
||||
// static event handlers CAN register other static event handlers.
|
||||
// unlike EventHandler.Create that will not create them.
|
||||
|
@ -307,8 +307,8 @@ class StaticEventHandler : Object native
|
|||
virtual native void WorldTick(WorldEvent e);
|
||||
|
||||
//
|
||||
virtual native void RenderFrame(RenderEvent e);
|
||||
virtual native void RenderOverlay(RenderEvent e);
|
||||
virtual native ui void RenderFrame(RenderEvent e);
|
||||
virtual native ui void RenderOverlay(RenderEvent e);
|
||||
|
||||
//
|
||||
virtual native void PlayerEntered(PlayerEvent e);
|
||||
|
@ -317,11 +317,12 @@ class StaticEventHandler : Object native
|
|||
virtual native void PlayerDisconnected(PlayerEvent e);
|
||||
|
||||
//
|
||||
virtual native bool UiProcess(UiEvent e);
|
||||
virtual native ui bool UiProcess(UiEvent e);
|
||||
virtual native bool InputProcess(InputEvent e);
|
||||
|
||||
//
|
||||
virtual native void ConsoleProcess(ConsoleEvent e);
|
||||
virtual native ui void ConsoleProcess(ConsoleEvent e);
|
||||
virtual native void NetworkProcess(ConsoleEvent e);
|
||||
|
||||
// this value will be queried on Register() to decide the relative order of this handler to every other.
|
||||
// this is most useful in UI systems.
|
||||
|
@ -342,4 +343,6 @@ class EventHandler : StaticEventHandler native
|
|||
|
||||
static native bool Register(StaticEventHandler handler);
|
||||
static native bool Unregister(StaticEventHandler handler);
|
||||
|
||||
clearscope static native void SendNetworkEvent(String name, int arg1 = 0, int arg2 = 0, int arg3 = 0);
|
||||
}
|
||||
|
|
|
@ -751,7 +751,7 @@ class Inventory : Actor native
|
|||
virtual bool Use (bool pickup) { return false; }
|
||||
virtual double GetSpeedFactor() { return 1; }
|
||||
virtual bool GetNoTeleportFreeze() { return false; }
|
||||
virtual void AlterWeaponSprite(VisStyle vis, in out int changed) {}
|
||||
virtual ui void AlterWeaponSprite(VisStyle vis, in out int changed) {}
|
||||
virtual void OwnerDied() {}
|
||||
virtual Color GetBlend () { return 0; }
|
||||
|
||||
|
@ -818,7 +818,7 @@ class Inventory : Actor native
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
virtual bool DrawPowerup(int x, int y) { return false; }
|
||||
virtual ui bool DrawPowerup(int x, int y) { return false; }
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
|
|
|
@ -285,7 +285,7 @@ class Powerup : Inventory
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
virtual bool isBlinking()
|
||||
virtual bool isBlinking() const
|
||||
{
|
||||
return (EffectTics <= BLINKTHRESHOLD && (EffectTics & 8) && !bNoScreenBlink);
|
||||
}
|
||||
|
@ -934,7 +934,7 @@ class PowerFlight : Powerup
|
|||
+INVENTORY.HUBPOWER
|
||||
}
|
||||
|
||||
bool HitCenterFrame;
|
||||
ui bool HitCenterFrame;
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
struct SectorPortal native
|
||||
struct SectorPortal native play
|
||||
{
|
||||
enum EType
|
||||
{
|
||||
|
@ -31,12 +31,12 @@ struct SectorPortal native
|
|||
};
|
||||
|
||||
|
||||
struct Vertex native
|
||||
struct Vertex native play
|
||||
{
|
||||
native readonly Vector2 p;
|
||||
}
|
||||
|
||||
struct Side native
|
||||
struct Side native play
|
||||
{
|
||||
enum ETexpart
|
||||
{
|
||||
|
@ -100,7 +100,7 @@ struct Side native
|
|||
|
||||
};
|
||||
|
||||
struct Line native
|
||||
struct Line native play
|
||||
{
|
||||
enum ELineFlags
|
||||
{
|
||||
|
@ -143,7 +143,7 @@ struct Line native
|
|||
native int special;
|
||||
native int args[5]; // <--- hexen-style arguments (expanded to ZDoom's full width)
|
||||
native double alpha; // <--- translucency (0=invisibile, FRACUNIT=opaque)
|
||||
native Side sidedef[2];
|
||||
native readonly Side sidedef[2];
|
||||
native readonly double bbox[4]; // bounding box, for the extent of the LineDef.
|
||||
native readonly Sector frontsector, backsector;
|
||||
native int validcount; // if == validcount, already checked
|
||||
|
@ -171,25 +171,25 @@ struct Line native
|
|||
}
|
||||
}
|
||||
|
||||
struct SecPlane native
|
||||
struct SecPlane native play
|
||||
{
|
||||
native Vector3 Normal;
|
||||
native double D;
|
||||
native double negiC;
|
||||
|
||||
native bool isSlope();
|
||||
native int PointOnSide(Vector3 pos);
|
||||
native double ZatPoint (Vector2 v);
|
||||
native double ZatPointDist(Vector2 v, double dist);
|
||||
native bool isEqual(Secplane other);
|
||||
native bool isSlope() const;
|
||||
native int PointOnSide(Vector3 pos) const;
|
||||
native double ZatPoint (Vector2 v) const;
|
||||
native double ZatPointDist(Vector2 v, double dist) const;
|
||||
native bool isEqual(Secplane other) const;
|
||||
native void ChangeHeight(double hdiff);
|
||||
native double GetChangedHeight(double hdiff);
|
||||
native double GetChangedHeight(double hdiff) const;
|
||||
native double HeightDiff(double oldd, double newd = 0.0);
|
||||
native double PointToDist(Vector2 xy, double z);
|
||||
native double PointToDist(Vector2 xy, double z) const;
|
||||
}
|
||||
|
||||
// This encapsulates all info Doom's original 'special' field contained - for saving and transferring.
|
||||
struct SecSpecial
|
||||
struct SecSpecial play
|
||||
{
|
||||
Name damagetype;
|
||||
int damageamount;
|
||||
|
@ -199,7 +199,7 @@ struct SecSpecial
|
|||
int Flags;
|
||||
}
|
||||
|
||||
struct Sector native
|
||||
struct Sector native play
|
||||
{
|
||||
//secplane_t floorplane, ceilingplane; // defined internally
|
||||
//FDynamicColormap *ColorMap;
|
||||
|
|
|
@ -48,7 +48,7 @@ struct JoystickConfig native
|
|||
|
||||
}
|
||||
|
||||
class Menu : Object native
|
||||
class Menu : Object native ui
|
||||
{
|
||||
enum EMenuKey
|
||||
{
|
||||
|
@ -287,7 +287,7 @@ class Menu : Object native
|
|||
|
||||
}
|
||||
|
||||
class MenuDescriptor : Object native
|
||||
class MenuDescriptor : Object native ui
|
||||
{
|
||||
native Name mMenuName;
|
||||
native String mNetgameMessage;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//
|
||||
//=============================================================================
|
||||
|
||||
class MenuItemBase : Object native
|
||||
class MenuItemBase : Object native ui
|
||||
{
|
||||
protected native double mXpos, mYpos;
|
||||
protected native Name mAction;
|
||||
|
|
|
@ -150,7 +150,7 @@ class PlayerPawn : Actor native
|
|||
}
|
||||
|
||||
// This is for SBARINFO.
|
||||
int, int GetEffectTicsForItem(class<Inventory> item)
|
||||
int, int GetEffectTicsForItem(class<Inventory> item) const
|
||||
{
|
||||
let pg = (class<PowerupGiver>)(item);
|
||||
if (pg != null)
|
||||
|
@ -167,10 +167,10 @@ class PlayerPawn : Actor native
|
|||
return -1, -1;
|
||||
}
|
||||
|
||||
native int GetMaxHealth(bool withupgrades = false);
|
||||
native int GetMaxHealth(bool withupgrades = false) const;
|
||||
native bool ResetAirSupply (bool playgasp = false);
|
||||
native void CheckWeaponSwitch(class<Inventory> item);
|
||||
native static String GetPrintableDisplayName(Class<Actor> cls);
|
||||
native clearscope static String GetPrintableDisplayName(Class<Actor> cls);
|
||||
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ class PlayerChunk : PlayerPawn
|
|||
}
|
||||
}
|
||||
|
||||
class PSprite : Object native
|
||||
class PSprite : Object native play
|
||||
{
|
||||
enum PSPLayers
|
||||
{
|
||||
|
@ -239,7 +239,7 @@ enum EPlayerState
|
|||
PST_GONE // Player has left the game
|
||||
}
|
||||
|
||||
struct PlayerInfo native // this is what internally is known as player_t
|
||||
struct PlayerInfo native play // this is what internally is known as player_t
|
||||
{
|
||||
// technically engine constants but the only part of the playsim using them is the player.
|
||||
const NOFIXEDCOLORMAP = -1;
|
||||
|
@ -340,22 +340,22 @@ usercmd_t original_cmd;
|
|||
native void PoisonDamage(Actor source, int damage, bool playPainSound);
|
||||
native void SetPsprite(int id, State stat, bool pending = false);
|
||||
native void SetSafeFlash(Weapon weap, State flashstate, int index);
|
||||
native PSprite GetPSprite(int id);
|
||||
native PSprite FindPSprite(int id);
|
||||
native PSprite GetPSprite(int id) const;
|
||||
native PSprite FindPSprite(int id) const;
|
||||
native void SetLogNumber (int text);
|
||||
native void SetLogText (String text);
|
||||
native void DropWeapon();
|
||||
native void BringUpWeapon();
|
||||
|
||||
native String GetUserName();
|
||||
native Color GetColor();
|
||||
native int GetColorSet();
|
||||
native int GetPlayerClassNum();
|
||||
native int GetSkin();
|
||||
native bool GetNeverSwitch();
|
||||
native int GetGender();
|
||||
native int GetTeam();
|
||||
native float GetAutoaim();
|
||||
native String GetUserName() const;
|
||||
native Color GetColor() const;
|
||||
native int GetColorSet() const;
|
||||
native int GetPlayerClassNum() const;
|
||||
native int GetSkin() const;
|
||||
native bool GetNeverSwitch() const;
|
||||
native int GetGender() const;
|
||||
native int GetTeam() const;
|
||||
native float GetAutoaim() const;
|
||||
native void SetFOV(float fov);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue