From 3454314bb15ba4c94289d296169e520decb2b4a7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 11 Apr 2020 19:31:58 +0200 Subject: [PATCH] - separated the Doom specific parts from the compiler backend into a separate file, these parts now get invoked via callback hooks. --- src/CMakeLists.txt | 1 + src/common/utility/basics.h | 8 + src/gamedata/info.h | 8 - src/scripting/backend/codegen.cpp | 816 ++--------------- src/scripting/backend/codegen.h | 142 +-- src/scripting/backend/codegen_doom.cpp | 961 +++++++++++++++++++++ src/scripting/backend/codegen_doom.h | 112 +++ src/scripting/backend/vmbuilder.cpp | 2 +- src/scripting/decorate/thingdef_exp.cpp | 1 + src/scripting/decorate/thingdef_parse.cpp | 3 +- src/scripting/decorate/thingdef_states.cpp | 1 + src/scripting/thingdef.cpp | 2 + src/scripting/zscript/zcc_parser.cpp | 1 + wadsrc/static/zscript/base.zs | 1 + 14 files changed, 1164 insertions(+), 895 deletions(-) create mode 100644 src/scripting/backend/codegen_doom.cpp create mode 100644 src/scripting/backend/codegen_doom.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ef19390716..9be3c5387f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/common/utility/basics.h b/src/common/utility/basics.h index f5ecdd1a2a..503d3ea7ba 100644 --- a/src/common/utility/basics.h +++ b/src/common/utility/basics.h @@ -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, +}; diff --git a/src/gamedata/info.h b/src/gamedata/info.h index f8e37ab344..92e51e9459 100644 --- a/src/gamedata/info.h +++ b/src/gamedata/info.h @@ -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, diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index fb15b6060c..aaef459a66 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -38,22 +38,20 @@ */ #include -#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(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(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(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(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(ValueType), basex, Explicit); @@ -2818,19 +2752,21 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx) delete this; 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,32 +5921,13 @@ 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; - } - - FxExpression * x = new FxClassDefaults(new FxSelf(ScriptPosition), ScriptPosition); - delete this; - return x->Resolve(ctx); + auto result = compileEnvironment.CheckSpecialIdentifier(this, ctx); + if (result != this) return result; } + // 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. PSymbolTable *symtbl; @@ -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,22 +6092,13 @@ 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; - } - - FxExpression * x = new FxClassDefaults(object, ScriptPosition); - object = nullptr; - delete this; - return x->Resolve(ctx); + auto result = compileEnvironment.ResolveSpecialIdentifier(this, object, objtype, ctx); + if (result != this) return result; } + if (objtype != nullptr && (sym = objtype->Symbols.FindSymbolInTable(Identifier, symtbl)) != nullptr) { if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) @@ -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(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) @@ -7852,47 +7671,11 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) return x->Resolve(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(argex)->GetValue().GetName().GetIndex()); - } - else - { - assert(argex->ValueType->GetRegType() == REGT_INT); - if (argex->isConstant()) - { - emitters.AddParameterIntConst(static_cast(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(Self)->GetValue().GetName()); - if (cls == nullptr) - { - ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type, but got %s", static_cast(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(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(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) diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index e73d06e668..5a0fc06644 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -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 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 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 diff --git a/src/scripting/backend/codegen_doom.cpp b/src/scripting/backend/codegen_doom.cpp new file mode 100644 index 0000000000..dab3ed1f0d --- /dev/null +++ b/src/scripting/backend/codegen_doom.cpp @@ -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 +#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(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(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(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(argex)->GetValue().GetName().GetIndex()); + } + else + { + assert(argex->ValueType->GetRegType() == REGT_INT); + if (argex->isConstant()) + { + emitters.AddParameterIntConst(static_cast(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(Self)->GetValue().GetName()); + if (cls == nullptr) + { + ScriptPosition.Message(MSG_ERROR, "GetDefaultByType() requires an actor class type, but got %s", static_cast(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(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(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"; +} diff --git a/src/scripting/backend/codegen_doom.h b/src/scripting/backend/codegen_doom.h new file mode 100644 index 0000000000..eaa1f9e73b --- /dev/null +++ b/src/scripting/backend/codegen_doom.h @@ -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 names; +public: + + FxMultiNameState(const char *statestring, const FScriptPosition &pos, PClassActor *checkclass = nullptr); + FxExpression *Resolve(FCompileContext&); +}; diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index 5e0b314f66..4eeae8b668 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -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 { diff --git a/src/scripting/decorate/thingdef_exp.cpp b/src/scripting/decorate/thingdef_exp.cpp index 8030680f31..141b73d473 100644 --- a/src/scripting/decorate/thingdef_exp.cpp +++ b/src/scripting/decorate/thingdef_exp.cpp @@ -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"); diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index 04f875ae5e..3dcba999ce 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -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); //========================================================================== diff --git a/src/scripting/decorate/thingdef_states.cpp b/src/scripting/decorate/thingdef_states.cpp index 9443854034..ff7c9c3e45 100644 --- a/src/scripting/decorate/thingdef_states.cpp +++ b/src/scripting/decorate/thingdef_states.cpp @@ -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 diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index d3de454a49..8772f1eb4a 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -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(); diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 6974278a96..91facfedce 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -40,6 +40,7 @@ #include "version.h" #include "zcc_parser.h" #include "zcc_compile.h" +#include "templates.h" TArray Includes; TArray IncludeLocs; diff --git a/wadsrc/static/zscript/base.zs b/wadsrc/static/zscript/base.zs index cb18dad32f..12deba521d 100644 --- a/wadsrc/static/zscript/base.zs +++ b/wadsrc/static/zscript/base.zs @@ -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 cls, int outerclass, int compatibility); + private native static Object BuiltinNewDoom(Class 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);