- scriptified Hexen's flies.

A few notes:

 * this accesses the lines array in sector_t which effectively is a pointer to an array of pointers - a type the parser can not represent. The compiler has no problems with it, so for now it is defined internally.
 * array sizes were limited to 65536 entries because the 'bound' instruction only existed as an immediate version with no provisions for larger values. For the static map arrays 65536 is not sufficient so now there are alternative instructions for these cases.
 * despite the above, at the moment there is no proper bounds checking for arrays that have no fixed size. To do this, a lot more work is needed. The type system as-is is not prepared for such a scenario.
This commit is contained in:
Christoph Oelckers 2016-11-27 18:52:24 +01:00
parent aab304c0cf
commit de6969997a
14 changed files with 241 additions and 45 deletions

View file

@ -6578,9 +6578,16 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
PArray *arraytype = dyn_cast<PArray>(Array->ValueType);
if (arraytype == nullptr)
{
ScriptPosition.Message(MSG_ERROR, "'[]' can only be used with arrays.");
delete this;
return nullptr;
// Check if we got a pointer to an array. Some native data structures (like the line list in sectors) use this.
PPointer *ptype = dyn_cast<PPointer>(Array->ValueType);
if (ptype == nullptr || !ptype->PointedType->IsKindOf(RUNTIME_CLASS(PArray)))
{
ScriptPosition.Message(MSG_ERROR, "'[]' can only be used with arrays.");
delete this;
return nullptr;
}
arraytype = static_cast<PArray*>(ptype->PointedType);
arrayispointer = true;
}
if (index->isConstant())
{
@ -6592,37 +6599,40 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
return nullptr;
}
// if this is an array within a class or another struct we can simplify the expression by creating a new PField with a cumulative offset.
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
if (!arrayispointer)
{
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
// PFields are garbage collected so this will be automatically taken care of later.
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
static_cast<FxStructMember *>(Array)->membervar = newfield;
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto x = Array->Resolve(ctx);
Array = nullptr;
return x;
}
else if (Array->ExprType == EFX_GlobalVariable)
{
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
static_cast<FxGlobalVariable *>(Array)->membervar = newfield;
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto x = Array->Resolve(ctx);
Array = nullptr;
return x;
}
else if (Array->ExprType == EFX_StackVariable)
{
auto parentfield = static_cast<FxStackVariable *>(Array)->membervar;
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
static_cast<FxStackVariable *>(Array)->ReplaceField(newfield);
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto x = Array->Resolve(ctx);
Array = nullptr;
return x;
// if this is an array within a class or another struct we can simplify the expression by creating a new PField with a cumulative offset.
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
{
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
// PFields are garbage collected so this will be automatically taken care of later.
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
static_cast<FxStructMember *>(Array)->membervar = newfield;
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto x = Array->Resolve(ctx);
Array = nullptr;
return x;
}
else if (Array->ExprType == EFX_GlobalVariable)
{
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
static_cast<FxGlobalVariable *>(Array)->membervar = newfield;
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto x = Array->Resolve(ctx);
Array = nullptr;
return x;
}
else if (Array->ExprType == EFX_StackVariable)
{
auto parentfield = static_cast<FxStackVariable *>(Array)->membervar;
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
static_cast<FxStackVariable *>(Array)->ReplaceField(newfield);
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto x = Array->Resolve(ctx);
Array = nullptr;
return x;
}
}
}
@ -6644,8 +6654,17 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
{
PArray *arraytype;
if (arrayispointer)
{
arraytype = static_cast<PArray*>(static_cast<PPointer*>(Array->ValueType)->PointedType);
}
else
{
arraytype = static_cast<PArray*>(Array->ValueType);
}
ExpEmit start = Array->Emit(build);
PArray *const arraytype = static_cast<PArray*>(Array->ValueType);
/* what was this for?
if (start.Konst)
@ -6700,7 +6719,16 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
else
{
ExpEmit indexv(index->Emit(build));
build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount);
// Todo: For dynamically allocated arrays (like global sector and linedef tables) we need to get the bound value in here somehow.
// Right now their bounds are not properly checked for.
if (arraytype->ElementCount > 65535)
{
build->Emit(OP_BOUND_K, indexv.RegNum, build->GetConstantInt(arraytype->ElementCount));
}
else
{
build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount);
}
if (!start.Konst)
{

View file

@ -1416,6 +1416,7 @@ public:
FxExpression *index;
bool AddressRequested;
bool AddressWritable;
bool arrayispointer = false;
FxArrayElement(FxExpression*, FxExpression*);
~FxArrayElement();

View file

@ -43,6 +43,7 @@
#include "d_player.h"
#include "p_effect.h"
#include "autosegs.h"
#include "p_maputl.h"
#include "gi.h"
static TArray<FPropertyInfo*> properties;
@ -703,7 +704,11 @@ void InitThingdef()
auto sptr = NewPointer(sstruct);
sstruct->AddNativeField("soundtarget", TypeActor, myoffsetof(sector_t, SoundTarget));
// expose the glibal Multiplayer variable.
// expose the global validcount variable.
PField *vcf = new PField("validcount", TypeSInt32, VARF_Native | VARF_Static, (intptr_t)&validcount);
GlobalSymbols.AddSymbol(vcf);
// expose the global Multiplayer variable.
PField *multif = new PField("multiplayer", TypeBool, VARF_Native | VARF_ReadOnly | VARF_Static, (intptr_t)&multiplayer);
GlobalSymbols.AddSymbol(multif);
@ -726,6 +731,11 @@ void InitThingdef()
PField *playerf = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players);
GlobalSymbols.AddSymbol(playerf);
// set up the lines array in the sector struct. This is a bit messy because the type system is not prepared to handle a pointer to an array of pointers to a native struct even remotely well...
// 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.
pstruct = NewNativeStruct("Sector", nullptr);
pstruct->AddNativeField("lines", NewPointer(NewArray(NewPointer(NewNativeStruct("line", nullptr), false), 0x40000), false), myoffsetof(sector_t, lines), VARF_Native);
parray = NewArray(TypeBool, MAXPLAYERS);
playerf = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame);
GlobalSymbols.AddSymbol(playerf);

View file

@ -779,6 +779,7 @@ VMFunction *FFunctionBuildList::AddFunction(PFunction *functype, FxExpression *c
it.PrintableName = name;
it.Function = new VMScriptFunction;
it.Function->Name = functype->SymbolName;
it.Function->PrintableName = name;
it.Function->ImplicitArgs = functype->GetImplicitArgs();
it.Proto = nullptr;
it.FromDecorate = fromdecorate;
@ -881,7 +882,6 @@ void FFunctionBuildList::Build()
DumpFunction(dump, sfunc, item.PrintableName.GetChars(), (int)item.PrintableName.Len());
codesize += sfunc->CodeSize;
}
sfunc->PrintableName = item.PrintableName;
sfunc->Unsafe = ctx.Unsafe;
}
catch (CRecoverableError &err)

View file

@ -104,7 +104,6 @@
#define RIKIRI MODE_AI | MODE_BKI | MODE_CI
#define RIKII8 MODE_AI | MODE_BKI | MODE_CIMMZ
#define RIRIIs MODE_AI | MODE_BI | MODE_CIMMS
#define RIRI MODE_AI | MODE_BI | MODE_CUNUSED
#define I8RIRI MODE_AIMMZ | MODE_BI | MODE_CI
#define I8RIKI MODE_AIMMZ | MODE_BI | MODE_CKI
#define I8KIRI MODE_AIMMZ | MODE_BKI | MODE_CI

View file

@ -713,6 +713,7 @@ begin:
assert(0);
NEXTOP;
// Fixme: This really needs to throw something more informative than a number. Printing the message here instead of passing it to the exception is not sufficient.
OP(BOUND):
if (reg.d[a] >= BC)
{
@ -722,6 +723,26 @@ begin:
}
NEXTOP;
OP(BOUND_K):
ASSERTKD(BC);
if (reg.d[a] >= konstd[BC])
{
assert(false);
Printf("Array access out of bounds: Max. index = %u, current index = %u\n", konstd[BC], reg.d[a]);
THROW(X_ARRAY_OUT_OF_BOUNDS);
}
NEXTOP;
OP(BOUND_R):
ASSERTD(B);
if (reg.d[a] >= reg.d[B])
{
assert(false);
Printf("Array access out of bounds: Max. index = %u, current index = %u\n", reg.d[B], reg.d[a]);
THROW(X_ARRAY_OUT_OF_BOUNDS);
}
NEXTOP;
OP(CONCAT):
ASSERTS(a); ASSERTS(B); ASSERTS(C);
reg.s[a] = reg.s[B] + reg.s[C];

View file

@ -110,6 +110,8 @@ xx(CATCH, catch, CATCH, NOP, 0, 0), // A == 0: continue search on next try
// A == 3: (pkB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH
// for A > 0, exception is stored in pC
xx(BOUND, bound, RII16, NOP, 0, 0), // if rA >= BC, throw exception
xx(BOUND_K, bound, LKI, NOP, 0, 0), // if rA >= const[BC], throw exception
xx(BOUND_R, bound, RIRI, NOP, 0, 0), // if rA >= rB, throw exception
// String instructions.
xx(CONCAT, concat, RSRSRS, NOP, 0, 0), // sA = sB..sC

View file

@ -346,7 +346,9 @@ static void DoParse(int lumpnum)
}
}
#ifndef NDEBUG
if (f) fprintf(f, "Starting parsing %s\n", sc.String);
#endif
ParseSingleFile(sc.String, 0, parser, state);
}
}