From 7d3663500f5f9f47aebacb3dc3be274a99263bc3 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Thu, 16 Feb 2017 02:39:49 +0200 Subject: [PATCH] Disallow creation of abstract classes outside of their own class (this is so that modders can create their own factory methods, not just for native) --- src/dobject.h | 2 +- src/scripting/backend/codegen.cpp | 17 +++++++++++++++-- src/scripting/backend/codegen.h | 1 + src/scripting/vm/vmexec.h | 3 ++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/dobject.h b/src/dobject.h index ce4714010e..01ee76fb2a 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -205,7 +205,7 @@ enum EObjectFlags OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk) OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning) OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function - OF_Abstract = 1 << 14, // Marks a class that cannot be created with CreateNew + OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function }; template class TObjPtr; diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index db7d7425fe..551378d8bc 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5023,6 +5023,7 @@ FxNew::FxNew(FxExpression *v) { val = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), v, false); ValueType = NewPointer(RUNTIME_CLASS(DObject)); + CallingClass = nullptr; } //========================================================================== @@ -5047,6 +5048,7 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) CHECKRESOLVED(); SAFE_RESOLVE(val, ctx); + CallingClass = (PClass*)ctx.Class; if (!val->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))) { ScriptPosition.Message(MSG_ERROR, "Class type expected"); @@ -5058,6 +5060,7 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) auto cls = static_cast(static_cast(val)->GetValue().GetPointer()); ValueType = NewPointer(cls); } + return this; } @@ -5072,7 +5075,7 @@ ExpEmit FxNew::Emit(VMFunctionBuilder *build) ExpEmit from = val->Emit(build); from.Free(build); ExpEmit to(build, REGT_POINTER); - build->Emit(from.Konst ? OP_NEW_K : OP_NEW, to.RegNum, from.RegNum); + build->Emit(from.Konst ? OP_NEW_K : OP_NEW, to.RegNum, from.RegNum, build->GetConstantAddress(CallingClass, ATAG_OBJECT)); return to; } @@ -7527,8 +7530,18 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) break; case NAME_New: - if (CheckArgSize(MethodName, ArgList, 1, 1, ScriptPosition)) + if (CheckArgSize(MethodName, ArgList, 0, 1, ScriptPosition)) { + // [ZZ] allow implicit new() call to mean "create current class instance" + if (!ArgList.Size() && !ctx.Class->IsKindOf(RUNTIME_CLASS(PClass))) + { + ScriptPosition.Message(MSG_ERROR, "Cannot use implicit new() in a struct"); + delete this; + return nullptr; + } + else if (!ArgList.Size()) + ArgList.Push(new FxConstant((PClass*)ctx.Class, NewClassPointer((PClass*)ctx.Class), ScriptPosition)); + func = new FxNew(ArgList[0]); ArgList[0] = nullptr; } diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index abbcbd54e3..90c4741706 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -1208,6 +1208,7 @@ private: class FxNew : public FxExpression { FxExpression *val; + PClass *CallingClass; public: diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 8da2c4d8d0..9f93af802e 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -787,7 +787,8 @@ begin: { b = B; PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[b] : konsta[b].v); - if (cls->ObjectFlags & OF_Abstract) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); + PClass *callingcls = (PClass*)konsta[C].o; // [ZZ] due to how this is set, it's always const + if ((cls->ObjectFlags & OF_Abstract) && callingcls != cls) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s outside of that class", cls->TypeName.GetChars()); reg.a[a] = cls->CreateNew(); reg.atag[a] = ATAG_OBJECT; NEXTOP;