Skip to content

Commit

Permalink
Improve parity handling of null in various operators (#2129)
Browse files Browse the repository at this point in the history
Co-authored-by: ike709 <[email protected]>
  • Loading branch information
ike709 and ike709 authored Jan 4, 2025
1 parent aab2f49 commit f6d9b55
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 33 deletions.
18 changes: 9 additions & 9 deletions Content.Tests/DMProject/Tests/Operators/Division.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,39 @@
var/list/expected = list(
1,
"Error",
10,
"Error",
"Error", // index 5
"Error",
"Error",
"Error",
"Error",
"Error", // index 10
"Error",
"Error",
"Error",
"Error",
"Error", // index 15
"Error",
"Error",
"Error",
"Error",
"Error", // index 20
"Error",
"Error",
"Error",
"Error",
"Error",
"Error",
"Error",
0,
0,
0,
0, // index 25
0,
0,
0,
0,
0, // index 30
0,
0,
0,
0,
"Error",
"Error",
"Error", // index 35
"Error",
"Error",
"Error",
Expand Down Expand Up @@ -128,4 +128,4 @@
"Error"
)

test_binary_operator(/proc/divide, expected)
test_binary_operator(/proc/divide, expected)
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var/list/operator_test_values = list(
result = "Error"

if (result ~! expected_result)
CRASH("Expected [json_encode(expected_result)] for [json_encode(a)], instead got [json_encode(result)]")
CRASH("Expected [json_encode(expected_result)] for [json_encode(a)], instead got [json_encode(result)] at index [i - 1]")

/proc/test_binary_operator(var/operator_proc, var/list/expected)
var/i = 1
Expand All @@ -48,4 +48,4 @@ var/list/operator_test_values = list(
result = "Error"

if (result ~! expected_result)
CRASH("Expected [json_encode(expected_result)] for [json_encode(a)] and [json_encode(b)], instead got [json_encode(result)]")
CRASH("Expected [json_encode(expected_result)] for [json_encode(a)] and [json_encode(b)], instead got [json_encode(result)] at index [i - 1]")
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
82 changes: 61 additions & 21 deletions OpenDreamRuntime/Procs/DMOpcodeHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -923,14 +923,16 @@ 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}");
}

return ProcStatus.Continue;
}


public static ProcStatus BitShiftLeftReference(DMProcState state) {
DreamReference reference = state.ReadReference();
DreamValue second = state.Pop();
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<DreamObject>(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;
}

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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}");
}
}

Expand Down

0 comments on commit f6d9b55

Please sign in to comment.