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 97% rename from Content.Tests/DMProject/Broken Tests/Operators/valid_and_null.dm rename to Content.Tests/DMProject/Tests/Operators/valid_and_null.dm index 169bd351bb..d7655bda0c 100644 --- a/Content.Tests/DMProject/Broken 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/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 51a05c774d..581015ed3e 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; @@ -1072,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; } @@ -1128,6 +1154,9 @@ 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.IsNull: + result = new DreamValue(0); + break; default: throw new Exception("Invalid mask operation on " + first + " and " + second); } @@ -2739,8 +2768,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}"); } }