diff --git a/Content.Tests/DMProject/Broken Tests/Stdlib/issaved.dm b/Content.Tests/DMProject/Broken Tests/Stdlib/issaved.dm deleted file mode 100644 index 073b9306dc..0000000000 --- a/Content.Tests/DMProject/Broken Tests/Stdlib/issaved.dm +++ /dev/null @@ -1,17 +0,0 @@ - -// TODO: This test needs further cleanup/validation but I cba and we need more issaved() tests - -//# issue 684 - -/obj - var/V - var/const/C - - proc/log_vars() - for(var/vname in vars) - world.log << (issaved(vars[vname])) - -/proc/RunTest() - var/obj/o = new - o.log_vars() - ASSERT(FALSE) // To ensure this test fails until it's been revisited (in case we add CI to check broken tests) diff --git a/Content.Tests/DMProject/Tests/Special Procs/issaved/issaved_vars_index.dm b/Content.Tests/DMProject/Tests/Special Procs/issaved/issaved_vars_index.dm index 293fcc7d2b..f77f95dc52 100644 --- a/Content.Tests/DMProject/Tests/Special Procs/issaved/issaved_vars_index.dm +++ b/Content.Tests/DMProject/Tests/Special Procs/issaved/issaved_vars_index.dm @@ -1,23 +1,23 @@ -// !issaved(B) commented out because of TODO in DMOpcodeHandlers.IsSaved /obj/o var/A - //var/tmp/B + var/tmp/B /obj/o/proc/IsSavedSrcVars() ASSERT(issaved(A)) - //ASSERT(!issaved(B)) + ASSERT(!issaved(B)) ASSERT(issaved(vars["A"])) - //ASSERT(!issaved(vars["B"])) + ASSERT(!issaved(vars["B"])) /proc/RunTest() var/obj/o/test = new + ASSERT(!issaved(test.type)) ASSERT(issaved(test.A)) - //ASSERT(!issaved(test.B)) + ASSERT(!issaved(test.B)) // Note that this doesn't work on most lists and will instead return false ASSERT(issaved(test.vars["A"])) - //ASSERT(!issaved(test.vars["B"])) + ASSERT(!issaved(test.vars["B"])) /* var/expected = prob(50) diff --git a/DMCompiler/DM/DMObject.cs b/DMCompiler/DM/DMObject.cs index 1c488a92b4..de02e80c06 100644 --- a/DMCompiler/DM/DMObject.cs +++ b/DMCompiler/DM/DMObject.cs @@ -21,6 +21,8 @@ internal sealed class DMObject { public Dictionary VariableOverrides = new(); public Dictionary GlobalVariables = new(); /// A list of var and verb initializations implicitly done before the user's New() is called. + public HashSet ConstVariables = new(); + public HashSet TmpVariables = new(); public List InitializationProcExpressions = new(); public int? InitializationProc; @@ -169,6 +171,14 @@ public DreamTypeJson CreateJsonRepresentation() { typeJson.GlobalVariables = GlobalVariables; } + if (ConstVariables.Count > 0) { + typeJson.ConstVariables = ConstVariables; + } + + if (TmpVariables.Count > 0) { + typeJson.TmpVariables = TmpVariables; + } + if (InitializationProc != null) { typeJson.InitProc = InitializationProc; } diff --git a/DMCompiler/DM/DMObjectTree.cs b/DMCompiler/DM/DMObjectTree.cs index daffd397c0..6773b4f58d 100644 --- a/DMCompiler/DM/DMObjectTree.cs +++ b/DMCompiler/DM/DMObjectTree.cs @@ -173,7 +173,7 @@ public static bool TryGetTypeId(DreamPath path, out int typeId) { public static int CreateGlobal(out DMVariable global, DreamPath? type, string name, bool isConst, DMValueType valType = DMValueType.Anything) { int id = Globals.Count; - global = new DMVariable(type, name, true, isConst, valType); + global = new DMVariable(type, name, true, isConst, false, valType); Globals.Add(global); return id; } diff --git a/DMCompiler/DM/DMVariable.cs b/DMCompiler/DM/DMVariable.cs index 86f63e91c9..6f2efaea20 100644 --- a/DMCompiler/DM/DMVariable.cs +++ b/DMCompiler/DM/DMVariable.cs @@ -10,14 +10,16 @@ sealed class DMVariable { /// NOTE: This DMVariable may be forced constant through opendream_compiletimereadonly. This only marks that the variable has the DM quality of /const/ness. /// public bool IsConst; + public bool IsTmp; public DMExpression Value; public DMValueType ValType; - public DMVariable(DreamPath? type, string name, bool isGlobal, bool isConst, DMValueType valType = DMValueType.Anything) { + public DMVariable(DreamPath? type, string name, bool isGlobal, bool isConst, bool isTmp, DMValueType valType = DMValueType.Anything) { Type = type; Name = name; IsGlobal = isGlobal; IsConst = isConst; + IsTmp = isTmp; Value = null; ValType = valType; } @@ -33,7 +35,7 @@ public DMVariable WriteToValue(Expressions.Constant value) { return this; } - DMVariable clone = new DMVariable(Type, Name, IsGlobal, IsConst, ValType); + DMVariable clone = new DMVariable(Type, Name, IsGlobal, IsConst, IsTmp, ValType); clone.Value = value; return clone; } diff --git a/DMCompiler/DM/Visitors/DMObjectBuilder.cs b/DMCompiler/DM/Visitors/DMObjectBuilder.cs index 1b766b1719..9d62e01b4d 100644 --- a/DMCompiler/DM/Visitors/DMObjectBuilder.cs +++ b/DMCompiler/DM/Visitors/DMObjectBuilder.cs @@ -274,8 +274,16 @@ private static void ProcessVarDefinition(DMObject? varObject, DMASTObjectVarDefi if (varDefinition.IsStatic) { variable = varObject.CreateGlobalVariable(varDefinition.Type, varDefinition.Name, varDefinition.IsConst, varDefinition.ValType); } else { - variable = new DMVariable(varDefinition.Type, varDefinition.Name, false, varDefinition.IsConst,varDefinition.ValType); + variable = new DMVariable(varDefinition.Type, varDefinition.Name, false, varDefinition.IsConst, varDefinition.IsTmp, varDefinition.ValType); varObject.Variables[variable.Name] = variable; + if(varDefinition.IsConst){ + varObject.ConstVariables ??= new HashSet(); + varObject.ConstVariables.Add(varDefinition.Name); + } + if(varDefinition.IsTmp){ + varObject.TmpVariables ??= new HashSet(); + varObject.TmpVariables.Add(varDefinition.Name); + } } } diff --git a/DMCompiler/DMStandard/Types/Client.dm b/DMCompiler/DMStandard/Types/Client.dm index 4ec61aeeab..4f6f895580 100644 --- a/DMCompiler/DMStandard/Types/Client.dm +++ b/DMCompiler/DMStandard/Types/Client.dm @@ -9,7 +9,7 @@ var/default_verb_category = "Commands" var/tag - var/type = /client + var/const/type = /client var/mob/mob var/atom/eye diff --git a/DMCompiler/DMStandard/Types/Datum.dm b/DMCompiler/DMStandard/Types/Datum.dm index 66e47ef3ba..ce2d1c7b01 100644 --- a/DMCompiler/DMStandard/Types/Datum.dm +++ b/DMCompiler/DMStandard/Types/Datum.dm @@ -1,8 +1,8 @@ /datum - var/type - var/parent_type + var/const/type + var/tmp/parent_type - var/list/vars + var/const/list/vars var/tag diff --git a/DMCompiler/DMStandard/Types/List.dm b/DMCompiler/DMStandard/Types/List.dm index ca0faa0822..ce16246e56 100644 --- a/DMCompiler/DMStandard/Types/List.dm +++ b/DMCompiler/DMStandard/Types/List.dm @@ -1,6 +1,6 @@ /list var/len - var/type = /list + var/const/type = /list proc/New(Size) diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index 3d054eb781..373b491025 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -121,8 +121,10 @@ public virtual DreamValue Initial(string name) { } public virtual bool IsSaved(string name) { - //TODO: Add support for var/const/ and var/tmp/ once those are properly in - return ObjectDefinition.Variables.ContainsKey(name) && !ObjectDefinition.GlobalVariables.ContainsKey(name); + return ObjectDefinition.Variables.ContainsKey(name) + && !ObjectDefinition.GlobalVariables.ContainsKey(name) + && !(ObjectDefinition.ConstVariables is not null && ObjectDefinition.ConstVariables.Contains(name)) + && !(ObjectDefinition.TmpVariables is not null && ObjectDefinition.TmpVariables.Contains(name)); } public bool HasVariable(string name) { @@ -182,6 +184,8 @@ protected virtual void SetVar(string varName, DreamValue value) { Tag = newTag; break; default: + if (ObjectDefinition.ConstVariables is not null && ObjectDefinition.ConstVariables.Contains(varName)) + throw new Exception($"Cannot set const var \"{varName}\" on {ObjectDefinition.Type}"); if (!ObjectDefinition.Variables.ContainsKey(varName)) throw new Exception($"Cannot set var \"{varName}\" on {ObjectDefinition.Type}"); diff --git a/OpenDreamRuntime/Objects/DreamObjectDefinition.cs b/OpenDreamRuntime/Objects/DreamObjectDefinition.cs index 6aad54c8c6..92e24a8686 100644 --- a/OpenDreamRuntime/Objects/DreamObjectDefinition.cs +++ b/OpenDreamRuntime/Objects/DreamObjectDefinition.cs @@ -48,6 +48,10 @@ public bool NoConstructors { public readonly Dictionary Variables = new(); // Maps /static variables from name to their index in the global variable table. public readonly Dictionary GlobalVariables = new(); + // Contains hashes of variables that are tagged /const. + public HashSet? ConstVariables = null; + // Contains hashes of variables that are tagged /tmp. + public HashSet? TmpVariables = null; public DreamObjectDefinition(DreamObjectDefinition copyFrom) { DreamManager = copyFrom.DreamManager; @@ -68,6 +72,8 @@ public DreamObjectDefinition(DreamObjectDefinition copyFrom) { Variables = new Dictionary(copyFrom.Variables); GlobalVariables = new Dictionary(copyFrom.GlobalVariables); + ConstVariables = copyFrom.ConstVariables is not null ? new HashSet(copyFrom.ConstVariables) : null; + TmpVariables = copyFrom.TmpVariables is not null ? new HashSet(copyFrom.TmpVariables) : null; Procs = new Dictionary(copyFrom.Procs); OverridingProcs = new Dictionary(copyFrom.OverridingProcs); if (copyFrom.Verbs != null) @@ -97,6 +103,10 @@ public DreamObjectDefinition(DreamManager dreamManager, DreamObjectTree objectTr Verbs = new List(Parent.Verbs); if (Parent != ObjectTree.Root.ObjectDefinition) // Don't include root-level globals GlobalVariables = new Dictionary(Parent.GlobalVariables); + if (Parent.ConstVariables != null) + ConstVariables = new HashSet(Parent.ConstVariables); + if (Parent.TmpVariables != null) + TmpVariables = new HashSet(Parent.TmpVariables); } } diff --git a/OpenDreamRuntime/Objects/DreamObjectTree.cs b/OpenDreamRuntime/Objects/DreamObjectTree.cs index dc00e5caa4..6a0462300b 100644 --- a/OpenDreamRuntime/Objects/DreamObjectTree.cs +++ b/OpenDreamRuntime/Objects/DreamObjectTree.cs @@ -364,6 +364,20 @@ private void LoadVariablesFromJson(DreamObjectDefinition objectDefinition, Dream objectDefinition.GlobalVariables.Add(jsonGlobalVariable.Key, jsonGlobalVariable.Value); } } + + if (jsonObject.ConstVariables != null) { + objectDefinition.ConstVariables ??= new(); + foreach (string jsonConstVariable in jsonObject.ConstVariables) { + objectDefinition.ConstVariables.Add(jsonConstVariable); + } + } + + if(jsonObject.TmpVariables != null) { + objectDefinition.TmpVariables ??= new(); + foreach (string jsonTmpVariable in jsonObject.TmpVariables) { + objectDefinition.TmpVariables.Add(jsonTmpVariable); + } + } } public DreamProc LoadProcJson(int id, DreamTypeJson[] types, ProcDefinitionJson procDefinition) { diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index 0043d5c807..e35bba408e 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -2017,8 +2017,9 @@ public static ProcStatus IsSaved(DMProcState state) { throw new Exception($"Invalid owner for issaved() call {owner}"); } - //TODO: Add support for var/const/ and var/tmp/ once those are properly in - if (objectDefinition.GlobalVariables.ContainsKey(property)) { + if (objectDefinition.GlobalVariables.ContainsKey(property) + || (objectDefinition.ConstVariables is not null && objectDefinition.ConstVariables.Contains(property)) + || (objectDefinition.TmpVariables is not null && objectDefinition.TmpVariables.Contains(property))) { state.Push(new DreamValue(0)); } else { state.Push(new DreamValue(1)); diff --git a/OpenDreamShared/Json/DreamObjectJson.cs b/OpenDreamShared/Json/DreamObjectJson.cs index b34dfb057a..bc5421823f 100644 --- a/OpenDreamShared/Json/DreamObjectJson.cs +++ b/OpenDreamShared/Json/DreamObjectJson.cs @@ -20,6 +20,8 @@ public sealed class DreamTypeJson { public List Verbs { get; set; } public Dictionary Variables { get; set; } public Dictionary GlobalVariables { get; set; } + public HashSet? ConstVariables { get; set; } + public HashSet? TmpVariables { get; set; } } public sealed class GlobalListJson {