Lake
Lake

Reputation: 4092

Apparently strange behavior of |= assignment if the RHS expression is a method call that modifies the LHS by side effect

I have this very simple piece of code in java:

anyMoreDelayedExplosion |= performEffect(getPanelMap(), getGameState(), this, x, y, effect, true);

anyMoreDelayedExplosion is a boolean field of the class that contains this statement. performEffect is a static method that returns either true or false.

Now, I debugged this (with Android-studio debugger), and I made sure that before executing the =| operation, anyMoreDelayedExplosion is "true". Then, after the operation, it becomes "false".

If I change the code to:

boolean res = performEffect(getPanelMap(), getGameState(), this, x, y, effect, true);
anyMoreDelayedExplosion |= res;

And check again, it correctly stays "true".

How is this possible?

My guesses:

  1. The debugger is not reliable. Still, this results in an evident bug in the application, that unbelievably gets FIXED by the above correction.
  2. There is an optimization issue of some kind with the compiler.
  3. I am totally, completely and utterly missing some truth here.

If anyone has any guess or ideas about how to isolate this kind of behavior, I would be happy to hear them out. Thanks.

EDIT: Here are the declarations, for type completeness:

boolean anyMoreDelayedExplosion;
public static boolean performEffect(PanelMap pm, GameState state, GameSceneHittable scene, int x, int y, SpecialEffect effect, boolean playFX)

Upvotes: 4

Views: 301

Answers (3)

Andrew Janke
Andrew Janke

Reputation: 23908

It's an issue with side effects inside the method you're calling interacting with when you observe the field and when the |= operator "observes" the field.

In Java, the compound assignment operators like |= store the value of the left hand side of the expression before evaluating the right hand side. So, if the method called in the RHS of the expression alter the value of anyMoreDelayedExplosion, and they might, since you're passing this to it, those changes will not affect the result of the |= operation. And if you're observing that field value in the debugger after the side effect takes place, what you're seeing as the field is not what was used for the LHS input of the |= operation.

So in this case, anyMore is actually false at the time of the original method invocation, it gets set to true via a side effect inside performEffect, you're observing it in the debugger after that side effect happens but before assignment, and |= is using the old stored false value when it does the combination. Splitting them in to two lines causes it to evaluate the LHS using its state after performEffect has run (and set anyMore to true by side-effect) but before the assignment takes place.

You need to make sure to observe the state of anyMoreDelayedExplosion before the performEffect method is called at all, not just before it returns, for your view to be consistent with what the code is actually doing. Adding a logging or printf statement just before the call would be a good way to do this, or just being careful about when you look at that field value in the debugger.

Section 15.26.2 of the JLS describes the compound assignment operator behavior in detail: http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.26.2

Upvotes: 4

paxdiablo
paxdiablo

Reputation: 882686

First things first, a pragmatic approach is probably the best.

If you have a workaround for the problem, use it. It's a worry that it happened in the first place but there should be two parallel streams happening here.

The first is to deliver your code. If the workaround works (and it does, otherwise it's not a workaround), keep it in your code (with a comment) so that you can continue.


The other stream is investigative in nature. What you describe should not be happening, sans some external forces like threading or debugger issue as you posit, or the function itself changing the flag as a side-effect of its operation.

Your best bet is to narrow it down to the bare minimum of code that has the problem, and ship that off to Google for analysis (or post the complete sample here so we can do a proper analysis rather than (educated) guesswork).

If you want to continue investigating the problem rather than shipping it off to those who might be in a better position, I'd start by single-stepping into the performEffect() method to see what it does in detail. It may be that it's setting the boolean value itself or returning false under some circumstances. If that doesn't reveal anything, investigation of the byte-code would be my next option.

But, that's getting deep into something that you really shouldn't be worried about, especially if there's a viable workaround.

Upvotes: 1

gknicker
gknicker

Reputation: 5568

There's no logical explanation for the behavior you're describing. Surely the runtime behavior is correct, regardless of what you're seeing in the debugger.

If you've declared the variable anyMoreDelayedExplosion outside an outer loop where it's repeatedly initialized and then or-equaled in an inner loop for different scenarios, I suppose it's possible you could be seeing the value after it's been re-initialized to false. But it sounds like you know what you're doing better than to be confused by such.

Upvotes: 0

Related Questions