Skip to content

Commit

Permalink
Decent support for switch expressions, but more comprehensive support…
Browse files Browse the repository at this point in the history
… would require rewriting them in the bytecode AST layer.
  • Loading branch information
mstrobel committed Jun 19, 2021
1 parent ca1b948 commit 0ae5095
Show file tree
Hide file tree
Showing 13 changed files with 752 additions and 28 deletions.
3 changes: 3 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public interface ITextOutput {
void indent();
void unindent();

int indentDepth();

void write(final char ch);
void write(final String text);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.io.Writer;
import java.lang.reflect.UndeclaredThrowableException;

import static com.strobel.core.Comparer.coalesce;

public class PlainTextOutput implements ITextOutput {
private final static String NULL_TEXT = String.valueOf((Object) null);

Expand Down Expand Up @@ -73,7 +75,7 @@ protected void writeIndent() {
try {
_writer.write(indentToken);
}
catch (IOException e) {
catch (final IOException e) {
throw new UndeclaredThrowableException(e);
}
}
Expand All @@ -97,6 +99,11 @@ public void indent() {
++_indent;
}

@Override
public int indentDepth() {
return _indent;
}

@Override
public void unindent() {
--_indent;
Expand All @@ -114,7 +121,7 @@ public void write(final char ch) {
}
column++;
}
catch (IOException e) {
catch (final IOException e) {
throw new UndeclaredThrowableException(e);
}
}
Expand All @@ -134,9 +141,10 @@ protected void writeRaw(final String text) {
writeIndent();

try {
final int length = text != null ? text.length() : NULL_TEXT.length();
final String effectiveText = coalesce(text, NULL_TEXT);
final int length = effectiveText.length();

_writer.write(text);
_writer.write(effectiveText);

column += length;

Expand All @@ -157,7 +165,7 @@ else if (newLineSeen) {
}
}
}
catch (IOException e) {
catch (final IOException e) {
throw new UndeclaredThrowableException(e);
}
}
Expand Down Expand Up @@ -215,7 +223,7 @@ public void writeLine() {
try {
_writer.write("\n");
}
catch (IOException e) {
catch (final IOException e) {
throw new UndeclaredThrowableException(e);
}
_needsIndent = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import com.strobel.decompiler.languages.java.ast.WildcardType;
import com.strobel.decompiler.languages.java.utilities.TypeUtilities;
import com.strobel.decompiler.patterns.*;
import com.strobel.util.ContractUtils;

import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -49,6 +48,8 @@

@SuppressWarnings("ConstantConditions")
public final class JavaOutputVisitor implements IAstVisitor<Void, Void> {
private final static int MAX_SWITCH_EXPRESSION_ARM_VALUES_LENGTH = 40;

final TextOutputFormatter formatter;
final DecompilerSettings settings;
final JavaFormattingOptions policy;
Expand Down Expand Up @@ -215,7 +216,7 @@ void closeBrace(final BraceStyle style) {
formatter.closeBrace(style);
lastWritten = LastWritten.Other;
}

void writeIdentifier(final Identifier identifier, final String text) {
// for now, set the start location to *here*
identifier.setStartLocation(new TextLocation(output.getRow(), output.getColumn()));
Expand All @@ -236,7 +237,7 @@ boolean writeIdentifier(final String identifier, final Role<Identifier> identifi
writeSpecialsUpToRole(identifierRole != null ? identifierRole : Roles.IDENTIFIER);

boolean wroteSpace = false;

if (isKeyword(identifier, containerStack.peek())) {
if (lastWritten == LastWritten.KeywordOrIdentifier) {
space();
Expand All @@ -259,7 +260,7 @@ else if (lastWritten == LastWritten.KeywordOrIdentifier) {
}

lastWritten = LastWritten.KeywordOrIdentifier;

return wroteSpace;
}

Expand Down Expand Up @@ -646,7 +647,6 @@ else if (childNode instanceof ParameterReferenceNode) {
}
else if (childNode instanceof AstTypeMatch) {
visitAstTypeMatch((AstTypeMatch) childNode);

}
else {
writePrimitiveValue(childNode);
Expand Down Expand Up @@ -813,7 +813,9 @@ public Void visitJavaTokenNode(final JavaTokenNode node, final Void ignored) {
endNode(modifierToken);
}
else {
throw ContractUtils.unsupported();
startNode(node);
writeKeyword(node.getText());
endNode(node);
}
return null;
}
Expand Down Expand Up @@ -974,12 +976,32 @@ public Void visitExpressionStatement(final ExpressionStatement node, final Void
@Override
public Void visitBreakStatement(final BreakStatement node, final Void ignored) {
startNode(node);
writeKeyword("break");

final String label = node.getLabel();
JavaTokenNode yt = node.getYieldToken();

if (!StringUtilities.isNullOrEmpty(label)) {
writeIdentifier(label, Roles.LABEL);
if (yt.isNull()) {
yt = node.getBreakToken();
}

if (yt.isNull()) {
writeKeyword("break");
}
else {
yt.acceptVisitor(this, ignored);
}

final Expression value = node.getValue();

if (value.isNull()) {
final String label = node.getLabel();

if (!StringUtilities.isNullOrEmpty(label)) {
writeIdentifier(label, Roles.LABEL);
}
}
else {
space();
value.acceptVisitor(this, ignored);
}

semicolon();
Expand Down Expand Up @@ -1165,8 +1187,109 @@ public Void visitSwitchSection(final SwitchSection node, final Void ignored) {
first = false;
}

final boolean isBlock = node.getStatements().size() == 1 &&
firstOrDefault(node.getStatements()) instanceof BlockStatement;
writeSwitchSectionStatements(node.getStatements(), false);
endNode(node);
return null;
}

@Override
public Void visitSwitchExpression(final SwitchExpression node, final Void ignored) {
startNode(node);
writeKeyword(SwitchStatement.SWITCH_KEYWORD_ROLE);
space(policy.SpaceBeforeSwitchParentheses);
leftParenthesis();
space(policy.SpacesWithinSwitchParentheses);
node.getGoverningExpression().acceptVisitor(this, ignored);
space(policy.SpacesWithinSwitchParentheses);
rightParenthesis();
openBrace(policy.StatementBraceStyle);

if (policy.IndentSwitchBody) {
formatter.indent();
}

for (final SwitchExpressionArm section : node.getArms()) {
section.acceptVisitor(this, ignored);
}

if (policy.IndentSwitchBody) {
formatter.unindent();
}

closeBrace(policy.StatementBraceStyle);
endNode(node);

return null;
}

@Override
public Void visitSwitchExpressionArm(final SwitchExpressionArm node, final Void ignored) {
startNode(node);

final AstNodeCollection<Expression> values = node.getValues();
final boolean isDefault = values.isEmpty();

writeKeyword(isDefault ? SwitchExpressionArm.DEFAULT_KEYWORD_ROLE
: SwitchExpressionArm.CASE_KEYWORD_ROLE);

if (!isDefault) {
final int startColumn = output.getIndentToken().length() * output.indentDepth();
final int endColumn = output.getColumn();

String padding = null;
boolean comma = false;

for (final Expression value : values) {
if (comma) {
comma(value);
}

space();

if (output.getColumn() - endColumn >= MAX_SWITCH_EXPRESSION_ARM_VALUES_LENGTH) {
newLine();
if (padding == null) {
padding = StringUtilities.repeat(' ', endColumn - startColumn);
}
output.write(padding);
}

value.acceptVisitor(this, ignored);
comma = true;
}
}

if (node.isClassicStyle()) {
writeToken(SwitchExpressionArm.COLON_ROLE);
}
else {
space();
writeToken(SwitchExpressionArm.ARROW_ROLE);
}

space();

final Statement first;
final AstNodeCollection<Statement> statements = node.getStatements();

if (statements.size() == 1 && !((first = statements.firstOrNullObject()) instanceof BlockStatement)) {
first.acceptVisitor(this, ignored);
}
else {
writeSwitchSectionStatements(statements, true);
}

endNode(node);
return null;
}

private void writeSwitchSectionStatements(final AstNodeCollection<Statement> statements, final boolean forceBraces) {
final boolean isBlock = statements.size() == 1 &&
firstOrDefault(statements) instanceof BlockStatement;

if (forceBraces && !isBlock) {
openBrace(BraceStyle.EndOfLine);
}

if (policy.IndentCaseBody && !isBlock) {
formatter.indent();
Expand All @@ -1176,16 +1299,17 @@ public Void visitSwitchSection(final SwitchSection node, final Void ignored) {
newLine();
}

for (final Statement statement : node.getStatements()) {
statement.acceptVisitor(this, ignored);
for (final Statement statement : statements) {
statement.acceptVisitor(this, null);
}

if (policy.IndentCaseBody && !isBlock) {
formatter.unindent();
}

endNode(node);
return null;
if (forceBraces && !isBlock) {
closeBrace(BraceStyle.EndOfLine);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,53 @@
package com.strobel.decompiler.languages.java.ast;

import com.strobel.core.StringUtilities;
import com.strobel.decompiler.languages.TextLocation;
import com.strobel.decompiler.patterns.INode;
import com.strobel.decompiler.patterns.Match;

public class BreakStatement extends Statement {
public final static TokenRole BREAK_KEYWORD_ROLE = new TokenRole("break", TokenRole.FLAG_KEYWORD);
public final static TokenRole YIELD_KEYWORD_ROLE = new TokenRole("yield", TokenRole.FLAG_KEYWORD);

public BreakStatement( int offset) {
super( offset);
public BreakStatement(final int offset) {
super(offset);
}

public BreakStatement( int offset, final String label) {
super( offset);
public BreakStatement(final int offset, final String label) {
super(offset);
setLabel(label);
setChildByRole(BREAK_KEYWORD_ROLE, new JavaTokenNode(TextLocation.EMPTY));
}

public final JavaTokenNode getBreakToken() {
return getChildByRole(BREAK_KEYWORD_ROLE);
}

public final JavaTokenNode getYieldToken() {
return getChildByRole(YIELD_KEYWORD_ROLE);
}

public final Expression getValue() {
return getChildByRole(Roles.EXPRESSION);
}

public final void setValue(final Expression value) {
setChildByRole(Roles.EXPRESSION, value);
}

public final void setYield(final boolean isYield) {
if (isYield) {
final JavaTokenNode old = getChildByRole(BREAK_KEYWORD_ROLE);
old.remove();
setChildByRole(YIELD_KEYWORD_ROLE, old.isNull() ? new JavaTokenNode(TextLocation.EMPTY) : old);
}
else {
final JavaTokenNode old = getChildByRole(YIELD_KEYWORD_ROLE);
old.remove();
setChildByRole(BREAK_KEYWORD_ROLE, old.isNull() ? new JavaTokenNode(TextLocation.EMPTY) : old);
}
}

public final JavaTokenNode getSemicolonToken() {
return getChildByRole(Roles.SEMICOLON);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,16 @@ public S visitSwitchSection(final SwitchSection node, final T data) {
return visitChildren(node, data);
}

@Override
public S visitSwitchExpression(final SwitchExpression node, final T data) {
return visitChildren(node, data);
}

@Override
public S visitSwitchExpressionArm(final SwitchExpressionArm node, final T data) {
return visitChildren(node, data);
}

@Override
public S visitCaseLabel(final CaseLabel node, final T data) {
return visitChildren(node, data);
Expand Down
Loading

0 comments on commit 0ae5095

Please sign in to comment.