diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index fe325ec4..5d9df32e 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -6,6 +6,9 @@
+
+
+
diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/ITextOutput.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/ITextOutput.java
index 23b7d4b4..480393e6 100644
--- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/ITextOutput.java
+++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/ITextOutput.java
@@ -26,6 +26,8 @@ public interface ITextOutput {
void indent();
void unindent();
+ int indentDepth();
+
void write(final char ch);
void write(final String text);
diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/PlainTextOutput.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/PlainTextOutput.java
index 1b5e501c..7766dc30 100644
--- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/PlainTextOutput.java
+++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/PlainTextOutput.java
@@ -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);
@@ -73,7 +75,7 @@ protected void writeIndent() {
try {
_writer.write(indentToken);
}
- catch (IOException e) {
+ catch (final IOException e) {
throw new UndeclaredThrowableException(e);
}
}
@@ -97,6 +99,11 @@ public void indent() {
++_indent;
}
+ @Override
+ public int indentDepth() {
+ return _indent;
+ }
+
@Override
public void unindent() {
--_indent;
@@ -114,7 +121,7 @@ public void write(final char ch) {
}
column++;
}
- catch (IOException e) {
+ catch (final IOException e) {
throw new UndeclaredThrowableException(e);
}
}
@@ -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;
@@ -157,7 +165,7 @@ else if (newLineSeen) {
}
}
}
- catch (IOException e) {
+ catch (final IOException e) {
throw new UndeclaredThrowableException(e);
}
}
@@ -215,7 +223,7 @@ public void writeLine() {
try {
_writer.write("\n");
}
- catch (IOException e) {
+ catch (final IOException e) {
throw new UndeclaredThrowableException(e);
}
_needsIndent = true;
diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/JavaOutputVisitor.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/JavaOutputVisitor.java
index 3fa0b4e3..366efc2f 100644
--- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/JavaOutputVisitor.java
+++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/JavaOutputVisitor.java
@@ -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;
@@ -49,6 +48,8 @@
@SuppressWarnings("ConstantConditions")
public final class JavaOutputVisitor implements IAstVisitor {
+ private final static int MAX_SWITCH_EXPRESSION_ARM_VALUES_LENGTH = 40;
+
final TextOutputFormatter formatter;
final DecompilerSettings settings;
final JavaFormattingOptions policy;
@@ -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()));
@@ -236,7 +237,7 @@ boolean writeIdentifier(final String identifier, final Role identifi
writeSpecialsUpToRole(identifierRole != null ? identifierRole : Roles.IDENTIFIER);
boolean wroteSpace = false;
-
+
if (isKeyword(identifier, containerStack.peek())) {
if (lastWritten == LastWritten.KeywordOrIdentifier) {
space();
@@ -259,7 +260,7 @@ else if (lastWritten == LastWritten.KeywordOrIdentifier) {
}
lastWritten = LastWritten.KeywordOrIdentifier;
-
+
return wroteSpace;
}
@@ -646,7 +647,6 @@ else if (childNode instanceof ParameterReferenceNode) {
}
else if (childNode instanceof AstTypeMatch) {
visitAstTypeMatch((AstTypeMatch) childNode);
-
}
else {
writePrimitiveValue(childNode);
@@ -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;
}
@@ -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();
@@ -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 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 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 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();
@@ -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
diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/BreakStatement.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/BreakStatement.java
index b6f713fa..1a84ab11 100644
--- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/BreakStatement.java
+++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/BreakStatement.java
@@ -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);
}
diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/DepthFirstAstVisitor.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/DepthFirstAstVisitor.java
index d42dc2bd..ee447eee 100644
--- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/DepthFirstAstVisitor.java
+++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/DepthFirstAstVisitor.java
@@ -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);
diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/IAstVisitor.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/IAstVisitor.java
index 16b277e7..a168d17a 100644
--- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/IAstVisitor.java
+++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/IAstVisitor.java
@@ -42,6 +42,8 @@ public interface IAstVisitor {
R visitReturnStatement(ReturnStatement node, T data);
R visitSwitchStatement(SwitchStatement node, T data);
R visitSwitchSection(SwitchSection node, T data);
+ R visitSwitchExpression(SwitchExpression node, T data);
+ R visitSwitchExpressionArm(SwitchExpressionArm node, T data);
R visitCaseLabel(CaseLabel node, T data);
R visitThrowStatement(ThrowStatement node, T data);
R visitCatchClause(CatchClause node, T data);
diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/JavaNameResolver.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/JavaNameResolver.java
index 62423ab3..82878416 100644
--- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/JavaNameResolver.java
+++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/JavaNameResolver.java
@@ -482,6 +482,16 @@ public Set