From 05f2121f5c25cd7b12e41c761898d0dc04e80de3 Mon Sep 17 00:00:00 2001 From: wixoaGit Date: Fri, 19 Jan 2024 18:49:51 -0500 Subject: [PATCH] Fix edge-case positional/named argument handling in `image()` --- OpenDreamRuntime/DreamManager.cs | 6 ++++-- OpenDreamRuntime/Procs/DMProc.cs | 25 +++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 7453ae3c70..2bd61973cc 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -41,13 +41,14 @@ public sealed partial class DreamManager { // Global state that may not really (really really) belong here public DreamValue[] Globals { get; set; } = Array.Empty(); - public List GlobalNames { get; private set; } = new List(); + public List GlobalNames { get; private set; } = new(); public Dictionary ReferenceIDs { get; } = new(); public Dictionary ReferenceIDsToDreamObject { get; } = new(); public HashSet Clients { get; set; } = new(); public HashSet Datums { get; set; } = new(); public Random Random { get; set; } = new(); public Dictionary> Tags { get; set; } = new(); + public DreamProc ImageConstructor, ImageFactoryProc; private int _dreamObjectRefIdCounter; private DreamCompiledJson _compiledJson; @@ -123,8 +124,9 @@ public bool LoadJson(string? jsonPath) { throw new FileNotFoundException("Interface DMF not found at "+Path.Join(rootPath,_compiledJson.Interface)); _objectTree.LoadJson(json); - DreamProcNative.SetupNativeProcs(_objectTree); + ImageConstructor = _objectTree.Image.ObjectDefinition.GetProc("New"); + _objectTree.TryGetGlobalProc("image", out ImageFactoryProc!); _dreamMapManager.Initialize(); WorldInstance = new DreamObjectWorld(_objectTree.World.ObjectDefinition); diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index a5d2a3c796..72cce21987 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -977,6 +977,9 @@ public DreamProcArguments CreateProcArguments(ReadOnlySpan values, D var argumentCount = argumentStackSize / 2; var arguments = new DreamValue[Math.Max(argumentCount, proc.ArgumentNames.Count)]; + var skippingArg = false; + var isImageConstructor = proc == Proc.DreamManager.ImageConstructor || + proc == Proc.DreamManager.ImageFactoryProc; Array.Fill(arguments, DreamValue.Null); for (int i = 0; i < argumentCount; i++) { @@ -984,7 +987,15 @@ public DreamProcArguments CreateProcArguments(ReadOnlySpan values, D var value = values[i*2+1]; if (key.IsNull) { - arguments[i] = value; + // image() or new /image() will skip the loc arg if the second arg is a string + // Really don't like this but it's BYOND behavior + // Note that the way we're doing it leads to different argument placement when there are no named args + // Hopefully nothing depends on that though + // TODO: We aim to do sanity improvements in the future, yea? Big one here + if (isImageConstructor && i == 1 && value.Type == DreamValue.DreamValueType.String) + skippingArg = true; + + arguments[skippingArg ? i + 1 : i] = value; } else { string argumentName = key.MustGetValueAsString(); int argumentIndex = proc.ArgumentNames.IndexOf(argumentName); @@ -1005,6 +1016,9 @@ public DreamProcArguments CreateProcArguments(ReadOnlySpan values, D var listValues = argList.GetValues(); var arguments = new DreamValue[Math.Max(listValues.Count, proc.ArgumentNames.Count)]; + var skippingArg = false; + var isImageConstructor = proc == Proc.DreamManager.ImageConstructor || + proc == Proc.DreamManager.ImageFactoryProc; Array.Fill(arguments, DreamValue.Null); for (int i = 0; i < listValues.Count; i++) { @@ -1020,8 +1034,15 @@ public DreamProcArguments CreateProcArguments(ReadOnlySpan values, D arguments[argumentIndex] = argList.GetValue(value); } else { //Ordered argument + // image() or new /image() will skip the loc arg if the second arg is a string + // Really don't like this but it's BYOND behavior + // Note that the way we're doing it leads to different argument placement when there are no named args + // Hopefully nothing depends on that though + if (isImageConstructor && i == 1 && value.Type == DreamValue.DreamValueType.String) + skippingArg = true; + // TODO: Verify ordered args precede all named args - arguments[i] = value; + arguments[skippingArg ? i + 1 : i] = value; } }