From fff6c2ab7d86671bea04459afca59492ce20164c Mon Sep 17 00:00:00 2001 From: ike709 Date: Mon, 9 Dec 2024 19:57:07 -0600 Subject: [PATCH 1/3] Improve parity handling of `null` in various operators --- .../Operators/valid_and_null.dm | 0 .../Operators/valid_and_null_assign.dm | 0 OpenDreamRuntime/Procs/DMOpcodeHandlers.cs | 51 +++++++++++++++---- 3 files changed, 42 insertions(+), 9 deletions(-) rename Content.Tests/DMProject/{Broken Tests => Tests}/Operators/valid_and_null.dm (100%) rename Content.Tests/DMProject/{Broken Tests => Tests}/Operators/valid_and_null_assign.dm (100%) diff --git a/Content.Tests/DMProject/Broken Tests/Operators/valid_and_null.dm b/Content.Tests/DMProject/Tests/Operators/valid_and_null.dm similarity index 100% rename from Content.Tests/DMProject/Broken Tests/Operators/valid_and_null.dm rename to Content.Tests/DMProject/Tests/Operators/valid_and_null.dm diff --git a/Content.Tests/DMProject/Broken Tests/Operators/valid_and_null_assign.dm b/Content.Tests/DMProject/Tests/Operators/valid_and_null_assign.dm similarity index 100% rename from Content.Tests/DMProject/Broken Tests/Operators/valid_and_null_assign.dm rename to Content.Tests/DMProject/Tests/Operators/valid_and_null_assign.dm diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index 29f372e2e1..b53c2dd57a 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -923,6 +923,9 @@ public static ProcStatus BitShiftLeft(DMProcState state) { case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float: state.Push(new DreamValue(first.MustGetValueAsInteger() << second.MustGetValueAsInteger())); break; + case DreamValue.DreamValueType.Float when second.IsNull: + state.Push(new DreamValue(first.MustGetValueAsInteger())); + break; default: throw new Exception($"Invalid bit shift left operation on {first} and {second}"); } @@ -930,7 +933,6 @@ public static ProcStatus BitShiftLeft(DMProcState state) { return ProcStatus.Continue; } - public static ProcStatus BitShiftLeftReference(DMProcState state) { DreamReference reference = state.ReadReference(); DreamValue second = state.Pop(); @@ -943,9 +945,13 @@ public static ProcStatus BitShiftLeftReference(DMProcState state) { case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float: result = new DreamValue(first.MustGetValueAsInteger() << second.MustGetValueAsInteger()); break; + case DreamValue.DreamValueType.Float when second.IsNull: + result = new DreamValue(first.MustGetValueAsInteger()); + break; default: throw new Exception($"Invalid bit shift left operation on {first} and {second}"); } + state.AssignReference(reference, result); state.Push(result); return ProcStatus.Continue; @@ -955,12 +961,18 @@ public static ProcStatus BitShiftRight(DMProcState state) { DreamValue second = state.Pop(); DreamValue first = state.Pop(); - if (first.IsNull) { - state.Push(new DreamValue(0)); - } else if (first.Type == DreamValue.DreamValueType.Float && second.Type == DreamValue.DreamValueType.Float) { - state.Push(new DreamValue(first.MustGetValueAsInteger() >> second.MustGetValueAsInteger())); - } else { - throw new Exception($"Invalid bit shift right operation on {first} and {second}"); + switch (first.Type) { + case DreamValue.DreamValueType.DreamObject when first.IsNull: + state.Push(new DreamValue(0)); + break; + case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float: + state.Push(new DreamValue(first.MustGetValueAsInteger() >> second.MustGetValueAsInteger())); + break; + case DreamValue.DreamValueType.Float when second.IsNull: + state.Push(new DreamValue(first.MustGetValueAsInteger())); + break; + default: + throw new Exception($"Invalid bit shift right operation on {first} and {second}"); } return ProcStatus.Continue; @@ -978,9 +990,13 @@ public static ProcStatus BitShiftRightReference(DMProcState state) { case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float: result = new DreamValue(first.MustGetValueAsInteger() >> second.MustGetValueAsInteger()); break; + case DreamValue.DreamValueType.Float when second.IsNull: + result = new DreamValue(first.MustGetValueAsInteger()); + break; default: throw new Exception($"Invalid bit shift right operation on {first} and {second}"); } + state.AssignReference(reference, result); state.Push(result); return ProcStatus.Continue; @@ -1128,6 +1144,12 @@ public static ProcStatus Mask(DMProcState state) { case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float: result = new DreamValue(first.MustGetValueAsInteger() & second.MustGetValueAsInteger()); break; + case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float: + result = new DreamValue(first.MustGetValueAsInteger() & second.MustGetValueAsInteger()); + break; + case DreamValue.DreamValueType.Float when second.IsNull: + result = new DreamValue(0); + break; default: throw new Exception("Invalid mask operation on " + first + " and " + second); } @@ -2728,8 +2750,19 @@ private static DreamValue BitXorValues(DreamObjectTree objectTree, DreamValue fi } return new DreamValue(newList); - } else { - return new DreamValue(first.MustGetValueAsInteger() ^ second.MustGetValueAsInteger()); + } + + switch (first.Type) { + case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float: + return new DreamValue(first.MustGetValueAsInteger() ^ second.MustGetValueAsInteger()); + case DreamValue.DreamValueType.DreamObject when first.IsNull && second.IsNull: + return DreamValue.Null; + case DreamValue.DreamValueType.DreamObject when first.IsNull && second.Type == DreamValue.DreamValueType.Float: + return new DreamValue(second.MustGetValueAsInteger()); + case DreamValue.DreamValueType.Float when second.IsNull: + return new DreamValue(first.MustGetValueAsInteger()); + default: + throw new Exception($"Invalid xor operation on {first} and {second}"); } } From 85b46403421655ac41759660402957505afe7e5b Mon Sep 17 00:00:00 2001 From: ike709 Date: Mon, 9 Dec 2024 20:03:26 -0600 Subject: [PATCH 2/3] remove duplicated code --- OpenDreamRuntime/Procs/DMOpcodeHandlers.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index b53c2dd57a..1892632b88 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -1144,9 +1144,6 @@ public static ProcStatus Mask(DMProcState state) { case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float: result = new DreamValue(first.MustGetValueAsInteger() & second.MustGetValueAsInteger()); break; - case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float: - result = new DreamValue(first.MustGetValueAsInteger() & second.MustGetValueAsInteger()); - break; case DreamValue.DreamValueType.Float when second.IsNull: result = new DreamValue(0); break; From e9de495d3cd3414f7a7aa208415e6b79be51c08c Mon Sep 17 00:00:00 2001 From: ike709 Date: Fri, 27 Dec 2024 02:58:02 -0600 Subject: [PATCH 3/3] fix null division --- .../Tests/Operators/valid_and_null.dm | 3 +- OpenDreamRuntime/Procs/DMOpcodeHandlers.cs | 34 ++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Content.Tests/DMProject/Tests/Operators/valid_and_null.dm b/Content.Tests/DMProject/Tests/Operators/valid_and_null.dm index 169bd351bb..d7655bda0c 100644 --- a/Content.Tests/DMProject/Tests/Operators/valid_and_null.dm +++ b/Content.Tests/DMProject/Tests/Operators/valid_and_null.dm @@ -57,7 +57,8 @@ ASSERT(C(4) / C(2) == C(2)) ASSERT(C(null) / C(2) == C(0)) - //ASSERT(C(2) / C(null) == C(2)) //runtime: undefined operation + ASSERT(C(2) / C(null) == C(2)) + ASSERT(C(null) / C(null) == C(0)) ASSERT(C(4) % C(3) == C(1)) ASSERT(C(null) % C(3) == C(0)) diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index cc48c58978..581015ed3e 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -1088,20 +1088,30 @@ public static ProcStatus Combine(DMProcState state) { public static ProcStatus Divide(DMProcState state) { DreamValue second = state.Pop(); DreamValue first = state.Pop(); - if (first.IsNull) { - state.Push(new(0)); - } else if (first.TryGetValueAsFloat(out var firstFloat) && second.TryGetValueAsFloat(out var secondFloat)) { - if (secondFloat == 0) { - throw new Exception("Division by zero"); - } - state.Push(new(firstFloat / secondFloat)); - } else if (first.TryGetValueAsDreamObject(out var firstDreamObject)) { - var result = firstDreamObject.OperatorDivide(second, state); - state.Push(result); - } else { - throw new Exception($"Invalid divide operation on {first} and {second}"); + switch (first.Type) { + case DreamValue.DreamValueType.DreamObject when first.IsNull: + state.Push(new DreamValue(0)); + break; + case DreamValue.DreamValueType.Float when second.IsNull: + state.Push(new DreamValue(first.MustGetValueAsFloat())); + break; + case DreamValue.DreamValueType.Float when second.Type == DreamValue.DreamValueType.Float: + var secondFloat = second.MustGetValueAsFloat(); + if (secondFloat == 0) { + throw new Exception("Division by zero"); + } + + state.Push(new DreamValue(first.MustGetValueAsFloat() / secondFloat)); + break; + case DreamValue.DreamValueType.DreamObject: + var result = first.MustGetValueAsDreamObject()!.OperatorDivide(second, state); + state.Push(result); + break; + default: + throw new Exception($"Invalid divide operation on {first} and {second}"); } + return ProcStatus.Continue; }