fix(as2): correct Decrement cast and guard empty output in ActionSetMember (PR261)

handleSetMember crashed while decompiling AS2 code that applies `--` to a
virtual (`__get__`/`__set__`) property, e.g. `this.prop--` where `prop` is
defined via addProperty. The decrement-setter branch built a
PreDecrementActionItem but cast the value to IncrementActionItem instead of
DecrementActionItem, throwing ClassCastException.

That exception was then masked: it propagated into the finally block before
addToOutput ran, so `output` was empty and `output.remove(output.size() - 1)`
threw IndexOutOfBoundsException, replacing the real cause. The reported error
was therefore always a misleading "Index -1 out of bounds for length 0",
which also aborts loading entirely when "automatically rename identifiers" is
enabled (deobfuscateIdentifiers decompiles every script on load).

Fixes:
- Cast value to DecrementActionItem in the PreDecrement setter branch.
- Guard the finally against an empty output (mirrors cleanupTemp) so a future
  exception in the try block is no longer masked.

Affected code now decompiles correctly to `++prop` / `--prop`.
This commit is contained in:
Symere
2026-05-24 03:07:34 -04:00
committed by GitHub
parent 3ed2bdd5a6
commit 01bf4af552

View File

@@ -188,7 +188,7 @@ public class ActionSetMember extends Action {
((GetMemberActionItem) ((DecrementActionItem) value).object).object = ((GetMemberActionItem) ((DecrementActionItem) value).object).object.getThroughDuplicate();
cleanupTemp(((GetMemberActionItem) ((DecrementActionItem) value).object).object, object, output, stack);
if (setter) {
stack.addToOutput(new PreDecrementActionItem(action, lineStartAction, ((IncrementActionItem) value).object.getThroughDuplicate()));
stack.addToOutput(new PreDecrementActionItem(action, lineStartAction, ((DecrementActionItem) value).object.getThroughDuplicate()));
} else {
stack.addToOutput(new PostDecrementActionItem(action, lineStartAction, ((DecrementActionItem) value).object.getThroughDuplicate()));
}
@@ -252,8 +252,14 @@ public class ActionSetMember extends Action {
} finally {
if (setter) {
stack.finishBlock(output);
stack.push(output.remove(output.size() - 1));
stack.moveToStack(output);
// Guard against an empty output: if the try block exited via an
// exception before producing a statement, removing from an empty
// list would throw IndexOutOfBoundsException here and mask the
// original exception. Mirrors the check used in cleanupTemp().
if (!output.isEmpty()) {
stack.push(output.remove(output.size() - 1));
stack.moveToStack(output);
}
}
}
}