Tactical merge

This commit is contained in:
ZZYZX 2017-02-05 19:14:56 +02:00
commit e4970189b6
57 changed files with 1196 additions and 423 deletions

View file

@ -1420,6 +1420,76 @@ ExpEmit FxSoundCast::Emit(VMFunctionBuilder *build)
return to;
}
//==========================================================================
//
//
//
//==========================================================================
FxFontCast::FxFontCast(FxExpression *x)
: FxExpression(EFX_FontCast, x->ScriptPosition)
{
basex = x;
ValueType = TypeSound;
}
//==========================================================================
//
//
//
//==========================================================================
FxFontCast::~FxFontCast()
{
SAFE_DELETE(basex);
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxFontCast::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
SAFE_RESOLVE(basex, ctx);
if (basex->ValueType == TypeFont)
{
FxExpression *x = basex;
basex = nullptr;
delete this;
return x;
}
// This intentionally does not convert non-constants.
// The sole reason for this cast is to allow passing both font pointers and string constants to printing functions and have the font names checked at compile time.
else if ((basex->ValueType == TypeString || basex->ValueType == TypeName) && basex->isConstant())
{
ExpVal constval = static_cast<FxConstant *>(basex)->GetValue();
FFont *font = V_GetFont(constval.GetString());
// Font must exist. Most internal functions working with fonts do not like null pointers.
// If checking is needed scripts will have to call Font.GetFont themselves.
if (font == nullptr)
{
ScriptPosition.Message(MSG_ERROR, "Unknown font '%s'", constval.GetString().GetChars());
delete this;
return nullptr;
}
FxExpression *x = new FxConstant(font, ScriptPosition);
delete this;
return x;
}
else
{
ScriptPosition.Message(MSG_ERROR, "Cannot convert to font");
delete this;
return nullptr;
}
}
//==========================================================================
//
// generic type cast operator
@ -1649,6 +1719,14 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
{
goto basereturn;
}
else if (ValueType == TypeFont)
{
FxExpression *x = new FxFontCast(basex);
x = x->Resolve(ctx);
basex = nullptr;
delete this;
return x;
}
// todo: pointers to class objects.
// All other types are only compatible to themselves and have already been handled above by the equality check.
// Anything that falls through here is not compatible and must print an error.
@ -4918,6 +4996,70 @@ ExpEmit FxATan2::Emit(VMFunctionBuilder *build)
return out;
}
//==========================================================================
//
//
//
//==========================================================================
FxNew::FxNew(FxExpression *v)
: FxExpression(EFX_New, v->ScriptPosition)
{
val = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), v, false);
ValueType = NewPointer(RUNTIME_CLASS(DObject));
}
//==========================================================================
//
//
//
//==========================================================================
FxNew::~FxNew()
{
SAFE_DELETE(val);
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxNew::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
SAFE_RESOLVE(val, ctx);
if (!val->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)))
{
ScriptPosition.Message(MSG_ERROR, "Class type expected");
delete this;
return nullptr;
}
if (val->isConstant())
{
auto cls = static_cast<PClass *>(static_cast<FxConstant*>(val)->GetValue().GetPointer());
ValueType = NewPointer(cls);
}
return this;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxNew::Emit(VMFunctionBuilder *build)
{
assert(ValueType == val->ValueType);
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);
return to;
}
//==========================================================================
//
// The atan2 opcode only takes registers as parameters, so any constants
@ -6489,7 +6631,9 @@ ExpEmit FxStackVariable::Emit(VMFunctionBuilder *build)
if (membervar->BitValue == -1)
{
if (offsetreg == -1) offsetreg = build->GetConstantInt(0);
build->Emit(membervar->Type->GetLoadOp(), loc.RegNum, build->FramePointer.RegNum, offsetreg);
auto op = membervar->Type->GetLoadOp();
if (op == OP_LO) op = OP_LOS;
build->Emit(op, loc.RegNum, build->FramePointer.RegNum, offsetreg);
}
else
{
@ -7428,6 +7572,15 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
}
break;
case NAME_New:
if (CheckArgSize(MethodName, ArgList, 1, 1, ScriptPosition))
{
func = new FxNew(ArgList[0]);
ArgList[0] = nullptr;
}
break;
default:
ScriptPosition.Message(MSG_ERROR, "Call to unknown function '%s'", MethodName.GetChars());
break;

View file

@ -242,6 +242,7 @@ enum EFxType
EFX_Conditional,
EFX_Abs,
EFX_ATan2,
EFX_New,
EFX_MinMax,
EFX_Random,
EFX_RandomPick,
@ -293,6 +294,7 @@ enum EFxType
EFX_StrLen,
EFX_ColorLiteral,
EFX_GetDefaultByType,
EFX_FontCast,
EFX_COUNT
};
@ -487,6 +489,13 @@ public:
isresolved = true;
}
FxConstant(FFont *state, const FScriptPosition &pos) : FxExpression(EFX_Constant, pos)
{
value.pointer = state;
ValueType = value.Type = TypeFont;
isresolved = true;
}
FxConstant(const FScriptPosition &pos) : FxExpression(EFX_Constant, pos)
{
value.pointer = nullptr;
@ -663,6 +672,18 @@ public:
ExpEmit Emit(VMFunctionBuilder *build);
};
class FxFontCast : public FxExpression
{
FxExpression *basex;
public:
FxFontCast(FxExpression *x);
~FxFontCast();
FxExpression *Resolve(FCompileContext&);
};
//==========================================================================
//
// FxTypeCast
@ -1176,6 +1197,26 @@ public:
private:
ExpEmit ToReg(VMFunctionBuilder *build, FxExpression *val);
};
//==========================================================================
//
//
//
//==========================================================================
class FxNew : public FxExpression
{
FxExpression *val;
public:
FxNew(FxExpression *v);
~FxNew();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================
//
//

View file

@ -53,6 +53,7 @@
#include "vm.h"
#include "p_checkposition.h"
#include "r_sky.h"
#include "v_font.h"
static TArray<FPropertyInfo*> properties;
static TArray<AFuncDesc> AFTable;
@ -801,34 +802,51 @@ void InitThingdef()
pstruct->Size = sizeof(player_t);
pstruct->Align = alignof(player_t);
PArray *parray = NewArray(pstruct, MAXPLAYERS);
PField *playerf = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players);
Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
PField *fieldptr = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
pstruct->AddNativeField("weapons", NewNativeStruct("WeaponSlots", nullptr), myoffsetof(player_t, weapons), VARF_Native);
parray = NewArray(TypeBool, MAXPLAYERS);
playerf = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame);
Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
fieldptr = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
playerf = new PField("gameaction", TypeUInt8, VARF_Native | VARF_Static, (intptr_t)&gameaction);
Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
fieldptr = new PField("gameaction", TypeUInt8, VARF_Native | VARF_Static, (intptr_t)&gameaction);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
playerf = new PField("skyflatnum", TypeTextureID, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&skyflatnum);
Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
fieldptr = new PField("skyflatnum", TypeTextureID, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&skyflatnum);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
playerf = new PField("globalfreeze", TypeUInt8, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&bglobal.freeze);
Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
fieldptr = new PField("globalfreeze", TypeUInt8, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&bglobal.freeze);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
playerf = new PField("consoleplayer", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&consoleplayer);
Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
fieldptr = new PField("consoleplayer", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&consoleplayer);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
auto fontptr = NewPointer(NewNativeStruct("Font", nullptr));
fieldptr = new PField("smallfont", fontptr, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&SmallFont);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
fieldptr = new PField("smallfont2", fontptr, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&SmallFont2);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
fieldptr = new PField("bigfont", fontptr, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&BigFont);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
fieldptr = new PField("confont", fontptr, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&ConFont);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
fieldptr = new PField("intermissionfont", fontptr, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&IntermissionFont);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
// Argh. It sucks when bad hacks need to be supported. WP_NOCHANGE is just a bogus pointer but it used everywhere as a special flag.
// It cannot be defined as constant because constants can either be numbers or strings but nothing else, so the only 'solution'
// is to create a static variable from it and reference that in the script. Yuck!!!
static AWeapon *wpnochg = WP_NOCHANGE;
playerf = new PField("WP_NOCHANGE", NewPointer(RUNTIME_CLASS(AWeapon), false), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&wpnochg);
Namespaces.GlobalNamespace->Symbols.AddSymbol(playerf);
fieldptr = new PField("WP_NOCHANGE", NewPointer(RUNTIME_CLASS(AWeapon), false), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&wpnochg);
Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr);
// synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them.
for (auto &fl : FlagLists)

View file

@ -193,6 +193,7 @@ enum
enum EVMAbortException
{
X_OTHER,
X_READ_NIL,
X_WRITE_NIL,
X_TOO_MANY_TRIES,

View file

@ -281,8 +281,8 @@ unsigned VMFunctionBuilder::GetConstantAddress(void *ptr, VM_ATAG tag)
AddrKonst *locp = AddressConstantMap.CheckKey(ptr);
if (locp != NULL)
{
// There should only be one tag associated with a memory location.
assert(locp->Tag == tag);
// There should only be one tag associated with a memory location. Exceptions are made for null pointers that got allocated through constant arrays.
assert(ptr == nullptr || locp->Tag == tag);
return locp->KonstNum;
}
else

View file

@ -72,6 +72,7 @@
#define RFRF MODE_AF | MODE_BF | MODE_CUNUSED
#define RSRS MODE_AS | MODE_BS | MODE_CUNUSED
#define RPRP MODE_AP | MODE_BP | MODE_CUNUSED
#define RPKP MODE_AP | MODE_BKP | MODE_CUNUSED
#define RXRXI8 MODE_AX | MODE_BX | MODE_CIMMZ
#define RPRPRP MODE_AP | MODE_BP | MODE_CP
#define RPRPKP MODE_AP | MODE_BP | MODE_CKP

View file

@ -209,6 +209,18 @@ begin:
reg.a[a] = GC::ReadBarrier(*(DObject **)ptr);
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(LOS):
ASSERTA(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.a[a] = *(DObject **)ptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(LOS_R):
ASSERTA(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.a[a] = *(DObject **)ptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(LP):
ASSERTA(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
@ -759,6 +771,14 @@ begin:
assert(0);
NEXTOP;
OP(NEW_K):
OP(NEW):
{
PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[C] : konsta[C].v);
reg.a[B] = cls->CreateNew();
NEXTOP;
}
OP(TRY):
assert(try_depth < MAX_TRY_DEPTH);
if (try_depth >= MAX_TRY_DEPTH)

View file

@ -554,6 +554,10 @@ CVMAbortException::CVMAbortException(EVMAbortException reason, const char *morei
AppendMessage("string format failed.");
break;
case X_OTHER:
// no prepended message.
break;
default:
{
size_t len = strlen(m_Message);

View file

@ -44,6 +44,8 @@ xx(LS, ls, RSRPKI, LS_R, 4, REGT_INT), // load string
xx(LS_R, ls, RSRPRI, NOP, 0, 0),
xx(LO, lo, RPRPKI, LO_R, 4, REGT_INT), // load object
xx(LO_R, lo, RPRPRI, NOP, 0, 0),
xx(LOS, los, RPRPKI, LOS_R, 4, REGT_INT), // load object (stack version without read barrier)
xx(LOS_R, lo, RPRPRI, NOP, 0, 0),
xx(LP, lp, RPRPKI, LP_R, 4, REGT_INT), // load pointer
xx(LP_R, lp, RPRPRI, NOP, 0, 0),
xx(LV2, lv2, RVRPKI, LV2_R, 4, REGT_INT), // load vector2
@ -104,6 +106,8 @@ xx(TAIL_K, tail, KPI8, TAIL, 1, REGT_POINTER),
xx(RESULT, result, __BCP, NOP, 0, 0), // Result should go in register encoded in BC (in caller, after CALL)
xx(RET, ret, I8BCP, NOP, 0, 0), // Copy value from register encoded in BC to return value A, possibly returning
xx(RETI, reti, I8I16, NOP, 0, 0), // Copy immediate from BC to return value A, possibly returning
xx(NEW, new, RPRP, NOP, 0, 0),
xx(NEW_K, new, RPKP, NOP, 0, 0),
xx(TRY, try, I24, NOP, 0, 0), // When an exception is thrown, start searching for a handler at pc + ABC
xx(UNTRY, untry, I8, NOP, 0, 0), // Pop A entries off the exception stack
xx(THROW, throw, THROW, NOP, 0, 0), // A == 0: Throw exception object pB