From 75892a9fd3891f371258eac823ba325fa6ba887c Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Mon, 6 Jun 2022 20:03:00 +0200 Subject: [PATCH 01/14] prepare code for line stretch (line number realignment) --- .../assembler/metadata/MethodReference.java | 28 +- .../languages/java/JavaOutputVisitor.java | 1 + .../java/MinMaxLineNumberVisitor.java | 383 ++++++++++++++++++ .../languages/java/TextOutputFormatter.java | 8 +- .../languages/java/ast/EntityDeclaration.java | 23 ++ .../languages/java/ast/FieldDeclaration.java | 16 + .../languages/java/ast/MethodDeclaration.java | 14 + .../languages/java/ast/TypeDeclaration.java | 13 + ...ReOrderMembersForLineStretchTransform.java | 61 +++ .../RewriteInitForLineStretchTransform.java | 194 +++++++++ .../transforms/TransformationPipeline.java | 6 +- .../com/strobel/decompiler/NameTests.java | 42 +- .../com/strobel/decompiler/OperatorTests.java | 13 +- .../strobel/decompiler/PrimitiveTests.java | 14 +- .../com/strobel/decompiler/RealignTest.java | 239 +++++++++++ gradle/wrapper/gradle-wrapper.properties | 2 +- 16 files changed, 1005 insertions(+), 52 deletions(-) create mode 100644 Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/MinMaxLineNumberVisitor.java create mode 100644 Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/ReOrderMembersForLineStretchTransform.java create mode 100644 Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java create mode 100644 Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/assembler/metadata/MethodReference.java b/Procyon.CompilerTools/src/main/java/com/strobel/assembler/metadata/MethodReference.java index a3a0caf9..9b4c67b2 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/assembler/metadata/MethodReference.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/assembler/metadata/MethodReference.java @@ -17,7 +17,6 @@ package com.strobel.assembler.metadata; import com.strobel.core.StringUtilities; -import com.strobel.core.VerifyArgument; import com.strobel.util.ContractUtils; import java.util.Collections; @@ -28,21 +27,24 @@ * Date: 1/6/13 * Time: 2:29 PM */ -public abstract class MethodReference extends MemberReference implements IMethodSignature, - IGenericParameterProvider, - IGenericContext { - protected final static String CONSTRUCTOR_NAME = ""; - protected final static String STATIC_INITIALIZER_NAME = ""; +public abstract class MethodReference extends MemberReference implements IMethodSignature { + protected static final String CONSTRUCTOR_NAME = ""; + protected static final String STATIC_INITIALIZER_NAME = ""; // - public abstract TypeReference getReturnType(); - public boolean hasParameters() { return !getParameters().isEmpty(); } - public abstract List getParameters(); + public boolean hasParameter(String name) { + for (ParameterDefinition parameterDefinition : getParameters()) { + if (parameterDefinition.getName().equals(name)) { + return true; + } + } + return false; + } public List getThrownTypes() { return Collections.emptyList(); @@ -117,11 +119,11 @@ protected StringBuilder appendName(final StringBuilder sb, final boolean fullNam } public boolean isConstructor() { - return MethodDefinition.CONSTRUCTOR_NAME.equals(getName()); + return CONSTRUCTOR_NAME.equals(getName()); } public boolean isTypeInitializer() { - return MethodDefinition.STATIC_INITIALIZER_NAME.equals(getName()); + return STATIC_INITIALIZER_NAME.equals(getName()); } // @@ -171,9 +173,9 @@ public GenericParameter findTypeVariable(final String name) { public MethodDefinition resolve() { final TypeReference declaringType = getDeclaringType(); - if (declaringType == null) + if (declaringType == null) { throw ContractUtils.unsupported(); - + } return declaringType.resolve(this); } 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 90cb2546..78b9a7cc 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 @@ -1654,6 +1654,7 @@ public Void visitParameterDeclaration(final ParameterDeclaration node, final Voi @Override public Void visitFieldDeclaration(final FieldDeclaration node, final Void ignored) { startNode(node); + formatter.resetLineNumberOffsets(OffsetToLineNumberConverter.NOOP_CONVERTER); writeAnnotations(node.getAnnotations(), true); writeModifiers(node.getModifiers()); diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/MinMaxLineNumberVisitor.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/MinMaxLineNumberVisitor.java new file mode 100644 index 00000000..38a27412 --- /dev/null +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/MinMaxLineNumberVisitor.java @@ -0,0 +1,383 @@ +/* + * MinMaxLineNumberVisitor.java + * + * Copyright (c) 2013-2022 Mike Strobel and other contributors + * + * This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain; + * and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. + * A copy of the license can be found in the License.html file at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + */ + +package com.strobel.decompiler.languages.java; + +import com.strobel.decompiler.languages.java.ast.Annotation; +import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.AssertStatement; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.BinaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.BreakStatement; +import com.strobel.decompiler.languages.java.ast.BytecodeConstant; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.ClassOfExpression; +import com.strobel.decompiler.languages.java.ast.ConditionalExpression; +import com.strobel.decompiler.languages.java.ast.ContinueStatement; +import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; +import com.strobel.decompiler.languages.java.ast.DoWhileStatement; +import com.strobel.decompiler.languages.java.ast.EmptyStatement; +import com.strobel.decompiler.languages.java.ast.Expression; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.ForEachStatement; +import com.strobel.decompiler.languages.java.ast.ForStatement; +import com.strobel.decompiler.languages.java.ast.GotoStatement; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.IfElseStatement; +import com.strobel.decompiler.languages.java.ast.IndexerExpression; +import com.strobel.decompiler.languages.java.ast.InlinedBytecodeExpression; +import com.strobel.decompiler.languages.java.ast.InstanceOfExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.LabelStatement; +import com.strobel.decompiler.languages.java.ast.LabeledStatement; +import com.strobel.decompiler.languages.java.ast.LambdaExpression; +import com.strobel.decompiler.languages.java.ast.LocalTypeDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.NullReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ParenthesizedExpression; +import com.strobel.decompiler.languages.java.ast.PrimitiveExpression; +import com.strobel.decompiler.languages.java.ast.ReturnStatement; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.SwitchExpression; +import com.strobel.decompiler.languages.java.ast.SwitchStatement; +import com.strobel.decompiler.languages.java.ast.SynchronizedStatement; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThrowStatement; +import com.strobel.decompiler.languages.java.ast.TryCatchStatement; +import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; +import com.strobel.decompiler.languages.java.ast.UnaryOperatorExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.WhileStatement; + +public class MinMaxLineNumberVisitor extends DepthFirstAstVisitor { + + private final LineNumberTableConverter lineNumberTableConverter; + private int minLineNumber = Integer.MAX_VALUE; + private int maxLineNumber = Integer.MIN_VALUE; + + public MinMaxLineNumberVisitor(final LineNumberTableConverter lineNumberTableConverter) { + this.lineNumberTableConverter = lineNumberTableConverter; + } + + private void updateMinMaxLineNumbers(int offset) { + if (offset != Expression.MYSTERY_OFFSET) { + int lineNumber = lineNumberTableConverter.getLineForOffset(offset); + minLineNumber = Math.min(minLineNumber, lineNumber); + maxLineNumber = Math.max(maxLineNumber, lineNumber); + } + } + + @Override + public Void visitInvocationExpression(InvocationExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitInvocationExpression(node, data); + } + + @Override + public Void visitTypeReference(TypeReferenceExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitTypeReference(node, data); + } + + @Override + public Void visitMemberReferenceExpression(MemberReferenceExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitMemberReferenceExpression(node, data); + } + + @Override + public Void visitNullReferenceExpression(NullReferenceExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitNullReferenceExpression(node, data); + } + + @Override + public Void visitThisReferenceExpression(ThisReferenceExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitThisReferenceExpression(node, data); + } + + @Override + public Void visitSuperReferenceExpression(SuperReferenceExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitSuperReferenceExpression(node, data); + } + + @Override + public Void visitClassOfExpression(ClassOfExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitClassOfExpression(node, data); + } + + @Override + public Void visitBlockStatement(BlockStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitBlockStatement(node, data); + } + + @Override + public Void visitExpressionStatement(ExpressionStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitExpressionStatement(node, data); + } + + @Override + public Void visitBreakStatement(BreakStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitBreakStatement(node, data); + } + + @Override + public Void visitContinueStatement(ContinueStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitContinueStatement(node, data); + } + + @Override + public Void visitDoWhileStatement(DoWhileStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitDoWhileStatement(node, data); + } + + @Override + public Void visitEmptyStatement(EmptyStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitEmptyStatement(node, data); + } + + @Override + public Void visitIfElseStatement(IfElseStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitIfElseStatement(node, data); + } + + @Override + public Void visitLabelStatement(LabelStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitLabelStatement(node, data); + } + + @Override + public Void visitLabeledStatement(LabeledStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitLabeledStatement(node, data); + } + + @Override + public Void visitReturnStatement(ReturnStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitReturnStatement(node, data); + } + + @Override + public Void visitSwitchStatement(SwitchStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitSwitchStatement(node, data); + } + + @Override + public Void visitSwitchExpression(SwitchExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitSwitchExpression(node, data); + } + + @Override + public Void visitThrowStatement(ThrowStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitThrowStatement(node, data); + } + + @Override + public Void visitAnnotation(Annotation node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitAnnotation(node, data); + } + + @Override + public Void visitVariableDeclaration(VariableDeclarationStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitVariableDeclaration(node, data); + } + + @Override + public Void visitWhileStatement(WhileStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitWhileStatement(node, data); + } + + @Override + public Void visitPrimitiveExpression(PrimitiveExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitPrimitiveExpression(node, data); + } + + @Override + public Void visitCastExpression(CastExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitCastExpression(node, data); + } + + @Override + public Void visitBinaryOperatorExpression(BinaryOperatorExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitBinaryOperatorExpression(node, data); + } + + @Override + public Void visitInstanceOfExpression(InstanceOfExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitInstanceOfExpression(node, data); + } + + @Override + public Void visitIndexerExpression(IndexerExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitIndexerExpression(node, data); + } + + @Override + public Void visitIdentifierExpression(IdentifierExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitIdentifierExpression(node, data); + } + + @Override + public Void visitUnaryOperatorExpression(UnaryOperatorExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitUnaryOperatorExpression(node, data); + } + + @Override + public Void visitConditionalExpression(ConditionalExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitConditionalExpression(node, data); + } + + @Override + public Void visitArrayInitializerExpression(ArrayInitializerExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitArrayInitializerExpression(node, data); + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitObjectCreationExpression(node, data); + } + + @Override + public Void visitArrayCreationExpression(ArrayCreationExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitArrayCreationExpression(node, data); + } + + @Override + public Void visitAssignmentExpression(AssignmentExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitAssignmentExpression(node, data); + } + + @Override + public Void visitForStatement(ForStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitForStatement(node, data); + } + + @Override + public Void visitForEachStatement(ForEachStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitForEachStatement(node, data); + } + + @Override + public Void visitTryCatchStatement(TryCatchStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitTryCatchStatement(node, data); + } + + @Override + public Void visitGotoStatement(GotoStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitGotoStatement(node, data); + } + + @Override + public Void visitParenthesizedExpression(ParenthesizedExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitParenthesizedExpression(node, data); + } + + @Override + public Void visitSynchronizedStatement(SynchronizedStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitSynchronizedStatement(node, data); + } + + @Override + public Void visitAnonymousObjectCreationExpression(AnonymousObjectCreationExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitAnonymousObjectCreationExpression(node, data); + } + + @Override + public Void visitMethodGroupExpression(MethodGroupExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitMethodGroupExpression(node, data); + } + + @Override + public Void visitAssertStatement(AssertStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitAssertStatement(node, data); + } + + @Override + public Void visitLambdaExpression(LambdaExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitLambdaExpression(node, data); + } + + @Override + public Void visitLocalTypeDeclarationStatement(LocalTypeDeclarationStatement node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitLocalTypeDeclarationStatement(node, data); + } + + @Override + public Void visitInlinedBytecode(InlinedBytecodeExpression node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitInlinedBytecode(node, data); + } + + @Override + public Void visitBytecodeConstant(BytecodeConstant node, Void data) { + updateMinMaxLineNumbers(node.getOffset()); + return super.visitBytecodeConstant(node, data); + } + + public int getMinLineNumber() { + return minLineNumber; + } + + public int getMaxLineNumber() { + return maxLineNumber; + } +} \ No newline at end of file diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/TextOutputFormatter.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/TextOutputFormatter.java index 763f942c..7bf26df6 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/TextOutputFormatter.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/TextOutputFormatter.java @@ -99,10 +99,16 @@ public void startNode(final AstNode node) { else if (node instanceof Statement) { offset = ((Statement) node).getOffset(); prefix = "/*SL:"; + } else if (node instanceof FieldDeclaration + && node.getLastChild() instanceof VariableInitializer + && node.getLastChild().getLastChild() instanceof Expression) { + offset = ((Expression) node.getLastChild().getLastChild()).getOffset(); + prefix = "/*SL:"; } if (offset != Expression.MYSTERY_OFFSET) { // Convert to a line number. - final int lineNumber = offset2LineNumber.getLineForOffset(offset); + final int lineNumber = node instanceof FieldDeclaration ? ((FieldDeclaration) node).getLineNumber() + : offset2LineNumber.getLineForOffset(offset); if (lineNumber > lastObservedLineNumber) { // Record a data structure mapping original to actual line numbers. final int lineOfComment = output.getRow(); diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/EntityDeclaration.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/EntityDeclaration.java index f12a18e6..b4b38c8c 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/EntityDeclaration.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/EntityDeclaration.java @@ -16,7 +16,12 @@ package com.strobel.decompiler.languages.java.ast; +import com.strobel.assembler.ir.attributes.AttributeNames; +import com.strobel.assembler.ir.attributes.LineNumberTableAttribute; +import com.strobel.assembler.ir.attributes.LineNumberTableEntry; +import com.strobel.assembler.ir.attributes.SourceAttribute; import com.strobel.assembler.metadata.Flags; +import com.strobel.assembler.metadata.MethodDefinition; import com.strobel.decompiler.languages.EntityType; import com.strobel.decompiler.languages.TextLocation; import com.strobel.decompiler.patterns.Match; @@ -179,4 +184,22 @@ static boolean removeModifier(final AstNode node, final Flags.Flag modifier) { return false; } + + public int getFirstKnownLineNumber() { + MethodDefinition methodDefinition = getUserData(Keys.METHOD_DEFINITION); + if (methodDefinition != null) { + final LineNumberTableAttribute lineNumberTableAttribute = SourceAttribute.find(AttributeNames.LineNumberTable, methodDefinition.getSourceAttributes()); + if (lineNumberTableAttribute != null) { + List entries = lineNumberTableAttribute.getEntries(); + if (entries != null) { + for (LineNumberTableEntry entry : entries) { + if (entry != null) { + return entry.getLineNumber(); + } + } + } + } + } + return 0; + } } diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/FieldDeclaration.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/FieldDeclaration.java index 59dec7a9..6cce00bf 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/FieldDeclaration.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/FieldDeclaration.java @@ -21,6 +21,9 @@ import com.strobel.decompiler.patterns.Match; public class FieldDeclaration extends EntityDeclaration { + + private int lineNumber; + public final AstNodeCollection getVariables() { return getChildrenByRole(Roles.VARIABLE); } @@ -48,4 +51,17 @@ public boolean matches(final INode other, final Match match) { return false; } + + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + @Override + public int getFirstKnownLineNumber() { + return lineNumber; + } } diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/MethodDeclaration.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/MethodDeclaration.java index 0538cb54..8be93db5 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/MethodDeclaration.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/MethodDeclaration.java @@ -30,6 +30,8 @@ public class MethodDeclaration extends EntityDeclaration { public final static TokenRole DEFAULT_KEYWORD = new TokenRole("default", TokenRole.FLAG_KEYWORD); public final static TokenRole THROWS_KEYWORD = new TokenRole("throws", TokenRole.FLAG_KEYWORD); + private int firstKnownLineNumber; + public final AstType getPrivateImplementationType() { return getChildByRole(PRIVATE_IMPLEMENTATION_TYPE_ROLE); } @@ -111,6 +113,18 @@ public boolean matches(final INode other, final Match match) { return false; } + + @Override + public int getFirstKnownLineNumber() { + if (firstKnownLineNumber > 0) { + return firstKnownLineNumber; + } + return super.getFirstKnownLineNumber(); + } + + public void setFirstKnownLineNumber(int firstKnownLineNumber) { + this.firstKnownLineNumber = firstKnownLineNumber; + } // diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/TypeDeclaration.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/TypeDeclaration.java index f03d604a..829f85ad 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/TypeDeclaration.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/TypeDeclaration.java @@ -123,6 +123,19 @@ public boolean matches(final INode other, final Match match) { return false; } + @Override + public int getFirstKnownLineNumber() { + for (EntityDeclaration entityDeclaration : getMembers()) { + if (entityDeclaration != null) { + int firstKnownLineNumber = entityDeclaration.getFirstKnownLineNumber(); + if (firstKnownLineNumber > 0) { + return firstKnownLineNumber; + } + } + } + return 0; + } + // public final static TypeDeclaration NULL = new NullTypeDeclaration(); diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/ReOrderMembersForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/ReOrderMembersForLineStretchTransform.java new file mode 100644 index 00000000..c1cbd2bf --- /dev/null +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/ReOrderMembersForLineStretchTransform.java @@ -0,0 +1,61 @@ +/* + * ReOrderMembersForLineStretchTransform.java + * + * Copyright (c) 2013-2022 Mike Strobel and other contributors + * + * This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain; + * and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. + * A copy of the license can be found in the License.html file at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + */ + +package com.strobel.decompiler.languages.java.ast.transforms; + +import com.strobel.decompiler.DecompilerContext; +import com.strobel.decompiler.languages.java.ast.AstNodeCollection; +import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor; +import com.strobel.decompiler.languages.java.ast.EntityDeclaration; +import com.strobel.decompiler.languages.java.ast.Roles; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class ReOrderMembersForLineStretchTransform extends ContextTrackingVisitor { + + public ReOrderMembersForLineStretchTransform(DecompilerContext context) { + super(context); + } + + @Override + protected Void visitTypeDeclarationOverride(TypeDeclaration typeDeclaration, Void p) { + AstNodeCollection members = typeDeclaration.getChildrenByRole(Roles.TYPE_MEMBER); + List sortedMembers = new ArrayList<>(members); + Collections.sort(sortedMembers, new MemberComparator()); + for (EntityDeclaration member : members) { + member.remove(); + } + for (EntityDeclaration member : sortedMembers) { + typeDeclaration.addChild(member, Roles.TYPE_MEMBER); + } + return p; + } + + private static class MemberComparator implements Comparator, Serializable { + + private static final long serialVersionUID = 1L; + + @Override + public int compare(EntityDeclaration e1, EntityDeclaration e2) { + return Integer.compare(e1.getFirstKnownLineNumber(), e2.getFirstKnownLineNumber()); + } + } +} diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java new file mode 100644 index 00000000..d2407c99 --- /dev/null +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -0,0 +1,194 @@ +/* + * RewriteInitForLineStretchTransform.java + * + * Copyright (c) 2013-2022 Mike Strobel and other contributors + * + * This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain; + * and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. + * A copy of the license can be found in the License.html file at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + */ + +package com.strobel.decompiler.languages.java.ast.transforms; + +import com.strobel.assembler.ir.attributes.AttributeNames; +import com.strobel.assembler.ir.attributes.LineNumberTableAttribute; +import com.strobel.assembler.ir.attributes.SourceAttribute; +import com.strobel.assembler.metadata.FieldDefinition; +import com.strobel.assembler.metadata.MemberReference; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.ParameterDefinition; +import com.strobel.decompiler.DecompilerContext; +import com.strobel.decompiler.languages.java.LineNumberTableConverter; +import com.strobel.decompiler.languages.java.MinMaxLineNumberVisitor; +import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor; +import com.strobel.decompiler.languages.java.ast.Expression; +import com.strobel.decompiler.languages.java.ast.ExpressionStatement; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.Roles; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RewriteInitForLineStretchTransform extends ContextTrackingVisitor { + + private Map fieldDeclarations = new HashMap<>(); + private int constructorCount; + + public RewriteInitForLineStretchTransform(DecompilerContext context) { + super(context); + } + + @Override + public Void visitBlockStatement(BlockStatement node, Void data) { + if (node.getParent() instanceof MethodDeclaration) { + MethodDeclaration methodDeclaration = (MethodDeclaration) node.getParent(); + MethodDefinition methodDefinition = methodDeclaration.getUserData(Keys.METHOD_DEFINITION); + if (methodDefinition != null && methodDefinition.isTypeInitializer()) { + final LineNumberTableAttribute lineNumberTable = SourceAttribute.find(AttributeNames.LineNumberTable, methodDefinition.getSourceAttributes()); + if (lineNumberTable != null) { + LineNumberTableConverter lineNumberTableConverter = new LineNumberTableConverter(lineNumberTable); + int previousLineNumber = 0; + int pivotLineNumber = 0; + MethodDeclaration newMethodDeclaration = null; + for (AstNode child : node.getChildren()) { + MinMaxLineNumberVisitor minMaxLineNumberVisitor = new MinMaxLineNumberVisitor(lineNumberTableConverter); + child.acceptVisitor(minMaxLineNumberVisitor, null); + int currentLineNumber = minMaxLineNumberVisitor.getMinLineNumber(); + if (previousLineNumber > 0 && currentLineNumber > previousLineNumber + 3) { + newMethodDeclaration = (MethodDeclaration) methodDeclaration.clone(); + newMethodDeclaration.setFirstKnownLineNumber(currentLineNumber); + methodDeclaration.getParent().insertChildAfter(methodDeclaration, newMethodDeclaration, Roles.TYPE_MEMBER); + pivotLineNumber = currentLineNumber; + break; + } + previousLineNumber = minMaxLineNumberVisitor.getMaxLineNumber(); + } + if (pivotLineNumber > 0) { + for (AstNode child : node.getChildren()) { + MinMaxLineNumberVisitor minMaxLineNumberVisitor = new MinMaxLineNumberVisitor(lineNumberTableConverter); + child.acceptVisitor(minMaxLineNumberVisitor, null); + int currentLineNumber = minMaxLineNumberVisitor.getMinLineNumber(); + if (currentLineNumber >= pivotLineNumber) { + child.remove(); + } + } + } + if (newMethodDeclaration != null) { + BlockStatement body = newMethodDeclaration.getBody(); + if (body != null) { + for (AstNode child : body.getChildren()) { + MinMaxLineNumberVisitor minMaxLineNumberVisitor = new MinMaxLineNumberVisitor(lineNumberTableConverter); + child.acceptVisitor(minMaxLineNumberVisitor, null); + int currentLineNumber = minMaxLineNumberVisitor.getMinLineNumber(); + if (currentLineNumber < pivotLineNumber) { + child.remove(); + } + } + visitBlockStatement(body, data); + } + } + } + } + } + return super.visitBlockStatement(node, data); + } + + @Override + public Void visitAssignmentExpression(AssignmentExpression node, Void data) { + AstNode parent = node.getParent(); + if (parent instanceof ExpressionStatement && parent.getParent() instanceof BlockStatement && parent.getParent().getParent() instanceof ConstructorDeclaration + && node.getLeft() instanceof MemberReferenceExpression) { + ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) parent.getParent().getParent(); + MethodDefinition constructor = constructorDeclaration.getUserData(Keys.METHOD_DEFINITION); + final LineNumberTableAttribute lineNumberTable = SourceAttribute.find(AttributeNames.LineNumberTable, + constructor != null ? constructor.getSourceAttributes() : Collections.emptyList()); + LineNumberTableConverter lineNumberTableConverter = new LineNumberTableConverter(lineNumberTable); + MemberReferenceExpression memberReferenceExpression = (MemberReferenceExpression) node.getFirstChild(); + MemberReference memberReference = memberReferenceExpression.getUserData(Keys.MEMBER_REFERENCE); + FieldInit fieldInit = fieldDeclarations.get(memberReference.getFullName()); + if (fieldInit != null && (constructor == null || !constructor.hasParameter(memberReference.getName()))) { + Expression initializer = node.getRight(); + int fieldInitLineNo = lineNumberTableConverter.getLineForOffset(initializer.getOffset()); + if (fieldInitLineNo > 0) { + FieldDeclaration fieldDeclaration = fieldInit.declaration; + int fieldDeclLineNo = fieldDeclaration.getLineNumber(); + if (fieldInitLineNo == fieldDeclLineNo || (fieldInit.initializers.isEmpty() && fieldDeclLineNo == 0)) { + fieldDeclaration.setLineNumber(fieldInitLineNo); + fieldInit.initializers.add(initializer); + fieldInit.statements.add((ExpressionStatement) node.getParent()); + } + } + } + } + return super.visitAssignmentExpression(node, data); + } + + @Override + public Void visitFieldDeclaration(FieldDeclaration node, Void data) { + FieldDefinition fieldDefinition = node.getUserData(Keys.FIELD_DEFINITION); + if (fieldDefinition != null) { + fieldDeclarations.put(fieldDefinition.getFullName(), new FieldInit(node)); + } + return super.visitFieldDeclaration(node, data); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, Void p) { + constructorCount++; + return super.visitConstructorDeclaration(node, p); + } + + @Override + public void run(AstNode compilationUnit) { + super.run(compilationUnit); + if (constructorCount > 0) { + for (FieldInit fieldInit : fieldDeclarations.values()) { + if (fieldInit.initializers.size() == constructorCount) { + fieldInit.removeInitializers(); + fieldInit.removeStatements(); + fieldInit.declaration.getVariables().clear(); + fieldInit.declaration.getVariables().add(new VariableInitializer(fieldInit.declaration.getName(), fieldInit.initializers.get(0))); + } + } + } + } + + private static class FieldInit { + private final FieldDeclaration declaration; + private final List initializers = new ArrayList<>(); + private final List statements = new ArrayList<>(); + + public FieldInit(FieldDeclaration declaration) { + this.declaration = declaration; + } + + public void removeStatements() { + for (ExpressionStatement expressionStatement : statements) { + expressionStatement.remove(); + } + } + + public void removeInitializers() { + for (Expression initializer : initializers) { + initializer.remove(); + } + } + } +} diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/TransformationPipeline.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/TransformationPipeline.java index 6a2ff63f..3750c7b1 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/TransformationPipeline.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/TransformationPipeline.java @@ -1,7 +1,7 @@ /* * TransformationPipeline.java * - * Copyright (c) 2013 Mike Strobel + * Copyright (c) 2013-2022 Mike Strobel and other contributors * * This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain; * and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa. @@ -76,7 +76,9 @@ public static IAstTransform[] createPipeline(final DecompilerContext context) { new AddStandardAnnotationsTransform(context), new AddReferenceQualifiersTransform(context), new RemoveHiddenMembersTransform(context), - new CollapseImportsTransform(context) + new CollapseImportsTransform(context), + new RewriteInitForLineStretchTransform(context), + new ReOrderMembersForLineStretchTransform(context) }; } diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/NameTests.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/NameTests.java index 5c1822f4..9640e53f 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/NameTests.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/NameTests.java @@ -149,6 +149,9 @@ public void testVariablesHideClasses() throws Throwable { A.class, defaultSettings(), "static final class A {\n" + + " static class x {\n" + + " static int y;\n" + + " }\n" + " static final class Inner {\n" + " void f(final int x, final Object Integer) {\n" + " final x z = new x();\n" + @@ -158,9 +161,6 @@ public void testVariablesHideClasses() throws Throwable { " System.out.println(Integer instanceof Integer);\n" + " }\n" + " }\n" + - " static class x {\n" + - " static int y;\n" + - " }\n" + "}\n" ); } @@ -171,6 +171,8 @@ public void testLocalImportedAndNestedTypeNameCollisions() throws Throwable { B.class, defaultSettings(), "static final class B {\n" + + " static class Integer {\n" + + " }\n" + " void f(final Object o) {\n" + " class Integer {\n" + " }\n" + @@ -184,8 +186,6 @@ public void testLocalImportedAndNestedTypeNameCollisions() throws Throwable { " System.out.println(java.lang.Integer.class);\n" + " }\n" + " }\n" + - " static class Integer {\n" + - " }\n" + "}\n" ); } @@ -197,13 +197,13 @@ public void testFieldHidesImportedTypeName() throws Throwable { defaultSettings(), "static final class C {\n" + " static float Float;\n" + + " static {\n" + + " C.Float = java.lang.Float.NaN;\n" + + " }\n" + " void f() {\n" + " System.out.println(C.Float);\n" + " System.out.println(java.lang.Float.NaN);\n" + " }\n" + - " static {\n" + - " C.Float = java.lang.Float.NaN;\n" + - " }\n" + "}\n" ); } @@ -214,13 +214,13 @@ public void testFormalParameterHidesNestedClass() throws Throwable { D.class, defaultSettings(), "static final class D {\n" + + " static class x {\n" + + " static int y;\n" + + " }\n" + " void f(final int x) {\n" + " System.out.println(x * 2);\n" + " System.out.println(D.x.y);\n" + " }\n" + - " static class x {\n" + - " static int y;\n" + - " }\n" + "}\n" ); } @@ -247,14 +247,14 @@ public void testMethodTypeParameterHidesInnerClass() throws Throwable { F.class, defaultSettings(), "static final class F {\n" + + " static class x {\n" + + " static int y;\n" + + " }\n" + " void f(final x z) {\n" + " System.out.println(z);\n" + " System.out.println(F.x.y);\n" + " }\n" + " \n" + - " static class x {\n" + - " static int y;\n" + - " }\n" + "}\n" ); } @@ -265,14 +265,14 @@ public void testClassTypeParameterHidesInnerClass() throws Throwable { G.class, defaultSettings(), "static final class G {\n" + + " static class x {\n" + + " static int y;\n" + + " }\n" + " void f(final x z) {\n" + " System.out.println(z);\n" + " System.out.println(G.x.y);\n" + " }\n" + " \n" + - " static class x {\n" + - " static int y;\n" + - " }\n" + "}\n" ); } @@ -284,12 +284,12 @@ public void testInnerClassHidesBaseInnerClass() throws Throwable { defaultSettings(), "static final class H extends DeclaresX {\n" + " X z;\n" + + " static class X {\n" + + " }\n" + " void f(final DeclaresX.X x) {\n" + " System.out.println(x);\n" + " System.out.println(this.z);\n" + " }\n" + - " static class X {\n" + - " }\n" + "}\n" ); } @@ -316,13 +316,13 @@ public void testInnerClassHidesBaseInnerClassAndInterfaceInnerClass() throws Thr defaultSettings(), "static final class J extends DeclaresX implements IDeclaresX {\n" + " X z;\n" + + " static class X {\n" + + " }\n" + " void f(final DeclaresX.X x, final IDeclaresX.X y) {\n" + " System.out.println(x);\n" + " System.out.println(y);\n" + " System.out.println(this.z);\n" + " }\n" + - " static class X {\n" + - " }\n" + "}\n" ); } diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/OperatorTests.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/OperatorTests.java index 5f45ec31..f24770e6 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/OperatorTests.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/OperatorTests.java @@ -72,11 +72,11 @@ private static class G { private int x; private static int y; private int[] a; - private static int[] b = new int[] { 0 }; + private static int[] b = new int[] { 1 }; private G() { super(); - this.a = new int[] { 0 }; + this.a = new int[] { 1 }; } public int f() { @@ -255,11 +255,13 @@ public void testPostIncrementOptimizations() { "private static class G {\n" + " private int x;\n" + " private static int y;\n" + - " private int[] a;\n" + " private static int[] b;\n" + + " static {\n" + + " G.b = new int[] { 1 };\n" + + " }\n" + " private G() {\n" + - " this.a = new int[] { 0 };\n" + " }\n" + + " private int[] a = { 1 };\n" + " public int f() {\n" + " return this.x++;\n" + " }\n" + @@ -269,9 +271,6 @@ public void testPostIncrementOptimizations() { " public int h(int n) {\n" + " return (++this.x + this.a[n++]) / (this.a[++n] + ++this.a[n]) * ++G.b[++G.y];\n" + " }\n" + - " static {\n" + - " G.b = new int[] { 0 };\n" + - " }\n" + "}\n" ); } diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/PrimitiveTests.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/PrimitiveTests.java index d1e208de..a7d69da4 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/PrimitiveTests.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/PrimitiveTests.java @@ -43,6 +43,13 @@ public void testFloatingPointPrecision() throws Throwable { " static double x;\n" + " static double y;\n" + " static float z;\n" + + " static {\n" + + " A.d = 2.7182818459;\n" + + " A.w = Double.POSITIVE_INFINITY;\n" + + " A.x = Double.NEGATIVE_INFINITY;\n" + + " A.y = -4.9E-324;\n" + + " A.z = Float.MIN_VALUE;\n" + + " }\n" + " public static strictfp void test() {\n" + " final double t = 9.007199254740992E15;\n" + " final double x = t * t;\n" + @@ -57,13 +64,6 @@ public void testFloatingPointPrecision() throws Throwable { " System.out.println(A.z);\n" + " System.out.println((double)A.z);\n" + " }\n" + - " static {\n" + - " A.d = 2.7182818459;\n" + - " A.w = Double.POSITIVE_INFINITY;\n" + - " A.x = Double.NEGATIVE_INFINITY;\n" + - " A.y = -4.9E-324;\n" + - " A.z = Float.MIN_VALUE;\n" + - " }\n" + "}" ); } diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java new file mode 100644 index 00000000..012a9b30 --- /dev/null +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java @@ -0,0 +1,239 @@ +package com.strobel.decompiler; + +import org.junit.Test; + +public class RealignTest extends DecompilerTest { + + static class Test1 { + + public void method1() { + System.out.println("Test.method1"); + } + + public void method2() { + System.out.println("Test.method2"); + } + + class Inner1 { + + public Inner1() { + System.out.println("Inner1 constructor"); + } + + public void method1() { + System.out.println("Inner1.method1"); + } + + public void method2() { + System.out.println("Inner1.method2"); + } + } + + class Inner2 { + + public Inner2() { + System.out.println("Inner2 constructor"); + } + } + + + public void method3() { + System.out.println("Test.method3"); + } + + public void method4() { + System.out.println("Test.method4"); + } + + class Inner3 { + + public Inner3() { + System.out.println("Inner3 constructor"); + } + + public void method1() { + System.out.println("Inner3.method1"); + } + + public void method2() { + System.out.println("Inner3.method2"); + } + } + + class Inner4 { + + public Inner4() { + System.out.println("Inner4 constructor"); + } + + public void method1() { + System.out.println("Inner4.method1"); + } + + public void method2() { + System.out.println("Inner4.method2"); + } + } + + public void method5() { + System.out.println("Test.method5"); + } + + public void method6() { + System.out.println("Test.method6"); + } + } + + static class Test2 { + + private Test2 top = new Test2(0); + private Test2 test; + + static { + System.out.println("clinit1"); + } + + public Test2(int i) { + System.out.println(i); + } + + private Test2 middle = new Test2(false); + + public Test2(boolean flag) { + System.out.println(flag); + test = new Test2(0); + } + + static { + System.out.println("clinit2"); + } + + private Test2 bottom = new Test2(true); + + static { + System.out.println("clinit3"); + } + } + + @Test + public void testReOrderMembers() throws Throwable { + verifyOutput( + Test1.class, + lineNumberSettings(), + "static class Test1 {\n" + + " public void method1() {\n" + + " System.out.println(/*EL:10*/\"Test.method1\");\n" + + " }\n" + + " \n" + + " public void method2() {\n" + + " System.out.println(/*EL:14*/\"Test.method2\");\n" + + " }\n" + + " \n" + + " class Inner1 {\n" + + " public Inner1() {\n" + + " System.out.println(/*EL:20*/\"Inner1 constructor\");\n" + + " }\n" + + " \n" + + " public void method1() {\n" + + " System.out.println(/*EL:24*/\"Inner1.method1\");\n" + + " }\n" + + " \n" + + " public void method2() {\n" + + " System.out.println(/*EL:28*/\"Inner1.method2\");\n" + + " }\n" + + " }\n" + + " \n" + + " class Inner2 {\n" + + " public Inner2() {\n" + + " System.out.println(/*EL:35*/\"Inner2 constructor\");\n" + + " }\n" + + " }\n" + + " \n" + + " public void method3() {\n" + + " System.out.println(/*EL:41*/\"Test.method3\");\n" + + " }\n" + + " \n" + + " public void method4() {\n" + + " System.out.println(/*EL:45*/\"Test.method4\");\n" + + " }\n" + + " \n" + + " class Inner3 {\n" + + " public Inner3() {\n" + + " System.out.println(/*EL:51*/\"Inner3 constructor\");\n" + + " }\n" + + " \n" + + " public void method1() {\n" + + " System.out.println(/*EL:55*/\"Inner3.method1\");\n" + + " }\n" + + " \n" + + " public void method2() {\n" + + " System.out.println(/*EL:59*/\"Inner3.method2\");\n" + + " }\n" + + " }\n" + + " \n" + + " class Inner4 {\n" + + " public Inner4() {\n" + + " System.out.println(/*EL:66*/\"Inner4 constructor\");\n" + + " }\n" + + " \n" + + " public void method1() {\n" + + " System.out.println(/*EL:70*/\"Inner4.method1\");\n" + + " }\n" + + " \n" + + " public void method2() {\n" + + " System.out.println(/*EL:74*/\"Inner4.method2\");\n" + + " }\n" + + " }\n" + + " \n" + + " public void method5() {\n" + + " System.out.println(/*EL:79*/\"Test.method5\");\n" + + " }\n" + + " \n" + + " public void method6() {\n" + + " System.out.println(/*EL:83*/\"Test.method6\");\n" + + " }\n" + + "}" + ); + } + + + @Test + public void testRewriteInit() throws Throwable { + verifyOutput( + Test2.class, + lineNumberSettings(), + "static class Test2 {\n" + + " /*SL:89*/private Test2 top = new Test2(0);\n" + + " static {\n" + + " System.out.println(/*EL:93*/\"clinit1\");\n" + + " }\n" + + " \n" + + " public Test2(final int i) {\n" + + " System.out.println(/*EL:97*/i);\n" + + " }\n" + + " \n" + + " /*SL:100*/private Test2 middle = new Test2(false);\n" + + " \n" + + " public Test2(final boolean flag) {\n" + + " System.out.println(/*EL:103*/flag);\n" + + " /*SL:104*/this.test = new Test2(0);\n" + + " }\n" + + " \n" + + " private Test2 test;\n" + + " static {\n" + + " System.out.println(/*EL:108*/\"clinit2\");\n" + + " }\n" + + " /*SL:111*/private Test2 bottom = new Test2(true);\n" + + " static {\n" + + " System.out.println(/*EL:114*/\"clinit3\");\n" + + " }\n" + + "}" + ); + } + + private static DecompilerSettings lineNumberSettings() { + DecompilerSettings lineNumberSettings = defaultSettings(); + lineNumberSettings.setShowDebugLineNumbers(true); + return lineNumberSettings; + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 93eef23b..abf5c9b3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Sun Oct 27 14:39:59 EDT 2019 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists From 051f8b1d28714419d91ec8d477e83ef0b36df7b4 Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Mon, 6 Jun 2022 20:07:29 +0200 Subject: [PATCH 02/14] revert gradle to 6.9 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index abf5c9b3..93eef23b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Sun Oct 27 14:39:59 EDT 2019 -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists From 7d63946fb027fe06f81f66e91eaf50a41bc8072c Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Mon, 6 Jun 2022 22:32:07 +0200 Subject: [PATCH 03/14] fix regressions --- .../languages/java/ast/MethodDeclaration.java | 14 +++++++++----- .../RewriteInitForLineStretchTransform.java | 12 +++++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/MethodDeclaration.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/MethodDeclaration.java index 8be93db5..c81ac700 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/MethodDeclaration.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/MethodDeclaration.java @@ -25,10 +25,10 @@ import com.strobel.decompiler.patterns.Role; public class MethodDeclaration extends EntityDeclaration { - public final static Role DEFAULT_VALUE_ROLE = new Role<>("DefaultValue", Expression.class, Expression.NULL); + public static final Role DEFAULT_VALUE_ROLE = new Role<>("DefaultValue", Expression.class, Expression.NULL); - public final static TokenRole DEFAULT_KEYWORD = new TokenRole("default", TokenRole.FLAG_KEYWORD); - public final static TokenRole THROWS_KEYWORD = new TokenRole("throws", TokenRole.FLAG_KEYWORD); + public static final TokenRole DEFAULT_KEYWORD = new TokenRole("default", TokenRole.FLAG_KEYWORD); + public static final TokenRole THROWS_KEYWORD = new TokenRole("throws", TokenRole.FLAG_KEYWORD); private int firstKnownLineNumber; @@ -114,9 +114,13 @@ public boolean matches(final INode other, final Match match) { return false; } + public boolean isFirstLineNumberKnown() { + return firstKnownLineNumber > 0; + } + @Override public int getFirstKnownLineNumber() { - if (firstKnownLineNumber > 0) { + if (isFirstLineNumberKnown()) { return firstKnownLineNumber; } return super.getFirstKnownLineNumber(); @@ -132,7 +136,7 @@ public static MethodDeclaration forPattern(final Pattern pattern) { return new MethodDeclaration.PatternPlaceholder(VerifyArgument.notNull(pattern, "pattern")); } - private final static class PatternPlaceholder extends MethodDeclaration { + private static final class PatternPlaceholder extends MethodDeclaration { final Pattern child; PatternPlaceholder(final Pattern child) { diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java index d2407c99..fcc7347a 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -22,11 +22,11 @@ import com.strobel.assembler.metadata.FieldDefinition; import com.strobel.assembler.metadata.MemberReference; import com.strobel.assembler.metadata.MethodDefinition; -import com.strobel.assembler.metadata.ParameterDefinition; import com.strobel.decompiler.DecompilerContext; import com.strobel.decompiler.languages.java.LineNumberTableConverter; import com.strobel.decompiler.languages.java.MinMaxLineNumberVisitor; import com.strobel.decompiler.languages.java.ast.AssignmentExpression; +import com.strobel.decompiler.languages.java.ast.AssignmentOperatorType; import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.BlockStatement; import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; @@ -71,6 +71,9 @@ public Void visitBlockStatement(BlockStatement node, Void data) { MinMaxLineNumberVisitor minMaxLineNumberVisitor = new MinMaxLineNumberVisitor(lineNumberTableConverter); child.acceptVisitor(minMaxLineNumberVisitor, null); int currentLineNumber = minMaxLineNumberVisitor.getMinLineNumber(); + if (!methodDeclaration.isFirstLineNumberKnown()) { + methodDeclaration.setFirstKnownLineNumber(currentLineNumber); + } if (previousLineNumber > 0 && currentLineNumber > previousLineNumber + 3) { newMethodDeclaration = (MethodDeclaration) methodDeclaration.clone(); newMethodDeclaration.setFirstKnownLineNumber(currentLineNumber); @@ -113,8 +116,11 @@ public Void visitBlockStatement(BlockStatement node, Void data) { @Override public Void visitAssignmentExpression(AssignmentExpression node, Void data) { AstNode parent = node.getParent(); - if (parent instanceof ExpressionStatement && parent.getParent() instanceof BlockStatement && parent.getParent().getParent() instanceof ConstructorDeclaration - && node.getLeft() instanceof MemberReferenceExpression) { + if (parent instanceof ExpressionStatement + && parent.getParent() instanceof BlockStatement + && parent.getParent().getParent() instanceof ConstructorDeclaration + && node.getLeft() instanceof MemberReferenceExpression + && node.getOperator() == AssignmentOperatorType.ASSIGN) { ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) parent.getParent().getParent(); MethodDefinition constructor = constructorDeclaration.getUserData(Keys.METHOD_DEFINITION); final LineNumberTableAttribute lineNumberTable = SourceAttribute.find(AttributeNames.LineNumberTable, From ca11cbc3f123667885f2fad330bbf11ec865d368 Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Wed, 8 Jun 2022 21:44:27 +0200 Subject: [PATCH 04/14] move static block initializations to declarations where possible --- .../RewriteInitForLineStretchTransform.java | 72 +++++++++++-------- .../com/strobel/decompiler/EncodingTests.java | 5 +- .../com/strobel/decompiler/NameTests.java | 5 +- .../com/strobel/decompiler/OperatorTests.java | 5 +- .../strobel/decompiler/PrimitiveTests.java | 17 ++--- 5 files changed, 51 insertions(+), 53 deletions(-) diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java index fcc7347a..60c9c007 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -31,6 +31,7 @@ import com.strobel.decompiler.languages.java.ast.BlockStatement; import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor; +import com.strobel.decompiler.languages.java.ast.EntityDeclaration; import com.strobel.decompiler.languages.java.ast.Expression; import com.strobel.decompiler.languages.java.ast.ExpressionStatement; import com.strobel.decompiler.languages.java.ast.FieldDeclaration; @@ -41,7 +42,6 @@ import com.strobel.decompiler.languages.java.ast.VariableInitializer; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -118,27 +118,29 @@ public Void visitAssignmentExpression(AssignmentExpression node, Void data) { AstNode parent = node.getParent(); if (parent instanceof ExpressionStatement && parent.getParent() instanceof BlockStatement - && parent.getParent().getParent() instanceof ConstructorDeclaration + && parent.getParent().getParent() instanceof EntityDeclaration && node.getLeft() instanceof MemberReferenceExpression && node.getOperator() == AssignmentOperatorType.ASSIGN) { - ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) parent.getParent().getParent(); - MethodDefinition constructor = constructorDeclaration.getUserData(Keys.METHOD_DEFINITION); - final LineNumberTableAttribute lineNumberTable = SourceAttribute.find(AttributeNames.LineNumberTable, - constructor != null ? constructor.getSourceAttributes() : Collections.emptyList()); - LineNumberTableConverter lineNumberTableConverter = new LineNumberTableConverter(lineNumberTable); - MemberReferenceExpression memberReferenceExpression = (MemberReferenceExpression) node.getFirstChild(); - MemberReference memberReference = memberReferenceExpression.getUserData(Keys.MEMBER_REFERENCE); - FieldInit fieldInit = fieldDeclarations.get(memberReference.getFullName()); - if (fieldInit != null && (constructor == null || !constructor.hasParameter(memberReference.getName()))) { - Expression initializer = node.getRight(); - int fieldInitLineNo = lineNumberTableConverter.getLineForOffset(initializer.getOffset()); - if (fieldInitLineNo > 0) { - FieldDeclaration fieldDeclaration = fieldInit.declaration; - int fieldDeclLineNo = fieldDeclaration.getLineNumber(); - if (fieldInitLineNo == fieldDeclLineNo || (fieldInit.initializers.isEmpty() && fieldDeclLineNo == 0)) { - fieldDeclaration.setLineNumber(fieldInitLineNo); - fieldInit.initializers.add(initializer); - fieldInit.statements.add((ExpressionStatement) node.getParent()); + EntityDeclaration entityDeclaration = (EntityDeclaration) parent.getParent().getParent(); + MethodDefinition methodDefinition = entityDeclaration.getUserData(Keys.METHOD_DEFINITION); + if (methodDefinition != null && (methodDefinition.isConstructor() || methodDefinition.isTypeInitializer())) { + LineNumberTableAttribute lineNumberTable = SourceAttribute.find(AttributeNames.LineNumberTable, methodDefinition.getSourceAttributes()); + LineNumberTableConverter lineNumberTableConverter = new LineNumberTableConverter(lineNumberTable); + MemberReferenceExpression memberReferenceExpression = (MemberReferenceExpression) node.getFirstChild(); + MemberReference memberReference = memberReferenceExpression.getUserData(Keys.MEMBER_REFERENCE); + FieldInit fieldInit = fieldDeclarations.get(memberReference.getFullName()); + if (fieldInit != null && !methodDefinition.hasParameter(memberReference.getName())) { + Expression initializer = node.getRight(); + int fieldInitLineNo = lineNumberTableConverter.getLineForOffset(initializer.getOffset()); + if (fieldInitLineNo > 0) { + FieldDeclaration fieldDeclaration = fieldInit.declaration; + int fieldDeclLineNo = fieldDeclaration.getLineNumber(); + if (fieldInitLineNo == fieldDeclLineNo || (fieldInit.initializers.isEmpty() && fieldDeclLineNo == 0)) { + fieldDeclaration.setLineNumber(fieldInitLineNo); + fieldInit.initializers.add(initializer); + fieldInit.statements.add((ExpressionStatement) node.getParent()); + fieldInit.initMethod = methodDefinition; + } } } } @@ -164,31 +166,35 @@ public Void visitConstructorDeclaration(ConstructorDeclaration node, Void p) { @Override public void run(AstNode compilationUnit) { super.run(compilationUnit); - if (constructorCount > 0) { - for (FieldInit fieldInit : fieldDeclarations.values()) { - if (fieldInit.initializers.size() == constructorCount) { - fieldInit.removeInitializers(); - fieldInit.removeStatements(); - fieldInit.declaration.getVariables().clear(); - fieldInit.declaration.getVariables().add(new VariableInitializer(fieldInit.declaration.getName(), fieldInit.initializers.get(0))); - } + for (FieldInit fieldInit : fieldDeclarations.values()) { + if (fieldInit.isInAllConstructors() || fieldInit.isInStaticBlock()) { + fieldInit.removeInitializers(); + fieldInit.removeStatements(); + fieldInit.declaration.getVariables().clear(); + fieldInit.declaration.getVariables().add(new VariableInitializer(fieldInit.declaration.getName(), fieldInit.initializers.get(0))); } } } - private static class FieldInit { + private class FieldInit { private final FieldDeclaration declaration; private final List initializers = new ArrayList<>(); private final List statements = new ArrayList<>(); + private MethodDefinition initMethod; public FieldInit(FieldDeclaration declaration) { this.declaration = declaration; } public void removeStatements() { + AstNode parent = null; for (ExpressionStatement expressionStatement : statements) { + parent = expressionStatement.getParent(); expressionStatement.remove(); } + if (isInStaticBlock() && parent != null && !parent.hasChildren() && parent.getParent() instanceof MethodDeclaration) { + parent.getParent().remove(); + } } public void removeInitializers() { @@ -196,5 +202,13 @@ public void removeInitializers() { initializer.remove(); } } + + public boolean isInAllConstructors() { + return initMethod != null && initMethod.isConstructor() && constructorCount > 0 && initializers.size() == constructorCount; + } + + public boolean isInStaticBlock() { + return initMethod != null && initMethod.isTypeInitializer(); + } } } diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/EncodingTests.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/EncodingTests.java index f0f7d80f..c99b7ea2 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/EncodingTests.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/EncodingTests.java @@ -61,7 +61,6 @@ public void testUnicodeIdentifierEscaping() { A.class, defaultSettings(), "private static final class A {\n" + - " static String \\ufe4f\\u2167;\n" + " static final transient short x\\u03a7x = 5;\n" + " private static String __\\u0130\\u00dfI(final A x) {\n" + " return A.\\ufe4f\\u2167;\n" + @@ -70,9 +69,7 @@ public void testUnicodeIdentifierEscaping() { " System.out.println(__\\u0130\\u00dfI(null));\n" + " System.out.println(\"\\\"\\u0000\\u000fu\\\\\\\"\\ff'\\rr'\\nn \\u0123\\u1234O\\uffffF\");\n" + " }\n" + - " static {\n" + - " A.\\ufe4f\\u2167 = \"\\ufeff\\ud800\\ud8d8\\udffd\";\n" + - " }\n" + + " static String \\ufe4f\\u2167 = \"\\ufeff\\ud800\\ud8d8\\udffd\";\n" + "}" ); } diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/NameTests.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/NameTests.java index 9640e53f..2f4e73d2 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/NameTests.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/NameTests.java @@ -196,10 +196,7 @@ public void testFieldHidesImportedTypeName() throws Throwable { C.class, defaultSettings(), "static final class C {\n" + - " static float Float;\n" + - " static {\n" + - " C.Float = java.lang.Float.NaN;\n" + - " }\n" + + " static float Float = java.lang.Float.NaN;\n" + " void f() {\n" + " System.out.println(C.Float);\n" + " System.out.println(java.lang.Float.NaN);\n" + diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/OperatorTests.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/OperatorTests.java index f24770e6..fd85b9db 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/OperatorTests.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/OperatorTests.java @@ -255,10 +255,7 @@ public void testPostIncrementOptimizations() { "private static class G {\n" + " private int x;\n" + " private static int y;\n" + - " private static int[] b;\n" + - " static {\n" + - " G.b = new int[] { 1 };\n" + - " }\n" + + " private static int[] b = { 1 };\n" + " private G() {\n" + " }\n" + " private int[] a = { 1 };\n" + diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/PrimitiveTests.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/PrimitiveTests.java index a7d69da4..a3c6c5aa 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/PrimitiveTests.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/PrimitiveTests.java @@ -38,18 +38,11 @@ public void testFloatingPointPrecision() throws Throwable { defaultSettings(), "private static final class A {\n" + " static final float e = 2.7182817f;\n" + - " static double d;\n" + - " static double w;\n" + - " static double x;\n" + - " static double y;\n" + - " static float z;\n" + - " static {\n" + - " A.d = 2.7182818459;\n" + - " A.w = Double.POSITIVE_INFINITY;\n" + - " A.x = Double.NEGATIVE_INFINITY;\n" + - " A.y = -4.9E-324;\n" + - " A.z = Float.MIN_VALUE;\n" + - " }\n" + + " static double d = 2.7182818459;\n" + + " static double w = Double.POSITIVE_INFINITY;\n" + + " static double x = Double.NEGATIVE_INFINITY;\n" + + " static double y = -4.9E-324;\n" + + " static float z = Float.MIN_VALUE;\n" + " public static strictfp void test() {\n" + " final double t = 9.007199254740992E15;\n" + " final double x = t * t;\n" + From ef9c28fa6dca41d74e6fc1ed278e99cc61972e63 Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Sun, 12 Jun 2022 08:52:15 +0200 Subject: [PATCH 05/14] refactoring RewriteInitForLineStretchTransform --- .../RewriteInitForLineStretchTransform.java | 131 ++++++++++++------ 1 file changed, 85 insertions(+), 46 deletions(-) diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java index 60c9c007..b3884dfc 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -42,13 +42,14 @@ import com.strobel.decompiler.languages.java.ast.VariableInitializer; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; public class RewriteInitForLineStretchTransform extends ContextTrackingVisitor { - private Map fieldDeclarations = new HashMap<>(); + private ConcurrentHashMap fieldDeclarations = new ConcurrentHashMap<>(); + private ConcurrentHashMap fieldInitLocations = new ConcurrentHashMap<>(); private int constructorCount; public RewriteInitForLineStretchTransform(DecompilerContext context) { @@ -116,11 +117,8 @@ public Void visitBlockStatement(BlockStatement node, Void data) { @Override public Void visitAssignmentExpression(AssignmentExpression node, Void data) { AstNode parent = node.getParent(); - if (parent instanceof ExpressionStatement - && parent.getParent() instanceof BlockStatement - && parent.getParent().getParent() instanceof EntityDeclaration - && node.getLeft() instanceof MemberReferenceExpression - && node.getOperator() == AssignmentOperatorType.ASSIGN) { + if (parent instanceof ExpressionStatement && parent.getParent() instanceof BlockStatement && parent.getParent().getParent() instanceof EntityDeclaration + && node.getLeft() instanceof MemberReferenceExpression && node.getOperator() == AssignmentOperatorType.ASSIGN) { EntityDeclaration entityDeclaration = (EntityDeclaration) parent.getParent().getParent(); MethodDefinition methodDefinition = entityDeclaration.getUserData(Keys.METHOD_DEFINITION); if (methodDefinition != null && (methodDefinition.isConstructor() || methodDefinition.isTypeInitializer())) { @@ -128,20 +126,15 @@ public Void visitAssignmentExpression(AssignmentExpression node, Void data) { LineNumberTableConverter lineNumberTableConverter = new LineNumberTableConverter(lineNumberTable); MemberReferenceExpression memberReferenceExpression = (MemberReferenceExpression) node.getFirstChild(); MemberReference memberReference = memberReferenceExpression.getUserData(Keys.MEMBER_REFERENCE); - FieldInit fieldInit = fieldDeclarations.get(memberReference.getFullName()); - if (fieldInit != null && !methodDefinition.hasParameter(memberReference.getName())) { - Expression initializer = node.getRight(); - int fieldInitLineNo = lineNumberTableConverter.getLineForOffset(initializer.getOffset()); - if (fieldInitLineNo > 0) { - FieldDeclaration fieldDeclaration = fieldInit.declaration; - int fieldDeclLineNo = fieldDeclaration.getLineNumber(); - if (fieldInitLineNo == fieldDeclLineNo || (fieldInit.initializers.isEmpty() && fieldDeclLineNo == 0)) { - fieldDeclaration.setLineNumber(fieldInitLineNo); - fieldInit.initializers.add(initializer); - fieldInit.statements.add((ExpressionStatement) node.getParent()); - fieldInit.initMethod = methodDefinition; - } - } + FieldDeclaration fieldDeclaration = fieldDeclarations.get(memberReference.getFullName()); + Expression initializer = node.getRight(); + int offset = initializer.getOffset(); + int lineNumber = lineNumberTableConverter.getLineForOffset(offset); + if (lineNumber > 0 && fieldDeclaration != null && !methodDefinition.hasParameter(memberReference.getName())) { + fieldDeclaration.setLineNumber(lineNumber); + FieldLocation fieldLocation = new FieldLocation(memberReference.getFullName(), offset); + fieldInitLocations.putIfAbsent(fieldLocation, new FieldInit(fieldDeclaration)); + fieldInitLocations.get(fieldLocation).init(initializer, (ExpressionStatement) node.getParent(), methodDefinition); } } } @@ -152,7 +145,7 @@ public Void visitAssignmentExpression(AssignmentExpression node, Void data) { public Void visitFieldDeclaration(FieldDeclaration node, Void data) { FieldDefinition fieldDefinition = node.getUserData(Keys.FIELD_DEFINITION); if (fieldDefinition != null) { - fieldDeclarations.put(fieldDefinition.getFullName(), new FieldInit(node)); + fieldDeclarations.put(fieldDefinition.getFullName(), node); } return super.visitFieldDeclaration(node, data); } @@ -166,49 +159,95 @@ public Void visitConstructorDeclaration(ConstructorDeclaration node, Void p) { @Override public void run(AstNode compilationUnit) { super.run(compilationUnit); - for (FieldInit fieldInit : fieldDeclarations.values()) { - if (fieldInit.isInAllConstructors() || fieldInit.isInStaticBlock()) { - fieldInit.removeInitializers(); - fieldInit.removeStatements(); - fieldInit.declaration.getVariables().clear(); - fieldInit.declaration.getVariables().add(new VariableInitializer(fieldInit.declaration.getName(), fieldInit.initializers.get(0))); + for (FieldInit fieldInit : fieldInitLocations.values()) { + if (fieldInit.isInAllConstructors() || fieldInit.isInTypeInitializer()) { + fieldInit.removeFieldInitStatements(); + fieldInit.createVariableInitializer(); } } } private class FieldInit { private final FieldDeclaration declaration; - private final List initializers = new ArrayList<>(); - private final List statements = new ArrayList<>(); - private MethodDefinition initMethod; + private final List fieldInitStatements = new ArrayList<>(); + private boolean inConstructor = true; + private boolean inTypeInitializer = true; public FieldInit(FieldDeclaration declaration) { this.declaration = declaration; } - public void removeStatements() { + public void init(Expression initializer, ExpressionStatement expressionStatement, MethodDefinition initMethod) { + inConstructor &= initMethod != null && initMethod.isConstructor(); + inTypeInitializer &= initMethod != null && initMethod.isTypeInitializer(); + fieldInitStatements.add(new FieldInitStatement(initializer, expressionStatement)); + } + + public void removeFieldInitStatements() { AstNode parent = null; - for (ExpressionStatement expressionStatement : statements) { - parent = expressionStatement.getParent(); - expressionStatement.remove(); + for (FieldInitStatement fieldInitStatement : fieldInitStatements) { + parent = fieldInitStatement.statement.getParent(); + fieldInitStatement.remove(); } - if (isInStaticBlock() && parent != null && !parent.hasChildren() && parent.getParent() instanceof MethodDeclaration) { + if (isInTypeInitializer() && parent != null && !parent.hasChildren() && parent.getParent() instanceof MethodDeclaration) { parent.getParent().remove(); } } - public void removeInitializers() { - for (Expression initializer : initializers) { - initializer.remove(); - } - } - public boolean isInAllConstructors() { - return initMethod != null && initMethod.isConstructor() && constructorCount > 0 && initializers.size() == constructorCount; + return inConstructor && constructorCount > 0 && fieldInitStatements.size() == constructorCount; } - public boolean isInStaticBlock() { - return initMethod != null && initMethod.isTypeInitializer(); + public boolean isInTypeInitializer() { + return inTypeInitializer; } + + public void createVariableInitializer() { + declaration.getVariables().clear(); + declaration.getVariables().add(new VariableInitializer(declaration.getName(), fieldInitStatements.get(0).initializer)); + } + } + + private static class FieldInitStatement { + private final Expression initializer; + private final ExpressionStatement statement; + + public FieldInitStatement(Expression initializer, ExpressionStatement statement) { + this.initializer = initializer; + this.statement = statement; + } + + public void remove() { + initializer.remove(); + statement.remove(); + } + } + + private static class FieldLocation { + private final String fieldName; + private final int fieldOffset; + + public FieldLocation(String fieldName, int fieldOffset) { + this.fieldName = fieldName; + this.fieldOffset = fieldOffset; + } + + @Override + public int hashCode() { + return Objects.hash(fieldName, fieldOffset); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + FieldLocation other = (FieldLocation) obj; + return Objects.equals(fieldName, other.fieldName) && fieldOffset == other.fieldOffset; + } + } } From 2312d2a366c0a807c5ff51d2a8e6b8a71758765b Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Sun, 12 Jun 2022 09:02:41 +0200 Subject: [PATCH 06/14] reformat RewriteInitForLineStretchTransform --- .../ast/transforms/RewriteInitForLineStretchTransform.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java index b3884dfc..d109c51e 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -117,8 +117,11 @@ public Void visitBlockStatement(BlockStatement node, Void data) { @Override public Void visitAssignmentExpression(AssignmentExpression node, Void data) { AstNode parent = node.getParent(); - if (parent instanceof ExpressionStatement && parent.getParent() instanceof BlockStatement && parent.getParent().getParent() instanceof EntityDeclaration - && node.getLeft() instanceof MemberReferenceExpression && node.getOperator() == AssignmentOperatorType.ASSIGN) { + if (parent instanceof ExpressionStatement + && parent.getParent() instanceof BlockStatement + && parent.getParent().getParent() instanceof EntityDeclaration + && node.getLeft() instanceof MemberReferenceExpression + && node.getOperator() == AssignmentOperatorType.ASSIGN) { EntityDeclaration entityDeclaration = (EntityDeclaration) parent.getParent().getParent(); MethodDefinition methodDefinition = entityDeclaration.getUserData(Keys.METHOD_DEFINITION); if (methodDefinition != null && (methodDefinition.isConstructor() || methodDefinition.isTypeInitializer())) { From 0d26e55211ac97b70ed175bc31711a028ecc6b21 Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Sun, 12 Jun 2022 12:09:05 +0200 Subject: [PATCH 07/14] handled new case and added new unit test for this(...) call --- .../assembler/metadata/MethodDefinition.java | 18 ++--- .../RewriteInitForLineStretchTransform.java | 48 +++++++++++-- .../com/strobel/decompiler/RealignTest.java | 67 ++++++++++++++++++- 3 files changed, 117 insertions(+), 16 deletions(-) diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/assembler/metadata/MethodDefinition.java b/Procyon.CompilerTools/src/main/java/com/strobel/assembler/metadata/MethodDefinition.java index c4a5d223..74e52a9e 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/assembler/metadata/MethodDefinition.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/assembler/metadata/MethodDefinition.java @@ -219,15 +219,17 @@ public int hashCode() { @Override public boolean equals(final Object obj) { - if (obj instanceof MethodDefinition) { - final MethodDefinition other = (MethodDefinition) obj; - - return StringUtilities.equals(getName(), other.getName()) && - StringUtilities.equals(getErasedSignature(), other.getErasedSignature()) && - typeNamesMatch(getDeclaringType(), other.getDeclaringType()); + if (obj == this) { + return true; + } + if (obj == null || obj.getClass() != getClass()) { + return false; } + final MethodDefinition other = (MethodDefinition) obj; - return false; + return StringUtilities.equals(getName(), other.getName()) && + StringUtilities.equals(getErasedSignature(), other.getErasedSignature()) && + typeNamesMatch(getDeclaringType(), other.getDeclaringType()); } @Override @@ -236,7 +238,7 @@ public void invalidateSignature() { _erasedSignature = null; } - private boolean typeNamesMatch(final TypeReference t1, final TypeReference t2) { + private static boolean typeNamesMatch(final TypeReference t1, final TypeReference t2) { return t1 != null && t2 != null && StringUtilities.equals(t1.getFullName(), t2.getFullName()); diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java index d109c51e..b29e203b 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -35,10 +35,12 @@ import com.strobel.decompiler.languages.java.ast.Expression; import com.strobel.decompiler.languages.java.ast.ExpressionStatement; import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; import com.strobel.decompiler.languages.java.ast.Keys; import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; import com.strobel.decompiler.languages.java.ast.MethodDeclaration; import com.strobel.decompiler.languages.java.ast.Roles; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; import com.strobel.decompiler.languages.java.ast.VariableInitializer; import java.util.ArrayList; @@ -50,8 +52,8 @@ public class RewriteInitForLineStretchTransform extends ContextTrackingVisitor fieldDeclarations = new ConcurrentHashMap<>(); private ConcurrentHashMap fieldInitLocations = new ConcurrentHashMap<>(); - private int constructorCount; - + private ConcurrentHashMap constructionDefinitionToDeclaration = new ConcurrentHashMap<>(); + public RewriteInitForLineStretchTransform(DecompilerContext context) { super(context); } @@ -154,13 +156,30 @@ public Void visitFieldDeclaration(FieldDeclaration node, Void data) { } @Override - public Void visitConstructorDeclaration(ConstructorDeclaration node, Void p) { - constructorCount++; - return super.visitConstructorDeclaration(node, p); + public Void visitThisReferenceExpression(ThisReferenceExpression node, Void data) { + if (node.getParent() instanceof InvocationExpression) { + MemberReference memberReference = node.getParent().getUserData(Keys.MEMBER_REFERENCE); + if (memberReference instanceof MethodDefinition) { + MethodDefinition methodDefinition = (MethodDefinition) memberReference; + ConstructorDeclaration constructorDeclaration = constructionDefinitionToDeclaration.get(methodDefinition); + if (constructorDeclaration != null) { + MethodDefinition currentMethodDefinition = context.getCurrentMethod(); + try { + context.setCurrentMethod(null); + visitConstructorDeclaration(constructorDeclaration, data); + } + finally { + context.setCurrentMethod(currentMethodDefinition); + } + } + } + } + return super.visitThisReferenceExpression(node, data); } - + @Override public void run(AstNode compilationUnit) { + new ConstructorGatherer(context).run(compilationUnit); super.run(compilationUnit); for (FieldInit fieldInit : fieldInitLocations.values()) { if (fieldInit.isInAllConstructors() || fieldInit.isInTypeInitializer()) { @@ -198,7 +217,7 @@ public void removeFieldInitStatements() { } public boolean isInAllConstructors() { - return inConstructor && constructorCount > 0 && fieldInitStatements.size() == constructorCount; + return inConstructor && !constructionDefinitionToDeclaration.isEmpty() && fieldInitStatements.size() == constructionDefinitionToDeclaration.size(); } public boolean isInTypeInitializer() { @@ -251,6 +270,21 @@ public boolean equals(Object obj) { FieldLocation other = (FieldLocation) obj; return Objects.equals(fieldName, other.fieldName) && fieldOffset == other.fieldOffset; } + } + + private class ConstructorGatherer extends ContextTrackingVisitor { + protected ConstructorGatherer(DecompilerContext context) { + super(context); + } + + @Override + public Void visitConstructorDeclaration(ConstructorDeclaration node, Void p) { + MethodDefinition methodDefinition = node.getUserData(Keys.METHOD_DEFINITION); + if (methodDefinition != null) { + constructionDefinitionToDeclaration.put(methodDefinition, node); + } + return super.visitConstructorDeclaration(node, p); + } } } diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java index 012a9b30..31eea034 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java @@ -101,7 +101,7 @@ public Test2(int i) { public Test2(boolean flag) { System.out.println(flag); - test = new Test2(0); + test = new Test2(0); // must not be moved to declaration } static { @@ -115,6 +115,38 @@ public Test2(boolean flag) { } } + static class Test3 { + + private Test3 top = new Test3(0); + private Test3 test; + + static { + System.out.println("clinit1"); + } + + public Test3(int i) { + this(false); + System.out.println(i); + } + + private Test3 middle = new Test3(false); + + public Test3(boolean flag) { + System.out.println(flag); + test = new Test3(0); // can be moved to declaration (because of this(...) call) + } + + static { + System.out.println("clinit2"); + } + + private Test3 bottom = new Test3(true); + + static { + System.out.println("clinit3"); + } + } + @Test public void testReOrderMembers() throws Throwable { verifyOutput( @@ -231,6 +263,39 @@ public void testRewriteInit() throws Throwable { ); } + @Test + public void testRewriteInit2() throws Throwable { + verifyOutput( + Test3.class, + lineNumberSettings(), + "static class Test3 {\n" + + " /*SL:120*/private Test3 top = new Test3(0);\n" + + " static {\n" + + " System.out.println(/*EL:124*/\"clinit1\");\n" + + " }\n" + + " \n" + + " public Test3(final int i) {\n" + + " /*SL:128*/this(false);\n" + + " System.out.println(/*EL:129*/i);\n" + + " }\n" + + " \n" + + " /*SL:132*/private Test3 middle = new Test3(false);\n" + + " \n" + + " public Test3(final boolean flag) {\n" + + " System.out.println(/*EL:135*/flag);\n" + + " }\n" + + " /*SL:136*/private Test3 test = new Test3(0);\n" + + " static {\n" + + " System.out.println(/*EL:140*/\"clinit2\");\n" + + " }\n" + + " /*SL:143*/private Test3 bottom = new Test3(true);\n" + + " static {\n" + + " System.out.println(/*EL:146*/\"clinit3\");\n" + + " }\n" + + "}" + ); + } + private static DecompilerSettings lineNumberSettings() { DecompilerSettings lineNumberSettings = defaultSettings(); lineNumberSettings.setShowDebugLineNumbers(true); From c64b5c5390279d8fa388d64437149fa533b6a5b6 Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Sun, 12 Jun 2022 13:51:35 +0200 Subject: [PATCH 08/14] fix: count only constructors of declaring type --- .../RewriteInitForLineStretchTransform.java | 17 +++- .../com/strobel/decompiler/RealignTest.java | 83 +++++++++++-------- 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java index b29e203b..c3240cd8 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -22,6 +22,7 @@ import com.strobel.assembler.metadata.FieldDefinition; import com.strobel.assembler.metadata.MemberReference; import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.decompiler.DecompilerContext; import com.strobel.decompiler.languages.java.LineNumberTableConverter; import com.strobel.decompiler.languages.java.MinMaxLineNumberVisitor; @@ -217,7 +218,21 @@ public void removeFieldInitStatements() { } public boolean isInAllConstructors() { - return inConstructor && !constructionDefinitionToDeclaration.isEmpty() && fieldInitStatements.size() == constructionDefinitionToDeclaration.size(); + int constructorCount = countConstructors(); + return inConstructor && constructorCount > 0 && fieldInitStatements.size() == constructorCount; + } + + private int countConstructors() { + int constructorCount = 0; + FieldDefinition fieldDefinition = declaration.getUserData(Keys.FIELD_DEFINITION); + if (fieldDefinition != null) { + for (MethodDefinition methodDefinition : constructionDefinitionToDeclaration.keySet()) { + if (methodDefinition.getDeclaringType() == fieldDefinition.getDeclaringType()) { + constructorCount++; + } + } + } + return constructorCount; } public boolean isInTypeInitializer() { diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java index 31eea034..cbf95f54 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java @@ -4,7 +4,7 @@ public class RealignTest extends DecompilerTest { - static class Test1 { + static class ReOrderMembers { public void method1() { System.out.println("Test.method1"); @@ -84,75 +84,81 @@ public void method6() { } } - static class Test2 { + static class RewriteInit { - private Test2 top = new Test2(0); - private Test2 test; + private RewriteInit top = new RewriteInit(0); + private RewriteInit test; static { System.out.println("clinit1"); } - public Test2(int i) { + public RewriteInit(int i) { System.out.println(i); } - private Test2 middle = new Test2(false); + private RewriteInit middle = new RewriteInit(false); - public Test2(boolean flag) { + public RewriteInit(boolean flag) { System.out.println(flag); - test = new Test2(0); // must not be moved to declaration + test = new RewriteInit(0); // must not be moved to declaration } static { System.out.println("clinit2"); } - private Test2 bottom = new Test2(true); + private RewriteInit bottom = new RewriteInit(true); static { System.out.println("clinit3"); } } - static class Test3 { + static class RewriteInit2 { - private Test3 top = new Test3(0); - private Test3 test; + private RewriteInit2 top = new RewriteInit2(0); + private RewriteInit2 test; static { System.out.println("clinit1"); } - public Test3(int i) { + public RewriteInit2(int i) { this(false); System.out.println(i); } - private Test3 middle = new Test3(false); + private RewriteInit2 middle = new RewriteInit2(false); - public Test3(boolean flag) { + public RewriteInit2(boolean flag) { System.out.println(flag); - test = new Test3(0); // can be moved to declaration (because of this(...) call) + test = new RewriteInit2(0); // can be moved to declaration (because of this(...) call) } static { System.out.println("clinit2"); } - private Test3 bottom = new Test3(true); + private RewriteInit2 bottom = new RewriteInit2(true); static { System.out.println("clinit3"); } + + class Inner { + Inner() { + System.out.println("Inner"); + } + } } @Test public void testReOrderMembers() throws Throwable { verifyOutput( - Test1.class, + ReOrderMembers.class, lineNumberSettings(), - "static class Test1 {\n" + + "static class ReOrderMembers {\n" + " public void method1() {\n" + " System.out.println(/*EL:10*/\"Test.method1\");\n" + " }\n" + @@ -232,30 +238,30 @@ public void testReOrderMembers() throws Throwable { @Test public void testRewriteInit() throws Throwable { verifyOutput( - Test2.class, + RewriteInit.class, lineNumberSettings(), - "static class Test2 {\n" + - " /*SL:89*/private Test2 top = new Test2(0);\n" + + "static class RewriteInit {\n" + + " /*SL:89*/private RewriteInit top = new RewriteInit(0);\n" + " static {\n" + " System.out.println(/*EL:93*/\"clinit1\");\n" + " }\n" + " \n" + - " public Test2(final int i) {\n" + + " public RewriteInit(final int i) {\n" + " System.out.println(/*EL:97*/i);\n" + " }\n" + " \n" + - " /*SL:100*/private Test2 middle = new Test2(false);\n" + + " /*SL:100*/private RewriteInit middle = new RewriteInit(false);\n" + " \n" + - " public Test2(final boolean flag) {\n" + + " public RewriteInit(final boolean flag) {\n" + " System.out.println(/*EL:103*/flag);\n" + - " /*SL:104*/this.test = new Test2(0);\n" + + " /*SL:104*/this.test = new RewriteInit(0);\n" + " }\n" + " \n" + - " private Test2 test;\n" + + " private RewriteInit test;\n" + " static {\n" + " System.out.println(/*EL:108*/\"clinit2\");\n" + " }\n" + - " /*SL:111*/private Test2 bottom = new Test2(true);\n" + + " /*SL:111*/private RewriteInit bottom = new RewriteInit(true);\n" + " static {\n" + " System.out.println(/*EL:114*/\"clinit3\");\n" + " }\n" + @@ -266,32 +272,37 @@ public void testRewriteInit() throws Throwable { @Test public void testRewriteInit2() throws Throwable { verifyOutput( - Test3.class, + RewriteInit2.class, lineNumberSettings(), - "static class Test3 {\n" + - " /*SL:120*/private Test3 top = new Test3(0);\n" + + "static class RewriteInit2 {\n" + + " /*SL:120*/private RewriteInit2 top = new RewriteInit2(0);\n" + " static {\n" + " System.out.println(/*EL:124*/\"clinit1\");\n" + " }\n" + " \n" + - " public Test3(final int i) {\n" + + " public RewriteInit2(final int i) {\n" + " /*SL:128*/this(false);\n" + " System.out.println(/*EL:129*/i);\n" + " }\n" + " \n" + - " /*SL:132*/private Test3 middle = new Test3(false);\n" + + " /*SL:132*/private RewriteInit2 middle = new RewriteInit2(false);\n" + " \n" + - " public Test3(final boolean flag) {\n" + + " public RewriteInit2(final boolean flag) {\n" + " System.out.println(/*EL:135*/flag);\n" + " }\n" + - " /*SL:136*/private Test3 test = new Test3(0);\n" + + " /*SL:136*/private RewriteInit2 test = new RewriteInit2(0);\n" + " static {\n" + " System.out.println(/*EL:140*/\"clinit2\");\n" + " }\n" + - " /*SL:143*/private Test3 bottom = new Test3(true);\n" + + " /*SL:143*/private RewriteInit2 bottom = new RewriteInit2(true);\n" + " static {\n" + " System.out.println(/*EL:146*/\"clinit3\");\n" + " }\n" + + " class Inner {\n" + + " Inner() {\n" + + " System.out.println(/*EL:151*/\"Inner\");\n" + + " }\n" + + " }\n" + "}" ); } From d6edb2acbb920dba8722f74dfa133c7f39050f29 Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Sun, 12 Jun 2022 13:59:11 +0200 Subject: [PATCH 09/14] removed unused import --- .../java/ast/transforms/RewriteInitForLineStretchTransform.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java index c3240cd8..d6c6c96e 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -22,7 +22,6 @@ import com.strobel.assembler.metadata.FieldDefinition; import com.strobel.assembler.metadata.MemberReference; import com.strobel.assembler.metadata.MethodDefinition; -import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.decompiler.DecompilerContext; import com.strobel.decompiler.languages.java.LineNumberTableConverter; import com.strobel.decompiler.languages.java.MinMaxLineNumberVisitor; From 05ee91462717362b57e47c4f522166b223280dc4 Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Sun, 12 Jun 2022 16:32:55 +0200 Subject: [PATCH 10/14] new case & unit test: check use of local variables in field initializers --- .../RewriteInitForLineStretchTransform.java | 65 ++++++++++++------- .../transforms/TransformationPipeline.java | 2 +- .../com/strobel/decompiler/RealignTest.java | 29 +++++++++ 3 files changed, 71 insertions(+), 25 deletions(-) diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java index d6c6c96e..75b43a05 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -18,6 +18,8 @@ import com.strobel.assembler.ir.attributes.AttributeNames; import com.strobel.assembler.ir.attributes.LineNumberTableAttribute; +import com.strobel.assembler.ir.attributes.LocalVariableTableAttribute; +import com.strobel.assembler.ir.attributes.LocalVariableTableEntry; import com.strobel.assembler.ir.attributes.SourceAttribute; import com.strobel.assembler.metadata.FieldDefinition; import com.strobel.assembler.metadata.MemberReference; @@ -29,12 +31,15 @@ import com.strobel.decompiler.languages.java.ast.AssignmentOperatorType; import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.BlockStatement; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor; +import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; import com.strobel.decompiler.languages.java.ast.EntityDeclaration; import com.strobel.decompiler.languages.java.ast.Expression; import com.strobel.decompiler.languages.java.ast.ExpressionStatement; import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.Identifier; import com.strobel.decompiler.languages.java.ast.InvocationExpression; import com.strobel.decompiler.languages.java.ast.Keys; import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; @@ -44,20 +49,18 @@ import com.strobel.decompiler.languages.java.ast.VariableInitializer; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -public class RewriteInitForLineStretchTransform extends ContextTrackingVisitor { +public class RewriteInitForLineStretchTransform extends DepthFirstAstVisitor implements IAstTransform { private ConcurrentHashMap fieldDeclarations = new ConcurrentHashMap<>(); private ConcurrentHashMap fieldInitLocations = new ConcurrentHashMap<>(); private ConcurrentHashMap constructionDefinitionToDeclaration = new ConcurrentHashMap<>(); - public RewriteInitForLineStretchTransform(DecompilerContext context) { - super(context); - } - @Override public Void visitBlockStatement(BlockStatement node, Void data) { if (node.getParent() instanceof MethodDeclaration) { @@ -136,10 +139,15 @@ public Void visitAssignmentExpression(AssignmentExpression node, Void data) { int offset = initializer.getOffset(); int lineNumber = lineNumberTableConverter.getLineForOffset(offset); if (lineNumber > 0 && fieldDeclaration != null && !methodDefinition.hasParameter(memberReference.getName())) { - fieldDeclaration.setLineNumber(lineNumber); - FieldLocation fieldLocation = new FieldLocation(memberReference.getFullName(), offset); - fieldInitLocations.putIfAbsent(fieldLocation, new FieldInit(fieldDeclaration)); - fieldInitLocations.get(fieldLocation).init(initializer, (ExpressionStatement) node.getParent(), methodDefinition); + LocalVariableTableAttribute localVariableTable = SourceAttribute.find(AttributeNames.LocalVariableTable, methodDefinition.getSourceAttributes()); + IdentifierGatherer identifierGatherer = new IdentifierGatherer(); + initializer.acceptVisitor(identifierGatherer, null); + if (localVariableTable == null || identifierGatherer.containsNoneOf(localVariableTable.getEntries())) { + fieldDeclaration.setLineNumber(lineNumber); + FieldLocation fieldLocation = new FieldLocation(memberReference.getFullName(), offset); + fieldInitLocations.putIfAbsent(fieldLocation, new FieldInit(fieldDeclaration)); + fieldInitLocations.get(fieldLocation).init(initializer, (ExpressionStatement) node.getParent(), methodDefinition); + } } } } @@ -163,14 +171,7 @@ public Void visitThisReferenceExpression(ThisReferenceExpression node, Void data MethodDefinition methodDefinition = (MethodDefinition) memberReference; ConstructorDeclaration constructorDeclaration = constructionDefinitionToDeclaration.get(methodDefinition); if (constructorDeclaration != null) { - MethodDefinition currentMethodDefinition = context.getCurrentMethod(); - try { - context.setCurrentMethod(null); - visitConstructorDeclaration(constructorDeclaration, data); - } - finally { - context.setCurrentMethod(currentMethodDefinition); - } + visitConstructorDeclaration(constructorDeclaration, data); } } } @@ -179,8 +180,8 @@ public Void visitThisReferenceExpression(ThisReferenceExpression node, Void data @Override public void run(AstNode compilationUnit) { - new ConstructorGatherer(context).run(compilationUnit); - super.run(compilationUnit); + compilationUnit.acceptVisitor(new ConstructorGatherer(), null); + compilationUnit.acceptVisitor(this, null); for (FieldInit fieldInit : fieldInitLocations.values()) { if (fieldInit.isInAllConstructors() || fieldInit.isInTypeInitializer()) { fieldInit.removeFieldInitStatements(); @@ -286,12 +287,8 @@ public boolean equals(Object obj) { } } - private class ConstructorGatherer extends ContextTrackingVisitor { + private class ConstructorGatherer extends DepthFirstAstVisitor { - protected ConstructorGatherer(DecompilerContext context) { - super(context); - } - @Override public Void visitConstructorDeclaration(ConstructorDeclaration node, Void p) { MethodDefinition methodDefinition = node.getUserData(Keys.METHOD_DEFINITION); @@ -301,4 +298,24 @@ public Void visitConstructorDeclaration(ConstructorDeclaration node, Void p) { return super.visitConstructorDeclaration(node, p); } } + + private static class IdentifierGatherer extends DepthFirstAstVisitor { + + private Set identifiers = new HashSet<>(); + + public boolean containsNoneOf(List entries) { + for (LocalVariableTableEntry entry : entries) { + if (identifiers.contains(entry.getName())) { + return false; + } + } + return true; + } + + @Override + public Void visitIdentifier(Identifier node, Void data) { + identifiers.add(node.getName()); + return super.visitIdentifier(node, data); + } + } } diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/TransformationPipeline.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/TransformationPipeline.java index 3750c7b1..1dd36a86 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/TransformationPipeline.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/TransformationPipeline.java @@ -77,7 +77,7 @@ public static IAstTransform[] createPipeline(final DecompilerContext context) { new AddReferenceQualifiersTransform(context), new RemoveHiddenMembersTransform(context), new CollapseImportsTransform(context), - new RewriteInitForLineStretchTransform(context), + new RewriteInitForLineStretchTransform(), new ReOrderMembersForLineStretchTransform(context) }; } diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java index cbf95f54..52c6863e 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java @@ -153,6 +153,18 @@ class Inner { } } + static class RewriteInit3 { + + private int _a, _b; + + public RewriteInit3(int a) { + _a = a; + double b = Math.random(); + _b = b < 0.5 ? 1 : 0; + } + } + + @Test public void testReOrderMembers() throws Throwable { verifyOutput( @@ -307,6 +319,23 @@ public void testRewriteInit2() throws Throwable { ); } + @Test + public void testRewriteInit3() throws Throwable { + verifyOutput( + RewriteInit3.class, + lineNumberSettings(), + "static class RewriteInit3 {\n" + + " private int _a;\n" + + " private int _b;\n" + + " public RewriteInit3(final int a) {\n" + + " /*SL:161*/this._a = a;\n" + + " final double b = /*EL:162*/Math.random();\n" + + " /*SL:163*/this._b = ((b < 0.5) ? 1 : 0);\n" + + " }\n" + + "}" + ); + } + private static DecompilerSettings lineNumberSettings() { DecompilerSettings lineNumberSettings = defaultSettings(); lineNumberSettings.setShowDebugLineNumbers(true); From e70abf91fd3dce893d19a5403f5fdf969b5c766f Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Sun, 12 Jun 2022 19:09:02 +0200 Subject: [PATCH 11/14] removed unused imports --- .../ast/transforms/RewriteInitForLineStretchTransform.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java index 75b43a05..de99b4b3 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -24,16 +24,13 @@ import com.strobel.assembler.metadata.FieldDefinition; import com.strobel.assembler.metadata.MemberReference; import com.strobel.assembler.metadata.MethodDefinition; -import com.strobel.decompiler.DecompilerContext; import com.strobel.decompiler.languages.java.LineNumberTableConverter; import com.strobel.decompiler.languages.java.MinMaxLineNumberVisitor; import com.strobel.decompiler.languages.java.ast.AssignmentExpression; import com.strobel.decompiler.languages.java.ast.AssignmentOperatorType; import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.BlockStatement; -import com.strobel.decompiler.languages.java.ast.CompilationUnit; import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; -import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor; import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; import com.strobel.decompiler.languages.java.ast.EntityDeclaration; import com.strobel.decompiler.languages.java.ast.Expression; From 12fa78bea576e9c6588e7fc3b963500a3167418e Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Sat, 3 Jan 2026 09:15:07 +0100 Subject: [PATCH 12/14] Fix negative offset issue --- .../RewriteInitForLineStretchTransform.java | 24 ++++++++++--------- .../com/strobel/decompiler/RealignTest.java | 11 ++++++++- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java index de99b4b3..734171e2 100644 --- a/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java +++ b/Procyon.CompilerTools/src/main/java/com/strobel/decompiler/languages/java/ast/transforms/RewriteInitForLineStretchTransform.java @@ -134,17 +134,19 @@ public Void visitAssignmentExpression(AssignmentExpression node, Void data) { FieldDeclaration fieldDeclaration = fieldDeclarations.get(memberReference.getFullName()); Expression initializer = node.getRight(); int offset = initializer.getOffset(); - int lineNumber = lineNumberTableConverter.getLineForOffset(offset); - if (lineNumber > 0 && fieldDeclaration != null && !methodDefinition.hasParameter(memberReference.getName())) { - LocalVariableTableAttribute localVariableTable = SourceAttribute.find(AttributeNames.LocalVariableTable, methodDefinition.getSourceAttributes()); - IdentifierGatherer identifierGatherer = new IdentifierGatherer(); - initializer.acceptVisitor(identifierGatherer, null); - if (localVariableTable == null || identifierGatherer.containsNoneOf(localVariableTable.getEntries())) { - fieldDeclaration.setLineNumber(lineNumber); - FieldLocation fieldLocation = new FieldLocation(memberReference.getFullName(), offset); - fieldInitLocations.putIfAbsent(fieldLocation, new FieldInit(fieldDeclaration)); - fieldInitLocations.get(fieldLocation).init(initializer, (ExpressionStatement) node.getParent(), methodDefinition); - } + if (offset != Expression.MYSTERY_OFFSET) { + int lineNumber = lineNumberTableConverter.getLineForOffset(offset); + if (lineNumber > 0 && fieldDeclaration != null && !methodDefinition.hasParameter(memberReference.getName())) { + LocalVariableTableAttribute localVariableTable = SourceAttribute.find(AttributeNames.LocalVariableTable, methodDefinition.getSourceAttributes()); + IdentifierGatherer identifierGatherer = new IdentifierGatherer(); + initializer.acceptVisitor(identifierGatherer, null); + if (localVariableTable == null || identifierGatherer.containsNoneOf(localVariableTable.getEntries())) { + fieldDeclaration.setLineNumber(lineNumber); + FieldLocation fieldLocation = new FieldLocation(memberReference.getFullName(), offset); + fieldInitLocations.putIfAbsent(fieldLocation, new FieldInit(fieldDeclaration)); + fieldInitLocations.get(fieldLocation).init(initializer, (ExpressionStatement) node.getParent(), methodDefinition); + } + } } } } diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java index 52c6863e..99b7ec36 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java @@ -164,7 +164,7 @@ public RewriteInit3(int a) { } } - + @Test public void testReOrderMembers() throws Throwable { verifyOutput( @@ -336,6 +336,15 @@ public void testRewriteInit3() throws Throwable { ); } + @Test + public void testRewriteInitThrowable() throws Throwable { + verifyOutput( + "java/lang/Throwable", + lineNumberSettings(), + "public class Throwable implements Serializable { private static final long serialVersionUID = -3042686055658047285L; private transient Object backtrace; private static final StackTraceElement[] UNASSIGNED_STACK; private StackTraceElement[] stackTrace; private static final List SUPPRESSED_SENTINEL; private List suppressedExceptions; private static final String NULL_CAUSE_MESSAGE = \"Cannot suppress a null exception.\"; private static final String SELF_SUPPRESSION_MESSAGE = \"Self-suppression not permitted\"; private static final String CAUSE_CAPTION = \"Caused by: \"; private static final String SUPPRESSED_CAPTION = \"Suppressed: \"; private static final Throwable[] EMPTY_THROWABLE_ARRAY; private native Throwable fillInStackTrace(final int p0); native int getStackTraceDepth(); native StackTraceElement getStackTraceElement(final int p0); private abstract static class PrintStreamOrWriter { abstract Object lock(); abstract void println(final Object p0); } private static class SentinelHolder { public static final StackTraceElement STACK_TRACE_ELEMENT_SENTINEL; public static final StackTraceElement[] STACK_TRACE_SENTINEL; static { /*SL:146*/STACK_TRACE_ELEMENT_SENTINEL = new StackTraceElement(\"\", \"\", null, Integer.MIN_VALUE); } static { /*SL:153*/STACK_TRACE_SENTINEL = new StackTraceElement[] { SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL }; } } static { /*SL:160*/UNASSIGNED_STACK = new StackTraceElement[0]; } static { /*SL:216*/SUPPRESSED_SENTINEL = Collections.unmodifiableList((List)new ArrayList(0)); } public Throwable() { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:251*/this.fillInStackTrace(); } public Throwable(final String detailMessage) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:266*/this.fillInStackTrace(); /*SL:267*/this.detailMessage = detailMessage; } public Throwable(final String detailMessage, final Throwable cause) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:288*/this.fillInStackTrace(); /*SL:289*/this.detailMessage = detailMessage; /*SL:290*/this.cause = cause; } public Throwable(final Throwable cause) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:311*/this.fillInStackTrace(); /*SL:312*/this.detailMessage = ((cause == null) ? null : cause.toString()); /*SL:313*/this.cause = cause; } protected Throwable(final String detailMessage, final Throwable cause, final boolean b, final boolean b2) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:360*/if (b2) { /*SL:361*/this.fillInStackTrace(); } else { /*SL:363*/this.stackTrace = null; } /*SL:365*/this.detailMessage = detailMessage; /*SL:366*/this.cause = cause; /*SL:367*/if (!b) { /*SL:368*/this.suppressedExceptions = null; } } private String detailMessage; /*SL:366*/private Throwable cause = this; public String getMessage() { /*SL:378*/return this.detailMessage; } public String getLocalizedMessage() { /*SL:392*/return this.getMessage(); } public synchronized Throwable getCause() { /*SL:416*/return (this.cause == this) ? null : this.cause; } public synchronized Throwable initCause(final Throwable cause) { /*SL:456*/if (this.cause != this) { /*SL:457*/throw new IllegalStateException(\"Can't overwrite cause with \" + /*EL:458*/Objects.toString(cause, \"a null\"), this); } /*SL:459*/if (cause == this) { /*SL:460*/throw new IllegalArgumentException(\"Self-causation not permitted\", this); } /*SL:461*/this.cause = cause; /*SL:462*/return this; } @Override public String toString() { final String name = /*EL:480*/this.getClass().getName(); final String localizedMessage = /*EL:481*/this.getLocalizedMessage(); /*SL:482*/return (localizedMessage != null) ? (name + \": \" + localizedMessage) : name; } public void printStackTrace() { /*SL:635*/this.printStackTrace(System.err); } public void printStackTrace(final PrintStream printStream) { /*SL:644*/this.printStackTrace(new WrappedPrintStream(printStream)); } private void printStackTrace(final PrintStreamOrWriter printStreamOrWriter) { final Set setFromMap = /*EL:651*/Collections.newSetFromMap(new IdentityHashMap()); /*SL:652*/setFromMap.add(this); /*SL:654*/synchronized (printStreamOrWriter.lock()) { /*SL:656*/printStreamOrWriter.println(this); final StackTraceElement[] ourStackTrace; final StackTraceElement[] array = /*EL:658*/ourStackTrace = this.getOurStackTrace(); for (int length = ourStackTrace.length, i = 0; i < length; ++i) { /*SL:659*/printStreamOrWriter.println(\"\\tat \" + ourStackTrace[i]); } final Throwable[] suppressed = /*EL:662*/this.getSuppressed(); for (int length2 = suppressed.length, j = 0; j < length2; ++j) { suppressed[j].printEnclosedStackTrace(/*EL:663*/printStreamOrWriter, array, \"Suppressed: \", \"\\t\", setFromMap); } final Throwable cause = /*EL:666*/this.getCause(); /*SL:667*/if (cause != null) { /*SL:668*/cause.printEnclosedStackTrace(printStreamOrWriter, array, \"Caused by: \", \"\", setFromMap); } } } private void printEnclosedStackTrace(final PrintStreamOrWriter printStreamOrWriter, final StackTraceElement[] array, final String s, final String s2, final Set set) { /*SL:681*/assert Thread.holdsLock(printStreamOrWriter.lock()); /*SL:682*/if (set.contains(this)) { /*SL:683*/printStreamOrWriter.println(\"\\t[CIRCULAR REFERENCE:\" + this + \"]\"); } else { /*SL:685*/set.add(this); final StackTraceElement[] ourStackTrace = /*EL:687*/this.getOurStackTrace(); int n = /*EL:688*/ourStackTrace.length - 1; /*SL:690*/for (int n2 = array.length - 1; n >= 0 && n2 >= 0 && ourStackTrace[n].equals(array[n2]); /*SL:691*/--n, --n2) {} final int n3 = /*EL:693*/ourStackTrace.length - 1 - n; /*SL:696*/printStreamOrWriter.println(s2 + s + this); /*SL:697*/for (int i = 0; i <= n; ++i) { /*SL:698*/printStreamOrWriter.println(s2 + \"\\tat \" + ourStackTrace[i]); } /*SL:699*/if (n3 != 0) { /*SL:700*/printStreamOrWriter.println(s2 + \"\\t... \" + n3 + \" more\"); } final Throwable[] suppressed = /*EL:703*/this.getSuppressed(); for (int length = suppressed.length, j = 0; j < length; ++j) { suppressed[j].printEnclosedStackTrace(/*EL:704*/printStreamOrWriter, ourStackTrace, \"Suppressed: \", s2 + \"\\t\", set); } final Throwable cause = /*EL:708*/this.getCause(); /*SL:709*/if (cause != null) { /*SL:710*/cause.printEnclosedStackTrace(printStreamOrWriter, ourStackTrace, \"Caused by: \", s2, set); } } } public void printStackTrace(final PrintWriter printWriter) { /*SL:722*/this.printStackTrace(new WrappedPrintWriter(printWriter)); } private static class WrappedPrintStream extends PrintStreamOrWriter { /*SL:741*/private final PrintStream printStream = printStream; WrappedPrintStream(final PrintStream printStream) { } @Override Object lock() { /*SL:745*/return this.printStream; } @Override void println(final Object o) { /*SL:749*/this.printStream.println(o); } } private static class WrappedPrintWriter extends PrintStreamOrWriter { /*SL:757*/private final PrintWriter printWriter = printWriter; WrappedPrintWriter(final PrintWriter printWriter) { } @Override Object lock() { /*SL:761*/return this.printWriter; } @Override void println(final Object o) { /*SL:765*/this.printWriter.println(o); } } public synchronized Throwable fillInStackTrace() { /*SL:782*/if (this.stackTrace != null || this.backtrace != null) { /*SL:784*/this.fillInStackTrace(0); /*SL:785*/this.stackTrace = Throwable.UNASSIGNED_STACK; } /*SL:787*/return this; } public StackTraceElement[] getStackTrace() { /*SL:817*/return this.getOurStackTrace().clone(); } private synchronized StackTraceElement[] getOurStackTrace() { /*SL:823*/if (this.stackTrace == Throwable.UNASSIGNED_STACK || (this.stackTrace == null && this.backtrace != null)) { final int stackTraceDepth = /*EL:825*/this.getStackTraceDepth(); /*SL:826*/this.stackTrace = new StackTraceElement[stackTraceDepth]; /*SL:827*/for (int i = 0; i < stackTraceDepth; ++i) { /*SL:828*/this.stackTrace[i] = this.getStackTraceElement(i); } } else/*SL:829*/ if (this.stackTrace == null) { /*SL:830*/return Throwable.UNASSIGNED_STACK; } /*SL:832*/return this.stackTrace; } public void setStackTrace(final StackTraceElement[] array) { final StackTraceElement[] stackTrace = /*EL:865*/array.clone(); /*SL:866*/for (int i = 0; i < stackTrace.length; ++i) { /*SL:867*/if (stackTrace[i] == null) { /*SL:868*/throw new NullPointerException(\"stackTrace[\" + i + \"]\"); } } /*SL:871*/synchronized (this) { /*SL:872*/if (this.stackTrace == null && this.backtrace == null) { /*SL:874*/return; } /*SL:875*/this.stackTrace = stackTrace; } } private void readObject(final ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException { /*SL:915*/objectInputStream.defaultReadObject(); final List suppressedExceptions = /*EL:920*/this.suppressedExceptions; /*SL:921*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; final StackTraceElement[] stackTrace = /*EL:923*/this.stackTrace; /*SL:924*/this.stackTrace = Throwable.UNASSIGNED_STACK.clone(); /*SL:926*/if (suppressedExceptions != null) { final int validateSuppressedExceptionsList = /*EL:927*/this.validateSuppressedExceptionsList(suppressedExceptions); /*SL:928*/if (validateSuppressedExceptionsList > 0) { final ArrayList suppressedExceptions2 = /*EL:929*/new ArrayList(Math.min(100, validateSuppressedExceptionsList)); /*SL:931*/for (final Throwable t : suppressedExceptions) { /*SL:934*/if (t == null) { /*SL:935*/throw new NullPointerException(\"Cannot suppress a null exception.\"); } /*SL:936*/if (t == this) { /*SL:937*/throw new IllegalArgumentException(\"Self-suppression not permitted\"); } /*SL:938*/suppressedExceptions2.add((Object)t); } /*SL:942*/this.suppressedExceptions = (List)suppressedExceptions2; } } else { /*SL:945*/this.suppressedExceptions = null; } /*SL:957*/if (stackTrace != null) { final StackTraceElement[] stackTrace2 = /*EL:960*/stackTrace.clone(); /*SL:961*/if (stackTrace2.length >= 1) { /*SL:962*/if (stackTrace2.length == 1 && SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(stackTrace2[0])) { /*SL:965*/this.stackTrace = null; } else { final StackTraceElement[] array = /*EL:967*/stackTrace2; for (int length = array.length, i = 0; i < length; ++i) { /*SL:968*/if (array[i] == null) { /*SL:969*/throw new NullPointerException(\"null StackTraceElement in serial stream.\"); } } /*SL:971*/this.stackTrace = stackTrace2; } } } } private int validateSuppressedExceptionsList(final List list) throws IOException { boolean b; try { /*SL:988*/b = (list.getClass().getClassLoader() == null); } catch (final SecurityException ex) { /*SL:990*/b = false; } /*SL:993*/if (!b) { /*SL:994*/throw new StreamCorruptedException(\"List implementation class was not loaded by bootstrap class loader.\"); } final int size = /*EL:997*/list.size(); /*SL:998*/if (size < 0) { /*SL:999*/throw new StreamCorruptedException(\"Negative list size reported.\"); } /*SL:1001*/return size; } private synchronized void writeObject(final ObjectOutputStream objectOutputStream) throws IOException { /*SL:1018*/this.getOurStackTrace(); final StackTraceElement[] stackTrace = /*EL:1020*/this.stackTrace; try { /*SL:1022*/if (this.stackTrace == null) { /*SL:1023*/this.stackTrace = SentinelHolder.STACK_TRACE_SENTINEL; } /*SL:1024*/objectOutputStream.defaultWriteObject(); } finally { /*SL:1026*/this.stackTrace = stackTrace; } } public final synchronized void addSuppressed(final Throwable t) { /*SL:1081*/if (t == this) { /*SL:1082*/throw new IllegalArgumentException(\"Self-suppression not permitted\", t); } /*SL:1084*/if (t == null) { /*SL:1085*/throw new NullPointerException(\"Cannot suppress a null exception.\"); } /*SL:1087*/if (this.suppressedExceptions == null) { /*SL:1088*/return; } /*SL:1090*/if (this.suppressedExceptions == Throwable.SUPPRESSED_SENTINEL) { /*SL:1091*/this.suppressedExceptions = new ArrayList(1); } /*SL:1093*/this.suppressedExceptions.add(t); } static { /*SL:1096*/EMPTY_THROWABLE_ARRAY = new Throwable[0]; } public final synchronized Throwable[] getSuppressed() { /*SL:1114*/if (this.suppressedExceptions == Throwable.SUPPRESSED_SENTINEL || this.suppressedExceptions == null) { /*SL:1116*/return Throwable.EMPTY_THROWABLE_ARRAY; } /*SL:1118*/return this.suppressedExceptions.toArray(Throwable.EMPTY_THROWABLE_ARRAY); } }" + ); + } + private static DecompilerSettings lineNumberSettings() { DecompilerSettings lineNumberSettings = defaultSettings(); lineNumberSettings.setShowDebugLineNumbers(true); From c6c7e1a1ec6f7a87dc103391d96539c69c6fbb83 Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Sat, 3 Jan 2026 10:02:46 +0100 Subject: [PATCH 13/14] update RealignTest --- .../src/test/java/com/strobel/decompiler/RealignTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java index 99b7ec36..9fe09bc9 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java @@ -341,7 +341,7 @@ public void testRewriteInitThrowable() throws Throwable { verifyOutput( "java/lang/Throwable", lineNumberSettings(), - "public class Throwable implements Serializable { private static final long serialVersionUID = -3042686055658047285L; private transient Object backtrace; private static final StackTraceElement[] UNASSIGNED_STACK; private StackTraceElement[] stackTrace; private static final List SUPPRESSED_SENTINEL; private List suppressedExceptions; private static final String NULL_CAUSE_MESSAGE = \"Cannot suppress a null exception.\"; private static final String SELF_SUPPRESSION_MESSAGE = \"Self-suppression not permitted\"; private static final String CAUSE_CAPTION = \"Caused by: \"; private static final String SUPPRESSED_CAPTION = \"Suppressed: \"; private static final Throwable[] EMPTY_THROWABLE_ARRAY; private native Throwable fillInStackTrace(final int p0); native int getStackTraceDepth(); native StackTraceElement getStackTraceElement(final int p0); private abstract static class PrintStreamOrWriter { abstract Object lock(); abstract void println(final Object p0); } private static class SentinelHolder { public static final StackTraceElement STACK_TRACE_ELEMENT_SENTINEL; public static final StackTraceElement[] STACK_TRACE_SENTINEL; static { /*SL:146*/STACK_TRACE_ELEMENT_SENTINEL = new StackTraceElement(\"\", \"\", null, Integer.MIN_VALUE); } static { /*SL:153*/STACK_TRACE_SENTINEL = new StackTraceElement[] { SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL }; } } static { /*SL:160*/UNASSIGNED_STACK = new StackTraceElement[0]; } static { /*SL:216*/SUPPRESSED_SENTINEL = Collections.unmodifiableList((List)new ArrayList(0)); } public Throwable() { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:251*/this.fillInStackTrace(); } public Throwable(final String detailMessage) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:266*/this.fillInStackTrace(); /*SL:267*/this.detailMessage = detailMessage; } public Throwable(final String detailMessage, final Throwable cause) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:288*/this.fillInStackTrace(); /*SL:289*/this.detailMessage = detailMessage; /*SL:290*/this.cause = cause; } public Throwable(final Throwable cause) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:311*/this.fillInStackTrace(); /*SL:312*/this.detailMessage = ((cause == null) ? null : cause.toString()); /*SL:313*/this.cause = cause; } protected Throwable(final String detailMessage, final Throwable cause, final boolean b, final boolean b2) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:360*/if (b2) { /*SL:361*/this.fillInStackTrace(); } else { /*SL:363*/this.stackTrace = null; } /*SL:365*/this.detailMessage = detailMessage; /*SL:366*/this.cause = cause; /*SL:367*/if (!b) { /*SL:368*/this.suppressedExceptions = null; } } private String detailMessage; /*SL:366*/private Throwable cause = this; public String getMessage() { /*SL:378*/return this.detailMessage; } public String getLocalizedMessage() { /*SL:392*/return this.getMessage(); } public synchronized Throwable getCause() { /*SL:416*/return (this.cause == this) ? null : this.cause; } public synchronized Throwable initCause(final Throwable cause) { /*SL:456*/if (this.cause != this) { /*SL:457*/throw new IllegalStateException(\"Can't overwrite cause with \" + /*EL:458*/Objects.toString(cause, \"a null\"), this); } /*SL:459*/if (cause == this) { /*SL:460*/throw new IllegalArgumentException(\"Self-causation not permitted\", this); } /*SL:461*/this.cause = cause; /*SL:462*/return this; } @Override public String toString() { final String name = /*EL:480*/this.getClass().getName(); final String localizedMessage = /*EL:481*/this.getLocalizedMessage(); /*SL:482*/return (localizedMessage != null) ? (name + \": \" + localizedMessage) : name; } public void printStackTrace() { /*SL:635*/this.printStackTrace(System.err); } public void printStackTrace(final PrintStream printStream) { /*SL:644*/this.printStackTrace(new WrappedPrintStream(printStream)); } private void printStackTrace(final PrintStreamOrWriter printStreamOrWriter) { final Set setFromMap = /*EL:651*/Collections.newSetFromMap(new IdentityHashMap()); /*SL:652*/setFromMap.add(this); /*SL:654*/synchronized (printStreamOrWriter.lock()) { /*SL:656*/printStreamOrWriter.println(this); final StackTraceElement[] ourStackTrace; final StackTraceElement[] array = /*EL:658*/ourStackTrace = this.getOurStackTrace(); for (int length = ourStackTrace.length, i = 0; i < length; ++i) { /*SL:659*/printStreamOrWriter.println(\"\\tat \" + ourStackTrace[i]); } final Throwable[] suppressed = /*EL:662*/this.getSuppressed(); for (int length2 = suppressed.length, j = 0; j < length2; ++j) { suppressed[j].printEnclosedStackTrace(/*EL:663*/printStreamOrWriter, array, \"Suppressed: \", \"\\t\", setFromMap); } final Throwable cause = /*EL:666*/this.getCause(); /*SL:667*/if (cause != null) { /*SL:668*/cause.printEnclosedStackTrace(printStreamOrWriter, array, \"Caused by: \", \"\", setFromMap); } } } private void printEnclosedStackTrace(final PrintStreamOrWriter printStreamOrWriter, final StackTraceElement[] array, final String s, final String s2, final Set set) { /*SL:681*/assert Thread.holdsLock(printStreamOrWriter.lock()); /*SL:682*/if (set.contains(this)) { /*SL:683*/printStreamOrWriter.println(\"\\t[CIRCULAR REFERENCE:\" + this + \"]\"); } else { /*SL:685*/set.add(this); final StackTraceElement[] ourStackTrace = /*EL:687*/this.getOurStackTrace(); int n = /*EL:688*/ourStackTrace.length - 1; /*SL:690*/for (int n2 = array.length - 1; n >= 0 && n2 >= 0 && ourStackTrace[n].equals(array[n2]); /*SL:691*/--n, --n2) {} final int n3 = /*EL:693*/ourStackTrace.length - 1 - n; /*SL:696*/printStreamOrWriter.println(s2 + s + this); /*SL:697*/for (int i = 0; i <= n; ++i) { /*SL:698*/printStreamOrWriter.println(s2 + \"\\tat \" + ourStackTrace[i]); } /*SL:699*/if (n3 != 0) { /*SL:700*/printStreamOrWriter.println(s2 + \"\\t... \" + n3 + \" more\"); } final Throwable[] suppressed = /*EL:703*/this.getSuppressed(); for (int length = suppressed.length, j = 0; j < length; ++j) { suppressed[j].printEnclosedStackTrace(/*EL:704*/printStreamOrWriter, ourStackTrace, \"Suppressed: \", s2 + \"\\t\", set); } final Throwable cause = /*EL:708*/this.getCause(); /*SL:709*/if (cause != null) { /*SL:710*/cause.printEnclosedStackTrace(printStreamOrWriter, ourStackTrace, \"Caused by: \", s2, set); } } } public void printStackTrace(final PrintWriter printWriter) { /*SL:722*/this.printStackTrace(new WrappedPrintWriter(printWriter)); } private static class WrappedPrintStream extends PrintStreamOrWriter { /*SL:741*/private final PrintStream printStream = printStream; WrappedPrintStream(final PrintStream printStream) { } @Override Object lock() { /*SL:745*/return this.printStream; } @Override void println(final Object o) { /*SL:749*/this.printStream.println(o); } } private static class WrappedPrintWriter extends PrintStreamOrWriter { /*SL:757*/private final PrintWriter printWriter = printWriter; WrappedPrintWriter(final PrintWriter printWriter) { } @Override Object lock() { /*SL:761*/return this.printWriter; } @Override void println(final Object o) { /*SL:765*/this.printWriter.println(o); } } public synchronized Throwable fillInStackTrace() { /*SL:782*/if (this.stackTrace != null || this.backtrace != null) { /*SL:784*/this.fillInStackTrace(0); /*SL:785*/this.stackTrace = Throwable.UNASSIGNED_STACK; } /*SL:787*/return this; } public StackTraceElement[] getStackTrace() { /*SL:817*/return this.getOurStackTrace().clone(); } private synchronized StackTraceElement[] getOurStackTrace() { /*SL:823*/if (this.stackTrace == Throwable.UNASSIGNED_STACK || (this.stackTrace == null && this.backtrace != null)) { final int stackTraceDepth = /*EL:825*/this.getStackTraceDepth(); /*SL:826*/this.stackTrace = new StackTraceElement[stackTraceDepth]; /*SL:827*/for (int i = 0; i < stackTraceDepth; ++i) { /*SL:828*/this.stackTrace[i] = this.getStackTraceElement(i); } } else/*SL:829*/ if (this.stackTrace == null) { /*SL:830*/return Throwable.UNASSIGNED_STACK; } /*SL:832*/return this.stackTrace; } public void setStackTrace(final StackTraceElement[] array) { final StackTraceElement[] stackTrace = /*EL:865*/array.clone(); /*SL:866*/for (int i = 0; i < stackTrace.length; ++i) { /*SL:867*/if (stackTrace[i] == null) { /*SL:868*/throw new NullPointerException(\"stackTrace[\" + i + \"]\"); } } /*SL:871*/synchronized (this) { /*SL:872*/if (this.stackTrace == null && this.backtrace == null) { /*SL:874*/return; } /*SL:875*/this.stackTrace = stackTrace; } } private void readObject(final ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException { /*SL:915*/objectInputStream.defaultReadObject(); final List suppressedExceptions = /*EL:920*/this.suppressedExceptions; /*SL:921*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; final StackTraceElement[] stackTrace = /*EL:923*/this.stackTrace; /*SL:924*/this.stackTrace = Throwable.UNASSIGNED_STACK.clone(); /*SL:926*/if (suppressedExceptions != null) { final int validateSuppressedExceptionsList = /*EL:927*/this.validateSuppressedExceptionsList(suppressedExceptions); /*SL:928*/if (validateSuppressedExceptionsList > 0) { final ArrayList suppressedExceptions2 = /*EL:929*/new ArrayList(Math.min(100, validateSuppressedExceptionsList)); /*SL:931*/for (final Throwable t : suppressedExceptions) { /*SL:934*/if (t == null) { /*SL:935*/throw new NullPointerException(\"Cannot suppress a null exception.\"); } /*SL:936*/if (t == this) { /*SL:937*/throw new IllegalArgumentException(\"Self-suppression not permitted\"); } /*SL:938*/suppressedExceptions2.add((Object)t); } /*SL:942*/this.suppressedExceptions = (List)suppressedExceptions2; } } else { /*SL:945*/this.suppressedExceptions = null; } /*SL:957*/if (stackTrace != null) { final StackTraceElement[] stackTrace2 = /*EL:960*/stackTrace.clone(); /*SL:961*/if (stackTrace2.length >= 1) { /*SL:962*/if (stackTrace2.length == 1 && SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(stackTrace2[0])) { /*SL:965*/this.stackTrace = null; } else { final StackTraceElement[] array = /*EL:967*/stackTrace2; for (int length = array.length, i = 0; i < length; ++i) { /*SL:968*/if (array[i] == null) { /*SL:969*/throw new NullPointerException(\"null StackTraceElement in serial stream.\"); } } /*SL:971*/this.stackTrace = stackTrace2; } } } } private int validateSuppressedExceptionsList(final List list) throws IOException { boolean b; try { /*SL:988*/b = (list.getClass().getClassLoader() == null); } catch (final SecurityException ex) { /*SL:990*/b = false; } /*SL:993*/if (!b) { /*SL:994*/throw new StreamCorruptedException(\"List implementation class was not loaded by bootstrap class loader.\"); } final int size = /*EL:997*/list.size(); /*SL:998*/if (size < 0) { /*SL:999*/throw new StreamCorruptedException(\"Negative list size reported.\"); } /*SL:1001*/return size; } private synchronized void writeObject(final ObjectOutputStream objectOutputStream) throws IOException { /*SL:1018*/this.getOurStackTrace(); final StackTraceElement[] stackTrace = /*EL:1020*/this.stackTrace; try { /*SL:1022*/if (this.stackTrace == null) { /*SL:1023*/this.stackTrace = SentinelHolder.STACK_TRACE_SENTINEL; } /*SL:1024*/objectOutputStream.defaultWriteObject(); } finally { /*SL:1026*/this.stackTrace = stackTrace; } } public final synchronized void addSuppressed(final Throwable t) { /*SL:1081*/if (t == this) { /*SL:1082*/throw new IllegalArgumentException(\"Self-suppression not permitted\", t); } /*SL:1084*/if (t == null) { /*SL:1085*/throw new NullPointerException(\"Cannot suppress a null exception.\"); } /*SL:1087*/if (this.suppressedExceptions == null) { /*SL:1088*/return; } /*SL:1090*/if (this.suppressedExceptions == Throwable.SUPPRESSED_SENTINEL) { /*SL:1091*/this.suppressedExceptions = new ArrayList(1); } /*SL:1093*/this.suppressedExceptions.add(t); } static { /*SL:1096*/EMPTY_THROWABLE_ARRAY = new Throwable[0]; } public final synchronized Throwable[] getSuppressed() { /*SL:1114*/if (this.suppressedExceptions == Throwable.SUPPRESSED_SENTINEL || this.suppressedExceptions == null) { /*SL:1116*/return Throwable.EMPTY_THROWABLE_ARRAY; } /*SL:1118*/return this.suppressedExceptions.toArray(Throwable.EMPTY_THROWABLE_ARRAY); } }" + "public class Throwable implements Serializable { private static final long serialVersionUID = -3042686055658047285L; private transient Object backtrace; private static final StackTraceElement[] UNASSIGNED_STACK; private StackTraceElement[] stackTrace; private static final List SUPPRESSED_SENTINEL; private List suppressedExceptions; private static final String NULL_CAUSE_MESSAGE = \"Cannot suppress a null exception.\"; private static final String SELF_SUPPRESSION_MESSAGE = \"Self-suppression not permitted\"; private static final String CAUSE_CAPTION = \"Caused by: \"; private static final String SUPPRESSED_CAPTION = \"Suppressed: \"; private static final Throwable[] EMPTY_THROWABLE_ARRAY; private native Throwable fillInStackTrace(final int p0); native int getStackTraceDepth(); native StackTraceElement getStackTraceElement(final int p0); private abstract static class PrintStreamOrWriter { abstract Object lock(); abstract void println(final Object p0); } private static class SentinelHolder { public static final StackTraceElement STACK_TRACE_ELEMENT_SENTINEL; public static final StackTraceElement[] STACK_TRACE_SENTINEL; static { /*SL:146*/STACK_TRACE_ELEMENT_SENTINEL = new StackTraceElement(\"\", \"\", null, Integer.MIN_VALUE); } static { /*SL:153*/STACK_TRACE_SENTINEL = new StackTraceElement[] { SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL }; } } static { /*SL:160*/UNASSIGNED_STACK = new StackTraceElement[0]; } static { /*SL:216*/SUPPRESSED_SENTINEL = Collections.unmodifiableList((List)new ArrayList(0)); } public Throwable() { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:251*/this.fillInStackTrace(); } public Throwable(final String detailMessage) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:266*/this.fillInStackTrace(); /*SL:267*/this.detailMessage = detailMessage; } public Throwable(final String detailMessage, final Throwable cause) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:288*/this.fillInStackTrace(); /*SL:289*/this.detailMessage = detailMessage; /*SL:290*/this.cause = cause; } public Throwable(final Throwable cause) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:311*/this.fillInStackTrace(); /*SL:312*/this.detailMessage = ((cause == null) ? null : cause.toString()); /*SL:313*/this.cause = cause; } protected Throwable(final String detailMessage, final Throwable cause, final boolean b, final boolean b2) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:360*/if (b2) { /*SL:361*/this.fillInStackTrace(); } else { /*SL:363*/this.stackTrace = null; } /*SL:365*/this.detailMessage = detailMessage; /*SL:366*/this.cause = cause; /*SL:367*/if (!b) { /*SL:368*/this.suppressedExceptions = null; } } private String detailMessage; /*SL:366*/private Throwable cause = this; public String getMessage() { /*SL:378*/return this.detailMessage; } public String getLocalizedMessage() { /*SL:392*/return this.getMessage(); } public synchronized Throwable getCause() { /*SL:416*/return (this.cause == this) ? null : this.cause; } public synchronized Throwable initCause(final Throwable cause) { /*SL:456*/if (this.cause != this) { /*SL:457*/throw new IllegalStateException(\"Can't overwrite cause with \" + /*EL:458*/Objects.toString(cause, \"a null\"), this); } /*SL:459*/if (cause == this) { /*SL:460*/throw new IllegalArgumentException(\"Self-causation not permitted\", this); } /*SL:461*/this.cause = cause; /*SL:462*/return this; } @Override public String toString() { final String name = /*EL:480*/this.getClass().getName(); final String localizedMessage = /*EL:481*/this.getLocalizedMessage(); /*SL:482*/return (localizedMessage != null) ? (name + \": \" + localizedMessage) : name; } public void printStackTrace() { /*SL:635*/this.printStackTrace(System.err); } public void printStackTrace(final PrintStream printStream) { /*SL:644*/this.printStackTrace(new WrappedPrintStream(printStream)); } private void printStackTrace(final PrintStreamOrWriter printStreamOrWriter) { final Set setFromMap = /*EL:651*/Collections.newSetFromMap(new IdentityHashMap()); /*SL:652*/setFromMap.add(this); /*SL:654*/synchronized (printStreamOrWriter.lock()) { /*SL:656*/printStreamOrWriter.println(this); final StackTraceElement[] ourStackTrace; final StackTraceElement[] array = /*EL:658*/ourStackTrace = this.getOurStackTrace(); for (int length = ourStackTrace.length, i = 0; i < length; ++i) { /*SL:659*/printStreamOrWriter.println(\"\\tat \" + ourStackTrace[i]); } final Throwable[] suppressed = /*EL:662*/this.getSuppressed(); for (int length2 = suppressed.length, j = 0; j < length2; ++j) { suppressed[j].printEnclosedStackTrace(/*EL:663*/printStreamOrWriter, array, \"Suppressed: \", \"\\t\", setFromMap); } final Throwable cause = /*EL:666*/this.getCause(); /*SL:667*/if (cause != null) { /*SL:668*/cause.printEnclosedStackTrace(printStreamOrWriter, array, \"Caused by: \", \"\", setFromMap); } } } private void printEnclosedStackTrace(final PrintStreamOrWriter printStreamOrWriter, final StackTraceElement[] array, final String s, final String s2, final Set set) { /*SL:681*/assert Thread.holdsLock(printStreamOrWriter.lock()); /*SL:682*/if (set.contains(this)) { /*SL:683*/printStreamOrWriter.println(s2 + s + \"[CIRCULAR REFERENCE: \" + this + \"]\"); } else { /*SL:685*/set.add(this); final StackTraceElement[] ourStackTrace = /*EL:687*/this.getOurStackTrace(); int n = /*EL:688*/ourStackTrace.length - 1; /*SL:690*/for (int n2 = array.length - 1; n >= 0 && n2 >= 0 && ourStackTrace[n].equals(array[n2]); /*SL:691*/--n, --n2) {} final int n3 = /*EL:693*/ourStackTrace.length - 1 - n; /*SL:696*/printStreamOrWriter.println(s2 + s + this); /*SL:697*/for (int i = 0; i <= n; ++i) { /*SL:698*/printStreamOrWriter.println(s2 + \"\\tat \" + ourStackTrace[i]); } /*SL:699*/if (n3 != 0) { /*SL:700*/printStreamOrWriter.println(s2 + \"\\t... \" + n3 + \" more\"); } final Throwable[] suppressed = /*EL:703*/this.getSuppressed(); for (int length = suppressed.length, j = 0; j < length; ++j) { suppressed[j].printEnclosedStackTrace(/*EL:704*/printStreamOrWriter, ourStackTrace, \"Suppressed: \", s2 + \"\\t\", set); } final Throwable cause = /*EL:708*/this.getCause(); /*SL:709*/if (cause != null) { /*SL:710*/cause.printEnclosedStackTrace(printStreamOrWriter, ourStackTrace, \"Caused by: \", s2, set); } } } public void printStackTrace(final PrintWriter printWriter) { /*SL:722*/this.printStackTrace(new WrappedPrintWriter(printWriter)); } private static class WrappedPrintStream extends PrintStreamOrWriter { /*SL:741*/private final PrintStream printStream = printStream; WrappedPrintStream(final PrintStream printStream) { } @Override Object lock() { /*SL:745*/return this.printStream; } @Override void println(final Object o) { /*SL:749*/this.printStream.println(o); } } private static class WrappedPrintWriter extends PrintStreamOrWriter { /*SL:757*/private final PrintWriter printWriter = printWriter; WrappedPrintWriter(final PrintWriter printWriter) { } @Override Object lock() { /*SL:761*/return this.printWriter; } @Override void println(final Object o) { /*SL:765*/this.printWriter.println(o); } } public synchronized Throwable fillInStackTrace() { /*SL:782*/if (this.stackTrace != null || this.backtrace != null) { /*SL:784*/this.fillInStackTrace(0); /*SL:785*/this.stackTrace = Throwable.UNASSIGNED_STACK; } /*SL:787*/return this; } public StackTraceElement[] getStackTrace() { /*SL:817*/return this.getOurStackTrace().clone(); } private synchronized StackTraceElement[] getOurStackTrace() { /*SL:823*/if (this.stackTrace == Throwable.UNASSIGNED_STACK || (this.stackTrace == null && this.backtrace != null)) { final int stackTraceDepth = /*EL:825*/this.getStackTraceDepth(); /*SL:826*/this.stackTrace = new StackTraceElement[stackTraceDepth]; /*SL:827*/for (int i = 0; i < stackTraceDepth; ++i) { /*SL:828*/this.stackTrace[i] = this.getStackTraceElement(i); } } else/*SL:829*/ if (this.stackTrace == null) { /*SL:830*/return Throwable.UNASSIGNED_STACK; } /*SL:832*/return this.stackTrace; } public void setStackTrace(final StackTraceElement[] array) { final StackTraceElement[] stackTrace = /*EL:865*/array.clone(); /*SL:866*/for (int i = 0; i < stackTrace.length; ++i) { /*SL:867*/if (stackTrace[i] == null) { /*SL:868*/throw new NullPointerException(\"stackTrace[\" + i + \"]\"); } } /*SL:871*/synchronized (this) { /*SL:872*/if (this.stackTrace == null && this.backtrace == null) { /*SL:874*/return; } /*SL:875*/this.stackTrace = stackTrace; } } private void readObject(final ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException { /*SL:915*/objectInputStream.defaultReadObject(); final List suppressedExceptions = /*EL:920*/this.suppressedExceptions; /*SL:921*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; final StackTraceElement[] stackTrace = /*EL:923*/this.stackTrace; /*SL:924*/this.stackTrace = Throwable.UNASSIGNED_STACK.clone(); /*SL:926*/if (suppressedExceptions != null) { final int validateSuppressedExceptionsList = /*EL:927*/this.validateSuppressedExceptionsList(suppressedExceptions); /*SL:928*/if (validateSuppressedExceptionsList > 0) { final ArrayList suppressedExceptions2 = /*EL:929*/new ArrayList(Math.min(100, validateSuppressedExceptionsList)); /*SL:931*/for (final Throwable t : suppressedExceptions) { /*SL:934*/if (t == null) { /*SL:935*/throw new NullPointerException(\"Cannot suppress a null exception.\"); } /*SL:936*/if (t == this) { /*SL:937*/throw new IllegalArgumentException(\"Self-suppression not permitted\"); } /*SL:938*/suppressedExceptions2.add((Object)t); } /*SL:942*/this.suppressedExceptions = (List)suppressedExceptions2; } } else { /*SL:945*/this.suppressedExceptions = null; } /*SL:957*/if (stackTrace != null) { final StackTraceElement[] stackTrace2 = /*EL:960*/stackTrace.clone(); /*SL:961*/if (stackTrace2.length >= 1) { /*SL:962*/if (stackTrace2.length == 1 && SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(stackTrace2[0])) { /*SL:965*/this.stackTrace = null; } else { final StackTraceElement[] array = /*EL:967*/stackTrace2; for (int length = array.length, i = 0; i < length; ++i) { /*SL:968*/if (array[i] == null) { /*SL:969*/throw new NullPointerException(\"null StackTraceElement in serial stream.\"); } } /*SL:971*/this.stackTrace = stackTrace2; } } } } private int validateSuppressedExceptionsList(final List list) throws IOException { /*SL:984*/if (Object.class.getClassLoader() != list.getClass().getClassLoader()) { /*SL:985*/throw new StreamCorruptedException(\"List implementation not on the bootclasspath.\"); } final int size = /*EL:987*/list.size(); /*SL:988*/if (size < 0) { /*SL:989*/throw new StreamCorruptedException(\"Negative list size reported.\"); } /*SL:991*/return size; } private synchronized void writeObject(final ObjectOutputStream objectOutputStream) throws IOException { /*SL:1008*/this.getOurStackTrace(); final StackTraceElement[] stackTrace = /*EL:1010*/this.stackTrace; try { /*SL:1012*/if (this.stackTrace == null) { /*SL:1013*/this.stackTrace = SentinelHolder.STACK_TRACE_SENTINEL; } /*SL:1014*/objectOutputStream.defaultWriteObject(); } finally { /*SL:1016*/this.stackTrace = stackTrace; } } public final synchronized void addSuppressed(final Throwable t) { /*SL:1071*/if (t == this) { /*SL:1072*/throw new IllegalArgumentException(\"Self-suppression not permitted\", t); } /*SL:1074*/if (t == null) { /*SL:1075*/throw new NullPointerException(\"Cannot suppress a null exception.\"); } /*SL:1077*/if (this.suppressedExceptions == null) { /*SL:1078*/return; } /*SL:1080*/if (this.suppressedExceptions == Throwable.SUPPRESSED_SENTINEL) { /*SL:1081*/this.suppressedExceptions = new ArrayList(1); } /*SL:1083*/this.suppressedExceptions.add(t); } static { /*SL:1086*/EMPTY_THROWABLE_ARRAY = new Throwable[0]; } public final synchronized Throwable[] getSuppressed() { /*SL:1104*/if (this.suppressedExceptions == Throwable.SUPPRESSED_SENTINEL || this.suppressedExceptions == null) { /*SL:1106*/return Throwable.EMPTY_THROWABLE_ARRAY; } /*SL:1108*/return this.suppressedExceptions.toArray(Throwable.EMPTY_THROWABLE_ARRAY); } }" ); } From a4a1b7c0b3209bf334b1fc3b6de19ab6049bbba4 Mon Sep 17 00:00:00 2001 From: nbauma109 Date: Fri, 30 Jan 2026 08:57:59 +0100 Subject: [PATCH 14/14] simplify non-maintainable test --- .../java/com/strobel/decompiler/DecompilerTest.java | 1 + .../test/java/com/strobel/decompiler/RealignTest.java | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/DecompilerTest.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/DecompilerTest.java index 06ff764e..a52eebd1 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/DecompilerTest.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/DecompilerTest.java @@ -113,6 +113,7 @@ protected void verifyOutput(final Class type, final DecompilerSettings settin throw ExceptionUtilities.asRuntimeException(e); } } + protected void verifyOutput(final String internalName, final DecompilerSettings settings, final String expectedOutput) { final PlainTextOutput writer = new PlainTextOutput(); diff --git a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java index 9fe09bc9..ae50ff11 100644 --- a/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java +++ b/Procyon.CompilerTools/src/test/java/com/strobel/decompiler/RealignTest.java @@ -338,11 +338,11 @@ public void testRewriteInit3() throws Throwable { @Test public void testRewriteInitThrowable() throws Throwable { - verifyOutput( - "java/lang/Throwable", - lineNumberSettings(), - "public class Throwable implements Serializable { private static final long serialVersionUID = -3042686055658047285L; private transient Object backtrace; private static final StackTraceElement[] UNASSIGNED_STACK; private StackTraceElement[] stackTrace; private static final List SUPPRESSED_SENTINEL; private List suppressedExceptions; private static final String NULL_CAUSE_MESSAGE = \"Cannot suppress a null exception.\"; private static final String SELF_SUPPRESSION_MESSAGE = \"Self-suppression not permitted\"; private static final String CAUSE_CAPTION = \"Caused by: \"; private static final String SUPPRESSED_CAPTION = \"Suppressed: \"; private static final Throwable[] EMPTY_THROWABLE_ARRAY; private native Throwable fillInStackTrace(final int p0); native int getStackTraceDepth(); native StackTraceElement getStackTraceElement(final int p0); private abstract static class PrintStreamOrWriter { abstract Object lock(); abstract void println(final Object p0); } private static class SentinelHolder { public static final StackTraceElement STACK_TRACE_ELEMENT_SENTINEL; public static final StackTraceElement[] STACK_TRACE_SENTINEL; static { /*SL:146*/STACK_TRACE_ELEMENT_SENTINEL = new StackTraceElement(\"\", \"\", null, Integer.MIN_VALUE); } static { /*SL:153*/STACK_TRACE_SENTINEL = new StackTraceElement[] { SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL }; } } static { /*SL:160*/UNASSIGNED_STACK = new StackTraceElement[0]; } static { /*SL:216*/SUPPRESSED_SENTINEL = Collections.unmodifiableList((List)new ArrayList(0)); } public Throwable() { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:251*/this.fillInStackTrace(); } public Throwable(final String detailMessage) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:266*/this.fillInStackTrace(); /*SL:267*/this.detailMessage = detailMessage; } public Throwable(final String detailMessage, final Throwable cause) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:288*/this.fillInStackTrace(); /*SL:289*/this.detailMessage = detailMessage; /*SL:290*/this.cause = cause; } public Throwable(final Throwable cause) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:311*/this.fillInStackTrace(); /*SL:312*/this.detailMessage = ((cause == null) ? null : cause.toString()); /*SL:313*/this.cause = cause; } protected Throwable(final String detailMessage, final Throwable cause, final boolean b, final boolean b2) { /*SL:211*/this.stackTrace = Throwable.UNASSIGNED_STACK; /*SL:228*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; /*SL:360*/if (b2) { /*SL:361*/this.fillInStackTrace(); } else { /*SL:363*/this.stackTrace = null; } /*SL:365*/this.detailMessage = detailMessage; /*SL:366*/this.cause = cause; /*SL:367*/if (!b) { /*SL:368*/this.suppressedExceptions = null; } } private String detailMessage; /*SL:366*/private Throwable cause = this; public String getMessage() { /*SL:378*/return this.detailMessage; } public String getLocalizedMessage() { /*SL:392*/return this.getMessage(); } public synchronized Throwable getCause() { /*SL:416*/return (this.cause == this) ? null : this.cause; } public synchronized Throwable initCause(final Throwable cause) { /*SL:456*/if (this.cause != this) { /*SL:457*/throw new IllegalStateException(\"Can't overwrite cause with \" + /*EL:458*/Objects.toString(cause, \"a null\"), this); } /*SL:459*/if (cause == this) { /*SL:460*/throw new IllegalArgumentException(\"Self-causation not permitted\", this); } /*SL:461*/this.cause = cause; /*SL:462*/return this; } @Override public String toString() { final String name = /*EL:480*/this.getClass().getName(); final String localizedMessage = /*EL:481*/this.getLocalizedMessage(); /*SL:482*/return (localizedMessage != null) ? (name + \": \" + localizedMessage) : name; } public void printStackTrace() { /*SL:635*/this.printStackTrace(System.err); } public void printStackTrace(final PrintStream printStream) { /*SL:644*/this.printStackTrace(new WrappedPrintStream(printStream)); } private void printStackTrace(final PrintStreamOrWriter printStreamOrWriter) { final Set setFromMap = /*EL:651*/Collections.newSetFromMap(new IdentityHashMap()); /*SL:652*/setFromMap.add(this); /*SL:654*/synchronized (printStreamOrWriter.lock()) { /*SL:656*/printStreamOrWriter.println(this); final StackTraceElement[] ourStackTrace; final StackTraceElement[] array = /*EL:658*/ourStackTrace = this.getOurStackTrace(); for (int length = ourStackTrace.length, i = 0; i < length; ++i) { /*SL:659*/printStreamOrWriter.println(\"\\tat \" + ourStackTrace[i]); } final Throwable[] suppressed = /*EL:662*/this.getSuppressed(); for (int length2 = suppressed.length, j = 0; j < length2; ++j) { suppressed[j].printEnclosedStackTrace(/*EL:663*/printStreamOrWriter, array, \"Suppressed: \", \"\\t\", setFromMap); } final Throwable cause = /*EL:666*/this.getCause(); /*SL:667*/if (cause != null) { /*SL:668*/cause.printEnclosedStackTrace(printStreamOrWriter, array, \"Caused by: \", \"\", setFromMap); } } } private void printEnclosedStackTrace(final PrintStreamOrWriter printStreamOrWriter, final StackTraceElement[] array, final String s, final String s2, final Set set) { /*SL:681*/assert Thread.holdsLock(printStreamOrWriter.lock()); /*SL:682*/if (set.contains(this)) { /*SL:683*/printStreamOrWriter.println(s2 + s + \"[CIRCULAR REFERENCE: \" + this + \"]\"); } else { /*SL:685*/set.add(this); final StackTraceElement[] ourStackTrace = /*EL:687*/this.getOurStackTrace(); int n = /*EL:688*/ourStackTrace.length - 1; /*SL:690*/for (int n2 = array.length - 1; n >= 0 && n2 >= 0 && ourStackTrace[n].equals(array[n2]); /*SL:691*/--n, --n2) {} final int n3 = /*EL:693*/ourStackTrace.length - 1 - n; /*SL:696*/printStreamOrWriter.println(s2 + s + this); /*SL:697*/for (int i = 0; i <= n; ++i) { /*SL:698*/printStreamOrWriter.println(s2 + \"\\tat \" + ourStackTrace[i]); } /*SL:699*/if (n3 != 0) { /*SL:700*/printStreamOrWriter.println(s2 + \"\\t... \" + n3 + \" more\"); } final Throwable[] suppressed = /*EL:703*/this.getSuppressed(); for (int length = suppressed.length, j = 0; j < length; ++j) { suppressed[j].printEnclosedStackTrace(/*EL:704*/printStreamOrWriter, ourStackTrace, \"Suppressed: \", s2 + \"\\t\", set); } final Throwable cause = /*EL:708*/this.getCause(); /*SL:709*/if (cause != null) { /*SL:710*/cause.printEnclosedStackTrace(printStreamOrWriter, ourStackTrace, \"Caused by: \", s2, set); } } } public void printStackTrace(final PrintWriter printWriter) { /*SL:722*/this.printStackTrace(new WrappedPrintWriter(printWriter)); } private static class WrappedPrintStream extends PrintStreamOrWriter { /*SL:741*/private final PrintStream printStream = printStream; WrappedPrintStream(final PrintStream printStream) { } @Override Object lock() { /*SL:745*/return this.printStream; } @Override void println(final Object o) { /*SL:749*/this.printStream.println(o); } } private static class WrappedPrintWriter extends PrintStreamOrWriter { /*SL:757*/private final PrintWriter printWriter = printWriter; WrappedPrintWriter(final PrintWriter printWriter) { } @Override Object lock() { /*SL:761*/return this.printWriter; } @Override void println(final Object o) { /*SL:765*/this.printWriter.println(o); } } public synchronized Throwable fillInStackTrace() { /*SL:782*/if (this.stackTrace != null || this.backtrace != null) { /*SL:784*/this.fillInStackTrace(0); /*SL:785*/this.stackTrace = Throwable.UNASSIGNED_STACK; } /*SL:787*/return this; } public StackTraceElement[] getStackTrace() { /*SL:817*/return this.getOurStackTrace().clone(); } private synchronized StackTraceElement[] getOurStackTrace() { /*SL:823*/if (this.stackTrace == Throwable.UNASSIGNED_STACK || (this.stackTrace == null && this.backtrace != null)) { final int stackTraceDepth = /*EL:825*/this.getStackTraceDepth(); /*SL:826*/this.stackTrace = new StackTraceElement[stackTraceDepth]; /*SL:827*/for (int i = 0; i < stackTraceDepth; ++i) { /*SL:828*/this.stackTrace[i] = this.getStackTraceElement(i); } } else/*SL:829*/ if (this.stackTrace == null) { /*SL:830*/return Throwable.UNASSIGNED_STACK; } /*SL:832*/return this.stackTrace; } public void setStackTrace(final StackTraceElement[] array) { final StackTraceElement[] stackTrace = /*EL:865*/array.clone(); /*SL:866*/for (int i = 0; i < stackTrace.length; ++i) { /*SL:867*/if (stackTrace[i] == null) { /*SL:868*/throw new NullPointerException(\"stackTrace[\" + i + \"]\"); } } /*SL:871*/synchronized (this) { /*SL:872*/if (this.stackTrace == null && this.backtrace == null) { /*SL:874*/return; } /*SL:875*/this.stackTrace = stackTrace; } } private void readObject(final ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException { /*SL:915*/objectInputStream.defaultReadObject(); final List suppressedExceptions = /*EL:920*/this.suppressedExceptions; /*SL:921*/this.suppressedExceptions = Throwable.SUPPRESSED_SENTINEL; final StackTraceElement[] stackTrace = /*EL:923*/this.stackTrace; /*SL:924*/this.stackTrace = Throwable.UNASSIGNED_STACK.clone(); /*SL:926*/if (suppressedExceptions != null) { final int validateSuppressedExceptionsList = /*EL:927*/this.validateSuppressedExceptionsList(suppressedExceptions); /*SL:928*/if (validateSuppressedExceptionsList > 0) { final ArrayList suppressedExceptions2 = /*EL:929*/new ArrayList(Math.min(100, validateSuppressedExceptionsList)); /*SL:931*/for (final Throwable t : suppressedExceptions) { /*SL:934*/if (t == null) { /*SL:935*/throw new NullPointerException(\"Cannot suppress a null exception.\"); } /*SL:936*/if (t == this) { /*SL:937*/throw new IllegalArgumentException(\"Self-suppression not permitted\"); } /*SL:938*/suppressedExceptions2.add((Object)t); } /*SL:942*/this.suppressedExceptions = (List)suppressedExceptions2; } } else { /*SL:945*/this.suppressedExceptions = null; } /*SL:957*/if (stackTrace != null) { final StackTraceElement[] stackTrace2 = /*EL:960*/stackTrace.clone(); /*SL:961*/if (stackTrace2.length >= 1) { /*SL:962*/if (stackTrace2.length == 1 && SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(stackTrace2[0])) { /*SL:965*/this.stackTrace = null; } else { final StackTraceElement[] array = /*EL:967*/stackTrace2; for (int length = array.length, i = 0; i < length; ++i) { /*SL:968*/if (array[i] == null) { /*SL:969*/throw new NullPointerException(\"null StackTraceElement in serial stream.\"); } } /*SL:971*/this.stackTrace = stackTrace2; } } } } private int validateSuppressedExceptionsList(final List list) throws IOException { /*SL:984*/if (Object.class.getClassLoader() != list.getClass().getClassLoader()) { /*SL:985*/throw new StreamCorruptedException(\"List implementation not on the bootclasspath.\"); } final int size = /*EL:987*/list.size(); /*SL:988*/if (size < 0) { /*SL:989*/throw new StreamCorruptedException(\"Negative list size reported.\"); } /*SL:991*/return size; } private synchronized void writeObject(final ObjectOutputStream objectOutputStream) throws IOException { /*SL:1008*/this.getOurStackTrace(); final StackTraceElement[] stackTrace = /*EL:1010*/this.stackTrace; try { /*SL:1012*/if (this.stackTrace == null) { /*SL:1013*/this.stackTrace = SentinelHolder.STACK_TRACE_SENTINEL; } /*SL:1014*/objectOutputStream.defaultWriteObject(); } finally { /*SL:1016*/this.stackTrace = stackTrace; } } public final synchronized void addSuppressed(final Throwable t) { /*SL:1071*/if (t == this) { /*SL:1072*/throw new IllegalArgumentException(\"Self-suppression not permitted\", t); } /*SL:1074*/if (t == null) { /*SL:1075*/throw new NullPointerException(\"Cannot suppress a null exception.\"); } /*SL:1077*/if (this.suppressedExceptions == null) { /*SL:1078*/return; } /*SL:1080*/if (this.suppressedExceptions == Throwable.SUPPRESSED_SENTINEL) { /*SL:1081*/this.suppressedExceptions = new ArrayList(1); } /*SL:1083*/this.suppressedExceptions.add(t); } static { /*SL:1086*/EMPTY_THROWABLE_ARRAY = new Throwable[0]; } public final synchronized Throwable[] getSuppressed() { /*SL:1104*/if (this.suppressedExceptions == Throwable.SUPPRESSED_SENTINEL || this.suppressedExceptions == null) { /*SL:1106*/return Throwable.EMPTY_THROWABLE_ARRAY; } /*SL:1108*/return this.suppressedExceptions.toArray(Throwable.EMPTY_THROWABLE_ARRAY); } }" - ); + Decompiler.decompile( + "java/lang/Throwable", + new PlainTextOutput(), + lineNumberSettings() + ); } private static DecompilerSettings lineNumberSettings() {