mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-10 14:51:40 +00:00
- separated the Doom specific parts from the compiler backend into a separate file, these parts now get invoked via callback hooks.
This commit is contained in:
parent
64dc9ac456
commit
3454314bb1
14 changed files with 1164 additions and 895 deletions
|
@ -1044,6 +1044,7 @@ set (PCH_SOURCES
|
|||
scripting/thingdef_data.cpp
|
||||
scripting/thingdef_properties.cpp
|
||||
scripting/backend/codegen.cpp
|
||||
scripting/backend/codegen_doom.cpp
|
||||
scripting/backend/vmbuilder.cpp
|
||||
scripting/decorate/olddecorations.cpp
|
||||
scripting/decorate/thingdef_exp.cpp
|
||||
|
|
|
@ -104,3 +104,11 @@ inline float RAD2DEG(float deg)
|
|||
#define SECTION_YREG "yreg"
|
||||
#endif
|
||||
|
||||
// This is needed in common code, despite being Doom specific.
|
||||
enum EStateUseFlags
|
||||
{
|
||||
SUF_ACTOR = 1,
|
||||
SUF_OVERLAY = 2,
|
||||
SUF_WEAPON = 4,
|
||||
SUF_ITEM = 8,
|
||||
};
|
||||
|
|
|
@ -74,14 +74,6 @@ enum EStateFlags
|
|||
STF_DEHACKED = 64, // Modified by Dehacked
|
||||
};
|
||||
|
||||
enum EStateUseFlags
|
||||
{
|
||||
SUF_ACTOR = 1,
|
||||
SUF_OVERLAY = 2,
|
||||
SUF_WEAPON = 4,
|
||||
SUF_ITEM = 8,
|
||||
};
|
||||
|
||||
enum EStateType : int // this must ensure proper alignment.
|
||||
{
|
||||
STATE_Actor,
|
||||
|
|
|
@ -38,22 +38,20 @@
|
|||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "actor.h"
|
||||
#include "cmdlib.h"
|
||||
#include "a_pickups.h"
|
||||
#include "thingdef.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "codegen.h"
|
||||
#include "v_text.h"
|
||||
#include "filesystem.h"
|
||||
#include "doomstat.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "v_video.h"
|
||||
#include "utf8.h"
|
||||
#include "texturemanager.h"
|
||||
#include "m_random.h"
|
||||
#include "v_font.h"
|
||||
#include "templates.h"
|
||||
|
||||
extern FRandom pr_exrandom;
|
||||
FMemArena FxAlloc(65536);
|
||||
CompileEnvironment compileEnvironment;
|
||||
|
||||
struct FLOP
|
||||
{
|
||||
|
@ -228,12 +226,6 @@ static PClass *FindClassType(FName name, FCompileContext &ctx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool isActor(PContainerType *type)
|
||||
{
|
||||
auto cls = PType::toClass(type);
|
||||
return cls ? cls->Descriptor->IsDescendantOf(RUNTIME_CLASS(AActor)) : false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ExpEmit
|
||||
|
@ -272,7 +264,7 @@ void ExpEmit::Reuse(VMFunctionBuilder *build)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
static PFunction *FindBuiltinFunction(FName funcname)
|
||||
PFunction *FindBuiltinFunction(FName funcname)
|
||||
{
|
||||
return dyn_cast<PFunction>(RUNTIME_CLASS(DObject)->FindSymbol(funcname, true));
|
||||
}
|
||||
|
@ -1181,7 +1173,7 @@ ExpEmit FxNameCast::Emit(VMFunctionBuilder *build)
|
|||
assert(ptr.RegType == REGT_POINTER);
|
||||
ptr.Free(build);
|
||||
ExpEmit to(build, REGT_INT);
|
||||
build->Emit(OP_LW, to.RegNum, ptr.RegNum, build->GetConstantInt(myoffsetof(PClassActor, TypeName)));
|
||||
build->Emit(OP_LW, to.RegNum, ptr.RegNum, build->GetConstantInt(myoffsetof(PClass, TypeName)));
|
||||
return to;
|
||||
}
|
||||
}
|
||||
|
@ -1244,7 +1236,7 @@ FxExpression *FxStringCast::Resolve(FCompileContext &ctx)
|
|||
if (basex->isConstant())
|
||||
{
|
||||
ExpVal constval = static_cast<FxConstant *>(basex)->GetValue();
|
||||
FxExpression *x = new FxConstant(S_GetSoundName(constval.GetInt()), ScriptPosition);
|
||||
FxExpression *x = new FxConstant(soundEngine->GetSoundName(constval.GetInt()), ScriptPosition);
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
|
@ -1559,6 +1551,12 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
|
|||
CHECKRESOLVED();
|
||||
SAFE_RESOLVE(basex, ctx);
|
||||
|
||||
if (compileEnvironment.SpecialTypeCast)
|
||||
{
|
||||
auto result = compileEnvironment.SpecialTypeCast(this, ctx);
|
||||
if (result != this) return result;
|
||||
}
|
||||
|
||||
// first deal with the simple types
|
||||
if (ValueType == TypeError || basex->ValueType == TypeError || basex->ValueType == nullptr)
|
||||
{
|
||||
|
@ -1648,70 +1646,6 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
|
|||
delete this;
|
||||
return x;
|
||||
}
|
||||
else if (ValueType == TypeStateLabel)
|
||||
{
|
||||
if (basex->ValueType == TypeNullPtr)
|
||||
{
|
||||
auto x = new FxConstant(0, ScriptPosition);
|
||||
x->ValueType = TypeStateLabel;
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
// Right now this only supports string constants. There should be an option to pass a string variable, too.
|
||||
if (basex->isConstant() && (basex->ValueType == TypeString || basex->ValueType == TypeName))
|
||||
{
|
||||
FString s= static_cast<FxConstant *>(basex)->GetValue().GetString();
|
||||
if (s.Len() == 0 && !ctx.FromDecorate) // DECORATE should never get here at all, but let's better be safe.
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "State jump to empty label.");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
FxExpression *x = new FxMultiNameState(s, basex->ScriptPosition);
|
||||
x = x->Resolve(ctx);
|
||||
basex = nullptr;
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
else if (basex->IsNumeric() && basex->ValueType != TypeSound && basex->ValueType != TypeColor)
|
||||
{
|
||||
if (ctx.StateIndex < 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "State jumps with index can only be used in anonymous state functions.");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
if (ctx.StateCount != 1)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "State jumps with index cannot be used on multistate definitions");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
if (basex->isConstant())
|
||||
{
|
||||
int i = static_cast<FxConstant *>(basex)->GetValue().GetInt();
|
||||
if (i <= 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "State index must be positive");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
FxExpression *x = new FxStateByIndex(ctx.StateIndex + i, ScriptPosition);
|
||||
x = x->Resolve(ctx);
|
||||
basex = nullptr;
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
else
|
||||
{
|
||||
FxExpression *x = new FxRuntimeStateIndex(basex);
|
||||
x = x->Resolve(ctx);
|
||||
basex = nullptr;
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ValueType->isClassPointer())
|
||||
{
|
||||
FxExpression *x = new FxClassTypeCast(static_cast<PClassPointer*>(ValueType), basex, Explicit);
|
||||
|
@ -2819,18 +2753,20 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (compileEnvironment.CheckForCustomAddition)
|
||||
{
|
||||
auto result = compileEnvironment.CheckForCustomAddition(this, ctx);
|
||||
if (result)
|
||||
{
|
||||
ABORT(right);
|
||||
goto goon;
|
||||
}
|
||||
}
|
||||
|
||||
if (left->ValueType == TypeTextureID && right->IsInteger())
|
||||
{
|
||||
ValueType = TypeTextureID;
|
||||
}
|
||||
else if (left->ValueType == TypeState && right->IsInteger() && Operator == '+' && !left->isConstant())
|
||||
{
|
||||
// This is the only special case of pointer addition that will be accepted - because it is used quite often in the existing game code.
|
||||
ValueType = TypeState;
|
||||
right = new FxMulDiv('*', right, new FxConstant((int)sizeof(FState), ScriptPosition)); // multiply by size here, so that constants can be better optimized.
|
||||
right = right->Resolve(ctx);
|
||||
ABORT(right);
|
||||
}
|
||||
else if (left->IsVector() && right->IsVector())
|
||||
{
|
||||
// a vector2 can be added to or subtracted from a vector 3 but it needs to be the right operand.
|
||||
|
@ -2852,7 +2788,7 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx)
|
|||
// To check: It may be that this could pass in DECORATE, although setting TypeVoid here would pretty much prevent that.
|
||||
goto error;
|
||||
}
|
||||
|
||||
goon:
|
||||
if (left->isConstant() && right->isConstant())
|
||||
{
|
||||
if (IsFloat())
|
||||
|
@ -5151,11 +5087,9 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx)
|
|||
|
||||
//==========================================================================
|
||||
//
|
||||
// The CVAR is for finding places where thinkers are created.
|
||||
// Those will require code changes in ZScript 4.0.
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
CVAR(Bool, vm_warnthinkercreation, false, 0)
|
||||
|
||||
static DObject *BuiltinNew(PClass *cls, int outerside, int backwardscompatible)
|
||||
{
|
||||
|
@ -5174,28 +5108,9 @@ static DObject *BuiltinNew(PClass *cls, int outerside, int backwardscompatible)
|
|||
ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
// Creating actors here must be outright prohibited,
|
||||
if (cls->IsDescendantOf(NAME_Actor))
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot create actors with 'new'");
|
||||
return nullptr;
|
||||
}
|
||||
if ((vm_warnthinkercreation || !backwardscompatible) && cls->IsDescendantOf(NAME_Thinker))
|
||||
{
|
||||
// This must output a diagnostic warning
|
||||
Printf("Using 'new' to create thinkers is deprecated.");
|
||||
}
|
||||
// [ZZ] validate readonly and between scope construction
|
||||
if (outerside) FScopeBarrier::ValidateNew(cls, outerside - 1);
|
||||
DObject *object;
|
||||
if (!cls->IsDescendantOf(NAME_Thinker))
|
||||
{
|
||||
object = cls->CreateNew();
|
||||
}
|
||||
else
|
||||
{
|
||||
object = currentVMLevel->CreateThinker(cls);
|
||||
}
|
||||
DObject *object = cls->CreateNew();
|
||||
return object;
|
||||
}
|
||||
|
||||
|
@ -5214,7 +5129,7 @@ ExpEmit FxNew::Emit(VMFunctionBuilder *build)
|
|||
|
||||
// Call DecoRandom to generate a random number.
|
||||
VMFunction *callfunc;
|
||||
auto sym = FindBuiltinFunction(NAME_BuiltinNew);
|
||||
auto sym = FindBuiltinFunction(compileEnvironment.CustomBuiltinNew != NAME_None? compileEnvironment.CustomBuiltinNew : NAME_BuiltinNew);
|
||||
|
||||
assert(sym);
|
||||
callfunc = sym->Variants[0].Implementation;
|
||||
|
@ -5979,7 +5894,6 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
|
|||
{
|
||||
PSymbol * sym;
|
||||
FxExpression *newex = nullptr;
|
||||
int num;
|
||||
|
||||
CHECKRESOLVED();
|
||||
|
||||
|
@ -6007,31 +5921,12 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
}
|
||||
|
||||
if (Identifier == NAME_Default)
|
||||
if (compileEnvironment.CheckSpecialIdentifier)
|
||||
{
|
||||
if (ctx.Function == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Unable to access class defaults from constant declaration");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
if (ctx.Function->Variants[0].SelfClass == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Unable to access class defaults from static function");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
if (!isActor(ctx.Function->Variants[0].SelfClass))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type.");
|
||||
delete this;
|
||||
return nullptr;
|
||||
auto result = compileEnvironment.CheckSpecialIdentifier(this, ctx);
|
||||
if (result != this) return result;
|
||||
}
|
||||
|
||||
FxExpression * x = new FxClassDefaults(new FxSelf(ScriptPosition), ScriptPosition);
|
||||
delete this;
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
|
||||
// Ugh, the horror. Constants need to be taken from the owning class, but members from the self class to catch invalid accesses here...
|
||||
// see if the current class (if valid) defines something with this name.
|
||||
|
@ -6164,14 +6059,6 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
}
|
||||
|
||||
// and line specials
|
||||
if (newex == nullptr && (num = P_FindLineSpecial(Identifier.GetChars(), nullptr, nullptr)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as line special %d\n", Identifier.GetChars(), num);
|
||||
newex = new FxConstant(num, ScriptPosition);
|
||||
goto foundit;
|
||||
}
|
||||
|
||||
if (auto *cvar = FindCVar(Identifier.GetChars(), nullptr))
|
||||
{
|
||||
if (cvar->GetFlags() & CVAR_USERINFO)
|
||||
|
@ -6205,21 +6092,12 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PContainerType *
|
|||
PSymbolTable *symtbl;
|
||||
bool isclass = objtype->isClass();
|
||||
|
||||
if (Identifier == NAME_Default)
|
||||
if (compileEnvironment.ResolveSpecialIdentifier)
|
||||
{
|
||||
if (!isActor(objtype))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type.");
|
||||
delete object;
|
||||
object = nullptr;
|
||||
return nullptr;
|
||||
auto result = compileEnvironment.ResolveSpecialIdentifier(this, object, objtype, ctx);
|
||||
if (result != this) return result;
|
||||
}
|
||||
|
||||
FxExpression * x = new FxClassDefaults(object, ScriptPosition);
|
||||
object = nullptr;
|
||||
delete this;
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
|
||||
if (objtype != nullptr && (sym = objtype->Symbols.FindSymbolInTable(Identifier, symtbl)) != nullptr)
|
||||
{
|
||||
|
@ -6668,56 +6546,6 @@ FxExpression *FxSuper::Resolve(FCompileContext& ctx)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FxClassDefaults::FxClassDefaults(FxExpression *X, const FScriptPosition &pos)
|
||||
: FxExpression(EFX_ClassDefaults, pos)
|
||||
{
|
||||
obj = X;
|
||||
}
|
||||
|
||||
FxClassDefaults::~FxClassDefaults()
|
||||
{
|
||||
SAFE_DELETE(obj);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxClassDefaults::Resolve(FCompileContext& ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
SAFE_RESOLVE(obj, ctx);
|
||||
assert(obj->ValueType->isRealPointer());
|
||||
ValueType = NewPointer(obj->ValueType->toPointer()->PointedType, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
ExpEmit ob = obj->Emit(build);
|
||||
ob.Free(build);
|
||||
ExpEmit meta(build, REGT_POINTER);
|
||||
build->Emit(OP_CLSS, meta.RegNum, ob.RegNum);
|
||||
build->Emit(OP_LP, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults)));
|
||||
return meta;
|
||||
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxGlobalVariable::FxGlobalVariable(PField* mem, const FScriptPosition &pos)
|
||||
: FxMemberBase(EFX_GlobalVariable, mem, pos)
|
||||
{
|
||||
|
@ -7060,19 +6888,10 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx)
|
|||
CHECKRESOLVED();
|
||||
SAFE_RESOLVE(classx, ctx);
|
||||
|
||||
if (membervar->SymbolName == NAME_Default)
|
||||
if (compileEnvironment.CheckSpecialMember)
|
||||
{
|
||||
if (!classx->ValueType->isObjectPointer()
|
||||
|| !static_cast<PObjectPointer *>(classx->ValueType)->PointedClass()->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
FxExpression * x = new FxClassDefaults(classx, ScriptPosition);
|
||||
classx = nullptr;
|
||||
delete this;
|
||||
return x->Resolve(ctx);
|
||||
auto result = compileEnvironment.CheckSpecialMember(this, ctx);
|
||||
if (result != this) return result;
|
||||
}
|
||||
|
||||
// [ZZ] support magic
|
||||
|
@ -7710,7 +7529,7 @@ FxFunctionCall::~FxFunctionCall()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
static bool CheckArgSize(FName fname, FArgumentList &args, int min, int max, FScriptPosition &sc)
|
||||
bool CheckArgSize(FName fname, FArgumentList &args, int min, int max, FScriptPosition &sc)
|
||||
{
|
||||
int s = args.Size();
|
||||
if (s < min)
|
||||
|
@ -7853,46 +7672,10 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
}
|
||||
|
||||
int min, max, special;
|
||||
if (MethodName == NAME_ACS_NamedExecuteWithResult || MethodName == NAME_CallACS)
|
||||
if (compileEnvironment.CheckCustomGlobalFunctions)
|
||||
{
|
||||
special = -ACS_ExecuteWithResult;
|
||||
min = 1;
|
||||
max = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This alias is needed because Actor has a Teleport function.
|
||||
if (MethodName == NAME_TeleportSpecial) MethodName = NAME_Teleport;
|
||||
special = P_FindLineSpecial(MethodName.GetChars(), &min, &max);
|
||||
}
|
||||
if (special != 0 && min >= 0)
|
||||
{
|
||||
int paramcount = ArgList.Size();
|
||||
if (ctx.Function == nullptr || ctx.Class == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Unable to call action special %s from constant declaration", MethodName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (paramcount < min)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Not enough parameters for '%s' (expected %d, got %d)",
|
||||
MethodName.GetChars(), min, paramcount);
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (paramcount > max)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "too many parameters for '%s' (expected %d, got %d)",
|
||||
MethodName.GetChars(), max, paramcount);
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
FxExpression *self = (ctx.Function && (ctx.Function->Variants[0].Flags & VARF_Method) && isActor(ctx.Class)) ? new FxSelf(ScriptPosition) : (FxExpression*)new FxConstant(ScriptPosition);
|
||||
FxExpression *x = new FxActionSpecialCall(self, special, ArgList, ScriptPosition);
|
||||
delete this;
|
||||
return x->Resolve(ctx);
|
||||
auto result = compileEnvironment.CheckCustomGlobalFunctions(this, ctx);
|
||||
if (result != this) return result;
|
||||
}
|
||||
|
||||
PClass *cls = FindClassType(MethodName, ctx);
|
||||
|
@ -7976,14 +7759,6 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
break;
|
||||
|
||||
case NAME_GetDefaultByType:
|
||||
if (CheckArgSize(NAME_GetDefaultByType, ArgList, 1, 1, ScriptPosition))
|
||||
{
|
||||
func = new FxGetDefaultByType(ArgList[0]);
|
||||
ArgList[0] = nullptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAME_SetRandomSeed:
|
||||
if (CheckArgSize(NAME_Random, ArgList, 1, 1, ScriptPosition))
|
||||
{
|
||||
|
@ -8640,176 +8415,6 @@ isresolved:
|
|||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxActionSpecialCall
|
||||
//
|
||||
// If special is negative, then the first argument will be treated as a
|
||||
// name for ACS_NamedExecuteWithResult.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxActionSpecialCall::FxActionSpecialCall(FxExpression *self, int special, FArgumentList &args, const FScriptPosition &pos)
|
||||
: FxExpression(EFX_ActionSpecialCall, pos)
|
||||
{
|
||||
Self = self;
|
||||
Special = special;
|
||||
ArgList = std::move(args);
|
||||
while (ArgList.Size() < 5)
|
||||
{
|
||||
ArgList.Push(new FxConstant(0, ScriptPosition));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxActionSpecialCall::~FxActionSpecialCall()
|
||||
{
|
||||
SAFE_DELETE(Self);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
bool failed = false;
|
||||
|
||||
SAFE_RESOLVE_OPT(Self, ctx);
|
||||
for (unsigned i = 0; i < ArgList.Size(); i++)
|
||||
{
|
||||
ArgList[i] = ArgList[i]->Resolve(ctx);
|
||||
if (ArgList[i] == nullptr)
|
||||
{
|
||||
failed = true;
|
||||
}
|
||||
else if (Special < 0 && i == 0)
|
||||
{
|
||||
if (ArgList[i]->ValueType == TypeString)
|
||||
{
|
||||
ArgList[i] = new FxNameCast(ArgList[i]);
|
||||
ArgList[i] = ArgList[i]->Resolve(ctx);
|
||||
if (ArgList[i] == nullptr)
|
||||
{
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
else if (ArgList[i]->ValueType != TypeName)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Name expected for parameter %d", i);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
else if (!ArgList[i]->IsInteger())
|
||||
{
|
||||
if (ArgList[i]->ValueType->GetRegType() == REGT_FLOAT /* lax */)
|
||||
{
|
||||
ArgList[i] = new FxIntCast(ArgList[i], ctx.FromDecorate);
|
||||
ArgList[i] = ArgList[i]->Resolve(ctx);
|
||||
if (ArgList[i] == nullptr)
|
||||
{
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Integer expected for parameter %d", i);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (failed)
|
||||
{
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
ValueType = TypeSInt32;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int BuiltinCallLineSpecial(int special, AActor *activator, int arg1, int arg2, int arg3, int arg4, int arg5)
|
||||
{
|
||||
return P_ExecuteSpecial(currentVMLevel , special, nullptr, activator, 0, arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DObject, BuiltinCallLineSpecial, BuiltinCallLineSpecial)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(special);
|
||||
PARAM_OBJECT(activator, AActor);
|
||||
PARAM_INT(arg1);
|
||||
PARAM_INT(arg2);
|
||||
PARAM_INT(arg3);
|
||||
PARAM_INT(arg4);
|
||||
PARAM_INT(arg5);
|
||||
|
||||
ACTION_RETURN_INT(P_ExecuteSpecial(currentVMLevel, special, nullptr, activator, 0, arg1, arg2, arg3, arg4, arg5));
|
||||
}
|
||||
|
||||
ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
unsigned i = 0;
|
||||
|
||||
// Call the BuiltinCallLineSpecial function to perform the desired special.
|
||||
static uint8_t reginfo[] = { REGT_INT, REGT_POINTER, REGT_INT, REGT_INT, REGT_INT, REGT_INT, REGT_INT };
|
||||
auto sym = FindBuiltinFunction(NAME_BuiltinCallLineSpecial);
|
||||
|
||||
assert(sym);
|
||||
auto callfunc = sym->Variants[0].Implementation;
|
||||
|
||||
FunctionCallEmitter emitters(callfunc);
|
||||
|
||||
emitters.AddParameterIntConst(abs(Special)); // pass special number
|
||||
emitters.AddParameter(build, Self);
|
||||
|
||||
|
||||
for (; i < ArgList.Size(); ++i)
|
||||
{
|
||||
FxExpression *argex = ArgList[i];
|
||||
if (Special < 0 && i == 0)
|
||||
{
|
||||
assert(argex->ValueType == TypeName);
|
||||
assert(argex->isConstant());
|
||||
emitters.AddParameterIntConst(-static_cast<FxConstant *>(argex)->GetValue().GetName().GetIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(argex->ValueType->GetRegType() == REGT_INT);
|
||||
if (argex->isConstant())
|
||||
{
|
||||
emitters.AddParameterIntConst(static_cast<FxConstant *>(argex)->GetValue().GetInt());
|
||||
}
|
||||
else
|
||||
{
|
||||
emitters.AddParameter(build, argex);
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgList.DeleteAndClear();
|
||||
ArgList.ShrinkToFit();
|
||||
|
||||
emitters.AddReturn(REGT_INT);
|
||||
return emitters.EmitCall(build);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxVMFunctionCall
|
||||
|
@ -8901,41 +8506,6 @@ VMFunction *FxVMFunctionCall::GetDirectFunction(PFunction *callingfunc, const Ve
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxVMFunctionCall :: UnravelVarArgAJump
|
||||
//
|
||||
// Converts A_Jump(chance, a, b, c, d) -> A_Jump(chance, RandomPick[cajump](a, b, c, d))
|
||||
// so that varargs are restricted to either text formatting or graphics drawing.
|
||||
//
|
||||
//==========================================================================
|
||||
extern FRandom pr_cajump;
|
||||
|
||||
bool FxVMFunctionCall::UnravelVarArgAJump(FCompileContext &ctx)
|
||||
{
|
||||
FArgumentList rplist;
|
||||
|
||||
for (unsigned i = 1; i < ArgList.Size(); i++)
|
||||
{
|
||||
// This needs a bit of casting voodoo because RandomPick wants integer parameters.
|
||||
auto x = new FxIntCast(new FxTypeCast(ArgList[i], TypeStateLabel, true, true), true, true);
|
||||
rplist.Push(x->Resolve(ctx));
|
||||
ArgList[i] = nullptr;
|
||||
if (rplist[i - 1] == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
FxExpression *x = new FxRandomPick(&pr_cajump, rplist, false, ScriptPosition, true);
|
||||
x = x->Resolve(ctx);
|
||||
// This cannot be done with a cast because that interprets the value as an index.
|
||||
// All we want here is to take the literal value and change its type.
|
||||
if (x) x->ValueType = TypeStateLabel;
|
||||
ArgList[1] = x;
|
||||
ArgList.Clamp(2);
|
||||
return x != nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxVMFunctionCall :: Resolve
|
||||
|
@ -8968,20 +8538,13 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Unfortunately the PrintableName is the only safe thing to catch this special case here.
|
||||
if (Function->Variants[0].Implementation->PrintableName.CompareNoCase("Actor.A_Jump [Native]") == 0)
|
||||
if (compileEnvironment.ResolveSpecialFunction)
|
||||
{
|
||||
// Unravel the varargs part of this function here so that the VM->native interface does not have to deal with it anymore.
|
||||
if (ArgList.Size() > 2)
|
||||
{
|
||||
auto ret = UnravelVarArgAJump(ctx);
|
||||
if (!ret)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
auto result = compileEnvironment.ResolveSpecialFunction(this, ctx);
|
||||
if (!result) return nullptr;
|
||||
}
|
||||
|
||||
|
||||
CallingFunction = ctx.Function;
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
|
@ -9318,7 +8881,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
|||
ArgList.ShrinkToFit();
|
||||
|
||||
if (!staticcall) emitters.SetVirtualReg(selfemit.RegNum);
|
||||
int resultcount = vmfunc->Proto->ReturnTypes.Size() == 0 ? 0 : MAX(AssignCount, 1);
|
||||
int resultcount = vmfunc->Proto->ReturnTypes.Size() == 0 ? 0 : std::max(AssignCount, 1);
|
||||
|
||||
assert((unsigned)resultcount <= vmfunc->Proto->ReturnTypes.Size());
|
||||
for (int i = 0; i < resultcount; i++)
|
||||
|
@ -9680,78 +9243,6 @@ ExpEmit FxGetClassName::Emit(VMFunctionBuilder *build)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FxGetDefaultByType::FxGetDefaultByType(FxExpression *self)
|
||||
:FxExpression(EFX_GetDefaultByType, self->ScriptPosition)
|
||||
{
|
||||
Self = self;
|
||||
}
|
||||
|
||||
FxGetDefaultByType::~FxGetDefaultByType()
|
||||
{
|
||||
SAFE_DELETE(Self);
|
||||
}
|
||||
|
||||
FxExpression *FxGetDefaultByType::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
SAFE_RESOLVE(Self, ctx);
|
||||
PClass *cls = nullptr;
|
||||
|
||||
if (Self->ValueType == TypeString || Self->ValueType == TypeName)
|
||||
{
|
||||
if (Self->isConstant())
|
||||
{
|
||||
cls = PClass::FindActor(static_cast<FxConstant *>(Self)->GetValue().GetName());
|
||||
if (cls == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type, but got %s", static_cast<FxConstant *>(Self)->GetValue().GetString().GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
Self = new FxConstant(cls, NewClassPointer(cls), ScriptPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is the ugly case. We do not know what we have and cannot do proper type casting.
|
||||
// For now error out and let this case require explicit handling on the user side.
|
||||
ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type, but got %s", static_cast<FxConstant *>(Self)->GetValue().GetString().GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto cp = PType::toClassPointer(Self->ValueType);
|
||||
if (cp == nullptr || !cp->ClassRestriction->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
cls = cp->ClassRestriction;
|
||||
}
|
||||
ValueType = NewPointer(cls, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
ExpEmit FxGetDefaultByType::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
ExpEmit op = Self->Emit(build);
|
||||
op.Free(build);
|
||||
ExpEmit to(build, REGT_POINTER);
|
||||
if (op.Konst)
|
||||
{
|
||||
build->Emit(OP_LKP, to.RegNum, op.RegNum);
|
||||
op = to;
|
||||
}
|
||||
build->Emit(OP_LP, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults)));
|
||||
return to;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxColorLiteral::FxColorLiteral(FArgumentList &args, FScriptPosition &sc)
|
||||
:FxExpression(EFX_ColorLiteral, sc)
|
||||
{
|
||||
|
@ -11181,215 +10672,6 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build)
|
|||
return emitters.EmitCall(build);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Symbolic state labels.
|
||||
// Conversion will not happen inside the compiler anymore because it causes
|
||||
// just too many problems.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
ABORT(ctx.Class);
|
||||
auto vclass = PType::toClass(ctx.Class);
|
||||
assert(vclass != nullptr);
|
||||
auto aclass = ValidateActor(vclass->Descriptor);
|
||||
|
||||
// This expression type can only be used from actors, for everything else it has already produced a compile error.
|
||||
assert(aclass != nullptr && aclass->GetStateCount() > 0);
|
||||
|
||||
if (aclass->GetStateCount() <= index)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "%s: Attempt to jump to non existing state index %d",
|
||||
ctx.Class->TypeName.GetChars(), index);
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
int symlabel = StateLabels.AddPointer(aclass->GetStates() + index);
|
||||
FxExpression *x = new FxConstant(symlabel, ScriptPosition);
|
||||
x->ValueType = TypeStateLabel;
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxRuntimeStateIndex::FxRuntimeStateIndex(FxExpression *index)
|
||||
: FxExpression(EFX_RuntimeStateIndex, index->ScriptPosition), Index(index)
|
||||
{
|
||||
ValueType = TypeStateLabel;
|
||||
}
|
||||
|
||||
FxRuntimeStateIndex::~FxRuntimeStateIndex()
|
||||
{
|
||||
SAFE_DELETE(Index);
|
||||
}
|
||||
|
||||
FxExpression *FxRuntimeStateIndex::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
SAFE_RESOLVE(Index, ctx);
|
||||
|
||||
if (!Index->IsNumeric())
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (Index->isConstant())
|
||||
{
|
||||
int index = static_cast<FxConstant *>(Index)->GetValue().GetInt();
|
||||
if (index < 0 || (index == 0 && !ctx.FromDecorate))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "State index must be positive");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (index == 0)
|
||||
{
|
||||
int symlabel = StateLabels.AddPointer(nullptr);
|
||||
auto x = new FxConstant(symlabel, ScriptPosition);
|
||||
delete this;
|
||||
x->ValueType = TypeStateLabel;
|
||||
return x;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto x = new FxStateByIndex(ctx.StateIndex + index, ScriptPosition);
|
||||
delete this;
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
}
|
||||
else if (Index->ValueType->GetRegType() != REGT_INT)
|
||||
{ // Float.
|
||||
Index = new FxIntCast(Index, ctx.FromDecorate);
|
||||
SAFE_RESOLVE(Index, ctx);
|
||||
}
|
||||
|
||||
auto vclass = PType::toClass(ctx.Class);
|
||||
assert(vclass != nullptr);
|
||||
auto aclass = ValidateActor(vclass->Descriptor);
|
||||
assert(aclass != nullptr && aclass->GetStateCount() > 0);
|
||||
|
||||
symlabel = StateLabels.AddPointer(aclass->GetStates() + ctx.StateIndex);
|
||||
ValueType = TypeStateLabel;
|
||||
return this;
|
||||
}
|
||||
|
||||
ExpEmit FxRuntimeStateIndex::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
ExpEmit out = Index->Emit(build);
|
||||
// out = (clamp(Index, 0, 32767) << 16) | symlabel | 0x80000000; 0x80000000 is here to make it negative.
|
||||
build->Emit(OP_MAX_RK, out.RegNum, out.RegNum, build->GetConstantInt(0));
|
||||
build->Emit(OP_MIN_RK, out.RegNum, out.RegNum, build->GetConstantInt(32767));
|
||||
build->Emit(OP_SLL_RI, out.RegNum, out.RegNum, 16);
|
||||
build->Emit(OP_OR_RK, out.RegNum, out.RegNum, build->GetConstantInt(symlabel|0x80000000));
|
||||
return out;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxMultiNameState::FxMultiNameState(const char *_statestring, const FScriptPosition &pos, PClassActor *checkclass)
|
||||
:FxExpression(EFX_MultiNameState, pos)
|
||||
{
|
||||
FName scopename = NAME_None;
|
||||
FString statestring = _statestring;
|
||||
int scopeindex = statestring.IndexOf("::");
|
||||
|
||||
if (scopeindex >= 0)
|
||||
{
|
||||
scopename = FName(statestring, scopeindex, false);
|
||||
statestring = statestring.Right(statestring.Len() - scopeindex - 2);
|
||||
}
|
||||
names = MakeStateNameList(statestring);
|
||||
names.Insert(0, scopename);
|
||||
scope = checkclass;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
ABORT(ctx.Class);
|
||||
int symlabel;
|
||||
|
||||
auto vclass = PType::toClass(ctx.Class);
|
||||
//assert(vclass != nullptr);
|
||||
auto clstype = vclass == nullptr? nullptr : ValidateActor(vclass->Descriptor);
|
||||
|
||||
if (names[0] == NAME_None)
|
||||
{
|
||||
scope = nullptr;
|
||||
}
|
||||
else if (clstype == nullptr)
|
||||
{
|
||||
// not in an actor, so any further checks are pointless.
|
||||
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (names[0] == NAME_Super)
|
||||
{
|
||||
scope = ValidateActor(clstype->ParentClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
scope = PClass::FindActor(names[0]);
|
||||
if (scope == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Unknown class '%s' in state label", names[0].GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (!scope->IsAncestorOf(clstype))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (scope != nullptr)
|
||||
{
|
||||
FState *destination = nullptr;
|
||||
// If the label is class specific we can resolve it right here
|
||||
if (names[1] != NAME_None)
|
||||
{
|
||||
destination = scope->FindState(names.Size()-1, &names[1], false);
|
||||
if (destination == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_OPTERROR, "Unknown state jump destination");
|
||||
/* lax */
|
||||
return this;
|
||||
}
|
||||
}
|
||||
symlabel = StateLabels.AddPointer(destination);
|
||||
}
|
||||
else
|
||||
{
|
||||
names.Delete(0);
|
||||
symlabel = StateLabels.AddNames(names);
|
||||
}
|
||||
FxExpression *x = new FxConstant(symlabel, ScriptPosition);
|
||||
x->ValueType = TypeStateLabel;
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// declares a single local variable (no arrays)
|
||||
|
|
|
@ -42,14 +42,14 @@
|
|||
|
||||
#include "m_random.h"
|
||||
#include "sc_man.h"
|
||||
#include "s_sound.h"
|
||||
#include "actor.h"
|
||||
#include "s_soundinternal.h"
|
||||
#include "vmbuilder.h"
|
||||
#include "scopebarrier.h"
|
||||
#include "types.h"
|
||||
#include "vmintern.h"
|
||||
#include "c_cvars.h"
|
||||
|
||||
struct FState; // needed for FxConstant. Maybe move the state constructor to a subclass later?
|
||||
|
||||
#define CHECKRESOLVED() if (isresolved) return this; isresolved=true;
|
||||
#define SAFE_DELETE(p) if (p!=NULL) { delete p; p=NULL; }
|
||||
|
@ -398,23 +398,6 @@ public:
|
|||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxClassDefaults
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxClassDefaults : public FxExpression
|
||||
{
|
||||
FxExpression *obj;
|
||||
|
||||
public:
|
||||
FxClassDefaults(FxExpression *, const FScriptPosition &);
|
||||
~FxClassDefaults();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxConstant
|
||||
|
@ -706,12 +689,11 @@ public:
|
|||
|
||||
class FxTypeCast : public FxExpression
|
||||
{
|
||||
public:
|
||||
FxExpression *basex;
|
||||
bool NoWarn;
|
||||
bool Explicit;
|
||||
|
||||
public:
|
||||
|
||||
FxTypeCast(FxExpression *x, PType *type, bool nowarn, bool explicitly = false);
|
||||
~FxTypeCast();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
|
@ -1539,11 +1521,11 @@ public:
|
|||
|
||||
class FxFunctionCall : public FxExpression
|
||||
{
|
||||
FName MethodName;
|
||||
FRandom *RNG;
|
||||
FArgumentList ArgList;
|
||||
|
||||
public:
|
||||
FName MethodName;
|
||||
FArgumentList ArgList;
|
||||
|
||||
FxFunctionCall(FName methodname, FName rngname, FArgumentList &args, const FScriptPosition &pos);
|
||||
~FxFunctionCall();
|
||||
|
@ -1571,26 +1553,6 @@ public:
|
|||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxActionSpecialCall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxActionSpecialCall : public FxExpression
|
||||
{
|
||||
int Special;
|
||||
FxExpression *Self;
|
||||
FArgumentList ArgList;
|
||||
|
||||
public:
|
||||
|
||||
FxActionSpecialCall(FxExpression *self, int special, FArgumentList &args, const FScriptPosition &pos);
|
||||
~FxActionSpecialCall();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxFlopFunctionCall
|
||||
|
@ -1701,24 +1663,6 @@ public:
|
|||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxGetDefaultByType
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxGetDefaultByType : public FxExpression
|
||||
{
|
||||
FxExpression *Self;
|
||||
|
||||
public:
|
||||
|
||||
FxGetDefaultByType(FxExpression *self);
|
||||
~FxGetDefaultByType();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxColorLiteral
|
||||
|
@ -1750,17 +1694,18 @@ class FxVMFunctionCall : public FxExpression
|
|||
bool NoVirtual;
|
||||
bool hasStringArgs = false;
|
||||
FxExpression *Self;
|
||||
PFunction *Function;
|
||||
FArgumentList ArgList;
|
||||
// for multi assignment
|
||||
int AssignCount = 0;
|
||||
TArray<ExpEmit> ReturnRegs;
|
||||
PFunction *CallingFunction;
|
||||
|
||||
bool CheckAccessibility(const VersionInfo &ver);
|
||||
bool UnravelVarArgAJump(FCompileContext&);
|
||||
|
||||
public:
|
||||
|
||||
FArgumentList ArgList;
|
||||
PFunction* Function;
|
||||
|
||||
FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual);
|
||||
~FxVMFunctionCall();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
|
@ -2041,60 +1986,6 @@ public:
|
|||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Only used to resolve the old jump by index feature of DECORATE
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxStateByIndex : public FxExpression
|
||||
{
|
||||
unsigned index;
|
||||
|
||||
public:
|
||||
|
||||
FxStateByIndex(int i, const FScriptPosition &pos) : FxExpression(EFX_StateByIndex, pos)
|
||||
{
|
||||
index = i;
|
||||
}
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Same as above except for expressions which means it will have to be
|
||||
// evaluated at runtime
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxRuntimeStateIndex : public FxExpression
|
||||
{
|
||||
FxExpression *Index;
|
||||
int symlabel;
|
||||
|
||||
public:
|
||||
FxRuntimeStateIndex(FxExpression *index);
|
||||
~FxRuntimeStateIndex();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxMultiNameState : public FxExpression
|
||||
{
|
||||
PClassActor *scope;
|
||||
TArray<FName> names;
|
||||
public:
|
||||
|
||||
FxMultiNameState(const char *statestring, const FScriptPosition &pos, PClassActor *checkclass = nullptr);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
|
@ -2234,4 +2125,19 @@ public:
|
|||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
|
||||
struct CompileEnvironment
|
||||
{
|
||||
FxExpression* (*SpecialTypeCast)(FxTypeCast* func, FCompileContext& ctx);
|
||||
bool (*CheckForCustomAddition)(FxAddSub* func, FCompileContext& ctx);
|
||||
FxExpression* (*CheckSpecialIdentifier)(FxIdentifier* func, FCompileContext& ctx);
|
||||
FxExpression* (*ResolveSpecialIdentifier)(FxIdentifier* func, FxExpression*& object, PContainerType* objtype, FCompileContext& ctx);
|
||||
FxExpression* (*CheckSpecialMember)(FxStructMember* func, FCompileContext& ctx);
|
||||
FxExpression* (*CheckCustomGlobalFunctions)(FxFunctionCall* func, FCompileContext& ctx);
|
||||
bool (*ResolveSpecialFunction)(FxVMFunctionCall* func, FCompileContext& ctx);
|
||||
FName CustomBuiltinNew; //override the 'new' function if some classes need special treatment.
|
||||
};
|
||||
|
||||
extern CompileEnvironment compileEnvironment;
|
||||
|
||||
#endif
|
||||
|
|
961
src/scripting/backend/codegen_doom.cpp
Normal file
961
src/scripting/backend/codegen_doom.cpp
Normal file
|
@ -0,0 +1,961 @@
|
|||
/*
|
||||
** codegen.cpp
|
||||
**
|
||||
** Compiler backend / code generation for ZScript and DECORATE
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2008-2016 Christoph Oelckers
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "cmdlib.h"
|
||||
#include "codegen.h"
|
||||
#include "codegen_doom.h"
|
||||
#include "v_text.h"
|
||||
#include "filesystem.h"
|
||||
#include "v_video.h"
|
||||
#include "utf8.h"
|
||||
#include "texturemanager.h"
|
||||
#include "m_random.h"
|
||||
#include "v_font.h"
|
||||
#include "templates.h"
|
||||
#include "actor.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
PFunction* FindBuiltinFunction(FName funcname);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool isActor(PContainerType *type)
|
||||
{
|
||||
auto cls = PType::toClass(type);
|
||||
return cls ? cls->Descriptor->IsDescendantOf(RUNTIME_CLASS(AActor)) : false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static FxExpression *CustomTypeCast(FxTypeCast *func, FCompileContext &ctx)
|
||||
{
|
||||
if (func->ValueType == TypeStateLabel)
|
||||
{
|
||||
auto& basex = func->basex;
|
||||
auto& ScriptPosition = func->ScriptPosition;
|
||||
if (basex->ValueType == TypeNullPtr)
|
||||
{
|
||||
auto x = new FxConstant(0, ScriptPosition);
|
||||
x->ValueType = TypeStateLabel;
|
||||
delete func;
|
||||
return x;
|
||||
}
|
||||
// Right now this only supports string constants. There should be an option to pass a string variable, too.
|
||||
if (basex->isConstant() && (basex->ValueType == TypeString || basex->ValueType == TypeName))
|
||||
{
|
||||
FString s= static_cast<FxConstant *>(basex)->GetValue().GetString();
|
||||
if (s.Len() == 0 && !ctx.FromDecorate) // DECORATE should never get here at all, but let's better be safe.
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "State jump to empty label.");
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
FxExpression *x = new FxMultiNameState(s, basex->ScriptPosition);
|
||||
x = x->Resolve(ctx);
|
||||
basex = nullptr;
|
||||
delete func;
|
||||
return x;
|
||||
}
|
||||
else if (basex->IsNumeric() && basex->ValueType != TypeSound && basex->ValueType != TypeColor)
|
||||
{
|
||||
if (ctx.StateIndex < 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "State jumps with index can only be used in anonymous state functions.");
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
if (ctx.StateCount != 1)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "State jumps with index cannot be used on multistate definitions");
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
if (basex->isConstant())
|
||||
{
|
||||
int i = static_cast<FxConstant *>(basex)->GetValue().GetInt();
|
||||
if (i <= 0)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "State index must be positive");
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
FxExpression *x = new FxStateByIndex(ctx.StateIndex + i, ScriptPosition);
|
||||
x = x->Resolve(ctx);
|
||||
basex = nullptr;
|
||||
delete func;
|
||||
return x;
|
||||
}
|
||||
else
|
||||
{
|
||||
FxExpression *x = new FxRuntimeStateIndex(basex);
|
||||
x = x->Resolve(ctx);
|
||||
basex = nullptr;
|
||||
delete func;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static bool CheckForCustomAddition(FxAddSub *func, FCompileContext &ctx)
|
||||
{
|
||||
if (func->left->ValueType == TypeState && func->right->IsInteger() && func->Operator == '+' && !func->left->isConstant())
|
||||
{
|
||||
// This is the only special case of pointer addition that will be accepted - because it is used quite often in the existing game code.
|
||||
func->ValueType = TypeState;
|
||||
func->right = new FxMulDiv('*', func->right, new FxConstant((int)sizeof(FState), func->ScriptPosition)); // multiply by size here, so that constants can be better optimized.
|
||||
func->right = func->right->Resolve(ctx);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static FxExpression* CheckForDefault(FxIdentifier *func, FCompileContext &ctx)
|
||||
{
|
||||
auto& ScriptPosition = func->ScriptPosition;
|
||||
|
||||
if (func->Identifier == NAME_Default)
|
||||
{
|
||||
if (ctx.Function == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Unable to access class defaults from constant declaration");
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
if (ctx.Function->Variants[0].SelfClass == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Unable to access class defaults from static function");
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
if (!isActor(ctx.Function->Variants[0].SelfClass))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type.");
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FxExpression * x = new FxClassDefaults(new FxSelf(ScriptPosition), ScriptPosition);
|
||||
delete func;
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
|
||||
// and line specials
|
||||
int num;
|
||||
if ((num = P_FindLineSpecial(func->Identifier.GetChars(), nullptr, nullptr)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as line special %d\n", func->Identifier.GetChars(), num);
|
||||
auto newex = new FxConstant(num, ScriptPosition);
|
||||
delete func;
|
||||
return newex? newex->Resolve(ctx) : nullptr;
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static FxExpression *ResolveForDefault(FxIdentifier *expr, FxExpression*& object, PContainerType* objtype, FCompileContext &ctx)
|
||||
{
|
||||
if (expr->Identifier == NAME_Default)
|
||||
{
|
||||
if (!isActor(objtype))
|
||||
{
|
||||
expr->ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type.");
|
||||
delete object;
|
||||
object = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FxExpression * x = new FxClassDefaults(object, expr->ScriptPosition);
|
||||
object = nullptr;
|
||||
delete expr;
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression* CheckForMemberDefault(FxStructMember *func, FCompileContext &ctx)
|
||||
{
|
||||
auto& membervar = func->membervar;
|
||||
auto& classx = func->classx;
|
||||
auto& ScriptPosition = func->ScriptPosition;
|
||||
|
||||
if (membervar->SymbolName == NAME_Default)
|
||||
{
|
||||
if (!classx->ValueType->isObjectPointer()
|
||||
|| !static_cast<PObjectPointer *>(classx->ValueType)->PointedClass()->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type");
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
FxExpression * x = new FxClassDefaults(classx, ScriptPosition);
|
||||
classx = nullptr;
|
||||
delete func;
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxVMFunctionCall :: UnravelVarArgAJump
|
||||
//
|
||||
// Converts A_Jump(chance, a, b, c, d) -> A_Jump(chance, RandomPick[cajump](a, b, c, d))
|
||||
// so that varargs are restricted to either text formatting or graphics drawing.
|
||||
//
|
||||
//==========================================================================
|
||||
extern FRandom pr_cajump;
|
||||
|
||||
static bool UnravelVarArgAJump(FxVMFunctionCall *func, FCompileContext &ctx)
|
||||
{
|
||||
auto& ArgList = func->ArgList;
|
||||
FArgumentList rplist;
|
||||
|
||||
for (unsigned i = 1; i < ArgList.Size(); i++)
|
||||
{
|
||||
// This needs a bit of casting voodoo because RandomPick wants integer parameters.
|
||||
auto x = new FxIntCast(new FxTypeCast(ArgList[i], TypeStateLabel, true, true), true, true);
|
||||
rplist.Push(x->Resolve(ctx));
|
||||
ArgList[i] = nullptr;
|
||||
if (rplist[i - 1] == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
FxExpression *x = new FxRandomPick(&pr_cajump, rplist, false, func->ScriptPosition, true);
|
||||
x = x->Resolve(ctx);
|
||||
// This cannot be done with a cast because that interprets the value as an index.
|
||||
// All we want here is to take the literal value and change its type.
|
||||
if (x) x->ValueType = TypeStateLabel;
|
||||
ArgList[1] = x;
|
||||
ArgList.Clamp(2);
|
||||
return x != nullptr;
|
||||
}
|
||||
|
||||
static bool AJumpProcessing(FxVMFunctionCall *func, FCompileContext &ctx)
|
||||
{
|
||||
// Unfortunately the PrintableName is the only safe thing to catch this special case here.
|
||||
if (func->Function->Variants[0].Implementation->PrintableName.CompareNoCase("Actor.A_Jump [Native]") == 0)
|
||||
{
|
||||
// Unravel the varargs part of this function here so that the VM->native interface does not have to deal with it anymore.
|
||||
if (func->ArgList.Size() > 2)
|
||||
{
|
||||
auto ret = UnravelVarArgAJump(func, ctx);
|
||||
if (!ret)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool CheckArgSize(FName fname, FArgumentList &args, int min, int max, FScriptPosition &sc);
|
||||
|
||||
static FxExpression *ResolveGlobalCustomFunction(FxFunctionCall *func, FCompileContext &ctx)
|
||||
{
|
||||
auto& ScriptPosition = func->ScriptPosition;
|
||||
if (func->MethodName == NAME_GetDefaultByType)
|
||||
{
|
||||
if (CheckArgSize(NAME_GetDefaultByType, func->ArgList, 1, 1, ScriptPosition))
|
||||
{
|
||||
auto newfunc = new FxGetDefaultByType(func->ArgList[0]);
|
||||
func->ArgList[0] = nullptr;
|
||||
delete func;
|
||||
return newfunc->Resolve(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int min, max, special;
|
||||
if (func->MethodName == NAME_ACS_NamedExecuteWithResult || func->MethodName == NAME_CallACS)
|
||||
{
|
||||
special = -ACS_ExecuteWithResult;
|
||||
min = 1;
|
||||
max = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This alias is needed because Actor has a Teleport function.
|
||||
if (func->MethodName == NAME_TeleportSpecial) func->MethodName = NAME_Teleport;
|
||||
special = P_FindLineSpecial(func->MethodName.GetChars(), &min, &max);
|
||||
}
|
||||
if (special != 0 && min >= 0)
|
||||
{
|
||||
int paramcount = func->ArgList.Size();
|
||||
if (ctx.Function == nullptr || ctx.Class == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Unable to call action special %s from constant declaration", func->MethodName.GetChars());
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
else if (paramcount < min)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Not enough parameters for '%s' (expected %d, got %d)",
|
||||
func->MethodName.GetChars(), min, paramcount);
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
else if (paramcount > max)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "too many parameters for '%s' (expected %d, got %d)",
|
||||
func->MethodName.GetChars(), max, paramcount);
|
||||
delete func;
|
||||
return nullptr;
|
||||
}
|
||||
FxExpression *self = (ctx.Function && (ctx.Function->Variants[0].Flags & VARF_Method) && isActor(ctx.Class)) ? new FxSelf(ScriptPosition) : (FxExpression*)new FxConstant(ScriptPosition);
|
||||
FxExpression *x = new FxActionSpecialCall(self, special, func->ArgList, ScriptPosition);
|
||||
delete func;
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxActionSpecialCall
|
||||
//
|
||||
// If special is negative, then the first argument will be treated as a
|
||||
// name for ACS_NamedExecuteWithResult.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxActionSpecialCall::FxActionSpecialCall(FxExpression *self, int special, FArgumentList &args, const FScriptPosition &pos)
|
||||
: FxExpression(EFX_ActionSpecialCall, pos)
|
||||
{
|
||||
Self = self;
|
||||
Special = special;
|
||||
ArgList = std::move(args);
|
||||
while (ArgList.Size() < 5)
|
||||
{
|
||||
ArgList.Push(new FxConstant(0, ScriptPosition));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxActionSpecialCall::~FxActionSpecialCall()
|
||||
{
|
||||
SAFE_DELETE(Self);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
bool failed = false;
|
||||
|
||||
SAFE_RESOLVE_OPT(Self, ctx);
|
||||
for (unsigned i = 0; i < ArgList.Size(); i++)
|
||||
{
|
||||
ArgList[i] = ArgList[i]->Resolve(ctx);
|
||||
if (ArgList[i] == nullptr)
|
||||
{
|
||||
failed = true;
|
||||
}
|
||||
else if (Special < 0 && i == 0)
|
||||
{
|
||||
if (ArgList[i]->ValueType == TypeString)
|
||||
{
|
||||
ArgList[i] = new FxNameCast(ArgList[i]);
|
||||
ArgList[i] = ArgList[i]->Resolve(ctx);
|
||||
if (ArgList[i] == nullptr)
|
||||
{
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
else if (ArgList[i]->ValueType != TypeName)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Name expected for parameter %d", i);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
else if (!ArgList[i]->IsInteger())
|
||||
{
|
||||
if (ArgList[i]->ValueType->GetRegType() == REGT_FLOAT /* lax */)
|
||||
{
|
||||
ArgList[i] = new FxIntCast(ArgList[i], ctx.FromDecorate);
|
||||
ArgList[i] = ArgList[i]->Resolve(ctx);
|
||||
if (ArgList[i] == nullptr)
|
||||
{
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Integer expected for parameter %d", i);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (failed)
|
||||
{
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
ValueType = TypeSInt32;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int BuiltinCallLineSpecial(int special, AActor *activator, int arg1, int arg2, int arg3, int arg4, int arg5)
|
||||
{
|
||||
return P_ExecuteSpecial(currentVMLevel , special, nullptr, activator, 0, arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DObject, BuiltinCallLineSpecial, BuiltinCallLineSpecial)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(special);
|
||||
PARAM_OBJECT(activator, AActor);
|
||||
PARAM_INT(arg1);
|
||||
PARAM_INT(arg2);
|
||||
PARAM_INT(arg3);
|
||||
PARAM_INT(arg4);
|
||||
PARAM_INT(arg5);
|
||||
|
||||
ACTION_RETURN_INT(P_ExecuteSpecial(currentVMLevel, special, nullptr, activator, 0, arg1, arg2, arg3, arg4, arg5));
|
||||
}
|
||||
|
||||
ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
unsigned i = 0;
|
||||
|
||||
// Call the BuiltinCallLineSpecial function to perform the desired special.
|
||||
static uint8_t reginfo[] = { REGT_INT, REGT_POINTER, REGT_INT, REGT_INT, REGT_INT, REGT_INT, REGT_INT };
|
||||
auto sym = FindBuiltinFunction(NAME_BuiltinCallLineSpecial);
|
||||
|
||||
assert(sym);
|
||||
auto callfunc = sym->Variants[0].Implementation;
|
||||
|
||||
FunctionCallEmitter emitters(callfunc);
|
||||
|
||||
emitters.AddParameterIntConst(abs(Special)); // pass special number
|
||||
emitters.AddParameter(build, Self);
|
||||
|
||||
|
||||
for (; i < ArgList.Size(); ++i)
|
||||
{
|
||||
FxExpression *argex = ArgList[i];
|
||||
if (Special < 0 && i == 0)
|
||||
{
|
||||
assert(argex->ValueType == TypeName);
|
||||
assert(argex->isConstant());
|
||||
emitters.AddParameterIntConst(-static_cast<FxConstant *>(argex)->GetValue().GetName().GetIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(argex->ValueType->GetRegType() == REGT_INT);
|
||||
if (argex->isConstant())
|
||||
{
|
||||
emitters.AddParameterIntConst(static_cast<FxConstant *>(argex)->GetValue().GetInt());
|
||||
}
|
||||
else
|
||||
{
|
||||
emitters.AddParameter(build, argex);
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgList.DeleteAndClear();
|
||||
ArgList.ShrinkToFit();
|
||||
|
||||
emitters.AddReturn(REGT_INT);
|
||||
return emitters.EmitCall(build);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxClassDefaults::FxClassDefaults(FxExpression *X, const FScriptPosition &pos)
|
||||
: FxExpression(EFX_ClassDefaults, pos)
|
||||
{
|
||||
obj = X;
|
||||
}
|
||||
|
||||
FxClassDefaults::~FxClassDefaults()
|
||||
{
|
||||
SAFE_DELETE(obj);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxClassDefaults::Resolve(FCompileContext& ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
SAFE_RESOLVE(obj, ctx);
|
||||
assert(obj->ValueType->isRealPointer());
|
||||
ValueType = NewPointer(obj->ValueType->toPointer()->PointedType, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
ExpEmit ob = obj->Emit(build);
|
||||
ob.Free(build);
|
||||
ExpEmit meta(build, REGT_POINTER);
|
||||
build->Emit(OP_CLSS, meta.RegNum, ob.RegNum);
|
||||
build->Emit(OP_LP, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults)));
|
||||
return meta;
|
||||
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxGetDefaultByType::FxGetDefaultByType(FxExpression *self)
|
||||
:FxExpression(EFX_GetDefaultByType, self->ScriptPosition)
|
||||
{
|
||||
Self = self;
|
||||
}
|
||||
|
||||
FxGetDefaultByType::~FxGetDefaultByType()
|
||||
{
|
||||
SAFE_DELETE(Self);
|
||||
}
|
||||
|
||||
FxExpression *FxGetDefaultByType::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
SAFE_RESOLVE(Self, ctx);
|
||||
PClass *cls = nullptr;
|
||||
|
||||
if (Self->ValueType == TypeString || Self->ValueType == TypeName)
|
||||
{
|
||||
if (Self->isConstant())
|
||||
{
|
||||
cls = PClass::FindActor(static_cast<FxConstant *>(Self)->GetValue().GetName());
|
||||
if (cls == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type, but got %s", static_cast<FxConstant *>(Self)->GetValue().GetString().GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
Self = new FxConstant(cls, NewClassPointer(cls), ScriptPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is the ugly case. We do not know what we have and cannot do proper type casting.
|
||||
// For now error out and let this case require explicit handling on the user side.
|
||||
ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type, but got %s", static_cast<FxConstant *>(Self)->GetValue().GetString().GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto cp = PType::toClassPointer(Self->ValueType);
|
||||
if (cp == nullptr || !cp->ClassRestriction->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
cls = cp->ClassRestriction;
|
||||
}
|
||||
ValueType = NewPointer(cls, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
ExpEmit FxGetDefaultByType::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
ExpEmit op = Self->Emit(build);
|
||||
op.Free(build);
|
||||
ExpEmit to(build, REGT_POINTER);
|
||||
if (op.Konst)
|
||||
{
|
||||
build->Emit(OP_LKP, to.RegNum, op.RegNum);
|
||||
op = to;
|
||||
}
|
||||
build->Emit(OP_LP, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults)));
|
||||
return to;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Symbolic state labels.
|
||||
// Conversion will not happen inside the compiler anymore because it causes
|
||||
// just too many problems.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
ABORT(ctx.Class);
|
||||
auto vclass = PType::toClass(ctx.Class);
|
||||
assert(vclass != nullptr);
|
||||
auto aclass = ValidateActor(vclass->Descriptor);
|
||||
|
||||
// This expression type can only be used from actors, for everything else it has already produced a compile error.
|
||||
assert(aclass != nullptr && aclass->GetStateCount() > 0);
|
||||
|
||||
if (aclass->GetStateCount() <= index)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "%s: Attempt to jump to non existing state index %d",
|
||||
ctx.Class->TypeName.GetChars(), index);
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
int symlabel = StateLabels.AddPointer(aclass->GetStates() + index);
|
||||
FxExpression *x = new FxConstant(symlabel, ScriptPosition);
|
||||
x->ValueType = TypeStateLabel;
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxRuntimeStateIndex::FxRuntimeStateIndex(FxExpression *index)
|
||||
: FxExpression(EFX_RuntimeStateIndex, index->ScriptPosition), Index(index)
|
||||
{
|
||||
ValueType = TypeStateLabel;
|
||||
}
|
||||
|
||||
FxRuntimeStateIndex::~FxRuntimeStateIndex()
|
||||
{
|
||||
SAFE_DELETE(Index);
|
||||
}
|
||||
|
||||
FxExpression *FxRuntimeStateIndex::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
SAFE_RESOLVE(Index, ctx);
|
||||
|
||||
if (!Index->IsNumeric())
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (Index->isConstant())
|
||||
{
|
||||
int index = static_cast<FxConstant *>(Index)->GetValue().GetInt();
|
||||
if (index < 0 || (index == 0 && !ctx.FromDecorate))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "State index must be positive");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (index == 0)
|
||||
{
|
||||
int symlabel = StateLabels.AddPointer(nullptr);
|
||||
auto x = new FxConstant(symlabel, ScriptPosition);
|
||||
delete this;
|
||||
x->ValueType = TypeStateLabel;
|
||||
return x;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto x = new FxStateByIndex(ctx.StateIndex + index, ScriptPosition);
|
||||
delete this;
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
}
|
||||
else if (Index->ValueType->GetRegType() != REGT_INT)
|
||||
{ // Float.
|
||||
Index = new FxIntCast(Index, ctx.FromDecorate);
|
||||
SAFE_RESOLVE(Index, ctx);
|
||||
}
|
||||
|
||||
auto vclass = PType::toClass(ctx.Class);
|
||||
assert(vclass != nullptr);
|
||||
auto aclass = ValidateActor(vclass->Descriptor);
|
||||
assert(aclass != nullptr && aclass->GetStateCount() > 0);
|
||||
|
||||
symlabel = StateLabels.AddPointer(aclass->GetStates() + ctx.StateIndex);
|
||||
ValueType = TypeStateLabel;
|
||||
return this;
|
||||
}
|
||||
|
||||
ExpEmit FxRuntimeStateIndex::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
ExpEmit out = Index->Emit(build);
|
||||
// out = (clamp(Index, 0, 32767) << 16) | symlabel | 0x80000000; 0x80000000 is here to make it negative.
|
||||
build->Emit(OP_MAX_RK, out.RegNum, out.RegNum, build->GetConstantInt(0));
|
||||
build->Emit(OP_MIN_RK, out.RegNum, out.RegNum, build->GetConstantInt(32767));
|
||||
build->Emit(OP_SLL_RI, out.RegNum, out.RegNum, 16);
|
||||
build->Emit(OP_OR_RK, out.RegNum, out.RegNum, build->GetConstantInt(symlabel|0x80000000));
|
||||
return out;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxMultiNameState::FxMultiNameState(const char *_statestring, const FScriptPosition &pos, PClassActor *checkclass)
|
||||
:FxExpression(EFX_MultiNameState, pos)
|
||||
{
|
||||
FName scopename = NAME_None;
|
||||
FString statestring = _statestring;
|
||||
int scopeindex = statestring.IndexOf("::");
|
||||
|
||||
if (scopeindex >= 0)
|
||||
{
|
||||
scopename = FName(statestring, scopeindex, false);
|
||||
statestring = statestring.Right(statestring.Len() - scopeindex - 2);
|
||||
}
|
||||
names = MakeStateNameList(statestring);
|
||||
names.Insert(0, scopename);
|
||||
scope = checkclass;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
ABORT(ctx.Class);
|
||||
int symlabel;
|
||||
|
||||
auto vclass = PType::toClass(ctx.Class);
|
||||
//assert(vclass != nullptr);
|
||||
auto clstype = vclass == nullptr? nullptr : ValidateActor(vclass->Descriptor);
|
||||
|
||||
if (names[0] == NAME_None)
|
||||
{
|
||||
scope = nullptr;
|
||||
}
|
||||
else if (clstype == nullptr)
|
||||
{
|
||||
// not in an actor, so any further checks are pointless.
|
||||
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (names[0] == NAME_Super)
|
||||
{
|
||||
scope = ValidateActor(clstype->ParentClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
scope = PClass::FindActor(names[0]);
|
||||
if (scope == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Unknown class '%s' in state label", names[0].GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
else if (!scope->IsAncestorOf(clstype))
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (scope != nullptr)
|
||||
{
|
||||
FState *destination = nullptr;
|
||||
// If the label is class specific we can resolve it right here
|
||||
if (names[1] != NAME_None)
|
||||
{
|
||||
destination = scope->FindState(names.Size()-1, &names[1], false);
|
||||
if (destination == nullptr)
|
||||
{
|
||||
ScriptPosition.Message(MSG_OPTERROR, "Unknown state jump destination");
|
||||
/* lax */
|
||||
return this;
|
||||
}
|
||||
}
|
||||
symlabel = StateLabels.AddPointer(destination);
|
||||
}
|
||||
else
|
||||
{
|
||||
names.Delete(0);
|
||||
symlabel = StateLabels.AddNames(names);
|
||||
}
|
||||
FxExpression *x = new FxConstant(symlabel, ScriptPosition);
|
||||
x->ValueType = TypeStateLabel;
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// The CVAR is for finding places where thinkers are created.
|
||||
// Those will require code changes in ZScript 4.0.
|
||||
//
|
||||
//==========================================================================
|
||||
CVAR(Bool, vm_warnthinkercreation, false, 0)
|
||||
|
||||
static DObject *BuiltinNewDoom(PClass *cls, int outerside, int backwardscompatible)
|
||||
{
|
||||
if (cls == nullptr)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "New without a class");
|
||||
return nullptr;
|
||||
}
|
||||
if (cls->ConstructNative == nullptr)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
if (cls->bAbstract)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
// Creating actors here must be outright prohibited,
|
||||
if (cls->IsDescendantOf(NAME_Actor))
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Cannot create actors with 'new'");
|
||||
return nullptr;
|
||||
}
|
||||
if ((vm_warnthinkercreation || !backwardscompatible) && cls->IsDescendantOf(NAME_Thinker))
|
||||
{
|
||||
// This must output a diagnostic warning
|
||||
Printf("Using 'new' to create thinkers is deprecated.");
|
||||
}
|
||||
// [ZZ] validate readonly and between scope construction
|
||||
if (outerside) FScopeBarrier::ValidateNew(cls, outerside - 1);
|
||||
DObject *object;
|
||||
if (!cls->IsDescendantOf(NAME_Thinker))
|
||||
{
|
||||
object = cls->CreateNew();
|
||||
}
|
||||
else
|
||||
{
|
||||
object = currentVMLevel->CreateThinker(cls);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DObject, BuiltinNewDoom, BuiltinNewDoom)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_CLASS(cls, DObject);
|
||||
PARAM_INT(outerside);
|
||||
PARAM_INT(compatible);
|
||||
ACTION_RETURN_OBJECT(BuiltinNewDoom(cls, outerside, compatible));
|
||||
}
|
||||
|
||||
|
||||
void SetDoomCompileEnvironment()
|
||||
{
|
||||
compileEnvironment.SpecialTypeCast = CustomTypeCast;
|
||||
compileEnvironment.CheckForCustomAddition = CheckForCustomAddition;
|
||||
compileEnvironment.CheckSpecialIdentifier = CheckForDefault;
|
||||
compileEnvironment.ResolveSpecialIdentifier = ResolveForDefault;
|
||||
compileEnvironment.CheckSpecialMember = CheckForMemberDefault;
|
||||
compileEnvironment.ResolveSpecialFunction = AJumpProcessing;
|
||||
compileEnvironment.CheckCustomGlobalFunctions = ResolveGlobalCustomFunction;
|
||||
compileEnvironment.CustomBuiltinNew = "BuiltinNewDoom";
|
||||
}
|
112
src/scripting/backend/codegen_doom.h
Normal file
112
src/scripting/backend/codegen_doom.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
#pragma once
|
||||
#include "codegen.h"
|
||||
#include "actor.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxActionSpecialCall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxActionSpecialCall : public FxExpression
|
||||
{
|
||||
int Special;
|
||||
FxExpression *Self;
|
||||
FArgumentList ArgList;
|
||||
|
||||
public:
|
||||
|
||||
FxActionSpecialCall(FxExpression *self, int special, FArgumentList &args, const FScriptPosition &pos);
|
||||
~FxActionSpecialCall();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxClassDefaults
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxClassDefaults : public FxExpression
|
||||
{
|
||||
FxExpression *obj;
|
||||
|
||||
public:
|
||||
FxClassDefaults(FxExpression *, const FScriptPosition &);
|
||||
~FxClassDefaults();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxGetDefaultByType
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxGetDefaultByType : public FxExpression
|
||||
{
|
||||
FxExpression *Self;
|
||||
|
||||
public:
|
||||
|
||||
FxGetDefaultByType(FxExpression *self);
|
||||
~FxGetDefaultByType();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Only used to resolve the old jump by index feature of DECORATE
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxStateByIndex : public FxExpression
|
||||
{
|
||||
unsigned index;
|
||||
|
||||
public:
|
||||
|
||||
FxStateByIndex(int i, const FScriptPosition &pos) : FxExpression(EFX_StateByIndex, pos)
|
||||
{
|
||||
index = i;
|
||||
}
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Same as above except for expressions which means it will have to be
|
||||
// evaluated at runtime
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxRuntimeStateIndex : public FxExpression
|
||||
{
|
||||
FxExpression *Index;
|
||||
int symlabel;
|
||||
|
||||
public:
|
||||
FxRuntimeStateIndex(FxExpression *index);
|
||||
~FxRuntimeStateIndex();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxMultiNameState : public FxExpression
|
||||
{
|
||||
PClassActor *scope;
|
||||
TArray<FName> names;
|
||||
public:
|
||||
|
||||
FxMultiNameState(const char *statestring, const FScriptPosition &pos, PClassActor *checkclass = nullptr);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
};
|
|
@ -38,7 +38,7 @@
|
|||
#include "c_cvars.h"
|
||||
#include "jit.h"
|
||||
|
||||
EXTERN_CVAR(Bool, strictdecorate);
|
||||
CVAR(Bool, strictdecorate, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
|
||||
|
||||
struct VMRemap
|
||||
{
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "a_pickups.h"
|
||||
#include "thingdef.h"
|
||||
#include "backend/codegen.h"
|
||||
#include "backend/codegen_doom.h"
|
||||
|
||||
FRandom pr_exrandom ("EX_Random");
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "thingdef.h"
|
||||
#include "a_morph.h"
|
||||
#include "backend/codegen.h"
|
||||
#include "backend/codegen_doom.h"
|
||||
#include "filesystem.h"
|
||||
#include "v_text.h"
|
||||
#include "m_argv.h"
|
||||
|
@ -53,7 +54,7 @@
|
|||
#endif // !_MSC_VER
|
||||
|
||||
void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns);
|
||||
CVAR(Bool, strictdecorate, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
|
||||
EXTERN_CVAR(Bool, strictdecorate);
|
||||
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "p_local.h"
|
||||
#include "thingdef.h"
|
||||
#include "backend/codegen.h"
|
||||
#include "backend/codegen_doom.h"
|
||||
#ifndef _MSC_VER
|
||||
#include "i_system.h" // for strlwr()
|
||||
#endif // !_MSC_VER
|
||||
|
|
|
@ -406,6 +406,7 @@ void CheckDropItems(const PClassActor *const obj)
|
|||
void ParseScripts();
|
||||
void ParseAllDecorate();
|
||||
void SynthesizeFlagFields();
|
||||
void SetDoomCompileEnvironment();
|
||||
|
||||
void LoadActors()
|
||||
{
|
||||
|
@ -414,6 +415,7 @@ void LoadActors()
|
|||
timer.Reset(); timer.Clock();
|
||||
FScriptPosition::ResetErrorCounter();
|
||||
|
||||
SetDoomCompileEnvironment();
|
||||
InitThingdef();
|
||||
FScriptPosition::StrictErrors = true;
|
||||
ParseScripts();
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "version.h"
|
||||
#include "zcc_parser.h"
|
||||
#include "zcc_compile.h"
|
||||
#include "templates.h"
|
||||
|
||||
TArray<FString> Includes;
|
||||
TArray<FScriptPosition> IncludeLocs;
|
||||
|
|
|
@ -426,6 +426,7 @@ class Object native
|
|||
|
||||
// These must be defined in some class, so that the compiler can find them. Object is just fine, as long as they are private to external code.
|
||||
private native static Object BuiltinNew(Class<Object> cls, int outerclass, int compatibility);
|
||||
private native static Object BuiltinNewDoom(Class<Object> cls, int outerclass, int compatibility);
|
||||
private native static int BuiltinRandom(voidptr rng, int min, int max);
|
||||
private native static double BuiltinFRandom(voidptr rng, double min, double max);
|
||||
private native static int BuiltinRandom2(voidptr rng, int mask);
|
||||
|
|
Loading…
Reference in a new issue