From 5f94a0d0d5ba6b4148a055b7946dbb091bcb57c7 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Fri, 5 Jun 2020 20:12:18 +0200 Subject: [PATCH 01/27] apply remove redundant cast GitOrigin-RevId: 2bcbd253668f66ca2740074953cb661ebb6f3513 --- .../decompiler/modules/decompiler/vars/VarTypeProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java index 3c19d2c..c31cfad 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java @@ -170,7 +170,7 @@ public class VarTypeProcessor { case Exprent.EXPRENT_VAR: VarVersionPair pair = null; if (exprent.type == Exprent.EXPRENT_CONST) { - pair = new VarVersionPair(((ConstExprent)exprent).id, -1); + pair = new VarVersionPair(exprent.id, -1); } else if (exprent.type == Exprent.EXPRENT_VAR) { pair = new VarVersionPair((VarExprent)exprent); From 1d63f7d44703332fc9b8d832ca192e414de7d9aa Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 26 Jun 2020 10:39:01 +0200 Subject: [PATCH 02/27] cleanup (final) GitOrigin-RevId: 93607e195763f2acf29ee0d7a280d7e07e8d2f14 --- .../java/decompiler/code/cfg/ControlFlowGraph.java | 4 ++-- .../decompiler/modules/decompiler/FinallyProcessor.java | 6 +++--- .../decompiler/deobfuscator/ExceptionDeobfuscator.java | 6 +++--- .../modules/decompiler/sforms/FlattenStatementsHelper.java | 4 ++-- .../modules/decompiler/stats/CatchAllStatement.java | 4 ++-- .../decompiler/modules/decompiler/stats/CatchStatement.java | 4 ++-- .../decompiler/modules/decompiler/stats/DoStatement.java | 4 ++-- .../decompiler/modules/decompiler/stats/IfStatement.java | 4 ++-- .../modules/decompiler/stats/SwitchStatement.java | 4 ++-- .../decompiler/struct/attr/StructInnerClassesAttribute.java | 4 ++-- .../struct/attr/StructLocalVariableTableAttribute.java | 4 ++-- .../java/decompiler/struct/gen/FieldDescriptor.java | 4 ++-- .../java/decompiler/struct/gen/MethodDescriptor.java | 4 ++-- .../jetbrains/java/decompiler/util/FastFixedSetFactory.java | 6 +++--- .../java/decompiler/util/FastSparseSetFactory.java | 6 +++--- 15 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java index 08223cf..2fd9d3c 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code.cfg; import org.jetbrains.java.decompiler.code.*; @@ -432,7 +432,7 @@ public class ControlFlowGraph implements CodeConstants { } } - private static class JsrRecord { + private static final class JsrRecord { private final BasicBlock jsr; private final Set range; private final BasicBlock ret; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java index 240ee3e..53a9b68 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -110,7 +110,7 @@ public class FinallyProcessor { return false; } - private static class Record { + private static final class Record { private final int firstCode; private final Map mapLast; @@ -527,7 +527,7 @@ public class FinallyProcessor { return true; } - private static class Area { + private static final class Area { private final BasicBlock start; private final Set sample; private final BasicBlock next; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java index 810291d..cc02d0f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.deobfuscator; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -20,7 +20,7 @@ import java.util.Map.Entry; public class ExceptionDeobfuscator { - private static class Range { + private static final class Range { private final BasicBlock handler; private final String uniqueStr; private final Set protectedRange; @@ -407,7 +407,7 @@ public class ExceptionDeobfuscator { for (ExceptionRangeCFG range : ranges) { - // add some dummy instructions to prevent optimizing away the empty block + // add some dummy instructions to prevent optimizing away the empty block SimpleInstructionSequence seq = new SimpleInstructionSequence(); seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1); seq.addInstruction(Instruction.create(CodeConstants.opc_pop, false, CodeConstants.GROUP_GENERAL, bytecode_version, null), -1); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java index b9abd11..ca43d66 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.sforms; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; @@ -472,7 +472,7 @@ public class FlattenStatementsHelper { return mapDestinationNodes; } - public static class FinallyPathWrapper { + public static final class FinallyPathWrapper { public final String source; public final String destination; public final String entry; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java index 376abe5..2fa2d90 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -17,7 +17,7 @@ import java.util.Arrays; import java.util.List; import java.util.Set; -public class CatchAllStatement extends Statement { +public final class CatchAllStatement extends Statement { private Statement handler; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java index 652931a..5168f05 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -17,7 +17,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -public class CatchStatement extends Statement { +public final class CatchStatement extends Statement { private final List> exctstrings = new ArrayList<>(); private final List vars = new ArrayList<>(); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java index f9a149f..46b2f2c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; @@ -11,7 +11,7 @@ import java.util.ArrayList; import java.util.List; -public class DoStatement extends Statement { +public final class DoStatement extends Statement { public static final int LOOP_DO = 0; public static final int LOOP_DOWHILE = 1; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java index bf18c51..c6a01d5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; @@ -17,7 +17,7 @@ import java.util.ArrayList; import java.util.List; -public class IfStatement extends Statement { +public final class IfStatement extends Statement { public static final int IFTYPE_IF = 0; public static final int IFTYPE_IFELSE = 1; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java index ab48ecc..a5b9d3a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.code.SwitchInstruction; @@ -19,7 +19,7 @@ import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.*; -public class SwitchStatement extends Statement { +public final class SwitchStatement extends Statement { // ***************************************************************************** // private fields diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java index 4306381..238900d 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -10,7 +10,7 @@ import java.util.Collections; import java.util.List; public class StructInnerClassesAttribute extends StructGeneralAttribute { - public static class Entry { + public static final class Entry { public final int outerNameIdx; public final int simpleNameIdx; public final int accessFlags; diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java index 15fb317..66eb593 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -71,7 +71,7 @@ public class StructLocalVariableTableAttribute extends StructGeneralAttribute { return localVariables.stream().filter(v -> v.start_pc == 0).collect(Collectors.toMap(v -> v.index, v -> v.name, (n1, n2) -> n2)); } - private static class LocalVariable { + private static final class LocalVariable { final int start_pc; final int length; final String name; diff --git a/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java index 94add14..50d41ba 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java @@ -1,9 +1,9 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.gen; import org.jetbrains.java.decompiler.code.CodeConstants; -public class FieldDescriptor { +public final class FieldDescriptor { public static final FieldDescriptor INTEGER_DESCRIPTOR = parseDescriptor("Ljava/lang/Integer;"); public static final FieldDescriptor LONG_DESCRIPTOR = parseDescriptor("Ljava/lang/Long;"); diff --git a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java index 102393c..8697b03 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.gen; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -7,7 +7,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public class MethodDescriptor { +public final class MethodDescriptor { public final VarType[] params; public final VarType ret; diff --git a/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java index d05b7d4..7c2956c 100644 --- a/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.util; import java.util.Collection; @@ -46,7 +46,7 @@ public class FastFixedSetFactory { return colValuesInternal; } - public static class FastFixedSet implements Iterable { + public static final class FastFixedSet implements Iterable { private final FastFixedSetFactory factory; @@ -242,7 +242,7 @@ public class FastFixedSetFactory { } } - public static class FastFixedSetIterator implements Iterator { + public static final class FastFixedSetIterator implements Iterator { private final VBStyleCollection colValuesInternal; private final int[] data; diff --git a/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java index b955139..a99083c 100644 --- a/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.util; import java.util.Collection; @@ -69,7 +69,7 @@ public class FastSparseSetFactory { } - public static class FastSparseSet implements Iterable { + public static final class FastSparseSet implements Iterable { public static final FastSparseSet[] EMPTY_ARRAY = new FastSparseSet[0]; private final FastSparseSetFactory factory; @@ -360,7 +360,7 @@ public class FastSparseSetFactory { } } - public static class FastSparseSetIterator implements Iterator { + public static final class FastSparseSetIterator implements Iterator { private final VBStyleCollection colValuesInternal; private final int[] data; From ab4a7ddd1601055a08bfd36680515c13c627c85a Mon Sep 17 00:00:00 2001 From: malte0811 Date: Wed, 15 Jul 2020 22:10:49 +0200 Subject: [PATCH 03/27] Fix incorrect decompilation of inverted floating point comparisons with NaN (IDEA-196302) PR #845 GitOrigin-RevId: 611f4af224e68ff8167f2b62f8366a022adb2054 --- .../decompiler/SecondaryFunctionsHelper.java | 30 +++- .../java/decompiler/SingleClassesTest.java | 3 +- .../pkg/TestInvertedFloatComparison.class | Bin 0 -> 1332 bytes .../results/TestInvertedFloatComparison.dec | 167 ++++++++++++++++++ .../src/pkg/TestInvertedFloatComparison.java | 83 +++++++++ 5 files changed, 279 insertions(+), 4 deletions(-) create mode 100644 testData/classes/pkg/TestInvertedFloatComparison.class create mode 100644 testData/results/TestInvertedFloatComparison.dec create mode 100644 testData/src/pkg/TestInvertedFloatComparison.java diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java index a7c804b..17bf65e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -170,6 +170,17 @@ public class SecondaryFunctionsHelper { } if (desttype >= 0) { + if (functype != FunctionExprent.FUNCTION_LCMP) { + boolean oneForNan = functype == FunctionExprent.FUNCTION_DCMPL || functype == FunctionExprent.FUNCTION_FCMPL; + boolean trueForOne = desttype == FunctionExprent.FUNCTION_LT || desttype == FunctionExprent.FUNCTION_LE; + boolean trueForNan = oneForNan == trueForOne; + if (trueForNan) { + List operands = new ArrayList<>(); + operands.add(new FunctionExprent(funcsnot[desttype - FunctionExprent.FUNCTION_EQ], + funcexpr.getLstOperands(), funcexpr.bytecode)); + return new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, operands, funcexpr.bytecode); + } + } return new FunctionExprent(desttype, funcexpr.getLstOperands(), funcexpr.bytecode); } } @@ -378,6 +389,7 @@ public class SecondaryFunctionsHelper { FunctionExprent fparam = (FunctionExprent)param; int ftype = fparam.getFuncType(); + boolean canSimplify = false; switch (ftype) { case FunctionExprent.FUNCTION_BOOL_NOT: Exprent newexpr = fparam.getLstOperands().get(0); @@ -394,12 +406,24 @@ public class SecondaryFunctionsHelper { } case FunctionExprent.FUNCTION_EQ: case FunctionExprent.FUNCTION_NE: + canSimplify = true; case FunctionExprent.FUNCTION_LT: case FunctionExprent.FUNCTION_GE: case FunctionExprent.FUNCTION_GT: case FunctionExprent.FUNCTION_LE: - fparam.setFuncType(funcsnot[ftype - FunctionExprent.FUNCTION_EQ]); - return fparam; + if (!canSimplify) { + operands = fparam.getLstOperands(); + VarType left = operands.get(0).getExprType(); + VarType right = operands.get(1).getExprType(); + VarType commonSupertype = VarType.getCommonSupertype(left, right); + if (commonSupertype != null) { + canSimplify = commonSupertype.type != CodeConstants.TYPE_FLOAT && commonSupertype.type != CodeConstants.TYPE_DOUBLE; + } + } + if (canSimplify) { + fparam.setFuncType(funcsnot[ftype - FunctionExprent.FUNCTION_EQ]); + return fparam; + } } } } diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 1d0dc62..2f2e5e3 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler; import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; @@ -110,6 +110,7 @@ public class SingleClassesTest { @Test public void testMissingConstructorCallGood() { doTest("pkg/TestMissingConstructorCallGood"); } @Test public void testMissingConstructorCallBad() { doTest("pkg/TestMissingConstructorCallBad"); } @Test public void testEmptyBlocks() { doTest("pkg/TestEmptyBlocks"); } + @Test public void testInvertedFloatComparison() { doTest("pkg/TestInvertedFloatComparison"); } @Test public void testPrivateEmptyConstructor() { doTest("pkg/TestPrivateEmptyConstructor"); } @Test public void testSynchronizedUnprotected() { doTest("pkg/TestSynchronizedUnprotected"); } @Test public void testInterfaceSuper() { doTest("pkg/TestInterfaceSuper"); } diff --git a/testData/classes/pkg/TestInvertedFloatComparison.class b/testData/classes/pkg/TestInvertedFloatComparison.class new file mode 100644 index 0000000000000000000000000000000000000000..29c1432cb3f7dd7619183278468cc9f6101eaf9c GIT binary patch literal 1332 zcmb7@%TC)s6o&sv?3ftm3I)_Fu%HQxL_&x~RiUa}sz?+Tpf0+bgi&NLcIr613P@Ze z+DhGY(^aJ|`T#ss)v?D%?P8`4$!@ z$xv*y-!WKQ(Xn8t?fIeDpPcnYe8l^KtXP2( z!YJ9xI-OxGcp_p3SJvK8QcmaD`7q%DgO@i`niF|i`-9~b_b{5o1F`F;KeTux#z`+c z7jYtvcY}y0ThZBw$No49J16{{d$3?LJR6-3yZ`8AsHffCfQQ5GLH|Sy5-g!0_b7cD zkV}UxV@YODyFQNoj zR%{g9SLwV{n9ku&?w@}2_@jY!i7jWWrxjlr;3~mN?CHdJ2DVA85?6HMi(3OcN3arC zbmGqj_93xKT-Azi4DeHemAI-Ce=@Mk#42%3C%!a^R|r<(+W&}GiB;mdR&3hp4#7%X z*NIJAy+*7OH+13))8X16Scw}tvFUKVAXbT+TCwRmZ4#`+O`X_uon8~G#537Vo&60^ CeUmo; literal 0 HcmV?d00001 diff --git a/testData/results/TestInvertedFloatComparison.dec b/testData/results/TestInvertedFloatComparison.dec new file mode 100644 index 0000000..e252925 --- /dev/null +++ b/testData/results/TestInvertedFloatComparison.dec @@ -0,0 +1,167 @@ +package pkg; + +public class TestInvertedFloatComparison { + public boolean less(double var1, double var3) { + return var1 < var3;// 6 + } + + public boolean less(int var1, int var2) { + return var1 < var2;// 10 + } + + public boolean notLess(double var1, double var3) { + return !(var1 < var3);// 14 + } + + public boolean notLess(int var1, int var2) { + return var1 >= var2;// 18 + } + + public boolean greater(double var1, double var3) { + return var1 > var3;// 22 + } + + public boolean greater(int var1, int var2) { + return var1 > var2;// 26 + } + + public boolean notGreater(double var1, double var3) { + return !(var1 > var3);// 30 + } + + public boolean notGreater(int var1, int var2) { + return var1 <= var2;// 34 + } + + public boolean lessEqual(double var1, double var3) { + return var1 <= var3;// 38 + } + + public boolean lessEqual(int var1, int var2) { + return var1 <= var2;// 42 + } + + public boolean notLessEqual(double var1, double var3) { + return !(var1 <= var3);// 46 + } + + public boolean notLessEqual(int var1, int var2) { + return var1 > var2;// 50 + } + + public boolean greaterEqual(double var1, double var3) { + return var1 >= var3;// 54 + } + + public boolean greaterEqual(int var1, int var2) { + return var1 >= var2;// 58 + } + + public boolean notGreaterEqual(double var1, double var3) { + return !(var1 >= var3);// 62 + } + + public boolean notGreaterEqual(int var1, int var2) { + return var1 < var2;// 66 + } +} + +class 'pkg/TestInvertedFloatComparison' { + method 'less (DD)Z' { + 2 4 + b 4 + } + + method 'less (II)Z' { + 2 8 + a 8 + } + + method 'notLess (DD)Z' { + 2 12 + b 12 + } + + method 'notLess (II)Z' { + 2 16 + a 16 + } + + method 'greater (DD)Z' { + 2 20 + b 20 + } + + method 'greater (II)Z' { + 2 24 + a 24 + } + + method 'notGreater (DD)Z' { + 2 28 + b 28 + } + + method 'notGreater (II)Z' { + 2 32 + a 32 + } + + method 'lessEqual (DD)Z' { + 2 36 + b 36 + } + + method 'lessEqual (II)Z' { + 2 40 + a 40 + } + + method 'notLessEqual (DD)Z' { + 2 44 + b 44 + } + + method 'notLessEqual (II)Z' { + 2 48 + a 48 + } + + method 'greaterEqual (DD)Z' { + 2 52 + b 52 + } + + method 'greaterEqual (II)Z' { + 2 56 + a 56 + } + + method 'notGreaterEqual (DD)Z' { + 2 60 + b 60 + } + + method 'notGreaterEqual (II)Z' { + 2 64 + a 64 + } +} + +Lines mapping: +6 <-> 5 +10 <-> 9 +14 <-> 13 +18 <-> 17 +22 <-> 21 +26 <-> 25 +30 <-> 29 +34 <-> 33 +38 <-> 37 +42 <-> 41 +46 <-> 45 +50 <-> 49 +54 <-> 53 +58 <-> 57 +62 <-> 61 +66 <-> 65 diff --git a/testData/src/pkg/TestInvertedFloatComparison.java b/testData/src/pkg/TestInvertedFloatComparison.java new file mode 100644 index 0000000..d7a06b4 --- /dev/null +++ b/testData/src/pkg/TestInvertedFloatComparison.java @@ -0,0 +1,83 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pkg; + +public class TestInvertedFloatComparison { + + public boolean less(double a, double b) { + return a < b; + } + + public boolean less(int a, int b) { + return a < b; + } + + public boolean notLess(double a, double b) { + return !(a < b); + } + + public boolean notLess(int a, int b) { + return !(a < b); + } + + public boolean greater(double a, double b) { + return a > b; + } + + public boolean greater(int a, int b) { + return a > b; + } + + public boolean notGreater(double a, double b) { + return !(a > b); + } + + public boolean notGreater(int a, int b) { + return !(a > b); + } + + public boolean lessEqual(double a, double b) { + return a <= b; + } + + public boolean lessEqual(int a, int b) { + return a <= b; + } + + public boolean notLessEqual(double a, double b) { + return !(a <= b); + } + + public boolean notLessEqual(int a, int b) { + return !(a <= b); + } + + public boolean greaterEqual(double a, double b) { + return a >= b; + } + + public boolean greaterEqual(int a, int b) { + return a >= b; + } + + public boolean notGreaterEqual(double a, double b) { + return !(a >= b); + } + + public boolean notGreaterEqual(int a, int b) { + return !(a >= b); + } +} \ No newline at end of file From 44ae885a96c19a7320983d2cf10956ca93c3644d Mon Sep 17 00:00:00 2001 From: simon816 Date: Wed, 15 Jul 2020 23:47:08 +0200 Subject: [PATCH 04/27] Ignore library classes when saving decompilation result PR #750 GitOrigin-RevId: 9751ed38b212a13022b0df9143dfa73c74a01929 --- src/org/jetbrains/java/decompiler/struct/ContextUnit.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java index f9cd2af..ef580de 100644 --- a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java +++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -92,6 +92,9 @@ public class ContextUnit { // classes for (int i = 0; i < classes.size(); i++) { StructClass cl = classes.get(i); + if (!cl.isOwn()) { + continue; + } String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i)); if (entryName != null) { String content = decompiledData.getClassContent(cl); From 0749965bc9ff42dfdfe3c9f9e00055b2eee6c69b Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 29 Jul 2020 13:15:14 +0700 Subject: [PATCH 05/27] [java-decompiler] initConstant: fix getField/putField handling GitOrigin-RevId: b741b6e3faf5d800ca2b607439b6a459bcb2bf6a --- .../java/decompiler/struct/consts/LinkConstant.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java index d8a92ca..b38800d 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java @@ -26,10 +26,11 @@ public class LinkConstant extends PooledConstant { if (type == CONSTANT_Methodref || type == CONSTANT_InterfaceMethodref || type == CONSTANT_InvokeDynamic || - type == CONSTANT_MethodHandle) { + (type == CONSTANT_MethodHandle && index1 != CONSTANT_MethodHandle_REF_getField && index1 != CONSTANT_MethodHandle_REF_putField)) { int parenth = descriptor.indexOf(')'); if (descriptor.length() < 2 || parenth < 0 || descriptor.charAt(0) != '(') { - throw new IllegalArgumentException("Invalid descriptor: " + descriptor); + throw new IllegalArgumentException("Invalid descriptor: " + descriptor + + "; type = " + type + "; classname = " + classname + "; elementname = " + elementname); } } } From 1651445c909d7b3800ac0fe5c075cb94d46c1335 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 6 Aug 2020 13:30:07 +0700 Subject: [PATCH 06/27] [java-decompiler] IDEA-246839 Support java records in decompiler Also: support preview levels in ClsFileImpl Also fixes: IDEA-247551 Exception on first opening of record .class file GitOrigin-RevId: 4362d669d1c16b8230d6d8ab803465b6a7476803 --- .../java/decompiler/main/ClassWriter.java | 81 ++++++++++++++++-- .../java/decompiler/struct/StructClass.java | 12 +++ .../struct/StructRecordComponent.java | 47 ++++++++++ .../struct/attr/StructGeneralAttribute.java | 4 + .../struct/attr/StructRecordAttribute.java | 38 ++++++++ .../java/decompiler/SingleClassesTest.java | 5 ++ testData/classes/records/TestRecordAnno.class | Bin 0 -> 1693 bytes .../classes/records/TestRecordEmpty.class | Bin 0 -> 1173 bytes .../records/TestRecordGenericVararg.class | Bin 0 -> 2028 bytes .../classes/records/TestRecordSimple.class | Bin 0 -> 1448 bytes .../classes/records/TestRecordVararg.class | Bin 0 -> 1464 bytes testData/results/TestRecordAnno.dec | 66 ++++++++++++++ testData/results/TestRecordEmpty.dec | 35 ++++++++ testData/results/TestRecordGenericVararg.dec | 66 ++++++++++++++ testData/results/TestRecordSimple.dec | 64 ++++++++++++++ testData/results/TestRecordVararg.dec | 64 ++++++++++++++ testData/src/records/TestRecordAnno.java | 17 ++++ testData/src/records/TestRecordEmpty.java | 3 + .../src/records/TestRecordGenericVararg.java | 6 ++ testData/src/records/TestRecordSimple.java | 3 + testData/src/records/TestRecordVararg.java | 3 + 21 files changed, 508 insertions(+), 6 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java create mode 100644 testData/classes/records/TestRecordAnno.class create mode 100644 testData/classes/records/TestRecordEmpty.class create mode 100644 testData/classes/records/TestRecordGenericVararg.class create mode 100644 testData/classes/records/TestRecordSimple.class create mode 100644 testData/classes/records/TestRecordVararg.class create mode 100644 testData/results/TestRecordAnno.dec create mode 100644 testData/results/TestRecordEmpty.dec create mode 100644 testData/results/TestRecordGenericVararg.dec create mode 100644 testData/results/TestRecordSimple.dec create mode 100644 testData/results/TestRecordVararg.dec create mode 100644 testData/src/records/TestRecordAnno.java create mode 100644 testData/src/records/TestRecordEmpty.java create mode 100644 testData/src/records/TestRecordGenericVararg.java create mode 100644 testData/src/records/TestRecordSimple.java create mode 100644 testData/src/records/TestRecordVararg.java diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 72fefe4..bb5790b 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -14,10 +14,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; -import org.jetbrains.java.decompiler.struct.StructClass; -import org.jetbrains.java.decompiler.struct.StructField; -import org.jetbrains.java.decompiler.struct.StructMember; -import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.*; import org.jetbrains.java.decompiler.struct.attr.*; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; @@ -28,6 +25,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.*; +import java.util.stream.Collectors; public class ClassWriter { private final PoolInterceptor interceptor; @@ -162,11 +160,19 @@ public class ClassWriter { dummy_tracer.incrementCurrentSourceLine(buffer.countLines(start_class_def)); + List components = cl.getRecordComponents(); + for (StructField fd : cl.getFields()) { boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) || wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); if (hide) continue; + if (components != null && fd.getAccessFlags() == (CodeConstants.ACC_FINAL | CodeConstants.ACC_PRIVATE) && + components.stream().anyMatch(c -> c.getName().equals(fd.getName()) && c.getDescriptor().equals(fd.getDescriptor()))) { + // Record component field: skip it + continue; + } + boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); if (isEnum) { if (enumFields) { @@ -302,6 +308,13 @@ public class ClassWriter { flags &= ~CodeConstants.ACC_FINAL; } + List components = cl.getRecordComponents(); + + if (components != null) { + // records are implicitly final + flags &= ~CodeConstants.ACC_FINAL; + } + appendModifiers(buffer, flags, CLASS_ALLOWED, isInterface, CLASS_EXCLUDED); if (isEnum) { @@ -313,6 +326,9 @@ public class ClassWriter { } buffer.append("interface "); } + else if (components != null) { + buffer.append("record "); + } else { buffer.append("class "); } @@ -324,9 +340,22 @@ public class ClassWriter { appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds); } + if (components != null) { + buffer.append('('); + for (int i = 0; i < components.size(); i++) { + StructRecordComponent cd = components.get(i); + if (i > 0) { + buffer.append(", "); + } + boolean varArgComponent = i == components.size() - 1 && isVarArgRecord(cl); + recordComponentToJava(cd, buffer, varArgComponent); + } + buffer.append(')'); + } + buffer.append(' '); - if (!isEnum && !isInterface && cl.superClass != null) { + if (!isEnum && !isInterface && components == null && cl.superClass != null) { VarType supertype = new VarType(cl.superClass.getString(), true); if (!VarType.VARTYPE_OBJECT.equals(supertype)) { buffer.append("extends "); @@ -362,6 +391,13 @@ public class ClassWriter { buffer.append('{').appendLineSeparator(); } + private boolean isVarArgRecord(StructClass cl) { + String canonicalConstructorDescriptor = + cl.getRecordComponents().stream().map(c -> c.getDescriptor()).collect(Collectors.joining("", "(", ")V")); + StructMethod ctor = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor); + return ctor != null && ctor.hasModifier(CodeConstants.ACC_VARARGS); + } + private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { int start = buffer.length(); boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE); @@ -452,6 +488,33 @@ public class ClassWriter { } } + private static void recordComponentToJava(StructRecordComponent cd, TextBuffer buffer, boolean varArgComponent) { + appendAnnotations(buffer, -1, cd, TypeAnnotation.FIELD); + + VarType fieldType = new VarType(cd.getDescriptor(), false); + + GenericFieldDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = cd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE); + if (attr != null) { + descriptor = GenericMain.parseFieldSignature(attr.getSignature()); + } + } + + if (descriptor != null) { + buffer.append(GenericMain.getGenericCastTypeName(varArgComponent ? descriptor.type.decreaseArrayDim() : descriptor.type)); + } + else { + buffer.append(ExprProcessor.getCastTypeName(varArgComponent ? fieldType.decreaseArrayDim() : fieldType)); + } + if (varArgComponent) { + buffer.append("..."); + } + buffer.append(' '); + + buffer.append(cd.getName()); + } + private static void methodLambdaToJava(ClassNode lambdaNode, ClassWrapper classWrapper, StructMethod mt, @@ -959,7 +1022,13 @@ public class ClassWriter { for (AnnotationExprent annotation : attribute.getAnnotations()) { String text = annotation.toJava(indent, BytecodeMappingTracer.DUMMY).toString(); filter.add(text); - buffer.append(text).appendLineSeparator(); + buffer.append(text); + if (indent < 0) { + buffer.append(' '); + } + else { + buffer.appendLineSeparator(); + } } } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java index 0bb370c..7a46954 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructClass.java +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -2,6 +2,8 @@ package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructRecordAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; @@ -10,6 +12,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; +import java.util.List; /* class_file { @@ -132,6 +135,15 @@ public class StructClass extends StructMember { return pool; } + /** + * @return list of record components; null if this class is not a record + */ + public List getRecordComponents() { + StructRecordAttribute recordAttr = getAttribute(StructGeneralAttribute.ATTRIBUTE_RECORD); + if (recordAttr == null) return null; + return recordAttr.getComponents(); + } + public int[] getInterfaces() { return interfaces; } diff --git a/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java b/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java new file mode 100644 index 0000000..830078c --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java @@ -0,0 +1,47 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +import java.io.IOException; + +/* + record_component_info { + u2 name_index; + u2 descriptor_index; + u2 attributes_count; + attribute_info attributes[attributes_count]; + } +*/ +public class StructRecordComponent extends StructMember { + + private final String name; + private final String descriptor; + + + public StructRecordComponent(DataInputFullStream in, ConstantPool pool) throws IOException { + accessFlags = 0; + int nameIndex = in.readUnsignedShort(); + int descriptorIndex = in.readUnsignedShort(); + + name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString(); + descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString(); + + attributes = readAttributes(in, pool); + } + + public String getName() { + return name; + } + + public String getDescriptor() { + return descriptor; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index f421671..8fff5db 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -34,6 +34,7 @@ public class StructGeneralAttribute { public static final Key ATTRIBUTE_DEPRECATED = new Key<>("Deprecated"); public static final Key ATTRIBUTE_LINE_NUMBER_TABLE = new Key<>("LineNumberTable"); public static final Key ATTRIBUTE_METHOD_PARAMETERS = new Key<>("MethodParameters"); + public static final Key ATTRIBUTE_RECORD = new Key<>("Record"); public static class Key { private final String name; @@ -97,6 +98,9 @@ public class StructGeneralAttribute { else if (ATTRIBUTE_METHOD_PARAMETERS.getName().equals(name)) { attr = new StructMethodParametersAttribute(); } + else if (ATTRIBUTE_RECORD.getName().equals(name)) { + attr = new StructRecordAttribute(); + } else { // unsupported attribute return null; diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java new file mode 100644 index 0000000..fdbb271 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java @@ -0,0 +1,38 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.struct.StructRecordComponent; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/* + Record_attribute { + u2 attribute_name_index; + u4 attribute_length; + u2 components_count; + record_component_info components[components_count]; + } + */ +public class StructRecordAttribute extends StructGeneralAttribute { + List components; + + @Override + public void initContent(DataInputFullStream data, + ConstantPool pool) throws IOException { + int componentCount = data.readUnsignedShort(); + StructRecordComponent[] components = new StructRecordComponent[componentCount]; + for (int i = 0; i < componentCount; i++) { + components[i] = new StructRecordComponent(data, pool); + } + this.components = Arrays.asList(components); + } + + public List getComponents() { + return Collections.unmodifiableList(components); + } +} diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 2f2e5e3..c720949 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -129,6 +129,11 @@ public class SingleClassesTest { @Test public void testSuspendLambda() { doTest("pkg/TestSuspendLambdaKt"); } @Test public void testNamedSuspendFun2Kt() { doTest("pkg/TestNamedSuspendFun2Kt"); } @Test public void testGenericArgs() { doTest("pkg/TestGenericArgs"); } + @Test public void testRecordEmpty() { doTest("records/TestRecordEmpty"); } + @Test public void testRecordSimple() { doTest("records/TestRecordSimple"); } + @Test public void testRecordVararg() { doTest("records/TestRecordVararg"); } + @Test public void testRecordGenericVararg() { doTest("records/TestRecordGenericVararg"); } + @Test public void testRecordAnno() { doTest("records/TestRecordAnno"); } private void doTest(String testFile, String... companionFiles) { ConsoleDecompiler decompiler = fixture.getDecompiler(); diff --git a/testData/classes/records/TestRecordAnno.class b/testData/classes/records/TestRecordAnno.class new file mode 100644 index 0000000000000000000000000000000000000000..6751bc0ed00e1c00fdd8589ca5bce8fe14eec6b8 GIT binary patch literal 1693 zcma)6+fEZv6kVqjrY(bo0+pM93fh)S#k&?!E-^MKN`Ua7PeVC@fihEPrUd@U2T3F* zJoo{Al((cdnz`SMI0^*QgUTNRa5m&0>C;^~L zM*`gf5zk)p9Mjqsm`oL#v*gl+nm4|7PI2_=Na8Lzb_{oC(cY2*gV%H96{KIs00tS0 z{9Z9iuE5l_fI6FBHg{#w%cZ{xjI35H&)k=J>%er)&5~R{DoYirXLzPj@^fW>Q#$Jg;R1<*T{KD?hGVLGAPjpuCb{}*Tn%|8Z|Ab~cI>Sc z!!h=yCmm{!UH0wbvBAOi3(^(x^rV_(w_4-8KyRvf76dDsxcbEWUv}Xd%geMJTeB-p zQGPU47P=c7XId4UWjgP@T2faRRqzY8?YW*~l>IEY@ish<;RRl5c%|bt-mp@&o^kw| zW&Jt^5P|6CIg7w?eWa#!VDHJSulUKZwo1|+E7LB1g*HK>;5AcvTf&xS8q{u@Uqcj?#Scy>eAPSM>QMoYAkFOx* zBd%Ta=`qjBj~VXDkd`?`C-9TUtM*k;R0-^N@i&ud0`CES+zLK`bO4{FG38ezGjWQ( z>+nfm=OX+FGMFHPV7w{5sW`ILI}?`q1??s`S_7J(NOW3dNHA^9s5)*#HAwIobU9Ir@Heg*u+4v4Dhyq>V)^F=S8qId?qnpEw^x zM+RMnr32S@^>cihPo=;(U+qlE&)#tfqj z$H6Z`u-_Izi?=;8mT$@q_l|ksnmF1_>Yhs$iZcsmD3qoYKkj3LVXZXHA_J2~ zaMH8LJwy6P4ug((?HWskzuH_i0P4*3rDb18sg(}+;H}U-*;Sb(?4+@aM;0F2D51=- zJrN0pzIOY<3B@7^!*ctYh2b*p*Y(fknQ+3yH{9=fLhUrAJR1(`G4IiR$95uM>tY~Y z3e^eRftEoWa_DiT;u{gir+-P}>VN!ZQOi=;V*rEQ@O=@CiG{LIW!RlmKBp23PZ_r6 zr&7zqvQfh-JtgxcNs}HEjcXJ+G?NW1R=$B<(qaMYG@4@|fejk%Faftvq$o$P>o)Be zaE)e@XH`b9fUmTi1-E{~S8!(v{08vlzu-A+M(_g?GbN=edn06K;n%~QNS?BB7hA-T f#P(S49`284-~r8PI(bNlRaEdKiub8WH57jVVV)N(-H=<8g_AaVwI2nj}Ka< zWfc#8fFGrv*CIRW#OK6L}2K%~K8O3+#Wn-LC+kU}8TG2xOJFpnS(A@)fJPOZG}J z0TzW-G}w@dgE&O6h8;BKyk#jc{uf~LI`*)MBREP`>)vMc0UV@{~v9U)r z95Oz>t8M~Ao$>G1H!)7{7CglordiCuW2qY6@-z0qnpDzf;+)0*bd8$&@bi|doHe=V z1P&?AxUQ#c<#;X~4<-ayuvcW%OaW03SY7TkY|VLr!7jpSrG@uk>f+#U2e)?yy|(c zTkDlX4e=vMSmMAMJK(Mi>b|qCJUMvzQUs6Ap|@I1lmy z27mnm=Df?X66TVDf%^id+G2Ep-z?6+LxGW1EO0mtwFEM9j2^68AdY1lN5Wnv>jXj&oc!38S<@J3exo z5}OZbJIN97R_TH2j|%?fP=t?e*`>&b1~o(7NynCmL@hZ{3pbr$Y)EwEXpCt zHrXuy(`4C8I8Sd~{R~gKwBFKZ7(3P8yRCL@(Qr{KTp&yy7rVgr{}1dXHb<1;GOjRL o3RiKB>j-z9o!Hrj*XgsDly0ySv$%s<-t%4Bai_@% literal 0 HcmV?d00001 diff --git a/testData/classes/records/TestRecordSimple.class b/testData/classes/records/TestRecordSimple.class new file mode 100644 index 0000000000000000000000000000000000000000..91c748e44125980338aff60664e223a85595a093 GIT binary patch literal 1448 zcma)5T~pIQ6g{`JO{?#(&(oO?I-_n*^WXJ;_*GKU0)6eLxokS2`onFprcH0@pe zlc+gvoseF)Y|DQ|7%G-_vdAE-AgAIARKk=SfINL$cz#IOvRdt?AkZPNvc!+5$V)IC z6#$H>DBvm~<2zfvYuUSm)ncVz9-~z0ItuZI%rc>364y9O!}J;(PMrnk2W2Ts(zJ>h z%yLEIYsYMQ1bqM{ru(6~Cu+V?`bNCkp)W6=#=RU?p)0trVhs-ni(M7BWBXQ1=%HFf zA!MuPSqR5*!cu#}>LCo=&dA@DR;pIKA{RC;VCs4Gx6AT}7D1n6t4u&l9 z`FaTD{RZEd6r`r$B$Fp_lh4@%hvi3}R93ykdQxePcL`OrQ;Y)N`FK%54Iz=j7*FU@ z5BfOp?Nam^EJyTp7L)zRXe+0f>c^i9F_Ao@aT|9y193&?M)cqgq_iJU2H8kOX^30V ui#&W8auUU$(h@Y@N7!HZn&Dfn1Tn&0B-!gjJjN3|VlC+a literal 0 HcmV?d00001 diff --git a/testData/classes/records/TestRecordVararg.class b/testData/classes/records/TestRecordVararg.class new file mode 100644 index 0000000000000000000000000000000000000000..a720d7f271369d6dc0427822def2a586a1fb8478 GIT binary patch literal 1464 zcma)6T~pIQ6g^wormcZO1>{3MRM0da5x+GEGvYXQrl=zf5Au|@J2X(THAxwG^#AxE zGcwNb;1BReiFcFKMxh-alHI+z=iYPA-J74k&VHPq!^Bz+3G^vQsz@QtFt}$OT1LZi zc8w3bD%=`Fdc}5Zf0dz6D{g0zK~_Od#TBRwV{YK&8C%@*L%_D>TJA0bJEEr~{RdR! zF-WIlim|g(DgY>`xQbzhj4w8Q*LDc7q?NlxGmGU8uh8F=QASlB$3*;VDP07zURAEGvw#x(=c+lhh+u#RXo5$ zhM7(_x8?Zu0XM>|5riRIX-8o=i4(V-L$S||(D9As)EeBID+{sTYMQYkqTa_W5pe6c z$zO4=>e@|TxN*pO!}7eiA&RkcF-Sc7Uso>DE`!IU`jL5@rz=2DKZ9Cw9PS1=xTnBm znD2lKPibj~ppl)A&q-J4Iin>9iq-nby4i9Za7* zg`zK>qW^?U3CslUjNW~irT1*;iW|5|Cjteb<@bCLJb_!Z&nCz$O~4S1>>P+T?U!kl zu8Mv}c6_Dn#SCib5@i^oS)IQGJ_3CCFZd*GNAML&CiTeZ3uhSbh93=m{uG$O9cUDR zQAKScMsNmF`Zp-OXr!VrjHD<=G~(d?UJ|syN$5eP3s|IefcS;o410Muu+fhYN$U3_ SJi${uBU_HrJ;w@GG5#CoI5FA) literal 0 HcmV?d00001 diff --git a/testData/results/TestRecordAnno.dec b/testData/results/TestRecordAnno.dec new file mode 100644 index 0000000..1df3349 --- /dev/null +++ b/testData/results/TestRecordAnno.dec @@ -0,0 +1,66 @@ +package records; + +public record TestRecordAnno(@RC @TA int x, int y) { + public TestRecordAnno(@TA int x, @P int y) { + this.x = x; + this.y = y; + } + + public final String toString() { + return this.toString(this); + } + + public final int hashCode() { + return this.hashCode(this); + } + + public final boolean equals(Object o) { + return this.equals(this, o); + } + + @TA + public int x() { + return this.x; + } + + @M + public int y() { + return this.y;// 5 + } +} + +class 'records/TestRecordAnno' { + method ' (II)V' { + 6 4 + b 5 + e 6 + } + + method 'toString ()Ljava/lang/String;' { + 1 9 + 6 9 + } + + method 'hashCode ()I' { + 1 13 + 6 13 + } + + method 'equals (Ljava/lang/Object;)Z' { + 2 17 + 7 17 + } + + method 'x ()I' { + 1 22 + 4 22 + } + + method 'y ()I' { + 1 27 + 4 27 + } +} + +Lines mapping: +5 <-> 28 diff --git a/testData/results/TestRecordEmpty.dec b/testData/results/TestRecordEmpty.dec new file mode 100644 index 0000000..6111ddf --- /dev/null +++ b/testData/results/TestRecordEmpty.dec @@ -0,0 +1,35 @@ +package records; + +public record TestRecordEmpty() { + public final String toString() { + return this.toString(this); + } + + public final int hashCode() { + return this.hashCode(this); + } + + public final boolean equals(Object o) { + return this.equals(this, o);// 3 + } +} + +class 'records/TestRecordEmpty' { + method 'toString ()Ljava/lang/String;' { + 1 4 + 6 4 + } + + method 'hashCode ()I' { + 1 8 + 6 8 + } + + method 'equals (Ljava/lang/Object;)Z' { + 2 12 + 7 12 + } +} + +Lines mapping: +3 <-> 13 diff --git a/testData/results/TestRecordGenericVararg.dec b/testData/results/TestRecordGenericVararg.dec new file mode 100644 index 0000000..1a52a25 --- /dev/null +++ b/testData/results/TestRecordGenericVararg.dec @@ -0,0 +1,66 @@ +package records; + +public record TestRecordGenericVararg(T first, T... other) { + @SafeVarargs + public TestRecordGenericVararg(T first, T... other) { + this.first = first;// 5 + this.other = other; + } + + public final String toString() { + return this.toString(this); + } + + public final int hashCode() { + return this.hashCode(this); + } + + public final boolean equals(Object o) { + return this.equals(this, o); + } + + public T first() { + return this.first; + } + + public T[] other() { + return this.other;// 3 + } +} + +class 'records/TestRecordGenericVararg' { + method ' (Ljava/lang/Object;[Ljava/lang/Object;)V' { + 6 5 + b 6 + e 7 + } + + method 'toString ()Ljava/lang/String;' { + 1 10 + 6 10 + } + + method 'hashCode ()I' { + 1 14 + 6 14 + } + + method 'equals (Ljava/lang/Object;)Z' { + 2 18 + 7 18 + } + + method 'first ()Ljava/lang/Object;' { + 1 22 + 4 22 + } + + method 'other ()[Ljava/lang/Object;' { + 1 26 + 4 26 + } +} + +Lines mapping: +3 <-> 27 +5 <-> 6 diff --git a/testData/results/TestRecordSimple.dec b/testData/results/TestRecordSimple.dec new file mode 100644 index 0000000..fd1bf45 --- /dev/null +++ b/testData/results/TestRecordSimple.dec @@ -0,0 +1,64 @@ +package records; + +public record TestRecordSimple(int x, int y) { + public TestRecordSimple(int x, int y) { + this.x = x; + this.y = y; + } + + public final String toString() { + return this.toString(this); + } + + public final int hashCode() { + return this.hashCode(this); + } + + public final boolean equals(Object o) { + return this.equals(this, o); + } + + public int x() { + return this.x; + } + + public int y() { + return this.y;// 3 + } +} + +class 'records/TestRecordSimple' { + method ' (II)V' { + 6 4 + b 5 + e 6 + } + + method 'toString ()Ljava/lang/String;' { + 1 9 + 6 9 + } + + method 'hashCode ()I' { + 1 13 + 6 13 + } + + method 'equals (Ljava/lang/Object;)Z' { + 2 17 + 7 17 + } + + method 'x ()I' { + 1 21 + 4 21 + } + + method 'y ()I' { + 1 25 + 4 25 + } +} + +Lines mapping: +3 <-> 26 diff --git a/testData/results/TestRecordVararg.dec b/testData/results/TestRecordVararg.dec new file mode 100644 index 0000000..d0f4b66 --- /dev/null +++ b/testData/results/TestRecordVararg.dec @@ -0,0 +1,64 @@ +package records; + +public record TestRecordVararg(int x, int[]... y) { + public TestRecordVararg(int x, int[]... y) { + this.x = x; + this.y = y; + } + + public final String toString() { + return this.toString(this); + } + + public final int hashCode() { + return this.hashCode(this); + } + + public final boolean equals(Object o) { + return this.equals(this, o); + } + + public int x() { + return this.x; + } + + public int[][] y() { + return this.y;// 3 + } +} + +class 'records/TestRecordVararg' { + method ' (I[[I)V' { + 6 4 + b 5 + e 6 + } + + method 'toString ()Ljava/lang/String;' { + 1 9 + 6 9 + } + + method 'hashCode ()I' { + 1 13 + 6 13 + } + + method 'equals (Ljava/lang/Object;)Z' { + 2 17 + 7 17 + } + + method 'x ()I' { + 1 21 + 4 21 + } + + method 'y ()[[I' { + 1 25 + 4 25 + } +} + +Lines mapping: +3 <-> 26 diff --git a/testData/src/records/TestRecordAnno.java b/testData/src/records/TestRecordAnno.java new file mode 100644 index 0000000..c61a5b5 --- /dev/null +++ b/testData/src/records/TestRecordAnno.java @@ -0,0 +1,17 @@ +package records; + +import java.lang.annotation.*; + +public record TestRecordAnno(@TA @RC int x, @M @P int y) {} + +@Target(ElementType.TYPE_USE) +@interface TA {} + +@Target(ElementType.RECORD_COMPONENT) +@interface RC {} + +@Target(ElementType.METHOD) +@interface M {} + +@Target(ElementType.PARAMETER) +@interface P {} diff --git a/testData/src/records/TestRecordEmpty.java b/testData/src/records/TestRecordEmpty.java new file mode 100644 index 0000000..321165a --- /dev/null +++ b/testData/src/records/TestRecordEmpty.java @@ -0,0 +1,3 @@ +package records; + +public record TestRecordEmpty() {} \ No newline at end of file diff --git a/testData/src/records/TestRecordGenericVararg.java b/testData/src/records/TestRecordGenericVararg.java new file mode 100644 index 0000000..44e8fdc --- /dev/null +++ b/testData/src/records/TestRecordGenericVararg.java @@ -0,0 +1,6 @@ +package records; + +public record TestRecordGenericVararg(T first, T... other) { + @SafeVarargs + public TestRecordGenericVararg {} +} \ No newline at end of file diff --git a/testData/src/records/TestRecordSimple.java b/testData/src/records/TestRecordSimple.java new file mode 100644 index 0000000..3bc1e1a --- /dev/null +++ b/testData/src/records/TestRecordSimple.java @@ -0,0 +1,3 @@ +package records; + +public record TestRecordSimple(int x, int y) {} \ No newline at end of file diff --git a/testData/src/records/TestRecordVararg.java b/testData/src/records/TestRecordVararg.java new file mode 100644 index 0000000..8d23ed0 --- /dev/null +++ b/testData/src/records/TestRecordVararg.java @@ -0,0 +1,3 @@ +package records; + +public record TestRecordVararg(int x, int[]... y) {} \ No newline at end of file From f61e659e58c650330fae5fb28f6336b8b1b82d09 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 7 Aug 2020 17:03:22 +0700 Subject: [PATCH 07/27] [java-stubs] Hide synthetic equals/hashCode/toString in records (stubs+decompiler) Makes IDEA-247576 obsolete Review ID: IJ-CR-2597 GitOrigin-RevId: 4dbb09153b683f2c191d8ba89a3c4ad8c3da038d --- .../java/decompiler/main/ClassWriter.java | 18 ++++++++- .../classes/records/TestRecordEmpty.class | Bin 1173 -> 1127 bytes testData/results/TestRecordAnno.dec | 37 +++--------------- testData/results/TestRecordEmpty.dec | 28 +++---------- testData/results/TestRecordGenericVararg.dec | 37 +++--------------- testData/results/TestRecordSimple.dec | 37 +++--------------- testData/results/TestRecordVararg.dec | 37 +++--------------- testData/src/records/TestRecordEmpty.java | 6 ++- 8 files changed, 47 insertions(+), 153 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index bb5790b..aa03fa5 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -262,6 +262,21 @@ public class ClassWriter { DecompilerContext.getLogger().endWriteClass(); } + private static boolean isSyntheticRecordMethod(StructMethod mt, TextBuffer code) { + if (mt.getClassStruct().getRecordComponents() == null) return false; + String name = mt.getName(); + String descriptor = mt.getDescriptor(); + if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") || + name.equals("hashCode") && descriptor.equals("()I") || + name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) { + if (code.countLines() == 1) { + String str = code.toString().trim(); + return str.startsWith("return this." + name + "(this"); + } + } + return false; + } + private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) { StructLineNumberTableAttribute table = method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE); tracer.setLineNumberTable(table); @@ -880,7 +895,8 @@ public class ClassWriter { BytecodeMappingTracer codeTracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine()); TextBuffer code = root.toJava(indent + 1, codeTracer); - hideMethod = (code.length() == 0) && (clinit || dinit || hideConstructor(node, init, throwsExceptions, paramCount, flags)); + hideMethod = (code.length() == 0) && (clinit || dinit || hideConstructor(node, init, throwsExceptions, paramCount, flags)) + || isSyntheticRecordMethod(mt, code); buffer.append(code); diff --git a/testData/classes/records/TestRecordEmpty.class b/testData/classes/records/TestRecordEmpty.class index b4b6080c5a20189d025441b7b599832b9dc11353..027b70022032892456f63f405b798d8f6b5dbf3d 100644 GIT binary patch delta 445 zcmZ9HOHPAO7=_Q}Ar#SCt!RDVy8=~-Q>&wnNSwI>5=pD6n3Tq8bqPq^g##U#Fz5nY zhly)&p!L7Nfb&DTZn(|nrhFFdY$|0fI+lu zkD+eam$&|d@Amz$>s}29H(|G(Jh9>Yj)qfMy~JUxvj12Hgw8V9@FNQ4~Z9 zTFI~ANXHE3vuGxssE?`;z>h>>88f6cnp|??Av+&n?{pEcNXnl95lf_c9zY$-G%*-h zA?5H9InN2=E7uY!mq;NaM&P{m210B=)#xmhWiYnVf$z|X0TucJDx6tlo| rf6g0uCgU$C4Hauxr!ORIWQChBGY7WFHG0`5q>4k>#q7|MCusZwteG%L delta 430 zcmZvXJx{`L5XOK1wpb`dEmg3B@1nejZ>uB@OpI=>B$6;FCe|HC*QW7XI5;!7x%d&B zP5c(tYay5@%RP6O=XX!;EB%zQ-oHn$QN$Uif%Rag`(3}^zP;)x%E3h`+sOlqHV*mV zv89%Rx0|r7iiTdTb;3(^Kfh~HVb5gWrb;dRP@i5yfx_+#2EDuXb=UWMzR97Y66Umu zZgLz(TGMsWxMd1{+D>CR>TZ Vwy1@t*b&5~&Ox$JG&z!Ay&s+qEE)g+ diff --git a/testData/results/TestRecordAnno.dec b/testData/results/TestRecordAnno.dec index 1df3349..931c3e6 100644 --- a/testData/results/TestRecordAnno.dec +++ b/testData/results/TestRecordAnno.dec @@ -6,18 +6,6 @@ public record TestRecordAnno(@RC @TA int x, int y) { this.y = y; } - public final String toString() { - return this.toString(this); - } - - public final int hashCode() { - return this.hashCode(this); - } - - public final boolean equals(Object o) { - return this.equals(this, o); - } - @TA public int x() { return this.x; @@ -36,31 +24,16 @@ class 'records/TestRecordAnno' { e 6 } - method 'toString ()Ljava/lang/String;' { - 1 9 - 6 9 - } - - method 'hashCode ()I' { - 1 13 - 6 13 - } - - method 'equals (Ljava/lang/Object;)Z' { - 2 17 - 7 17 - } - method 'x ()I' { - 1 22 - 4 22 + 1 10 + 4 10 } method 'y ()I' { - 1 27 - 4 27 + 1 15 + 4 15 } } Lines mapping: -5 <-> 28 +5 <-> 16 diff --git a/testData/results/TestRecordEmpty.dec b/testData/results/TestRecordEmpty.dec index 6111ddf..9ac6460 100644 --- a/testData/results/TestRecordEmpty.dec +++ b/testData/results/TestRecordEmpty.dec @@ -1,35 +1,17 @@ package records; public record TestRecordEmpty() { - public final String toString() { - return this.toString(this); - } - - public final int hashCode() { - return this.hashCode(this); - } - - public final boolean equals(Object o) { - return this.equals(this, o);// 3 + public int hashCode() { + return 0;// 5 } } class 'records/TestRecordEmpty' { - method 'toString ()Ljava/lang/String;' { - 1 4 - 6 4 - } - method 'hashCode ()I' { - 1 8 - 6 8 - } - - method 'equals (Ljava/lang/Object;)Z' { - 2 12 - 7 12 + 0 4 + 1 4 } } Lines mapping: -3 <-> 13 +5 <-> 5 diff --git a/testData/results/TestRecordGenericVararg.dec b/testData/results/TestRecordGenericVararg.dec index 1a52a25..1d417b4 100644 --- a/testData/results/TestRecordGenericVararg.dec +++ b/testData/results/TestRecordGenericVararg.dec @@ -7,18 +7,6 @@ public record TestRecordGenericVararg(T first, T... other) { this.other = other; } - public final String toString() { - return this.toString(this); - } - - public final int hashCode() { - return this.hashCode(this); - } - - public final boolean equals(Object o) { - return this.equals(this, o); - } - public T first() { return this.first; } @@ -35,32 +23,17 @@ class 'records/TestRecordGenericVararg' { e 7 } - method 'toString ()Ljava/lang/String;' { - 1 10 - 6 10 - } - - method 'hashCode ()I' { - 1 14 - 6 14 - } - - method 'equals (Ljava/lang/Object;)Z' { - 2 18 - 7 18 - } - method 'first ()Ljava/lang/Object;' { - 1 22 - 4 22 + 1 10 + 4 10 } method 'other ()[Ljava/lang/Object;' { - 1 26 - 4 26 + 1 14 + 4 14 } } Lines mapping: -3 <-> 27 +3 <-> 15 5 <-> 6 diff --git a/testData/results/TestRecordSimple.dec b/testData/results/TestRecordSimple.dec index fd1bf45..222805f 100644 --- a/testData/results/TestRecordSimple.dec +++ b/testData/results/TestRecordSimple.dec @@ -6,18 +6,6 @@ public record TestRecordSimple(int x, int y) { this.y = y; } - public final String toString() { - return this.toString(this); - } - - public final int hashCode() { - return this.hashCode(this); - } - - public final boolean equals(Object o) { - return this.equals(this, o); - } - public int x() { return this.x; } @@ -34,31 +22,16 @@ class 'records/TestRecordSimple' { e 6 } - method 'toString ()Ljava/lang/String;' { - 1 9 - 6 9 - } - - method 'hashCode ()I' { - 1 13 - 6 13 - } - - method 'equals (Ljava/lang/Object;)Z' { - 2 17 - 7 17 - } - method 'x ()I' { - 1 21 - 4 21 + 1 9 + 4 9 } method 'y ()I' { - 1 25 - 4 25 + 1 13 + 4 13 } } Lines mapping: -3 <-> 26 +3 <-> 14 diff --git a/testData/results/TestRecordVararg.dec b/testData/results/TestRecordVararg.dec index d0f4b66..850ed38 100644 --- a/testData/results/TestRecordVararg.dec +++ b/testData/results/TestRecordVararg.dec @@ -6,18 +6,6 @@ public record TestRecordVararg(int x, int[]... y) { this.y = y; } - public final String toString() { - return this.toString(this); - } - - public final int hashCode() { - return this.hashCode(this); - } - - public final boolean equals(Object o) { - return this.equals(this, o); - } - public int x() { return this.x; } @@ -34,31 +22,16 @@ class 'records/TestRecordVararg' { e 6 } - method 'toString ()Ljava/lang/String;' { - 1 9 - 6 9 - } - - method 'hashCode ()I' { - 1 13 - 6 13 - } - - method 'equals (Ljava/lang/Object;)Z' { - 2 17 - 7 17 - } - method 'x ()I' { - 1 21 - 4 21 + 1 9 + 4 9 } method 'y ()[[I' { - 1 25 - 4 25 + 1 13 + 4 13 } } Lines mapping: -3 <-> 26 +3 <-> 14 diff --git a/testData/src/records/TestRecordEmpty.java b/testData/src/records/TestRecordEmpty.java index 321165a..f478efa 100644 --- a/testData/src/records/TestRecordEmpty.java +++ b/testData/src/records/TestRecordEmpty.java @@ -1,3 +1,7 @@ package records; -public record TestRecordEmpty() {} \ No newline at end of file +public record TestRecordEmpty() { + public int hashCode() { + return 0; + } +} \ No newline at end of file From 50691f39fb1428168481b86fac8251379446f1a4 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 2 Nov 2020 10:08:33 +0100 Subject: [PATCH 08/27] cleanup (final) GitOrigin-RevId: 69b73056f0c524ad7b6e7c5c2b3a4b58f64feed9 --- .../java/decompiler/code/interpreter/InstructionImpact.java | 4 ++-- src/org/jetbrains/java/decompiler/main/AssertProcessor.java | 4 ++-- .../java/decompiler/main/ClassReference14Processor.java | 4 ++-- src/org/jetbrains/java/decompiler/main/EnumProcessor.java | 4 ++-- .../java/decompiler/main/InitializerProcessor.java | 4 ++-- .../java/decompiler/modules/code/DeadCodeHelper.java | 4 ++-- .../java/decompiler/modules/decompiler/ClasspathHelper.java | 4 ++-- .../decompiler/modules/decompiler/ClearStructHelper.java | 4 ++-- .../decompiler/modules/decompiler/ConcatenationHelper.java | 4 ++-- .../java/decompiler/modules/decompiler/DecHelper.java | 4 ++-- .../java/decompiler/modules/decompiler/DomHelper.java | 4 ++-- .../java/decompiler/modules/decompiler/ExitHelper.java | 4 ++-- .../decompiler/modules/decompiler/IdeaNotNullHelper.java | 4 ++-- .../java/decompiler/modules/decompiler/IfHelper.java | 4 ++-- .../modules/decompiler/InlineSingleBlockHelper.java | 4 ++-- .../java/decompiler/modules/decompiler/LabelHelper.java | 4 ++-- .../decompiler/modules/decompiler/LoopExtractHelper.java | 4 ++-- .../java/decompiler/modules/decompiler/MergeHelper.java | 4 ++-- .../modules/decompiler/SecondaryFunctionsHelper.java | 2 +- .../java/decompiler/modules/decompiler/SequenceHelper.java | 4 ++-- .../java/decompiler/modules/decompiler/SwitchHelper.java | 4 ++-- .../decompiler/deobfuscator/ExceptionDeobfuscator.java | 2 +- .../decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java | 4 ++-- .../java/decompiler/modules/decompiler/exps/ExprUtil.java | 4 ++-- .../decompiler/modules/decompiler/stats/Statements.java | 4 ++-- .../java/decompiler/struct/gen/generics/GenericMain.java | 4 ++-- src/org/jetbrains/java/decompiler/util/InterpreterUtil.java | 4 ++-- src/org/jetbrains/java/decompiler/util/TextUtil.java | 6 ++---- 28 files changed, 54 insertions(+), 56 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java b/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java index 5c56593..aeafe5c 100644 --- a/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java +++ b/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code.interpreter; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -12,7 +12,7 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.ListStack; -public class InstructionImpact { +public final class InstructionImpact { // {read, write} private static final int[][][] stack_impact = { diff --git a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java index b0e7b71..a8863c5 100644 --- a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -21,7 +21,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public class AssertProcessor { +public final class AssertProcessor { private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/AssertionError"); diff --git a/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java b/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java index 41b5a9a..e082267 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -22,7 +22,7 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.util.*; import java.util.Map.Entry; -public class ClassReference14Processor { +public final class ClassReference14Processor { private static final ExitExprent BODY_EXPR; private static final ExitExprent HANDLER_EXPR; diff --git a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java index 08598e8..7f6beef 100644 --- a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -13,7 +13,7 @@ import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.util.InterpreterUtil; -public class EnumProcessor { +public final class EnumProcessor { public static void clearEnum(ClassWrapper wrapper) { StructClass cl = wrapper.getClassStruct(); diff --git a/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java b/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java index b929d0d..d3696dd 100644 --- a/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -18,7 +18,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; import java.util.ArrayList; import java.util.List; -public class InitializerProcessor { +public final class InitializerProcessor { public static void extractInitializers(ClassWrapper wrapper) { MethodWrapper method = wrapper.getMethodWrapper(CodeConstants.CLINIT_NAME, "()V"); if (method != null && method.root != null) { // successfully decompiled static constructor diff --git a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java index 9d28d0e..421ed98 100644 --- a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.code; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -13,7 +13,7 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import java.util.*; -public class DeadCodeHelper { +public final class DeadCodeHelper { public static void removeDeadBlocks(ControlFlowGraph graph) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java index 6e1155d..d6a7ed8 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; @@ -9,7 +9,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -public class ClasspathHelper { +public final class ClasspathHelper { private static final Map METHOD_CACHE = Collections.synchronizedMap(new HashMap<>()); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java index ede1dda..14576cc 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; @@ -7,7 +7,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import java.util.LinkedList; -public class ClearStructHelper { +public final class ClearStructHelper { public static void clearStatements(RootStatement root) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java index 7d5c207..3b255be 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -13,7 +13,7 @@ import java.util.Arrays; import java.util.List; import java.util.Set; -public class ConcatenationHelper { +public final class ConcatenationHelper { private static final String builderClass = "java/lang/StringBuilder"; private static final String bufferClass = "java/lang/StringBuffer"; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java index 1549734..50613fc 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; @@ -10,7 +10,7 @@ import java.util.List; import java.util.Set; -public class DecHelper { +public final class DecHelper { public static boolean checkStatementExceptions(List lst) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java index 2c5e3eb..7ef6d2f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; @@ -16,7 +16,7 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.util.*; -public class DomHelper { +public final class DomHelper { private static RootStatement graphToStatement(ControlFlowGraph graph) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java index 3b9847f..8e0ac9c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; @@ -13,7 +13,7 @@ import java.util.Arrays; import java.util.List; import java.util.Set; -public class ExitHelper { +public final class ExitHelper { public static boolean condenseExits(RootStatement root) { int changed = integrateExits(root); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java index 9598b3c..ff45d29 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -14,7 +14,7 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import java.util.List; -public class IdeaNotNullHelper { +public final class IdeaNotNullHelper { public static boolean removeHardcodedChecks(Statement root, StructMethod mt) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java index 89bfae3..afcf55e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; @@ -11,7 +11,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import java.util.*; -public class IfHelper { +public final class IfHelper { public static boolean mergeAllIfs(RootStatement root) { boolean res = mergeAllIfsRec(root, new HashSet<>()); if (res) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java index f6ee600..0c94761 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.stats.*; @@ -7,7 +7,7 @@ import java.util.ArrayList; import java.util.List; -public class InlineSingleBlockHelper { +public final class InlineSingleBlockHelper { public static boolean inlineSingleBlocks(RootStatement root) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java index 9c150b5..bf4d889 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; @@ -8,7 +8,7 @@ import java.util.*; import java.util.Map.Entry; -public class LabelHelper { +public final class LabelHelper { public static void cleanUpEdges(RootStatement root) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java index 8229127..b82562e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; @@ -11,7 +11,7 @@ import java.util.Arrays; import java.util.Set; -public class LoopExtractHelper { +public final class LoopExtractHelper { public static boolean extractLoops(Statement root) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index ed9f368..77c3dc3 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; @@ -12,7 +12,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -public class MergeHelper { +public final class MergeHelper { public static void enhanceLoops(Statement root) { while (enhanceLoopsRec(root)) /**/; SequenceHelper.condenseSequences(root); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java index 17bf65e..beaa6c2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java @@ -16,7 +16,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; -public class SecondaryFunctionsHelper { +public final class SecondaryFunctionsHelper { private static final int[] funcsnot = new int[]{ FunctionExprent.FUNCTION_NE, diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java index 4fe4127..2e00b7a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; @@ -13,7 +13,7 @@ import java.util.HashSet; import java.util.List; -public class SequenceHelper { +public final class SequenceHelper { public static void condenseSequences(Statement root) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java index b49bf39..1e08a6d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -14,7 +14,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class SwitchHelper { +public final class SwitchHelper { public static void simplify(SwitchStatement switchStatement) { SwitchExprent switchExprent = (SwitchExprent)switchStatement.getHeadexprent(); Exprent value = switchExprent.getValue(); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java index cc02d0f..c0a7769 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java @@ -18,7 +18,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; import java.util.*; import java.util.Map.Entry; -public class ExceptionDeobfuscator { +public final class ExceptionDeobfuscator { private static final class Range { private final BasicBlock handler; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java index 7fecbd9..9cab6a5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.deobfuscator; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; @@ -10,7 +10,7 @@ import java.util.HashSet; import java.util.Set; -public class IrreducibleCFGDeobfuscator { +public final class IrreducibleCFGDeobfuscator { public static boolean isStatementIrreducible(Statement statement) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java index 302d62f..58a653a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -13,7 +13,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class ExprUtil { +public final class ExprUtil { public static List getSyntheticParametersMask(String className, String descriptor, int parameters) { ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(className); return node != null ? getSyntheticParametersMask(node, descriptor, parameters) : null; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java index cadf50f..891ba35 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.main.rels.ClassWrapper; @@ -8,7 +8,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; -public class Statements { +public final class Statements { public static Statement findFirstData(Statement stat) { if (stat.getExprents() != null) { return stat; diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java index 3e4e1f1..8af6b50 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.gen.generics; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -9,7 +9,7 @@ import org.jetbrains.java.decompiler.util.TextUtil; import java.util.ArrayList; import java.util.List; -public class GenericMain { +public final class GenericMain { private static final String[] typeNames = { "byte", diff --git a/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java b/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java index 1d29253..0ba7dc4 100644 --- a/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java +++ b/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.util; import java.io.*; @@ -8,7 +8,7 @@ import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -public class InterpreterUtil { +public final class InterpreterUtil { public static final boolean IS_WINDOWS = System.getProperty("os.name", "").startsWith("Windows"); public static final int[] EMPTY_INT_ARRAY = new int[0]; diff --git a/src/org/jetbrains/java/decompiler/util/TextUtil.java b/src/org/jetbrains/java/decompiler/util/TextUtil.java index 3d92d2e..e2a08f0 100644 --- a/src/org/jetbrains/java/decompiler/util/TextUtil.java +++ b/src/org/jetbrains/java/decompiler/util/TextUtil.java @@ -1,6 +1,4 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - */ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.util; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -12,7 +10,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import java.util.Arrays; import java.util.HashSet; -public class TextUtil { +public final class TextUtil { private static final HashSet KEYWORDS = new HashSet<>(Arrays.asList( "abstract", "default", "if", "private", "this", "boolean", "do", "implements", "protected", "throw", "break", "double", "import", "public", "throws", "byte", "else", "instanceof", "return", "transient", "case", "extends", "int", "short", "try", "catch", "final", From 7f65f48b3f714fb0155b36c9750cd27070014e8f Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 12 Nov 2020 18:06:44 +0100 Subject: [PATCH 09/27] use more high-level methods to copy arrays instead of low-level cryptic System.arraycopy() GitOrigin-RevId: 16b9869eae4200f4ff24c4535d7f33d8e6847b4c --- .../struct/gen/MethodDescriptor.java | 3 +-- .../decompiler/util/FastFixedSetFactory.java | 8 ++---- .../decompiler/util/FastSparseSetFactory.java | 26 ++++--------------- .../decompiler/util/SFormsFastMapDirect.java | 5 ++-- 4 files changed, 10 insertions(+), 32 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java index 8697b03..2ad7c68 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java @@ -68,8 +68,7 @@ public final class MethodDescriptor { VarType[] newParams; if (params.length > 0) { - newParams = new VarType[params.length]; - System.arraycopy(params, 0, newParams, 0, params.length); + newParams = params.clone(); for (int i = 0; i < params.length; i++) { VarType substitute = buildNewType(params[i], builder); if (substitute != null) { diff --git a/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java index 7c2956c..265a538 100644 --- a/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java @@ -1,10 +1,7 @@ // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.util; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; +import java.util.*; public class FastFixedSetFactory { @@ -66,8 +63,7 @@ public class FastFixedSetFactory { FastFixedSet copy = new FastFixedSet<>(factory); int arrlength = data.length; - int[] cpdata = new int[arrlength]; - System.arraycopy(data, 0, cpdata, 0, arrlength); + int[] cpdata = Arrays.copyOf(data, arrlength); copy.setData(cpdata); return copy; diff --git a/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java index a99083c..9b66a2e 100644 --- a/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java @@ -1,10 +1,7 @@ // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.util; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; +import java.util.*; public class FastSparseSetFactory { @@ -97,15 +94,7 @@ public class FastSparseSetFactory { } public FastSparseSet getCopy() { - - int arrlength = data.length; - int[] cpdata = new int[arrlength]; - int[] cpnext = new int[arrlength]; - - System.arraycopy(data, 0, cpdata, 0, arrlength); - System.arraycopy(next, 0, cpnext, 0, arrlength); - - return new FastSparseSet<>(factory, cpdata, cpnext); + return new FastSparseSet<>(factory, data.clone(), next.clone()); } private int[] ensureCapacity(int index) { @@ -119,15 +108,10 @@ public class FastSparseSetFactory { newlength *= 2; } - int[] newdata = new int[newlength]; - System.arraycopy(data, 0, newdata, 0, data.length); - data = newdata; + data = Arrays.copyOf(data, newlength); + next = Arrays.copyOf(next, newlength); - int[] newnext = new int[newlength]; - System.arraycopy(next, 0, newnext, 0, next.length); - next = newnext; - - return newdata; + return data; } public void add(E element) { diff --git a/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java b/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java index 16a0484..7143281 100644 --- a/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java +++ b/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java @@ -5,6 +5,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map.Entry; import java.util.Set; @@ -66,9 +67,7 @@ public class SFormsFastMapDirect { int[] arrnext = next[i]; @SuppressWarnings("unchecked") FastSparseSet[] arrnew = new FastSparseSet[length]; - int[] arrnextnew = new int[length]; - - System.arraycopy(arrnext, 0, arrnextnew, 0, length); + int[] arrnextnew = Arrays.copyOf(arrnext, length); mapelements[i] = arrnew; mapnext[i] = arrnextnew; From 143e2396aed42e280f79f4b1a38fa35b2bb2e95d Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 2 Dec 2020 10:07:11 +0700 Subject: [PATCH 10/27] Avoid set.removeAll(list) GitOrigin-RevId: 6ed9b1ee1df87618c9b8c534e328ea89ead9fdc3 --- .../decompiler/modules/code/DeadCodeHelper.java | 15 ++++++++++----- .../decompiler/StrongConnectivityHelper.java | 4 +++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java index 421ed98..13106f5 100644 --- a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java @@ -267,7 +267,9 @@ public final class DeadCodeHelper { for (BasicBlock block : range.getProtectedRange()) { setPreds.addAll(block.getPreds()); } - setPreds.removeAll(range.getProtectedRange()); + for (BasicBlock basicBlock : range.getProtectedRange()) { + setPreds.remove(basicBlock); + } if(setPreds.size() != 1) { continue; // multiple predecessors, obfuscated range @@ -280,8 +282,7 @@ public final class DeadCodeHelper { } boolean monitorexit_in_range = false; - Set setProtectedBlocks = new HashSet<>(); - setProtectedBlocks.addAll(range.getProtectedRange()); + Set setProtectedBlocks = new HashSet<>(range.getProtectedRange()); setProtectedBlocks.add(range.getHandler()); for (BasicBlock block : setProtectedBlocks) { @@ -305,7 +306,9 @@ public final class DeadCodeHelper { for (BasicBlock block : range.getProtectedRange()) { setSuccs.addAll(block.getSuccs()); } - setSuccs.removeAll(range.getProtectedRange()); + for (BasicBlock basicBlock : range.getProtectedRange()) { + setSuccs.remove(basicBlock); + } if(setSuccs.size() != 1) { continue; // non-unique successor @@ -461,7 +464,9 @@ public final class DeadCodeHelper { } // add exception ranges from predecessors - setPredHandlersIntersection.removeAll(block.getSuccExceptions()); + for (BasicBlock basicBlock : block.getSuccExceptions()) { + setPredHandlersIntersection.remove(basicBlock); + } BasicBlock predecessor = block.getPreds().get(0); for (BasicBlock handler : setPredHandlersIntersection) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java index 4e81646..fcea19f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java @@ -90,7 +90,9 @@ public class StrongConnectivityHelper { for (Statement stat : lst) { set.addAll(stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)); } - set.removeAll(lst); + for (Statement stat : lst) { + set.remove(stat); + } return (set.size() == 0); } From dbf407a6554b9dee5a11894bf9af5df73c2d7ec6 Mon Sep 17 00:00:00 2001 From: Maxim Degtyarev Date: Fri, 18 Dec 2020 20:17:05 +0100 Subject: [PATCH 11/27] Fix NPE inside `ExceptionRangeCFG::toString()` for `finally` exception range PR #1026 GitOrigin-RevId: 18492644f44796505f5a4be9471344c1ce1d0f3a --- .../java/decompiler/code/cfg/ExceptionRangeCFG.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java b/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java index ce143aa..395d03d 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code.cfg; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -31,9 +31,16 @@ public class ExceptionRangeCFG { StringBuilder buf = new StringBuilder(); buf.append("exceptionType:"); - for (String exception_type : exceptionTypes) { - buf.append(" ").append(exception_type); + + if (exceptionTypes == null) { + buf.append(" null"); + } + else { + for (String exception_type : exceptionTypes) { + buf.append(" ").append(exception_type); + } } + buf.append(new_line_separator); buf.append("handler: ").append(handler.id).append(new_line_separator); From e19aab61cedc6ba1894d2f1fbfb0d9e95e3cbbd8 Mon Sep 17 00:00:00 2001 From: Nikolay Chashnikov Date: Fri, 15 Jan 2021 19:22:29 +0300 Subject: [PATCH 12/27] [java] API cleanup: remove unused API which was deprecated for a long time (IDEA-259329) GitOrigin-RevId: b2f2c85514d360e8d716e6f136f7c11760086f0e --- .../decompiler/main/decompiler/BaseDecompiler.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java index 57bae44..7837939 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java @@ -25,17 +25,6 @@ public class BaseDecompiler { engine.addLibrary(library); } - /** @deprecated use {@link #addSource(File)} / {@link #addLibrary(File)} instead */ - @Deprecated - public void addSpace(File file, boolean own) { - if (own) { - addSource(file); - } - else { - addLibrary(file); - } - } - public void decompileContext() { try { engine.decompileContext(); From e708ad639394a5d5b1817953b67c70351c0a5438 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Sat, 16 Jan 2021 20:33:55 +0100 Subject: [PATCH 13/27] java 11 migration: explicit -> diamond for anonymous classes GitOrigin-RevId: ce15f37f8882fb7915cf5c8b62e3cc4601a5cc76 --- src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java b/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java index 7143281..bb8b4dd 100644 --- a/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java +++ b/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java @@ -342,7 +342,7 @@ public class SFormsFastMapDirect { if (ent != null) { final int key = i == 0 ? ikey : (i == 1 ? ikey + VarExprent.STACK_BASE : -ikey); - list.add(new Entry>() { + list.add(new Entry<>() { private final Integer var = key; private final FastSparseSet val = ent; From 78d932a0cb32a499201faf3d924a42a4131e9015 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 18 Jan 2021 21:48:41 +0100 Subject: [PATCH 14/27] [java-decompiler] aligning language level with the main project; bumping Gradle version GitOrigin-RevId: 068aadf4b64097c66de34ff19134f57083d2ac66 --- build.gradle | 8 ++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 9694117..797515b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,9 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. apply plugin: 'java' compileJava { - sourceCompatibility '1.8' - targetCompatibility '1.8' + sourceCompatibility '11' + targetCompatibility '11' } sourceSets { @@ -22,4 +22,4 @@ jar { manifest { attributes 'Main-Class': 'org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler' } -} \ No newline at end of file +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 657de85..56a6b16 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip From f259b38c72268b494f87cb666f67837dd2797fef Mon Sep 17 00:00:00 2001 From: leonardosnt Date: Thu, 11 Feb 2021 12:45:52 +0100 Subject: [PATCH 15/27] IDEA-245329: Handle CONSTANT_Module and CONSTANT_Package PR #1406 GitOrigin-RevId: 501d3b66e790316f8ab52606ea4cba41665213c0 --- .../java/decompiler/code/CodeConstants.java | 6 +- .../java/decompiler/main/ClassWriter.java | 117 ++++++++- .../struct/attr/StructGeneralAttribute.java | 6 +- .../struct/attr/StructModuleAttribute.java | 227 ++++++++++++++++++ .../struct/consts/ConstantPool.java | 4 +- .../struct/consts/PrimitiveConstant.java | 4 +- .../java/decompiler/SingleClassesTest.java | 3 +- testData/classes/java9/module-info.class | Bin 0 -> 298 bytes testData/results/module-info.dec | 13 + .../src/java9/sample.module/module-info.java | 15 ++ .../java9/sample.module/test/TestService.java | 3 + .../sample.module/test/TestServiceImpl.java | 3 + .../java9/sample.module/test2/TestClass.java | 3 + 13 files changed, 396 insertions(+), 8 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java create mode 100644 testData/classes/java9/module-info.class create mode 100644 testData/results/module-info.dec create mode 100644 testData/src/java9/sample.module/module-info.java create mode 100644 testData/src/java9/sample.module/test/TestService.java create mode 100644 testData/src/java9/sample.module/test/TestServiceImpl.java create mode 100644 testData/src/java9/sample.module/test2/TestClass.java diff --git a/src/org/jetbrains/java/decompiler/code/CodeConstants.java b/src/org/jetbrains/java/decompiler/code/CodeConstants.java index 720d2a7..2c5cb42 100644 --- a/src/org/jetbrains/java/decompiler/code/CodeConstants.java +++ b/src/org/jetbrains/java/decompiler/code/CodeConstants.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code; @SuppressWarnings({"unused", "SpellCheckingInspection"}) @@ -64,6 +64,7 @@ public interface CodeConstants { int ACC_STATIC = 0x0008; int ACC_FINAL = 0x0010; int ACC_SYNCHRONIZED = 0x0020; + int ACC_OPEN = 0x0020; int ACC_NATIVE = 0x0100; int ACC_ABSTRACT = 0x0400; int ACC_STRICT = 0x0800; @@ -75,6 +76,7 @@ public interface CodeConstants { int ACC_ANNOTATION = 0x2000; int ACC_ENUM = 0x4000; int ACC_MANDATED = 0x8000; + int ACC_MODULE = 0x8000; // ---------------------------------------------------------------------- // CLASS FLAGS @@ -112,6 +114,8 @@ public interface CodeConstants { int CONSTANT_MethodHandle = 15; int CONSTANT_MethodType = 16; int CONSTANT_InvokeDynamic = 18; + int CONSTANT_Module = 19; + int CONSTANT_Package = 20; // ---------------------------------------------------------------------- // MethodHandle reference_kind values diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index aa03fa5..bfd46f2 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -249,6 +249,12 @@ public class ClassWriter { } } + boolean isModuleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); + + if (isModuleInfo) { + writeModuleInfoBody(buffer, cl); + } + buffer.appendIndent(indent).append('}'); if (node.type != ClassNode.CLASS_ANONYMOUS) { @@ -277,6 +283,99 @@ public class ClassWriter { return false; } + private void writeModuleInfoBody(TextBuffer buffer, StructClass cl) { + StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); + + for (StructModuleAttribute.RequiresEntry requires : moduleAttribute.requires) { + String moduleName = requires.moduleName.replace('/', '.'); + + buffer.appendIndent(1) + .append("requires ") + .append(moduleName) + .append(';') + .appendLineSeparator(); + } + + for (StructModuleAttribute.ExportsEntry exports : moduleAttribute.exports) { + String packageName = exports.packageName.replace('/', '.'); + + buffer.appendIndent(1).append("exports ").append(packageName); + + List exportToModules = exports.exportToModules; + if (exportToModules.size() > 0) { + buffer.append(" to").appendLineSeparator(); + + int lastIndex = exportToModules.size() - 1; + for (int i = 0; i < exportToModules.size(); i++) { + String moduleName = exportToModules.get(i).replace('/', '.'); + char separator = i == lastIndex ? ';' : ','; + + buffer.appendIndent(2) + .append(moduleName) + .append(separator) + .appendLineSeparator(); + } + } else { + buffer.append(';').appendLineSeparator(); + } + } + + for (StructModuleAttribute.OpensEntry opens : moduleAttribute.opens) { + String packageName = opens.packageName.replace('/', '.'); + + buffer.appendIndent(1).append("opens ").append(packageName); + + List opensToModules = opens.opensToModules; + if (opensToModules.size() > 0) { + buffer.append(" to").appendLineSeparator(); + + int lastIndex = opensToModules.size() - 1; + for (int i = 0; i < opensToModules.size(); i++) { + String moduleName = opensToModules.get(i).replace('/', '.'); + char separator = i == lastIndex ? ';' : ','; + + buffer.appendIndent(2) + .append(moduleName) + .append(separator) + .appendLineSeparator(); + } + } else { + buffer.append(';').appendLineSeparator(); + } + } + + for (String uses : moduleAttribute.uses) { + String className = ExprProcessor.buildJavaClassName(uses); + + buffer.appendIndent(1) + .append("uses ") + .append(className) + .append(';') + .appendLineSeparator(); + } + + for (StructModuleAttribute.ProvidesEntry provides : moduleAttribute.provides) { + String interfaceName = ExprProcessor.buildJavaClassName(provides.interfaceName); + + buffer.appendIndent(1) + .append("provides ") + .append(interfaceName) + .append(" with") + .appendLineSeparator(); + + int lastIndex = provides.implementationNames.size() - 1; + for (int i = 0; i < provides.implementationNames.size(); i++) { + String className = ExprProcessor.buildJavaClassName(provides.implementationNames.get(i)); + char separator = i == lastIndex ? ';' : ','; + + buffer.appendIndent(2) + .append(className) + .append(separator) + .appendLineSeparator(); + } + } + } + private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) { StructLineNumberTableAttribute table = method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE); tracer.setLineNumberTable(table); @@ -299,6 +398,7 @@ public class ClassWriter { boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; + boolean isModuleInfo = (flags & CodeConstants.ACC_MODULE) != 0 && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); if (isDeprecated) { appendDeprecation(buffer, indent); @@ -344,11 +444,24 @@ public class ClassWriter { else if (components != null) { buffer.append("record "); } + else if (isModuleInfo) { + StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); + + if ((moduleAttribute.moduleFlags & CodeConstants.ACC_OPEN) != 0) { + buffer.append("open "); + } + + buffer.append("module "); + buffer.append(moduleAttribute.moduleName); + } else { buffer.append("class "); } - buffer.append(node.simpleName); + // Handled above + if (!isModuleInfo) { + buffer.append(node.simpleName); + } GenericClassDescriptor descriptor = getGenericClassDescriptor(cl); if (descriptor != null && !descriptor.fparameters.isEmpty()) { diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index 8fff5db..041e266 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -34,6 +34,7 @@ public class StructGeneralAttribute { public static final Key ATTRIBUTE_DEPRECATED = new Key<>("Deprecated"); public static final Key ATTRIBUTE_LINE_NUMBER_TABLE = new Key<>("LineNumberTable"); public static final Key ATTRIBUTE_METHOD_PARAMETERS = new Key<>("MethodParameters"); + public static final Key ATTRIBUTE_MODULE = new Key<>("Module"); public static final Key ATTRIBUTE_RECORD = new Key<>("Record"); public static class Key { @@ -98,6 +99,9 @@ public class StructGeneralAttribute { else if (ATTRIBUTE_METHOD_PARAMETERS.getName().equals(name)) { attr = new StructMethodParametersAttribute(); } + else if (ATTRIBUTE_MODULE.getName().equals(name)) { + attr = new StructModuleAttribute(); + } else if (ATTRIBUTE_RECORD.getName().equals(name)) { attr = new StructRecordAttribute(); } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java new file mode 100644 index 0000000..23c0486 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java @@ -0,0 +1,227 @@ +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class StructModuleAttribute extends StructGeneralAttribute { + public String moduleName; + public int moduleFlags; + public String moduleVersion; + + public List requires; + public List exports; + public List opens; + public List uses; + public List provides; + + @Override + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { + int moduleNameIndex = data.readUnsignedShort(); + + this.moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); + this.moduleFlags = data.readUnsignedShort(); + + int moduleVersionIndex = data.readUnsignedShort(); + if (moduleVersionIndex != 0) { + moduleVersion = pool.getPrimitiveConstant(moduleVersionIndex).getString(); + } + + this.requires = readRequires(data, pool); + this.exports = readExports(data, pool); + this.opens = readOpens(data, pool); + this.uses = readUses(data, pool); + this.provides = readProvides(data, pool); + } + + public List readRequires(DataInputFullStream data, ConstantPool pool) throws IOException { + int requiresCount = data.readUnsignedShort(); + + if (requiresCount <= 0) { + return Collections.emptyList(); + } + + List requires = new ArrayList<>(requiresCount); + for (int i = 0; i < requiresCount; i++) { + int moduleNameIndex = data.readUnsignedShort(); + int moduleFlags = data.readUnsignedShort(); + int versionIndex = data.readUnsignedShort(); + + String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); + String version = versionIndex == 0 ? null : pool.getPrimitiveConstant(versionIndex).getString(); + + requires.add(new RequiresEntry(moduleName, moduleFlags, version)); + } + + return requires; + } + + private List readExports(DataInputFullStream data, ConstantPool pool) throws IOException { + int exportsCount = data.readUnsignedShort(); + + if (exportsCount <= 0) { + return Collections.emptyList(); + } + + List exports = new ArrayList<>(exportsCount); + + for (int i = 0; i < exportsCount; i++) { + int packageNameIndex = data.readUnsignedShort(); + int exportsFlags = data.readUnsignedShort(); + int exportsToCount = data.readUnsignedShort(); + + List exportsToModules; + if (exportsToCount > 0) { + exportsToModules = new ArrayList<>(exportsToCount); + + for (int j = 0; j < exportsToCount; j++) { + int moduleNameIndex = data.readUnsignedShort(); + String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); + + exportsToModules.add(moduleName); + } + } else { + exportsToModules = Collections.emptyList(); + } + + String packageName = pool.getPrimitiveConstant(packageNameIndex).getString(); + + exports.add(new ExportsEntry(packageName, exportsFlags, exportsToModules)); + } + + return exports; + } + + private List readOpens(DataInputFullStream data, ConstantPool pool) throws IOException { + int opensCount = data.readUnsignedShort(); + + if (opensCount <= 0) { + return Collections.emptyList(); + } + + List opens = new ArrayList<>(opensCount); + + for (int i = 0; i < opensCount; i++) { + int packageNameIndex = data.readUnsignedShort(); + int opensFlags = data.readUnsignedShort(); + int opensToCount = data.readUnsignedShort(); + + List opensToModules; + if (opensToCount > 0) { + opensToModules = new ArrayList<>(opensToCount); + + for (int j = 0; j < opensToCount; j++) { + int moduleNameIndex = data.readUnsignedShort(); + String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); + + opensToModules.add(moduleName); + } + } else { + opensToModules = Collections.emptyList(); + } + + String packageName = pool.getPrimitiveConstant(packageNameIndex).getString(); + + opens.add(new OpensEntry(packageName, opensFlags, opensToModules)); + } + + return opens; + } + + private List readUses(DataInputFullStream data, ConstantPool pool) throws IOException { + int usesCount = data.readUnsignedShort(); + if (usesCount <= 0) { + return Collections.emptyList(); + } + + List uses = new ArrayList<>(usesCount); + for (int i = 0; i < usesCount; i++) { + int classNameIndex = data.readUnsignedShort(); + String className = pool.getPrimitiveConstant(classNameIndex).getString(); + + uses.add(className); + } + + return uses; + } + + private List readProvides(DataInputFullStream data, ConstantPool pool) throws IOException { + int providesCount = data.readUnsignedShort(); + if (providesCount <= 0) { + return Collections.emptyList(); + } + + List provides = new ArrayList<>(providesCount); + for (int i = 0; i < providesCount; i++) { + int interfaceNameIndex = data.readUnsignedShort(); + String interfaceName = pool.getPrimitiveConstant(interfaceNameIndex).getString(); + + // Always nonzero + int providesWithCount = data.readUnsignedShort(); + List implementationNames = new ArrayList<>(providesWithCount); + + for (int j = 0; j < providesWithCount; j++) { + int classNameIndex = data.readUnsignedShort(); + String className = pool.getPrimitiveConstant(classNameIndex).getString(); + + implementationNames.add(className); + } + + provides.add(new ProvidesEntry(interfaceName, implementationNames)); + } + + return provides; + } + + public static final class RequiresEntry { + public String moduleName; + public int moduleFlags; + public String moduleVersion; + + public RequiresEntry(String moduleName, int moduleFlags, String moduleVersion) { + this.moduleName = moduleName; + this.moduleFlags = moduleFlags; + this.moduleVersion = moduleVersion; + } + } + + public static final class ExportsEntry { + public String packageName; + public int exportsFlags; + public List exportToModules; + + public ExportsEntry(String packageName, int exportsFlags, List exportToModules) { + this.packageName = packageName; + this.exportsFlags = exportsFlags; + this.exportToModules = exportToModules; + } + } + + public static final class OpensEntry { + public String packageName; + public int opensFlags; + public List opensToModules; + + public OpensEntry(String packageName, int exportsFlags, List exportToModules) { + this.packageName = packageName; + this.opensFlags = exportsFlags; + this.opensToModules = exportToModules; + } + } + + public static final class ProvidesEntry { + public String interfaceName; + public List implementationNames; + + public ProvidesEntry(String interfaceName, List implementationNames) { + this.interfaceName = interfaceName; + this.implementationNames = implementationNames; + } + } + +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java index 0ea6e01..d90ba1d 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.consts; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -64,6 +64,8 @@ public class ConstantPool implements NewClassNameBuilder { case CodeConstants.CONSTANT_Class: case CodeConstants.CONSTANT_String: case CodeConstants.CONSTANT_MethodType: + case CodeConstants.CONSTANT_Module: + case CodeConstants.CONSTANT_Package: pool.add(new PrimitiveConstant(tag, in.readUnsignedShort())); nextPass[0].set(i); break; diff --git a/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java index 626a91b..408622a 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.consts; public class PrimitiveConstant extends PooledConstant { @@ -31,7 +31,7 @@ public class PrimitiveConstant extends PooledConstant { @Override public void resolveConstant(ConstantPool pool) { - if (type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType) { + if (type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType || type == CONSTANT_Module || type == CONSTANT_Package) { value = pool.getPrimitiveConstant(index).getString(); initConstant(); } diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index c720949..c6f1b8c 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -74,6 +74,7 @@ public class SingleClassesTest { "pkg/TestShadowingSuperClass"); } @Test public void testStringConcat() { doTest("pkg/TestStringConcat"); } @Test public void testJava9StringConcat() { doTest("java9/TestJava9StringConcat"); } + @Test public void testJava9ModuleInfo() { doTest("java9/module-info"); } @Test public void testJava11StringConcat() { doTest("java11/TestJava11StringConcat"); } @Test public void testMethodReferenceSameName() { doTest("pkg/TestMethodReferenceSameName"); } @Test public void testMethodReferenceLetterClass() { doTest("pkg/TestMethodReferenceLetterClass"); } @@ -175,4 +176,4 @@ public class SingleClassesTest { return files; } -} \ No newline at end of file +} diff --git a/testData/classes/java9/module-info.class b/testData/classes/java9/module-info.class new file mode 100644 index 0000000000000000000000000000000000000000..59005e4978071319bbaa38052ffa71306eec0b30 GIT binary patch literal 298 zcmZ9INeTin5Ji8rL!75>H{QXtx)D5p;6f1e02bi66)?QxsMXAQBGBo5-Lt+gpu0{vs9Zfx#@6ct=Ysj*GCm| zKkiVt-`VJ2sFFce`W?i6CU~ZjNFs~K9&e5@d&p*Pv$+PtV DKj}Rb literal 0 HcmV?d00001 diff --git a/testData/results/module-info.dec b/testData/results/module-info.dec new file mode 100644 index 0000000..c45d666 --- /dev/null +++ b/testData/results/module-info.dec @@ -0,0 +1,13 @@ +module sample.module { + requires java.base; + exports test; + exports test2 to + java.base; + opens test; + opens test2 to + java.base; + uses java.util.spi.ToolProvider; + provides test.TestService with + test.TestServiceImpl; +} + diff --git a/testData/src/java9/sample.module/module-info.java b/testData/src/java9/sample.module/module-info.java new file mode 100644 index 0000000..ea71a46 --- /dev/null +++ b/testData/src/java9/sample.module/module-info.java @@ -0,0 +1,15 @@ +module sample.module { + requires java.base; + + uses java.util.spi.ToolProvider; + + provides test.TestService with test.TestServiceImpl; + + exports test; + + exports test2 to java.base; + + opens test; + + opens test2 to java.base; +} \ No newline at end of file diff --git a/testData/src/java9/sample.module/test/TestService.java b/testData/src/java9/sample.module/test/TestService.java new file mode 100644 index 0000000..c2a6cd7 --- /dev/null +++ b/testData/src/java9/sample.module/test/TestService.java @@ -0,0 +1,3 @@ +package test; + +public interface TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/test/TestServiceImpl.java b/testData/src/java9/sample.module/test/TestServiceImpl.java new file mode 100644 index 0000000..9a153e8 --- /dev/null +++ b/testData/src/java9/sample.module/test/TestServiceImpl.java @@ -0,0 +1,3 @@ +package test; + +public class TestServiceImpl implements TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/test2/TestClass.java b/testData/src/java9/sample.module/test2/TestClass.java new file mode 100644 index 0000000..202153e --- /dev/null +++ b/testData/src/java9/sample.module/test2/TestClass.java @@ -0,0 +1,3 @@ +package test2; + +public class TestClass {} \ No newline at end of file From f40b96ebcfe1cb3e5346cf8537fda711bdce6788 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 12 Feb 2021 00:07:47 +0100 Subject: [PATCH 16/27] [java decompiler] minor refactoring + cleanup (duplicates; dependencies; test data layout; typos; formatting) GitOrigin-RevId: 3589e4d8f2dfa8a5096fcf49070bc65ba6734482 --- .../decompiler/code/cfg/ControlFlowGraph.java | 11 +- .../java/decompiler/main/ClassWriter.java | 196 +++++++----------- .../decompiler/main/ClassesProcessor.java | 4 +- .../decompiler/main/rels/ClassWrapper.java | 6 +- .../decompiler/main/rels/LambdaProcessor.java | 19 +- .../main/rels/MethodProcessorRunnable.java | 23 +- .../modules/decompiler/FinallyProcessor.java | 27 +-- .../modules/decompiler/exps/VarExprent.java | 5 +- .../modules/decompiler/vars/VarProcessor.java | 12 +- .../java/decompiler/struct/ContextUnit.java | 4 +- .../java/decompiler/struct/StructClass.java | 112 +++++----- .../java/decompiler/struct/StructContext.java | 9 +- .../java/decompiler/struct/StructField.java | 33 +-- .../java/decompiler/struct/StructMember.java | 53 +++-- .../java/decompiler/struct/StructMethod.java | 118 +++++------ .../struct/StructRecordComponent.java | 33 +-- .../struct/attr/StructCodeAttribute.java | 43 ++++ .../struct/attr/StructGeneralAttribute.java | 96 ++++----- .../struct/attr/StructModuleAttribute.java | 140 ++++--------- .../struct/attr/StructRecordAttribute.java | 7 +- .../struct/consts/ConstantPool.java | 10 +- .../decompiler/struct/lazy/LazyLoader.java | 29 +-- testData/classes/java9/module-info.class | Bin 298 -> 392 bytes testData/results/module-info.dec | 13 +- .../src/java9/sample.module/TestClass.java | 3 + .../src/java9/sample.module/TestService.java | 3 + .../java9/sample.module/TestServiceImpl.java | 3 + .../src/java9/sample.module/module-info.java | 13 +- .../java9/sample.module/test/TestService.java | 3 - .../sample.module/test/TestServiceImpl.java | 3 - .../java9/sample.module/test2/TestClass.java | 3 - 31 files changed, 468 insertions(+), 566 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java create mode 100644 testData/src/java9/sample.module/TestClass.java create mode 100644 testData/src/java9/sample.module/TestService.java create mode 100644 testData/src/java9/sample.module/TestServiceImpl.java delete mode 100644 testData/src/java9/sample.module/test/TestService.java delete mode 100644 testData/src/java9/sample.module/test/TestServiceImpl.java delete mode 100644 testData/src/java9/sample.module/test2/TestClass.java diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java index 2fd9d3c..f060fbd 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java @@ -1,10 +1,11 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code.cfg; import org.jetbrains.java.decompiler.code.*; import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper; +import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.gen.DataPoint; @@ -96,9 +97,9 @@ public class ControlFlowGraph implements CodeConstants { return buf.toString(); } - public void inlineJsr(StructMethod mt) { + public void inlineJsr(StructClass cl, StructMethod mt) { processJsr(); - removeJsr(mt); + removeJsr(cl, mt); removeMarkers(); @@ -668,8 +669,8 @@ public class ControlFlowGraph implements CodeConstants { } } - private void removeJsr(StructMethod mt) { - removeJsrInstructions(mt.getClassStruct().getPool(), first, DataPoint.getInitialDataPoint(mt)); + private void removeJsr(StructClass cl, StructMethod mt) { + removeJsrInstructions(cl.getPool(), first, DataPoint.getInitialDataPoint(mt)); } private static void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) { diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index bfd46f2..3da67c6 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -41,7 +41,7 @@ public class ClassWriter { InitializerProcessor.extractInitializers(wrapper); if (node.type == ClassNode.CLASS_ROOT && - !cl.isVersionGE_1_5() && + !cl.isVersion5() && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) { ClassReference14Processor.processClassReferences(node); } @@ -249,10 +249,11 @@ public class ClassWriter { } } - boolean isModuleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); - - if (isModuleInfo) { - writeModuleInfoBody(buffer, cl); + if (cl.hasModifier(CodeConstants.ACC_MODULE)) { + StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); + if (moduleAttribute != null) { + writeModuleInfoBody(buffer, moduleAttribute); + } } buffer.appendIndent(indent).append('}'); @@ -268,111 +269,61 @@ public class ClassWriter { DecompilerContext.getLogger().endWriteClass(); } - private static boolean isSyntheticRecordMethod(StructMethod mt, TextBuffer code) { - if (mt.getClassStruct().getRecordComponents() == null) return false; - String name = mt.getName(); - String descriptor = mt.getDescriptor(); - if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") || - name.equals("hashCode") && descriptor.equals("()I") || - name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) { - if (code.countLines() == 1) { - String str = code.toString().trim(); - return str.startsWith("return this." + name + "(this"); + private static boolean isSyntheticRecordMethod(StructClass cl, StructMethod mt, TextBuffer code) { + if (cl.getRecordComponents() != null) { + String name = mt.getName(), descriptor = mt.getDescriptor(); + //noinspection SpellCheckingInspection + if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") || + name.equals("hashCode") && descriptor.equals("()I") || + name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) { + if (code.countLines() == 1) { + String str = code.toString().trim(); + return str.startsWith("return this." + name + "(this"); + } } } return false; } - private void writeModuleInfoBody(TextBuffer buffer, StructClass cl) { - StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); - + private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) { for (StructModuleAttribute.RequiresEntry requires : moduleAttribute.requires) { - String moduleName = requires.moduleName.replace('/', '.'); - - buffer.appendIndent(1) - .append("requires ") - .append(moduleName) - .append(';') - .appendLineSeparator(); + buffer.appendIndent(1).append("requires ").append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator(); } for (StructModuleAttribute.ExportsEntry exports : moduleAttribute.exports) { - String packageName = exports.packageName.replace('/', '.'); - - buffer.appendIndent(1).append("exports ").append(packageName); + buffer.appendIndent(1).append("exports ").append(exports.packageName.replace('/', '.')); List exportToModules = exports.exportToModules; if (exportToModules.size() > 0) { buffer.append(" to").appendLineSeparator(); - - int lastIndex = exportToModules.size() - 1; - for (int i = 0; i < exportToModules.size(); i++) { - String moduleName = exportToModules.get(i).replace('/', '.'); - char separator = i == lastIndex ? ';' : ','; - - buffer.appendIndent(2) - .append(moduleName) - .append(separator) - .appendLineSeparator(); - } - } else { - buffer.append(';').appendLineSeparator(); + appendFQClassNames(buffer, exportToModules); } + + buffer.append(';').appendLineSeparator(); } for (StructModuleAttribute.OpensEntry opens : moduleAttribute.opens) { - String packageName = opens.packageName.replace('/', '.'); - - buffer.appendIndent(1).append("opens ").append(packageName); + buffer.appendIndent(1).append("opens ").append(opens.packageName.replace('/', '.')); List opensToModules = opens.opensToModules; if (opensToModules.size() > 0) { buffer.append(" to").appendLineSeparator(); - - int lastIndex = opensToModules.size() - 1; - for (int i = 0; i < opensToModules.size(); i++) { - String moduleName = opensToModules.get(i).replace('/', '.'); - char separator = i == lastIndex ? ';' : ','; - - buffer.appendIndent(2) - .append(moduleName) - .append(separator) - .appendLineSeparator(); - } - } else { - buffer.append(';').appendLineSeparator(); + appendFQClassNames(buffer, opensToModules); } + + buffer.append(';').appendLineSeparator(); } for (String uses : moduleAttribute.uses) { - String className = ExprProcessor.buildJavaClassName(uses); - - buffer.appendIndent(1) - .append("uses ") - .append(className) - .append(';') - .appendLineSeparator(); + buffer.appendIndent(1).append("uses ").append(ExprProcessor.buildJavaClassName(uses)).append(';').appendLineSeparator(); } for (StructModuleAttribute.ProvidesEntry provides : moduleAttribute.provides) { - String interfaceName = ExprProcessor.buildJavaClassName(provides.interfaceName); - - buffer.appendIndent(1) - .append("provides ") - .append(interfaceName) - .append(" with") - .appendLineSeparator(); - - int lastIndex = provides.implementationNames.size() - 1; - for (int i = 0; i < provides.implementationNames.size(); i++) { - String className = ExprProcessor.buildJavaClassName(provides.implementationNames.get(i)); - char separator = i == lastIndex ? ';' : ','; - - buffer.appendIndent(2) - .append(className) - .append(separator) - .appendLineSeparator(); - } + buffer.appendIndent(1).append("provides ").append(ExprProcessor.buildJavaClassName(provides.interfaceName)).append(" with").appendLineSeparator(); + @SuppressWarnings({"SSBasedInspection", "RedundantSuppression"}) List javaNames = + provides.implementationNames.stream().map(ExprProcessor::buildJavaClassName).collect(Collectors.toList()); + appendFQClassNames(buffer, javaNames); + buffer.append(';').appendLineSeparator(); } } @@ -519,11 +470,11 @@ public class ClassWriter { buffer.append('{').appendLineSeparator(); } - private boolean isVarArgRecord(StructClass cl) { + private static boolean isVarArgRecord(StructClass cl) { String canonicalConstructorDescriptor = cl.getRecordComponents().stream().map(c -> c.getDescriptor()).collect(Collectors.joining("", "(", ")V")); - StructMethod ctor = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor); - return ctor != null && ctor.hasModifier(CodeConstants.ACC_VARARGS); + StructMethod init = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor); + return init != null && init.hasModifier(CodeConstants.ACC_VARARGS); } private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { @@ -553,15 +504,9 @@ public class ClassWriter { appendModifiers(buffer, fd.getAccessFlags(), FIELD_ALLOWED, isInterface, FIELD_EXCLUDED); } - VarType fieldType = new VarType(fd.getDescriptor(), false); - - GenericFieldDescriptor descriptor = null; - if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE); - if (attr != null) { - descriptor = GenericMain.parseFieldSignature(attr.getSignature()); - } - } + Map.Entry fieldTypeData = getFieldTypeData(fd); + VarType fieldType = fieldTypeData.getKey(); + GenericFieldDescriptor descriptor = fieldTypeData.getValue(); if (!isEnum) { if (descriptor != null) { @@ -619,15 +564,9 @@ public class ClassWriter { private static void recordComponentToJava(StructRecordComponent cd, TextBuffer buffer, boolean varArgComponent) { appendAnnotations(buffer, -1, cd, TypeAnnotation.FIELD); - VarType fieldType = new VarType(cd.getDescriptor(), false); - - GenericFieldDescriptor descriptor = null; - if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = cd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE); - if (attr != null) { - descriptor = GenericMain.parseFieldSignature(attr.getSignature()); - } - } + Map.Entry fieldTypeData = getFieldTypeData(cd); + VarType fieldType = fieldTypeData.getKey(); + GenericFieldDescriptor descriptor = fieldTypeData.getValue(); if (descriptor != null) { buffer.append(GenericMain.getGenericCastTypeName(varArgComponent ? descriptor.type.decreaseArrayDim() : descriptor.type)); @@ -770,7 +709,7 @@ public class ClassWriter { boolean isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION); boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); boolean isDeprecated = mt.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED); - boolean clinit = false, init = false, dinit = false; + boolean clInit = false, init = false, dInit = false; MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); @@ -815,7 +754,7 @@ public class ClassWriter { if (CodeConstants.INIT_NAME.equals(name)) { if (node.type == ClassNode.CLASS_ANONYMOUS) { name = ""; - dinit = true; + dInit = true; } else { name = node.simpleName; @@ -824,7 +763,7 @@ public class ClassWriter { } else if (CodeConstants.CLINIT_NAME.equals(name)) { name = ""; - clinit = true; + clInit = true; } GenericMethodDescriptor descriptor = null; @@ -853,7 +792,7 @@ public class ClassWriter { boolean throwsExceptions = false; int paramCount = 0; - if (!clinit && !dinit) { + if (!clInit && !dInit) { boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC); if (descriptor != null && !descriptor.typeParameters.isEmpty()) { @@ -992,7 +931,7 @@ public class ClassWriter { buffer.appendLineSeparator(); } else { - if (!clinit && !dinit) { + if (!clInit && !dInit) { buffer.append(' '); } @@ -1008,8 +947,8 @@ public class ClassWriter { BytecodeMappingTracer codeTracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine()); TextBuffer code = root.toJava(indent + 1, codeTracer); - hideMethod = (code.length() == 0) && (clinit || dinit || hideConstructor(node, init, throwsExceptions, paramCount, flags)) - || isSyntheticRecordMethod(mt, code); + hideMethod = code.length() == 0 && (clInit || dInit || hideConstructor(node, init, throwsExceptions, paramCount, flags)) || + isSyntheticRecordMethod(cl, mt, code); buffer.append(code); @@ -1049,7 +988,6 @@ public class ClassWriter { } private static boolean hideConstructor(ClassNode node, boolean init, boolean throwsExceptions, int paramCount, int methodAccessFlags) { - if (!init || throwsExceptions || paramCount > 0 || !DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) { return false; } @@ -1057,11 +995,11 @@ public class ClassWriter { ClassWrapper wrapper = node.getWrapper(); StructClass cl = wrapper.getClassStruct(); - int classAccesFlags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access; + int classAccessFlags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access; boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); // default constructor requires same accessibility flags. Exception: enum constructor which is always private - if(!isEnum && ((classAccesFlags & ACCESSIBILITY_FLAGS) != (methodAccessFlags & ACCESSIBILITY_FLAGS))) { + if(!isEnum && ((classAccessFlags & ACCESSIBILITY_FLAGS) != (methodAccessFlags & ACCESSIBILITY_FLAGS))) { return false; } @@ -1077,6 +1015,20 @@ public class ClassWriter { return true; } + private static Map.Entry getFieldTypeData(StructField fd) { + VarType fieldType = new VarType(fd.getDescriptor(), false); + + GenericFieldDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE); + if (attr != null) { + descriptor = GenericMain.parseFieldSignature(attr.getSignature()); + } + } + + return new AbstractMap.SimpleImmutableEntry<>(fieldType, descriptor); + } + private static void appendDeprecation(TextBuffer buffer, int indent) { buffer.appendIndent(indent).append("/** @deprecated */").appendLineSeparator(); } @@ -1135,11 +1087,11 @@ public class ClassWriter { buffer.appendIndent(indent).append("// $FF: ").append(comment).appendLineSeparator(); } - private static final StructGeneralAttribute.Key[] ANNOTATION_ATTRIBUTES = { + private static final StructGeneralAttribute.Key[] ANNOTATION_ATTRIBUTES = { StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; - private static final StructGeneralAttribute.Key[] PARAMETER_ANNOTATION_ATTRIBUTES = { + private static final StructGeneralAttribute.Key[] PARAMETER_ANNOTATION_ATTRIBUTES = { StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; - private static final StructGeneralAttribute.Key[] TYPE_ANNOTATION_ATTRIBUTES = { + private static final StructGeneralAttribute.Key[] TYPE_ANNOTATION_ATTRIBUTES = { StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS}; private static void appendAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType) { @@ -1283,4 +1235,14 @@ public class ClassWriter { buffer.append('>'); } -} \ No newline at end of file + + private static void appendFQClassNames(TextBuffer buffer, List names) { + for (int i = 0; i < names.size(); i++) { + String name = names.get(i); + buffer.appendIndent(2).append(name); + if (i < names.size() - 1) { + buffer.append(',').appendLineSeparator(); + } + } + } +} diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index 1ee22f2..d05c839 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -245,7 +245,7 @@ public class ClassesProcessor implements CodeConstants { } try { - mt.expandData(); + mt.expandData(enclosingCl); InstructionSequence seq = mt.getInstructionSequence(); if (seq != null) { diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index ca63ad5..d0b73c0 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.rels; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -59,10 +59,10 @@ public class ClassWrapper { try { if (mt.containsCode()) { if (maxSec == 0 || testMode) { - root = MethodProcessorRunnable.codeToJava(mt, md, varProc); + root = MethodProcessorRunnable.codeToJava(classStruct, mt, md, varProc); } else { - MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, md, varProc, DecompilerContext.getCurrentContext()); + MethodProcessorRunnable mtProc = new MethodProcessorRunnable(classStruct, mt, md, varProc, DecompilerContext.getCurrentContext()); Thread mtThread = new Thread(mtProc, "Java decompiler"); long stopAt = System.currentTimeMillis() + maxSec * 1000L; diff --git a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java index fd0f261..2ad5b20 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.rels; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -33,17 +33,16 @@ public class LambdaProcessor { ClassesProcessor clProcessor = DecompilerContext.getClassProcessor(); StructClass cl = node.classStruct; - if (cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lambda beginning with Java 8 + if (!cl.isVersion8()) { // lambda beginning with Java 8 return; } - StructBootstrapMethodsAttribute bootstrap = - cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); + StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); if (bootstrap == null || bootstrap.getMethodsNumber() == 0) { return; // no bootstrap constants in pool } - BitSet lambda_methods = new BitSet(); + BitSet lambdaMethods = new BitSet(); // find lambda bootstrap constants for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) { @@ -52,11 +51,11 @@ public class LambdaProcessor { // FIXME: extend for Eclipse etc. at some point if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) && (JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) || JAVAC_LAMBDA_ALT_METHOD.equals(method_ref.elementname))) { - lambda_methods.set(i); + lambdaMethods.set(i); } } - if (lambda_methods.isEmpty()) { + if (lambdaMethods.isEmpty()) { return; // no lambda bootstrap constant found } @@ -64,7 +63,7 @@ public class LambdaProcessor { // iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes. for (StructMethod mt : cl.getMethods()) { - mt.expandData(); + mt.expandData(cl); InstructionSequence seq = mt.getInstructionSequence(); if (seq != null && seq.length() > 0) { @@ -76,7 +75,7 @@ public class LambdaProcessor { if (instr.opcode == CodeConstants.opc_invokedynamic) { LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.operand(0)); - if (lambda_methods.get(invoke_dynamic.index1)) { // lambda invocation found + if (lambdaMethods.get(invoke_dynamic.index1)) { // lambda invocation found List bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1); MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor); @@ -123,4 +122,4 @@ public class LambdaProcessor { // FIXME: mixed hierarchy? } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 726d747..d9de3d5 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.rels; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -22,6 +22,7 @@ import java.io.IOException; public class MethodProcessorRunnable implements Runnable { public final Object lock = new Object(); + private final StructClass klass; private final StructMethod method; private final MethodDescriptor methodDescriptor; private final VarProcessor varProc; @@ -31,10 +32,12 @@ public class MethodProcessorRunnable implements Runnable { private volatile Throwable error; private volatile boolean finished = false; - public MethodProcessorRunnable(StructMethod method, + public MethodProcessorRunnable(StructClass klass, + StructMethod method, MethodDescriptor methodDescriptor, VarProcessor varProc, DecompilerContext parentContext) { + this.klass = klass; this.method = method; this.methodDescriptor = methodDescriptor; this.varProc = varProc; @@ -48,7 +51,7 @@ public class MethodProcessorRunnable implements Runnable { try { DecompilerContext.setCurrentContext(parentContext); - root = codeToJava(method, methodDescriptor, varProc); + root = codeToJava(klass, method, methodDescriptor, varProc); } catch (Throwable t) { error = t; @@ -63,17 +66,15 @@ public class MethodProcessorRunnable implements Runnable { } } - public static RootStatement codeToJava(StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException { - StructClass cl = mt.getClassStruct(); - + public static RootStatement codeToJava(StructClass cl, StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException { boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only - mt.expandData(); + mt.expandData(cl); InstructionSequence seq = mt.getInstructionSequence(); ControlFlowGraph graph = new ControlFlowGraph(seq); DeadCodeHelper.removeDeadBlocks(graph); - graph.inlineJsr(mt); + graph.inlineJsr(cl, mt); // TODO: move to the start, before jsr inlining DeadCodeHelper.connectDummyExitBlock(graph); @@ -110,13 +111,13 @@ public class MethodProcessorRunnable implements Runnable { if (!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) { DecompilerContext.getLogger().writeMessage("Found multiple entry exception ranges which could not be splitted", IFernflowerLogger.Severity.WARN); } - ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, cl.getBytecodeVersion()); + ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, mt.getBytecodeVersion()); } RootStatement root = DomHelper.parseGraph(graph); FinallyProcessor fProc = new FinallyProcessor(md, varProc); - while (fProc.iterateGraph(mt, root, graph)) { + while (fProc.iterateGraph(cl, mt, root, graph)) { root = DomHelper.parseGraph(graph); } @@ -200,4 +201,4 @@ public class MethodProcessorRunnable implements Runnable { public boolean isFinished() { return finished; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java index 53a9b68..9f52895 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -26,6 +26,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; +import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; @@ -46,12 +47,8 @@ public class FinallyProcessor { varProcessor = varProc; } - public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) { - return processStatementEx(mt, root, graph); - } - - private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) { - int bytecode_version = mt.getClassStruct().getBytecodeVersion(); + public boolean iterateGraph(StructClass cl, StructMethod mt, RootStatement root, ControlFlowGraph graph) { + int bytecodeVersion = mt.getBytecodeVersion(); LinkedList stack = new LinkedList<>(); stack.add(root); @@ -77,22 +74,20 @@ public class FinallyProcessor { fin.setMonitor(var == null ? null : new VarExprent(var, VarType.VARTYPE_INT, varProcessor)); } else { - Record inf = getFinallyInformation(mt, root, fin); + Record inf = getFinallyInformation(cl, mt, root, fin); if (inf == null) { // inconsistent finally catchallBlockIDs.put(handler.id, null); } else { - if (DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) { finallyBlockIDs.put(handler.id, null); } else { + int varIndex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); + insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varIndex, inf, bytecodeVersion); - int varindex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); - insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf, bytecode_version); - - finallyBlockIDs.put(handler.id, varindex); + finallyBlockIDs.put(handler.id, varIndex); } DeadCodeHelper.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally @@ -120,7 +115,7 @@ public class FinallyProcessor { } } - private Record getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) { + private Record getFinallyInformation(StructClass cl, StructMethod mt, RootStatement root, CatchAllStatement fstat) { Map mapLast = new HashMap<>(); BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead(); @@ -138,7 +133,7 @@ public class FinallyProcessor { } ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor); - proc.processStatement(root, mt.getClassStruct()); + proc.processStatement(root, cl); SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); ssa.splitVariables(root, mt); @@ -977,4 +972,4 @@ public class FinallyProcessor { } } } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index f30082e..d0a2260 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.List; public class VarExprent extends Exprent { - public static final int STACK_BASE = 10000; public static final String VAR_NAMELESS_ENCLOSURE = ""; @@ -122,7 +121,7 @@ public class VarExprent extends Exprent { Integer origIndex = processor.getVarOriginalIndex(index); if (origIndex != null) { String name = attr.getName(origIndex, visibleOffset); - if (name != null && TextUtil.isValidIdentifier(name, method.getClassStruct().getBytecodeVersion())) { + if (name != null && TextUtil.isValidIdentifier(name, method.getBytecodeVersion())) { return name; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 9999b50..8d766e0 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.vars; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; @@ -55,7 +55,7 @@ public class VarProcessor { Integer index = mapOriginalVarIndices.get(pair.var); if (index != null) { String debugName = mapDebugVarNames.get(index); - if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getClassStruct().getBytecodeVersion())) { + if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getBytecodeVersion())) { name = debugName; } } @@ -72,11 +72,7 @@ public class VarProcessor { } public Integer getVarOriginalIndex(int index) { - if (varVersions == null) { - return null; - } - - return varVersions.getMapOriginalVarIndices().get(index); + return varVersions == null ? null : varVersions.getMapOriginalVarIndices().get(index); } public void refreshVarNames(VarNamesCollector vc) { @@ -125,4 +121,4 @@ public class VarProcessor { public Set getExternalVars() { return externalVars; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java index ef580de..c77cfaa 100644 --- a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java +++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -65,7 +65,7 @@ public class ContextUnit { StructClass newCl; try (DataInputFullStream in = loader.getClassStream(oldName)) { - newCl = new StructClass(in, cl.isOwn(), loader); + newCl = StructClass.create(in, cl.isOwn(), loader); } lstClasses.add(newCl); diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java index 7a46954..134813e 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructClass.java +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -13,6 +13,7 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; import java.util.List; +import java.util.Map; /* class_file { @@ -35,71 +36,86 @@ import java.util.List; } */ public class StructClass extends StructMember { - - public final String qualifiedName; - public final PrimitiveConstant superClass; - - private final boolean own; - private final LazyLoader loader; - private final int minorVersion; - private final int majorVersion; - private final int[] interfaces; - private final String[] interfaceNames; - private final VBStyleCollection fields; - private final VBStyleCollection methods; - - private ConstantPool pool; - - public StructClass(byte[] bytes, boolean own, LazyLoader loader) throws IOException { - this(new DataInputFullStream(bytes), own, loader); - } - - public StructClass(DataInputFullStream in, boolean own, LazyLoader loader) throws IOException { - this.own = own; - this.loader = loader; - + public static StructClass create(DataInputFullStream in, boolean own, LazyLoader loader) throws IOException { in.discard(4); + int minorVersion = in.readUnsignedShort(); + int majorVersion = in.readUnsignedShort(); + int bytecodeVersion = Math.max(majorVersion, CodeConstants.BYTECODE_JAVA_LE_4); - minorVersion = in.readUnsignedShort(); - majorVersion = in.readUnsignedShort(); + ConstantPool pool = new ConstantPool(in); - pool = new ConstantPool(in); - - accessFlags = in.readUnsignedShort(); + int accessFlags = in.readUnsignedShort(); int thisClassIdx = in.readUnsignedShort(); int superClassIdx = in.readUnsignedShort(); - qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString(); - superClass = pool.getPrimitiveConstant(superClassIdx); + String qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString(); + PrimitiveConstant superClass = pool.getPrimitiveConstant(superClassIdx); - // interfaces int length = in.readUnsignedShort(); - interfaces = new int[length]; - interfaceNames = new String[length]; + int[] interfaces = new int[length]; + String[] interfaceNames = new String[length]; for (int i = 0; i < length; i++) { interfaces[i] = in.readUnsignedShort(); interfaceNames[i] = pool.getPrimitiveConstant(interfaces[i]).getString(); } - // fields length = in.readUnsignedShort(); - fields = new VBStyleCollection<>(length); + VBStyleCollectionfields = new VBStyleCollection<>(length); for (int i = 0; i < length; i++) { - StructField field = new StructField(in, this); + StructField field = StructField.create(in, pool, qualifiedName); fields.addWithKey(field, InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor())); } - // methods length = in.readUnsignedShort(); - methods = new VBStyleCollection<>(length); + VBStyleCollectionmethods = new VBStyleCollection<>(length); for (int i = 0; i < length; i++) { - StructMethod method = new StructMethod(in, this); + StructMethod method = StructMethod.create(in, pool, qualifiedName, bytecodeVersion, own); methods.addWithKey(method, InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor())); } - // attributes - attributes = readAttributes(in, pool); + Map attributes = readAttributes(in, pool); + + StructClass cl = new StructClass( + accessFlags, attributes, qualifiedName, superClass, own, loader, minorVersion, majorVersion, interfaces, interfaceNames, fields, methods); + if (loader == null) cl.pool = pool; + return cl; + } + + public final String qualifiedName; + public final PrimitiveConstant superClass; + private final boolean own; + private final LazyLoader loader; + private final int minorVersion; + private final int majorVersion; + private final int[] interfaces; + private final String[] interfaceNames; + private final VBStyleCollection fields; + private final VBStyleCollection methods; + + private ConstantPool pool; - releaseResources(); + private StructClass(int accessFlags, + Map attributes, + String qualifiedName, + PrimitiveConstant superClass, + boolean own, + LazyLoader loader, + int minorVersion, + int majorVersion, + int[] interfaces, + String[] interfaceNames, + VBStyleCollection fields, + VBStyleCollection methods) { + super(accessFlags, attributes); + this.qualifiedName = qualifiedName; + this.superClass = superClass; + this.own = own; + this.loader = loader; + this.minorVersion = minorVersion; + this.majorVersion = majorVersion; + this.interfaces = interfaces; + this.interfaceNames = interfaceNames; + this.fields = fields; + this.methods = methods; } public boolean hasField(String name, String descriptor) { @@ -168,17 +184,13 @@ public class StructClass extends StructMember { return loader; } - public boolean isVersionGE_1_5() { + public boolean isVersion5() { return (majorVersion > CodeConstants.BYTECODE_JAVA_LE_4 || (majorVersion == CodeConstants.BYTECODE_JAVA_LE_4 && minorVersion > 0)); // FIXME: check second condition } - public boolean isVersionGE_1_7() { - return (majorVersion >= CodeConstants.BYTECODE_JAVA_7); - } - - public int getBytecodeVersion() { - return Math.max(majorVersion, CodeConstants.BYTECODE_JAVA_LE_4); + public boolean isVersion8() { + return majorVersion >= CodeConstants.BYTECODE_JAVA_8; } @Override diff --git a/src/org/jetbrains/java/decompiler/struct/StructContext.java b/src/org/jetbrains/java/decompiler/struct/StructContext.java index 6d07096..61486aa 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructContext.java +++ b/src/org/jetbrains/java/decompiler/struct/StructContext.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -17,7 +17,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class StructContext { - private final IResultSaver saver; private final IDecompiledData decompiledData; private final LazyLoader loader; @@ -106,7 +105,7 @@ public class StructContext { if (filename.endsWith(".class")) { try (DataInputFullStream in = loader.getClassStream(file.getAbsolutePath(), null)) { - StructClass cl = new StructClass(in, isOwn, loader); + StructClass cl = StructClass.create(in, isOwn, loader); classes.put(cl.qualifiedName, cl); unit.addClass(cl, filename); loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), null)); @@ -141,7 +140,7 @@ public class StructContext { if (!entry.isDirectory()) { if (name.endsWith(".class")) { byte[] bytes = InterpreterUtil.getBytes(archive, entry); - StructClass cl = new StructClass(bytes, isOwn, loader); + StructClass cl = StructClass.create(new DataInputFullStream(bytes), isOwn, loader); classes.put(cl.qualifiedName, cl); unit.addClass(cl, name); loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name)); @@ -160,4 +159,4 @@ public class StructContext { public Map getClasses() { return classes; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructField.java b/src/org/jetbrains/java/decompiler/struct/StructField.java index 6c85ca4..cfa64d2 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructField.java +++ b/src/org/jetbrains/java/decompiler/struct/StructField.java @@ -1,10 +1,12 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; +import java.util.Map; /* field_info { @@ -16,29 +18,32 @@ import java.io.IOException; } */ public class StructField extends StructMember { + public static StructField create(DataInputFullStream in, ConstantPool pool, String clQualifiedName) throws IOException { + int accessFlags = in.readUnsignedShort(); + int nameIndex = in.readUnsignedShort(); + int descriptorIndex = in.readUnsignedShort(); - private final String name; - private final String descriptor; + String[] values = pool.getClassElement(ConstantPool.FIELD, clQualifiedName, nameIndex, descriptorIndex); + Map attributes = readAttributes(in, pool); - public StructField(DataInputFullStream in, StructClass clStruct) throws IOException { - accessFlags = in.readUnsignedShort(); - int nameIndex = in.readUnsignedShort(); - int descriptorIndex = in.readUnsignedShort(); + return new StructField(accessFlags, attributes, values[0], values[1]); + } - ConstantPool pool = clStruct.getPool(); - String[] values = pool.getClassElement(ConstantPool.FIELD, clStruct.qualifiedName, nameIndex, descriptorIndex); - name = values[0]; - descriptor = values[1]; + private final String name; + private final String descriptor; - attributes = readAttributes(in, pool); + protected StructField(int accessFlags, Map attributes, String name, String descriptor) { + super(accessFlags, attributes); + this.name = name; + this.descriptor = descriptor; } - public String getName() { + public final String getName() { return name; } - public String getDescriptor() { + public final String getDescriptor() { return descriptor; } diff --git a/src/org/jetbrains/java/decompiler/struct/StructMember.java b/src/org/jetbrains/java/decompiler/struct/StructMember.java index a5b87f1..920e7f9 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMember.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMember.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -12,23 +12,26 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -public class StructMember { - - protected int accessFlags; - protected Map attributes; +public abstract class StructMember { + private final int accessFlags; + private final Map attributes; + protected StructMember(int accessFlags, Map attributes) { + this.accessFlags = accessFlags; + this.attributes = attributes; + } public int getAccessFlags() { return accessFlags; } public T getAttribute(StructGeneralAttribute.Key attribute) { - //noinspection unchecked - return (T)attributes.get(attribute.getName()); + @SuppressWarnings("unchecked") T t = (T)attributes.get(attribute.name); + return t; } public boolean hasAttribute(StructGeneralAttribute.Key attribute) { - return attributes.containsKey(attribute.getName()); + return attributes.containsKey(attribute.name); } public boolean hasModifier(int modifier) { @@ -39,45 +42,37 @@ public class StructMember { return hasModifier(CodeConstants.ACC_SYNTHETIC) || hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC); } - protected Map readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException { + public static Map readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException { int length = in.readUnsignedShort(); - Map attributes = new HashMap<>(length); + for (int i = 0; i < length; i++) { int nameIndex = in.readUnsignedShort(); String name = pool.getPrimitiveConstant(nameIndex).getString(); - StructGeneralAttribute attribute = readAttribute(in, pool, name); - - if (attribute != null) { - if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.getName().equals(name) && attributes.containsKey(name)) { + StructGeneralAttribute attribute = StructGeneralAttribute.createAttribute(name); + int attLength = in.readInt(); + if (attribute == null) { + in.discard(attLength); + } + else { + attribute.initContent(in, pool); + if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name) && attributes.containsKey(name)) { // merge all variable tables StructLocalVariableTableAttribute table = (StructLocalVariableTableAttribute)attributes.get(name); table.add((StructLocalVariableTableAttribute)attribute); } - else if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.getName().equals(name) && attributes.containsKey(name)) { + else if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.name.equals(name) && attributes.containsKey(name)) { // merge all variable tables StructLocalVariableTypeTableAttribute table = (StructLocalVariableTypeTableAttribute)attributes.get(name); table.add((StructLocalVariableTypeTableAttribute)attribute); } else { - attributes.put(attribute.getName(), attribute); + attributes.put(name, attribute); } } } return attributes; } - - protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException { - StructGeneralAttribute attribute = StructGeneralAttribute.createAttribute(name); - int length = in.readInt(); - if (attribute == null) { - in.discard(length); - } - else { - attribute.initContent(in, pool); - } - return attribute; - } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructMethod.java b/src/org/jetbrains/java/decompiler/struct/StructMethod.java index 859805c..a715593 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMethod.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMethod.java @@ -1,7 +1,8 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.*; +import org.jetbrains.java.decompiler.struct.attr.StructCodeAttribute; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -25,80 +26,66 @@ import static org.jetbrains.java.decompiler.code.CodeConstants.*; } */ public class StructMethod extends StructMember { + public static StructMethod create(DataInputFullStream in, ConstantPool pool, String clQualifiedName, int bytecodeVersion, boolean own) throws IOException { + int accessFlags = in.readUnsignedShort(); + int nameIndex = in.readUnsignedShort(); + int descriptorIndex = in.readUnsignedShort(); + + String[] values = pool.getClassElement(ConstantPool.METHOD, clQualifiedName, nameIndex, descriptorIndex); + + Map attributes = readAttributes(in, pool); + StructCodeAttribute code = (StructCodeAttribute)attributes.remove(StructGeneralAttribute.ATTRIBUTE_CODE.name); + if (code != null) { + attributes.putAll(code.codeAttributes); + } + + return new StructMethod(accessFlags, attributes, values[0], values[1], bytecodeVersion, own ? code : null); + } + private static final int[] opr_iconst = {-1, 0, 1, 2, 3, 4, 5}; private static final int[] opr_loadstore = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}; private static final int[] opcs_load = {opc_iload, opc_lload, opc_fload, opc_dload, opc_aload}; private static final int[] opcs_store = {opc_istore, opc_lstore, opc_fstore, opc_dstore, opc_astore}; - private final StructClass classStruct; private final String name; private final String descriptor; - - private boolean containsCode = false; - private int localVariables = 0; - private int codeLength = 0; - private int codeFullLength = 0; - private InstructionSequence seq; + private final int bytecodeVersion; + private final int localVariables; + private final int codeLength; + private final int codeFullLength; + private InstructionSequence seq = null; private boolean expanded = false; - private Map codeAttributes; - - public StructMethod(DataInputFullStream in, StructClass clStruct) throws IOException { - classStruct = clStruct; - accessFlags = in.readUnsignedShort(); - int nameIndex = in.readUnsignedShort(); - int descriptorIndex = in.readUnsignedShort(); - - ConstantPool pool = clStruct.getPool(); - String[] values = pool.getClassElement(ConstantPool.METHOD, clStruct.qualifiedName, nameIndex, descriptorIndex); - name = values[0]; - descriptor = values[1]; - - attributes = readAttributes(in, pool); - if (codeAttributes != null) { - attributes.putAll(codeAttributes); - codeAttributes = null; + private StructMethod(int accessFlags, + Map attributes, + String name, + String descriptor, + int bytecodeVersion, + StructCodeAttribute code) { + super(accessFlags, attributes); + this.name = name; + this.descriptor = descriptor; + this.bytecodeVersion = bytecodeVersion; + if (code != null) { + this.localVariables = code.localVariables; + this.codeLength = code.codeLength; + this.codeFullLength = code.codeFullLength; } - } - - @Override - protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException { - if (StructGeneralAttribute.ATTRIBUTE_CODE.getName().equals(name)) { - if (!classStruct.isOwn()) { - // skip code in foreign classes - in.discard(8); - in.discard(in.readInt()); - in.discard(8 * in.readUnsignedShort()); - } - else { - containsCode = true; - in.discard(6); - localVariables = in.readUnsignedShort(); - codeLength = in.readInt(); - in.discard(codeLength); - int excLength = in.readUnsignedShort(); - in.discard(excLength * 8); - codeFullLength = codeLength + excLength * 8 + 2; - } - - codeAttributes = readAttributes(in, pool); - - return null; + else { + this.localVariables = this.codeLength = this.codeFullLength = -1; } - - return super.readAttribute(in, pool, name); } - public void expandData() throws IOException { - if (containsCode && !expanded) { - byte[] code = classStruct.getLoader().loadBytecode(this, codeFullLength); + public void expandData(StructClass classStruct) throws IOException { + if (codeLength >= 0 && !expanded) { + byte[] code = classStruct.getLoader().loadBytecode(classStruct, this, codeFullLength); seq = parseBytecode(new DataInputFullStream(code), codeLength, classStruct.getPool()); expanded = true; } } public void releaseResources() { - if (containsCode && expanded) { + if (codeLength >= 0 && expanded) { seq = null; expanded = false; } @@ -108,10 +95,7 @@ public class StructMethod extends StructMember { private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException { VBStyleCollection instructions = new VBStyleCollection<>(); - int bytecode_version = classStruct.getBytecodeVersion(); - for (int i = 0; i < length; ) { - int offset = i; int opcode = in.readUnsignedByte(); @@ -197,7 +181,7 @@ public class StructMethod extends StructMember { } break; case opc_invokedynamic: - if (classStruct.isVersionGE_1_7()) { // instruction unused in Java 6 and before + if (bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) { // instruction unused in Java 6 and before operands.add(in.readUnsignedShort()); in.discard(2); group = GROUP_INVOCATION; @@ -313,7 +297,7 @@ public class StructMethod extends StructMember { } } - Instruction instr = Instruction.create(opcode, wide, group, bytecode_version, ops); + Instruction instr = Instruction.create(opcode, wide, group, bytecodeVersion, ops); instructions.addWithKey(instr, offset); @@ -355,10 +339,6 @@ public class StructMethod extends StructMember { return seq; } - public StructClass getClassStruct() { - return classStruct; - } - public String getName() { return name; } @@ -367,8 +347,12 @@ public class StructMethod extends StructMember { return descriptor; } + public int getBytecodeVersion() { + return bytecodeVersion; + } + public boolean containsCode() { - return containsCode; + return codeLength >= 0; } public int getLocalVariables() { @@ -387,4 +371,4 @@ public class StructMethod extends StructMember { public String toString() { return name; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java b/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java index 830078c..e50983d 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java +++ b/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java @@ -1,11 +1,13 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; +import java.util.Map; /* record_component_info { @@ -15,33 +17,20 @@ import java.io.IOException; attribute_info attributes[attributes_count]; } */ -public class StructRecordComponent extends StructMember { - - private final String name; - private final String descriptor; - - - public StructRecordComponent(DataInputFullStream in, ConstantPool pool) throws IOException { - accessFlags = 0; +public class StructRecordComponent extends StructField { + public static StructRecordComponent create(DataInputFullStream in, ConstantPool pool) throws IOException { int nameIndex = in.readUnsignedShort(); int descriptorIndex = in.readUnsignedShort(); - name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString(); - descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString(); + String name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString(); + String descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString(); - attributes = readAttributes(in, pool); - } - - public String getName() { - return name; - } + Map attributes = readAttributes(in, pool); - public String getDescriptor() { - return descriptor; + return new StructRecordComponent(0, attributes, name, descriptor); } - @Override - public String toString() { - return name; + private StructRecordComponent(int flags, Map attributes, String name, String descriptor) { + super(flags, attributes, name, descriptor); } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java new file mode 100644 index 0000000..87f02be --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java @@ -0,0 +1,43 @@ +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.struct.StructMember; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +import java.io.IOException; +import java.util.Map; + +/* + u2 max_stack; + u2 max_locals; + u4 code_length; + u1 code[]; + u2 exception_table_length; + exception_table[] { + u2 start_pc; + u2 end_pc; + u2 handler_pc; + u2 catch_type; + }; + u2 attributes_count; + attribute_info attributes[]; +*/ +public class StructCodeAttribute extends StructGeneralAttribute { + public int localVariables = 0; + public int codeLength = 0; + public int codeFullLength = 0; + public Map codeAttributes; + + @Override + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { + data.discard(2); + localVariables = data.readUnsignedShort(); + codeLength = data.readInt(); + data.discard(codeLength); + int excLength = data.readUnsignedShort(); + data.discard(excLength * 8); + codeFullLength = codeLength + excLength * 8 + 2; + codeAttributes = StructMember.readAttributes(data, pool); + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index 041e266..6239ee4 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -14,7 +14,7 @@ import java.io.IOException; } */ public class StructGeneralAttribute { - public static final Key ATTRIBUTE_CODE = new Key<>("Code"); + public static final Key ATTRIBUTE_CODE = new Key<>("Code"); public static final Key ATTRIBUTE_INNER_CLASSES = new Key<>("InnerClasses"); public static final Key ATTRIBUTE_SIGNATURE = new Key<>("Signature"); public static final Key ATTRIBUTE_ANNOTATION_DEFAULT = new Key<>("AnnotationDefault"); @@ -37,86 +37,74 @@ public class StructGeneralAttribute { public static final Key ATTRIBUTE_MODULE = new Key<>("Module"); public static final Key ATTRIBUTE_RECORD = new Key<>("Record"); + @SuppressWarnings("unused") public static class Key { - private final String name; + public final String name; public Key(String name) { this.name = name; } - - public String getName() { - return name; - } } - private String name; - public static StructGeneralAttribute createAttribute(String name) { - StructGeneralAttribute attr; - - if (ATTRIBUTE_INNER_CLASSES.getName().equals(name)) { - attr = new StructInnerClassesAttribute(); + if (ATTRIBUTE_CODE.name.equals(name)) { + return new StructCodeAttribute(); } - else if (ATTRIBUTE_CONSTANT_VALUE.getName().equals(name)) { - attr = new StructConstantValueAttribute(); + else if (ATTRIBUTE_INNER_CLASSES.name.equals(name)) { + return new StructInnerClassesAttribute(); } - else if (ATTRIBUTE_SIGNATURE.getName().equals(name)) { - attr = new StructGenericSignatureAttribute(); + else if (ATTRIBUTE_CONSTANT_VALUE.name.equals(name)) { + return new StructConstantValueAttribute(); } - else if (ATTRIBUTE_ANNOTATION_DEFAULT.getName().equals(name)) { - attr = new StructAnnDefaultAttribute(); + else if (ATTRIBUTE_SIGNATURE.name.equals(name)) { + return new StructGenericSignatureAttribute(); } - else if (ATTRIBUTE_EXCEPTIONS.getName().equals(name)) { - attr = new StructExceptionsAttribute(); + else if (ATTRIBUTE_ANNOTATION_DEFAULT.name.equals(name)) { + return new StructAnnDefaultAttribute(); } - else if (ATTRIBUTE_ENCLOSING_METHOD.getName().equals(name)) { - attr = new StructEnclosingMethodAttribute(); + else if (ATTRIBUTE_EXCEPTIONS.name.equals(name)) { + return new StructExceptionsAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.getName().equals(name)) { - attr = new StructAnnotationAttribute(); + else if (ATTRIBUTE_ENCLOSING_METHOD.name.equals(name)) { + return new StructEnclosingMethodAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.getName().equals(name)) { - attr = new StructAnnotationParameterAttribute(); + else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.name.equals(name)) { + return new StructAnnotationAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.getName().equals(name)) { - attr = new StructTypeAnnotationAttribute(); + else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.name.equals(name)) { + return new StructAnnotationParameterAttribute(); } - else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.getName().equals(name)) { - attr = new StructLocalVariableTableAttribute(); + else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.name.equals(name)) { + return new StructTypeAnnotationAttribute(); } - else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.getName().equals(name)) { - attr = new StructLocalVariableTypeTableAttribute(); + else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name)) { + return new StructLocalVariableTableAttribute(); } - else if (ATTRIBUTE_BOOTSTRAP_METHODS.getName().equals(name)) { - attr = new StructBootstrapMethodsAttribute(); + else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.name.equals(name)) { + return new StructLocalVariableTypeTableAttribute(); } - else if (ATTRIBUTE_SYNTHETIC.getName().equals(name) || ATTRIBUTE_DEPRECATED.getName().equals(name)) { - attr = new StructGeneralAttribute(); + else if (ATTRIBUTE_BOOTSTRAP_METHODS.name.equals(name)) { + return new StructBootstrapMethodsAttribute(); } - else if (ATTRIBUTE_LINE_NUMBER_TABLE.getName().equals(name)) { - attr = new StructLineNumberTableAttribute(); + else if (ATTRIBUTE_SYNTHETIC.name.equals(name) || ATTRIBUTE_DEPRECATED.name.equals(name)) { + return new StructGeneralAttribute(); } - else if (ATTRIBUTE_METHOD_PARAMETERS.getName().equals(name)) { - attr = new StructMethodParametersAttribute(); + else if (ATTRIBUTE_LINE_NUMBER_TABLE.name.equals(name)) { + return new StructLineNumberTableAttribute(); } - else if (ATTRIBUTE_MODULE.getName().equals(name)) { - attr = new StructModuleAttribute(); + else if (ATTRIBUTE_METHOD_PARAMETERS.name.equals(name)) { + return new StructMethodParametersAttribute(); } - else if (ATTRIBUTE_RECORD.getName().equals(name)) { - attr = new StructRecordAttribute(); + else if (ATTRIBUTE_MODULE.name.equals(name)) { + return new StructModuleAttribute(); + } + else if (ATTRIBUTE_RECORD.name.equals(name)) { + return new StructRecordAttribute(); } else { - // unsupported attribute - return null; + return null; // unsupported attribute } - - attr.name = name; - return attr; } public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { } - - public String getName() { - return name; - } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java index 23c0486..ba2005b 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java @@ -23,7 +23,6 @@ public class StructModuleAttribute extends StructGeneralAttribute { @Override public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int moduleNameIndex = data.readUnsignedShort(); - this.moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); this.moduleFlags = data.readUnsignedShort(); @@ -41,147 +40,87 @@ public class StructModuleAttribute extends StructGeneralAttribute { public List readRequires(DataInputFullStream data, ConstantPool pool) throws IOException { int requiresCount = data.readUnsignedShort(); - - if (requiresCount <= 0) { - return Collections.emptyList(); - } + if (requiresCount <= 0) return Collections.emptyList(); List requires = new ArrayList<>(requiresCount); for (int i = 0; i < requiresCount; i++) { int moduleNameIndex = data.readUnsignedShort(); int moduleFlags = data.readUnsignedShort(); int versionIndex = data.readUnsignedShort(); - String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); String version = versionIndex == 0 ? null : pool.getPrimitiveConstant(versionIndex).getString(); - requires.add(new RequiresEntry(moduleName, moduleFlags, version)); } - return requires; } - private List readExports(DataInputFullStream data, ConstantPool pool) throws IOException { + private static List readExports(DataInputFullStream data, ConstantPool pool) throws IOException { int exportsCount = data.readUnsignedShort(); - - if (exportsCount <= 0) { - return Collections.emptyList(); - } + if (exportsCount <= 0) return Collections.emptyList(); List exports = new ArrayList<>(exportsCount); - for (int i = 0; i < exportsCount; i++) { int packageNameIndex = data.readUnsignedShort(); int exportsFlags = data.readUnsignedShort(); - int exportsToCount = data.readUnsignedShort(); - - List exportsToModules; - if (exportsToCount > 0) { - exportsToModules = new ArrayList<>(exportsToCount); - - for (int j = 0; j < exportsToCount; j++) { - int moduleNameIndex = data.readUnsignedShort(); - String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); - - exportsToModules.add(moduleName); - } - } else { - exportsToModules = Collections.emptyList(); - } - + List exportsToModules = readStringList(data, pool); String packageName = pool.getPrimitiveConstant(packageNameIndex).getString(); - exports.add(new ExportsEntry(packageName, exportsFlags, exportsToModules)); } - return exports; } - private List readOpens(DataInputFullStream data, ConstantPool pool) throws IOException { + private static List readOpens(DataInputFullStream data, ConstantPool pool) throws IOException { int opensCount = data.readUnsignedShort(); - - if (opensCount <= 0) { - return Collections.emptyList(); - } + if (opensCount <= 0) return Collections.emptyList(); List opens = new ArrayList<>(opensCount); - for (int i = 0; i < opensCount; i++) { int packageNameIndex = data.readUnsignedShort(); int opensFlags = data.readUnsignedShort(); - int opensToCount = data.readUnsignedShort(); - - List opensToModules; - if (opensToCount > 0) { - opensToModules = new ArrayList<>(opensToCount); - - for (int j = 0; j < opensToCount; j++) { - int moduleNameIndex = data.readUnsignedShort(); - String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); - - opensToModules.add(moduleName); - } - } else { - opensToModules = Collections.emptyList(); - } - + List opensToModules = readStringList(data, pool); String packageName = pool.getPrimitiveConstant(packageNameIndex).getString(); - opens.add(new OpensEntry(packageName, opensFlags, opensToModules)); } - return opens; } - private List readUses(DataInputFullStream data, ConstantPool pool) throws IOException { - int usesCount = data.readUnsignedShort(); - if (usesCount <= 0) { - return Collections.emptyList(); - } - - List uses = new ArrayList<>(usesCount); - for (int i = 0; i < usesCount; i++) { - int classNameIndex = data.readUnsignedShort(); - String className = pool.getPrimitiveConstant(classNameIndex).getString(); - - uses.add(className); - } - - return uses; + private static List readUses(DataInputFullStream data, ConstantPool pool) throws IOException { + return readStringList(data, pool); } - private List readProvides(DataInputFullStream data, ConstantPool pool) throws IOException { + private static List readProvides(DataInputFullStream data, ConstantPool pool) throws IOException { int providesCount = data.readUnsignedShort(); - if (providesCount <= 0) { - return Collections.emptyList(); - } + if (providesCount <= 0) return Collections.emptyList(); List provides = new ArrayList<>(providesCount); for (int i = 0; i < providesCount; i++) { int interfaceNameIndex = data.readUnsignedShort(); String interfaceName = pool.getPrimitiveConstant(interfaceNameIndex).getString(); - - // Always nonzero - int providesWithCount = data.readUnsignedShort(); - List implementationNames = new ArrayList<>(providesWithCount); - - for (int j = 0; j < providesWithCount; j++) { - int classNameIndex = data.readUnsignedShort(); - String className = pool.getPrimitiveConstant(classNameIndex).getString(); - - implementationNames.add(className); - } - + List implementationNames = readStringList(data, pool); provides.add(new ProvidesEntry(interfaceName, implementationNames)); } - return provides; } + private static List readStringList(DataInputFullStream data, ConstantPool pool) throws IOException { + int count = data.readUnsignedShort(); + if (count <= 0) { + return Collections.emptyList(); + } + else { + List strings = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + int index = data.readUnsignedShort(); + strings.add(pool.getPrimitiveConstant(index).getString()); + } + return strings; + } + } + public static final class RequiresEntry { - public String moduleName; - public int moduleFlags; - public String moduleVersion; + public final String moduleName; + public final int moduleFlags; + public final String moduleVersion; public RequiresEntry(String moduleName, int moduleFlags, String moduleVersion) { this.moduleName = moduleName; @@ -191,9 +130,9 @@ public class StructModuleAttribute extends StructGeneralAttribute { } public static final class ExportsEntry { - public String packageName; - public int exportsFlags; - public List exportToModules; + public final String packageName; + public final int exportsFlags; + public final List exportToModules; public ExportsEntry(String packageName, int exportsFlags, List exportToModules) { this.packageName = packageName; @@ -203,9 +142,9 @@ public class StructModuleAttribute extends StructGeneralAttribute { } public static final class OpensEntry { - public String packageName; - public int opensFlags; - public List opensToModules; + public final String packageName; + public final int opensFlags; + public final List opensToModules; public OpensEntry(String packageName, int exportsFlags, List exportToModules) { this.packageName = packageName; @@ -215,13 +154,12 @@ public class StructModuleAttribute extends StructGeneralAttribute { } public static final class ProvidesEntry { - public String interfaceName; - public List implementationNames; + public final String interfaceName; + public final List implementationNames; public ProvidesEntry(String interfaceName, List implementationNames) { this.interfaceName = interfaceName; this.implementationNames = implementationNames; } } - -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java index fdbb271..24c6152 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.StructRecordComponent; @@ -22,12 +22,11 @@ public class StructRecordAttribute extends StructGeneralAttribute { List components; @Override - public void initContent(DataInputFullStream data, - ConstantPool pool) throws IOException { + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int componentCount = data.readUnsignedShort(); StructRecordComponent[] components = new StructRecordComponent[componentCount]; for (int i = 0; i < componentCount; i++) { - components[i] = new StructRecordComponent(data, pool); + components[i] = StructRecordComponent.create(data, pool); } this.components = Arrays.asList(components); } diff --git a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java index d90ba1d..3280038 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java @@ -212,18 +212,12 @@ public class ConstantPool implements NewClassNameBuilder { String newName = interceptor.getName(vt.value); if (newName != null) { StringBuilder buffer = new StringBuilder(); - if (vt.arrayDim > 0) { - for (int i = 0; i < vt.arrayDim; i++) { - buffer.append('['); - } - - buffer.append('L').append(newName).append(';'); + buffer.append("[".repeat(vt.arrayDim)).append('L').append(newName).append(';'); } else { buffer.append(newName); } - return buffer.toString(); } @@ -238,4 +232,4 @@ public class ConstantPool implements NewClassNameBuilder { return MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java index 63c0f8e..8b6105d 100644 --- a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java +++ b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java @@ -1,7 +1,8 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.lazy; import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; +import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -19,20 +20,20 @@ public class LazyLoader { this.provider = provider; } - public void addClassLink(String classname, Link link) { - mapClassLinks.put(classname, link); + public void addClassLink(String className, Link link) { + mapClassLinks.put(className, link); } - public void removeClassLink(String classname) { - mapClassLinks.remove(classname); + public void removeClassLink(String className) { + mapClassLinks.remove(className); } - public Link getClassLink(String classname) { - return mapClassLinks.get(classname); + public Link getClassLink(String className) { + return mapClassLinks.get(className); } - public ConstantPool loadPool(String classname) { - try (DataInputFullStream in = getClassStream(classname)) { + public ConstantPool loadPool(String className) { + try (DataInputFullStream in = getClassStream(className)) { if (in != null) { in.discard(8); return new ConstantPool(in); @@ -45,14 +46,14 @@ public class LazyLoader { } } - public byte[] loadBytecode(StructMethod mt, int codeFullLength) { - String className = mt.getClassStruct().qualifiedName; + public byte[] loadBytecode(StructClass classStruct, StructMethod mt, int codeFullLength) { + String className = classStruct.qualifiedName; try (DataInputFullStream in = getClassStream(className)) { if (in != null) { in.discard(8); - ConstantPool pool = mt.getClassStruct().getPool(); + ConstantPool pool = classStruct.getPool(); if (pool == null) { pool = new ConstantPool(in); } @@ -90,7 +91,7 @@ public class LazyLoader { for (int j = 0; j < attrSize; j++) { int attrNameIndex = in.readUnsignedShort(); String attrName = pool.getPrimitiveConstant(attrNameIndex).getString(); - if (!StructGeneralAttribute.ATTRIBUTE_CODE.getName().equals(attrName)) { + if (!StructGeneralAttribute.ATTRIBUTE_CODE.name.equals(attrName)) { in.discard(in.readInt()); continue; } @@ -138,4 +139,4 @@ public class LazyLoader { this.internalPath = internalPath; } } -} \ No newline at end of file +} diff --git a/testData/classes/java9/module-info.class b/testData/classes/java9/module-info.class index 59005e4978071319bbaa38052ffa71306eec0b30..7f4d09012a0fa34e1c5ed69a6fd48d9276214dc1 100644 GIT binary patch literal 392 zcmZ`#K~BRk5S(qHp$&yXLMz}6B!mkBPDls|2_!@-?rvf#gA+$_k~8n(zyo*`Vv}47 z5|-@o?2gA{e|>*^0yxLa!7gDmH&qeKTcssolA5H_@E8-4V6(?cH{j05g$wg_CpX(jwd#vyA)Eq8fsYJWLl Jo9knqJ3my$S-t=O delta 205 zcmeBRUd6ff$?3=9nB45I7|yc2nqRM~v+rCYq!yPjGO&WEi5C>=1V9Y^5FinpT2z*qoXW@`f-dBlTaeSh zz`zK!5dxSPn1Bo>24)5pAZ^9K3Kj-h3#8eB;v8T;Cy)e*q4OCTxPao^K+FRGRYD-X diff --git a/testData/results/module-info.dec b/testData/results/module-info.dec index c45d666..73de684 100644 --- a/testData/results/module-info.dec +++ b/testData/results/module-info.dec @@ -1,13 +1,14 @@ +@Deprecated module sample.module { requires java.base; - exports test; - exports test2 to + exports sample.pkg1; + exports sample.pkg2 to java.base; - opens test; - opens test2 to + opens sample.pkg1; + opens sample.pkg2 to java.base; uses java.util.spi.ToolProvider; - provides test.TestService with - test.TestServiceImpl; + provides sample.pkg1.TestService with + sample.pkg1.TestServiceImpl; } diff --git a/testData/src/java9/sample.module/TestClass.java b/testData/src/java9/sample.module/TestClass.java new file mode 100644 index 0000000..0a858dd --- /dev/null +++ b/testData/src/java9/sample.module/TestClass.java @@ -0,0 +1,3 @@ +package sample.pkg2; + +public class TestClass {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/TestService.java b/testData/src/java9/sample.module/TestService.java new file mode 100644 index 0000000..d73510f --- /dev/null +++ b/testData/src/java9/sample.module/TestService.java @@ -0,0 +1,3 @@ +package sample.pkg1; + +public interface TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/TestServiceImpl.java b/testData/src/java9/sample.module/TestServiceImpl.java new file mode 100644 index 0000000..0463d99 --- /dev/null +++ b/testData/src/java9/sample.module/TestServiceImpl.java @@ -0,0 +1,3 @@ +package sample.pkg1; + +public class TestServiceImpl implements TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/module-info.java b/testData/src/java9/sample.module/module-info.java index ea71a46..82d5266 100644 --- a/testData/src/java9/sample.module/module-info.java +++ b/testData/src/java9/sample.module/module-info.java @@ -1,15 +1,16 @@ +@Deprecated module sample.module { requires java.base; uses java.util.spi.ToolProvider; - provides test.TestService with test.TestServiceImpl; + provides sample.pkg1.TestService with sample.pkg1.TestServiceImpl; - exports test; + exports sample.pkg1; - exports test2 to java.base; + exports sample.pkg2 to java.base; - opens test; + opens sample.pkg1; - opens test2 to java.base; -} \ No newline at end of file + opens sample.pkg2 to java.base; +} diff --git a/testData/src/java9/sample.module/test/TestService.java b/testData/src/java9/sample.module/test/TestService.java deleted file mode 100644 index c2a6cd7..0000000 --- a/testData/src/java9/sample.module/test/TestService.java +++ /dev/null @@ -1,3 +0,0 @@ -package test; - -public interface TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/test/TestServiceImpl.java b/testData/src/java9/sample.module/test/TestServiceImpl.java deleted file mode 100644 index 9a153e8..0000000 --- a/testData/src/java9/sample.module/test/TestServiceImpl.java +++ /dev/null @@ -1,3 +0,0 @@ -package test; - -public class TestServiceImpl implements TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/test2/TestClass.java b/testData/src/java9/sample.module/test2/TestClass.java deleted file mode 100644 index 202153e..0000000 --- a/testData/src/java9/sample.module/test2/TestClass.java +++ /dev/null @@ -1,3 +0,0 @@ -package test2; - -public class TestClass {} \ No newline at end of file From 37bb6cf4d8adaa538401871ec0df86198b343e99 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 12 Feb 2021 09:20:45 +0100 Subject: [PATCH 17/27] [java decompiler] separating `module-info` sections GitOrigin-RevId: 63c315cd01502cbdd76a1ef2c86b3634ce6250fd --- .../java/decompiler/main/ClassWriter.java | 75 ++++++++++++------- testData/results/module-info.dec | 4 + 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 3da67c6..05b93c9 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -286,44 +286,63 @@ public class ClassWriter { } private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) { - for (StructModuleAttribute.RequiresEntry requires : moduleAttribute.requires) { - buffer.appendIndent(1).append("requires ").append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator(); - } - - for (StructModuleAttribute.ExportsEntry exports : moduleAttribute.exports) { - buffer.appendIndent(1).append("exports ").append(exports.packageName.replace('/', '.')); + boolean newLineNeeded = false; - List exportToModules = exports.exportToModules; - if (exportToModules.size() > 0) { - buffer.append(" to").appendLineSeparator(); - appendFQClassNames(buffer, exportToModules); + List requiresEntries = moduleAttribute.requires; + if (!requiresEntries.isEmpty()) { + for (StructModuleAttribute.RequiresEntry requires : requiresEntries) { + buffer.appendIndent(1).append("requires ").append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator(); } - - buffer.append(';').appendLineSeparator(); + newLineNeeded = true; } - for (StructModuleAttribute.OpensEntry opens : moduleAttribute.opens) { - buffer.appendIndent(1).append("opens ").append(opens.packageName.replace('/', '.')); - - List opensToModules = opens.opensToModules; - if (opensToModules.size() > 0) { - buffer.append(" to").appendLineSeparator(); - appendFQClassNames(buffer, opensToModules); + List exportsEntries = moduleAttribute.exports; + if (!exportsEntries.isEmpty()) { + if (newLineNeeded) buffer.appendLineSeparator(); + for (StructModuleAttribute.ExportsEntry exports : exportsEntries) { + buffer.appendIndent(1).append("exports ").append(exports.packageName.replace('/', '.')); + List exportToModules = exports.exportToModules; + if (exportToModules.size() > 0) { + buffer.append(" to").appendLineSeparator(); + appendFQClassNames(buffer, exportToModules); + } + buffer.append(';').appendLineSeparator(); } + newLineNeeded = true; + } - buffer.append(';').appendLineSeparator(); + List opensEntries = moduleAttribute.opens; + if (!opensEntries.isEmpty()) { + if (newLineNeeded) buffer.appendLineSeparator(); + for (StructModuleAttribute.OpensEntry opens : opensEntries) { + buffer.appendIndent(1).append("opens ").append(opens.packageName.replace('/', '.')); + List opensToModules = opens.opensToModules; + if (opensToModules.size() > 0) { + buffer.append(" to").appendLineSeparator(); + appendFQClassNames(buffer, opensToModules); + } + buffer.append(';').appendLineSeparator(); + } + newLineNeeded = true; } - for (String uses : moduleAttribute.uses) { - buffer.appendIndent(1).append("uses ").append(ExprProcessor.buildJavaClassName(uses)).append(';').appendLineSeparator(); + List usesEntries = moduleAttribute.uses; + if (!usesEntries.isEmpty()) { + if (newLineNeeded) buffer.appendLineSeparator(); + for (String uses : usesEntries) { + buffer.appendIndent(1).append("uses ").append(ExprProcessor.buildJavaClassName(uses)).append(';').appendLineSeparator(); + } + newLineNeeded = true; } - for (StructModuleAttribute.ProvidesEntry provides : moduleAttribute.provides) { - buffer.appendIndent(1).append("provides ").append(ExprProcessor.buildJavaClassName(provides.interfaceName)).append(" with").appendLineSeparator(); - @SuppressWarnings({"SSBasedInspection", "RedundantSuppression"}) List javaNames = - provides.implementationNames.stream().map(ExprProcessor::buildJavaClassName).collect(Collectors.toList()); - appendFQClassNames(buffer, javaNames); - buffer.append(';').appendLineSeparator(); + List providesEntries = moduleAttribute.provides; + if (!providesEntries.isEmpty()) { + if (newLineNeeded) buffer.appendLineSeparator(); + for (StructModuleAttribute.ProvidesEntry provides : providesEntries) { + buffer.appendIndent(1).append("provides ").append(ExprProcessor.buildJavaClassName(provides.interfaceName)).append(" with").appendLineSeparator(); + appendFQClassNames(buffer, provides.implementationNames.stream().map(ExprProcessor::buildJavaClassName).collect(Collectors.toList())); + buffer.append(';').appendLineSeparator(); + } } } diff --git a/testData/results/module-info.dec b/testData/results/module-info.dec index 73de684..12d289d 100644 --- a/testData/results/module-info.dec +++ b/testData/results/module-info.dec @@ -1,13 +1,17 @@ @Deprecated module sample.module { requires java.base; + exports sample.pkg1; exports sample.pkg2 to java.base; + opens sample.pkg1; opens sample.pkg2 to java.base; + uses java.util.spi.ToolProvider; + provides sample.pkg1.TestService with sample.pkg1.TestServiceImpl; } From 23b6aacfc7ed90b128f6ab153e19988d16cff193 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 12 Feb 2021 09:22:36 +0100 Subject: [PATCH 18/27] Cleanup (formatting) GitOrigin-RevId: f6f84befdd81adfe54616cad5e1b69514e0335dc --- test/org/jetbrains/java/decompiler/SingleClassesTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index c6f1b8c..7ac11d8 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler; import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; @@ -70,8 +70,7 @@ public class SingleClassesTest { @Test public void testAnonymousSignature() { doTest("pkg/TestAnonymousSignature"); } @Test public void testLocalsSignature() { doTest("pkg/TestLocalsSignature"); } @Test public void testParameterizedTypes() { doTest("pkg/TestParameterizedTypes"); } - @Test public void testShadowing() { doTest("pkg/TestShadowing", "pkg/Shadow", "ext/Shadow", - "pkg/TestShadowingSuperClass"); } + @Test public void testShadowing() { doTest("pkg/TestShadowing", "pkg/Shadow", "ext/Shadow", "pkg/TestShadowingSuperClass"); } @Test public void testStringConcat() { doTest("pkg/TestStringConcat"); } @Test public void testJava9StringConcat() { doTest("java9/TestJava9StringConcat"); } @Test public void testJava9ModuleInfo() { doTest("java9/module-info"); } From e130aa68964dea2160594700f67f195a06bcd32c Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 12 Feb 2021 12:30:20 +0100 Subject: [PATCH 19/27] [java decompiler] supporting `package-info` GitOrigin-RevId: e20e9b802600dec52700065a7a29799c73c1dc49 --- .../java/decompiler/main/ClassWriter.java | 50 +++++------ .../decompiler/main/ClassesProcessor.java | 80 ++++++++++-------- .../main/collectors/ImportCollector.java | 21 ++--- .../java/decompiler/SingleClassesTest.java | 2 +- testData/classes/java9/module-info.class | Bin 392 -> 417 bytes testData/classes/pkg/package-info.class | Bin 166 -> 186 bytes testData/results/module-info.dec | 5 +- testData/results/package-info.dec | 6 +- testData/src/ext/PkgAnno.java | 8 ++ .../java9/sample.module/TestModuleAnno.java | 8 ++ .../src/java9/sample.module/module-info.java | 4 +- testData/src/pkg/package-info.java | 4 +- 12 files changed, 108 insertions(+), 80 deletions(-) create mode 100644 testData/src/ext/PkgAnno.java create mode 100644 testData/src/java9/sample.module/TestModuleAnno.java diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 05b93c9..3f111a6 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -249,13 +249,6 @@ public class ClassWriter { } } - if (cl.hasModifier(CodeConstants.ACC_MODULE)) { - StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); - if (moduleAttribute != null) { - writeModuleInfoBody(buffer, moduleAttribute); - } - } - buffer.appendIndent(indent).append('}'); if (node.type != ClassNode.CLASS_ANONYMOUS) { @@ -269,10 +262,10 @@ public class ClassWriter { DecompilerContext.getLogger().endWriteClass(); } + @SuppressWarnings("SpellCheckingInspection") private static boolean isSyntheticRecordMethod(StructClass cl, StructMethod mt, TextBuffer code) { if (cl.getRecordComponents() != null) { String name = mt.getName(), descriptor = mt.getDescriptor(); - //noinspection SpellCheckingInspection if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") || name.equals("hashCode") && descriptor.equals("()I") || name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) { @@ -285,6 +278,30 @@ public class ClassWriter { return false; } + public static void packageInfoToJava(StructClass cl, TextBuffer buffer) { + appendAnnotations(buffer, 0, cl, -1); + + int index = cl.qualifiedName.lastIndexOf('/'); + String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); + buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator(); + } + + public static void moduleInfoToJava(StructClass cl, TextBuffer buffer) { + appendAnnotations(buffer, 0, cl, -1); + + StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); + + if ((moduleAttribute.moduleFlags & CodeConstants.ACC_OPEN) != 0) { + buffer.append("open "); + } + + buffer.append("module ").append(moduleAttribute.moduleName).append(" {").appendLineSeparator(); + + writeModuleInfoBody(buffer, moduleAttribute); + + buffer.append('}').appendLineSeparator(); + } + private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) { boolean newLineNeeded = false; @@ -368,7 +385,6 @@ public class ClassWriter { boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; - boolean isModuleInfo = (flags & CodeConstants.ACC_MODULE) != 0 && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); if (isDeprecated) { appendDeprecation(buffer, indent); @@ -414,24 +430,10 @@ public class ClassWriter { else if (components != null) { buffer.append("record "); } - else if (isModuleInfo) { - StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); - - if ((moduleAttribute.moduleFlags & CodeConstants.ACC_OPEN) != 0) { - buffer.append("open "); - } - - buffer.append("module "); - buffer.append(moduleAttribute.moduleName); - } else { buffer.append("class "); } - - // Handled above - if (!isModuleInfo) { - buffer.append(node.simpleName); - } + buffer.append(node.simpleName); GenericClassDescriptor descriptor = getGenericClassDescriptor(cl); if (descriptor != null && !descriptor.fparameters.isEmpty()) { diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index d05c839..7bb50aa 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -302,55 +302,65 @@ public class ClassesProcessor implements CodeConstants { return; } + boolean packageInfo = cl.isSynthetic() && "package-info".equals(root.simpleName); + boolean moduleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); + DecompilerContext.getLogger().startReadingClass(cl.qualifiedName); try { ImportCollector importCollector = new ImportCollector(root); DecompilerContext.startClass(importCollector); - new LambdaProcessor().processClass(root); + if (packageInfo) { + ClassWriter.packageInfoToJava(cl, buffer); - // add simple class names to implicit import - addClassnameToImport(root, importCollector); + importCollector.writeImports(buffer, false); + } + else if (moduleInfo) { + TextBuffer moduleBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); + ClassWriter.moduleInfoToJava(cl, moduleBuffer); - // build wrappers for all nested classes (that's where actual processing takes place) - initWrappers(root); + importCollector.writeImports(buffer, true); - new NestedClassProcessor().processClass(root, root); + buffer.append(moduleBuffer); + } + else { + new LambdaProcessor().processClass(root); - new NestedMemberAccess().propagateMemberAccess(root); + // add simple class names to implicit import + addClassNameToImport(root, importCollector); - TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); - new ClassWriter().classToJava(root, classBuffer, 0, null); + // build wrappers for all nested classes (that's where actual processing takes place) + initWrappers(root); - int index = cl.qualifiedName.lastIndexOf("/"); - if (index >= 0) { - String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); + new NestedClassProcessor().processClass(root, root); - buffer.append("package "); - buffer.append(packageName); - buffer.append(";"); - buffer.appendLineSeparator(); - buffer.appendLineSeparator(); - } + new NestedMemberAccess().propagateMemberAccess(root); - int import_lines_written = importCollector.writeImports(buffer); - if (import_lines_written > 0) { - buffer.appendLineSeparator(); - } + TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); + new ClassWriter().classToJava(root, classBuffer, 0, null); - int offsetLines = buffer.countLines(); + int index = cl.qualifiedName.lastIndexOf('/'); + if (index >= 0) { + String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); + buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator(); + } - buffer.append(classBuffer); + importCollector.writeImports(buffer, true); - if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { - BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper(); - mapper.addTotalOffset(offsetLines); - if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) { - buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping()); - } - if (DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE)) { - buffer.appendLineSeparator(); - mapper.dumpMapping(buffer, true); + int offsetLines = buffer.countLines(); + + buffer.append(classBuffer); + + if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { + BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper(); + mapper.addTotalOffset(offsetLines); + if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) { + buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping()); + } + if (DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE)) { + buffer.appendLineSeparator(); + mapper.dumpMapping(buffer, true); + } } } } @@ -375,13 +385,13 @@ public class ClassesProcessor implements CodeConstants { } } - private static void addClassnameToImport(ClassNode node, ImportCollector imp) { + private static void addClassNameToImport(ClassNode node, ImportCollector imp) { if (node.simpleName != null && node.simpleName.length() > 0) { imp.getShortName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false); } for (ClassNode nd : node.nested) { - addClassnameToImport(nd, imp); + addClassNameToImport(nd, imp); } } diff --git a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java index 7b8c3b4..647ac7c 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.collectors; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; @@ -150,21 +150,14 @@ public class ImportCollector { return result == null ? shortName : result; } - public int writeImports(TextBuffer buffer) { - int importLinesWritten = 0; - + public void writeImports(TextBuffer buffer, boolean addSeparator) { List imports = packImports(); - - for (String s : imports) { - buffer.append("import "); - buffer.append(s); - buffer.append(';'); + for (String line : imports) { + buffer.append("import ").append(line).append(';').appendLineSeparator(); + } + if (addSeparator && !imports.isEmpty()) { buffer.appendLineSeparator(); - - importLinesWritten++; } - - return importLinesWritten; } private List packImports() { @@ -181,4 +174,4 @@ public class ImportCollector { .map(ent -> ent.getValue() + "." + ent.getKey()) .collect(Collectors.toList()); } -} \ No newline at end of file +} diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 7ac11d8..feea5aa 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -115,9 +115,9 @@ public class SingleClassesTest { @Test public void testSynchronizedUnprotected() { doTest("pkg/TestSynchronizedUnprotected"); } @Test public void testInterfaceSuper() { doTest("pkg/TestInterfaceSuper"); } @Test public void testFieldSingleAccess() { doTest("pkg/TestFieldSingleAccess"); } + @Test public void testPackageInfo() { doTest("pkg/package-info"); } // TODO: fix all below - //@Test public void testPackageInfo() { doTest("pkg/package-info"); } //@Test public void testSwitchOnStrings() { doTest("pkg/TestSwitchOnStrings");} //@Test public void testUnionType() { doTest("pkg/TestUnionType"); } //@Test public void testInnerClassConstructor2() { doTest("pkg/TestInner2"); } diff --git a/testData/classes/java9/module-info.class b/testData/classes/java9/module-info.class index 7f4d09012a0fa34e1c5ed69a6fd48d9276214dc1..0380f41b55f6e340258f1b1a449435fa1567fe77 100644 GIT binary patch delta 229 zcmeBRUdYUQ>ff$?3=9nB43g{&f)ja_>ZOB9^GY&vQ$6#_GK({la#9`h^72a(OEUBG ziWwQCdff$?3=9nB4C3qz{1bVVVkCn~^GY&vQ^PWgGm~;s9rNm^Ye-s z8N__D63Y_xa}x8?^<7d6ic*skOHxy;85!7o^HWN5QiT}=gc$@G88{3L^$heZ^$bNA zghUvG*%?IG8ARC`#3s()=FY~z$iND;kqwBg7}$X%15ktmNOCeTF>rzT+)y!eJ|hDU KP%SSI^8o-l1R!Ss diff --git a/testData/classes/pkg/package-info.class b/testData/classes/pkg/package-info.class index eee29394a7d0983ed45f01bfd0318a0809373ab2..88f6acc440589c3652dddaa35ed6eb1043eead55 100644 GIT binary patch delta 99 zcmZ3+xQkKX)W2Q(7#JAL8MxUQIN2GvCJHE9Ne7kYm1O3odghg77H1~qq&nv1<(DLu zWaj4;Gcxe{q*j#Z2V|#%M6DSaSj!S~N>dpbnDzAZCdN7Pa5FG6umLf9F#`tx%?28a delta 79 zcmdnRxQtQY)W2Q(7#J8#7&zG(*x4C4CJHEPO9qwZm1O3ohGiCKCgr3$=H=y=B$j06 j=M^(D@cCq=Wb3 Date: Fri, 12 Feb 2021 21:43:25 +0100 Subject: [PATCH 20/27] [java decompiler] skipping generated `module-info` statements GitOrigin-RevId: c2f9cfd88f57e7d59cca54ec090f67f29377ff23 --- .../java/decompiler/main/ClassWriter.java | 44 +++++++++++------- .../struct/attr/StructModuleAttribute.java | 24 +++++----- testData/classes/java9/module-info.class | Bin 417 -> 443 bytes testData/results/module-info.dec | 4 +- .../java9/sample.module/TestModuleAnno.java | 2 +- .../src/java9/sample.module/module-info.java | 4 +- 6 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 3f111a6..2d57c02 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -308,39 +308,45 @@ public class ClassWriter { List requiresEntries = moduleAttribute.requires; if (!requiresEntries.isEmpty()) { for (StructModuleAttribute.RequiresEntry requires : requiresEntries) { - buffer.appendIndent(1).append("requires ").append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator(); + if (!isGenerated(requires.flags)) { + buffer.appendIndent(1).append("requires ").append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator(); + newLineNeeded = true; + } } - newLineNeeded = true; } List exportsEntries = moduleAttribute.exports; if (!exportsEntries.isEmpty()) { if (newLineNeeded) buffer.appendLineSeparator(); for (StructModuleAttribute.ExportsEntry exports : exportsEntries) { - buffer.appendIndent(1).append("exports ").append(exports.packageName.replace('/', '.')); - List exportToModules = exports.exportToModules; - if (exportToModules.size() > 0) { - buffer.append(" to").appendLineSeparator(); - appendFQClassNames(buffer, exportToModules); + if (!isGenerated(exports.flags)) { + buffer.appendIndent(1).append("exports ").append(exports.packageName.replace('/', '.')); + List exportToModules = exports.exportToModules; + if (exportToModules.size() > 0) { + buffer.append(" to").appendLineSeparator(); + appendFQClassNames(buffer, exportToModules); + } + buffer.append(';').appendLineSeparator(); + newLineNeeded = true; } - buffer.append(';').appendLineSeparator(); } - newLineNeeded = true; } List opensEntries = moduleAttribute.opens; if (!opensEntries.isEmpty()) { if (newLineNeeded) buffer.appendLineSeparator(); for (StructModuleAttribute.OpensEntry opens : opensEntries) { - buffer.appendIndent(1).append("opens ").append(opens.packageName.replace('/', '.')); - List opensToModules = opens.opensToModules; - if (opensToModules.size() > 0) { - buffer.append(" to").appendLineSeparator(); - appendFQClassNames(buffer, opensToModules); + if (!isGenerated(opens.flags)) { + buffer.appendIndent(1).append("opens ").append(opens.packageName.replace('/', '.')); + List opensToModules = opens.opensToModules; + if (opensToModules.size() > 0) { + buffer.append(" to").appendLineSeparator(); + appendFQClassNames(buffer, opensToModules); + } + buffer.append(';').appendLineSeparator(); + newLineNeeded = true; } - buffer.append(';').appendLineSeparator(); } - newLineNeeded = true; } List usesEntries = moduleAttribute.uses; @@ -363,6 +369,10 @@ public class ClassWriter { } } + private static boolean isGenerated(int flags) { + return (flags & (CodeConstants.ACC_SYNTHETIC | CodeConstants.ACC_MANDATED)) != 0; + } + private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) { StructLineNumberTableAttribute table = method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE); tracer.setLineNumberTable(table); @@ -1266,4 +1276,4 @@ public class ClassWriter { } } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java index ba2005b..9fd6f89 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java @@ -45,11 +45,11 @@ public class StructModuleAttribute extends StructGeneralAttribute { List requires = new ArrayList<>(requiresCount); for (int i = 0; i < requiresCount; i++) { int moduleNameIndex = data.readUnsignedShort(); - int moduleFlags = data.readUnsignedShort(); + int requiresFlags = data.readUnsignedShort(); int versionIndex = data.readUnsignedShort(); String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString(); String version = versionIndex == 0 ? null : pool.getPrimitiveConstant(versionIndex).getString(); - requires.add(new RequiresEntry(moduleName, moduleFlags, version)); + requires.add(new RequiresEntry(moduleName, requiresFlags, version)); } return requires; } @@ -119,36 +119,36 @@ public class StructModuleAttribute extends StructGeneralAttribute { public static final class RequiresEntry { public final String moduleName; - public final int moduleFlags; + public final int flags; public final String moduleVersion; - public RequiresEntry(String moduleName, int moduleFlags, String moduleVersion) { + public RequiresEntry(String moduleName, int flags, String moduleVersion) { this.moduleName = moduleName; - this.moduleFlags = moduleFlags; + this.flags = flags; this.moduleVersion = moduleVersion; } } public static final class ExportsEntry { public final String packageName; - public final int exportsFlags; + public final int flags; public final List exportToModules; - public ExportsEntry(String packageName, int exportsFlags, List exportToModules) { + public ExportsEntry(String packageName, int flags, List exportToModules) { this.packageName = packageName; - this.exportsFlags = exportsFlags; + this.flags = flags; this.exportToModules = exportToModules; } } public static final class OpensEntry { public final String packageName; - public final int opensFlags; + public final int flags; public final List opensToModules; - public OpensEntry(String packageName, int exportsFlags, List exportToModules) { + public OpensEntry(String packageName, int flags, List exportToModules) { this.packageName = packageName; - this.opensFlags = exportsFlags; + this.flags = flags; this.opensToModules = exportToModules; } } @@ -162,4 +162,4 @@ public class StructModuleAttribute extends StructGeneralAttribute { this.implementationNames = implementationNames; } } -} +} \ No newline at end of file diff --git a/testData/classes/java9/module-info.class b/testData/classes/java9/module-info.class index 0380f41b55f6e340258f1b1a449435fa1567fe77..cc01403116c0dfe7a50deef5fa4835416f0b31ae 100644 GIT binary patch delta 157 zcmZ3;yqlT#)W2Q(7#JAL8Kl`6geLMj@W}WSC*~I9r0N%Br%w#mQWO?u5MgBCFf`ON z(6iJt6lM?=VGt8x5NBtQU}unIXONmWUx$|`E3qt5FD11&yClD0;>o#=4h)m1Sj%3a7paIhKqD9!7#X;LBsT*S0}q(b3l&4>Gcxc2 J)$#+e0060R5w`#U diff --git a/testData/results/module-info.dec b/testData/results/module-info.dec index ec5e268..a2018a5 100644 --- a/testData/results/module-info.dec +++ b/testData/results/module-info.dec @@ -1,8 +1,8 @@ -import pkg.test1.TestModuleAnno; +import sample.pkg1.TestModuleAnno; @TestModuleAnno("...") module sample.module { - requires java.base; + requires java.desktop; exports sample.pkg1; exports sample.pkg2 to diff --git a/testData/src/java9/sample.module/TestModuleAnno.java b/testData/src/java9/sample.module/TestModuleAnno.java index fb28aca..192ee2b 100644 --- a/testData/src/java9/sample.module/TestModuleAnno.java +++ b/testData/src/java9/sample.module/TestModuleAnno.java @@ -1,4 +1,4 @@ -package pkg.test1; +package sample.pkg1; import java.lang.annotation.*; diff --git a/testData/src/java9/sample.module/module-info.java b/testData/src/java9/sample.module/module-info.java index f301ac5..9f42e18 100644 --- a/testData/src/java9/sample.module/module-info.java +++ b/testData/src/java9/sample.module/module-info.java @@ -1,8 +1,8 @@ -import pkg.test1.TestModuleAnno; +import sample.pkg1.TestModuleAnno; @TestModuleAnno("...") module sample.module { - requires java.base; + requires java.desktop; uses java.util.spi.ToolProvider; From e59c63ba567db925878145acc6b4122ac9374a57 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 17 Feb 2021 22:42:32 +0100 Subject: [PATCH 21/27] [java decompiler] switching Gradle build to Maven Central GitOrigin-RevId: ce4e98cb17253495944280269dee014dc49660f6 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 797515b..9ce3662 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ sourceSets { test.java.srcDirs 'test' } -repositories { jcenter() } +repositories { mavenCentral() } dependencies { testCompile 'junit:junit:4.12' testCompile 'org.assertj:assertj-core:3.12.2' From 56d2f5f887b189cfc5a8ae50c0eb18d1a7c15d87 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 5 Apr 2021 16:11:01 +0700 Subject: [PATCH 22/27] Unused assignments removed, misc other fixes GitOrigin-RevId: 9a18d056d33847d77a8ca2177567a6c5d5ec9d8b --- .../java/decompiler/modules/decompiler/exps/NewExprent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java index e946c9a..30cbed4 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java @@ -26,7 +26,7 @@ import java.util.Set; public class NewExprent extends Exprent { private InvocationExprent constructor; private final VarType newType; - private List lstDims = new ArrayList<>(); + private final List lstDims; private List lstArrayElements = new ArrayList<>(); private boolean directArrayInit; private boolean isVarArgParam; From ede143d4bdc4420f978f8ec5720f6870e0bf9afe Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 12 Apr 2021 08:57:47 +0200 Subject: [PATCH 23/27] use jdk9+ Map.entry API instead of AbstractMap.SimpleImmutableEntry GitOrigin-RevId: bac64af4495e36b044873f6a97625342af907bec --- src/org/jetbrains/java/decompiler/main/ClassWriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 2d57c02..e71b0f8 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -502,7 +502,7 @@ public class ClassWriter { } private static boolean isVarArgRecord(StructClass cl) { - String canonicalConstructorDescriptor = + String canonicalConstructorDescriptor = cl.getRecordComponents().stream().map(c -> c.getDescriptor()).collect(Collectors.joining("", "(", ")V")); StructMethod init = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor); return init != null && init.hasModifier(CodeConstants.ACC_VARARGS); From e16fb8ef1db5efd75045839ec6d71a31a50fa919 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 13 Apr 2021 14:04:59 +0700 Subject: [PATCH 24/27] Avoid assignment used as condition GitOrigin-RevId: 6eb52135a5bfdf772bb10d3120648500a95325a4 --- .../modules/decompiler/IfHelper.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java index afcf55e..66e857c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java @@ -31,7 +31,8 @@ public final class IfHelper { res |= mergeAllIfsRec(st, setReorderedIfs); // collapse composed if's - if (changed = mergeIfs(st, setReorderedIfs)) { + if (mergeIfs(st, setReorderedIfs)) { + changed = true; break; } } @@ -75,21 +76,25 @@ public final class IfHelper { continue; } - if (updated = collapseIfIf(rtnode)) { + if (collapseIfIf(rtnode)) { + updated = true; break; } if (!setReorderedIfs.contains(stat.id)) { - if (updated = collapseIfElse(rtnode)) { + if (collapseIfElse(rtnode)) { + updated = true; break; } - if (updated = collapseElse(rtnode)) { + if (collapseElse(rtnode)) { + updated = true; break; } } - if (updated = reorderIf((IfStatement)stat)) { + if (reorderIf((IfStatement)stat)) { + updated = true; setReorderedIfs.add(stat.id); break; } @@ -440,10 +445,9 @@ public final class IfHelper { if (sttemp == ifstat) { break; } - else { - if (elsedirectpath = existsPath(sttemp, next)) { - break; - } + else if (existsPath(sttemp, next)) { + elsedirectpath = true; + break; } } } From 31cff62c9494a0527d89dfe4d2fcb0c9f6f095ea Mon Sep 17 00:00:00 2001 From: Maxim Degtyarev Date: Thu, 15 Apr 2021 19:37:55 +0300 Subject: [PATCH 25/27] Fix for IDEABKL-8006 IDE hangs when decompiling class which is its own superclass GitOrigin-RevId: 1fe14694ce69b135f2e3fe4cde84ce3d42997228 --- .../main/collectors/ImportCollector.java | 17 ++++++++--- .../java/decompiler/SingleClassesTest.java | 11 +++++++ .../pkg/TestInheritanceChainCycle.class | Bin 0 -> 458 bytes .../results/TestInheritanceChainCycle.dec | 22 ++++++++++++++ .../src/pkg/TestInheritanceChainCycle.jasm | 27 ++++++++++++++++++ 5 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 testData/classes/pkg/TestInheritanceChainCycle.class create mode 100644 testData/results/TestInheritanceChainCycle.dec create mode 100644 testData/src/pkg/TestInheritanceChainCycle.jasm diff --git a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java index 647ac7c..dc6b3a5 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java @@ -39,8 +39,10 @@ public class ImportCollector { Map classes = DecompilerContext.getStructContext().getClasses(); LinkedList queue = new LinkedList<>(); + Set processedClasses = new HashSet<>(); StructClass currentClass = root.classStruct; while (currentClass != null) { + processedClasses.add(currentClass); if (currentClass.superClass != null) { queue.add(currentClass.superClass.getString()); } @@ -63,10 +65,17 @@ public class ImportCollector { } // .. and traverse through parent. - currentClass = !queue.isEmpty() ? classes.get(queue.removeFirst()) : null; - while (currentClass == null && !queue.isEmpty()) { - currentClass = classes.get(queue.removeFirst()); - } + do { + currentClass = queue.isEmpty() ? null : classes.get(queue.removeFirst()); + + if (currentClass != null && processedClasses.contains(currentClass)) { + // Class already processed, skipping. + + // This may be sign of circularity in the class hierarchy but in most cases this mean that same interface + // are listed as implemented several times in the class hierarchy. + currentClass = null; + } + } while (currentClass == null && !queue.isEmpty()); } } diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index feea5aa..e0a9b6f 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -5,7 +5,9 @@ import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import java.io.File; import java.io.IOException; @@ -19,6 +21,13 @@ import static org.junit.Assert.assertTrue; public class SingleClassesTest { private DecompilerTestFixture fixture; + /* + * Set individual test duration time limit to 60 seconds. + * This will help us to test bugs hanging decompiler. + */ + @Rule + public Timeout globalTimeout = Timeout.seconds(60); + @Before public void setUp() throws IOException { fixture = new DecompilerTestFixture(); @@ -135,6 +144,8 @@ public class SingleClassesTest { @Test public void testRecordGenericVararg() { doTest("records/TestRecordGenericVararg"); } @Test public void testRecordAnno() { doTest("records/TestRecordAnno"); } + @Test public void testInheritanceChainCycle() { doTest("pkg/TestInheritanceChainCycle"); } + private void doTest(String testFile, String... companionFiles) { ConsoleDecompiler decompiler = fixture.getDecompiler(); diff --git a/testData/classes/pkg/TestInheritanceChainCycle.class b/testData/classes/pkg/TestInheritanceChainCycle.class new file mode 100644 index 0000000000000000000000000000000000000000..0969e39ad78fea01e575e2aa749252540730efe9 GIT binary patch literal 458 zcmZ{f%TB^T6o&udRwxw^;|}A7#At$-1j2rblQP&&Njo*+v2>w{3m?FT zG9GFoVc|@2&di+ifB%`U?~hLa=h(}mUO*EC69zU+6tT-t?+j-AncE&$u07PDRBo?3 zW=IFIHjrnitjhP!OiZ+e9BK>)BN6EvH5H-MTunrO%BAWrCZ0H(@yIt|G1L;R4D1Ku zYN8A7Ti8K`!5jv0I1yLU6AZ?DS@#s#cDiE&RfhfflWYHLL396(z0U42WctA)5sTNt z^MaE@CwAKm<$+Y4HuCXK7*CaaVw&Ty9CKyNG0{9g)*{eW0UrB zrNEYdptwc1Bg%|&_V^v?7dkLBD3*{;BAFwTG;jY&P7?%Bctxtp-jG?zs?-eBN!%dM NCh}+zutaYum_Ml&V1obv literal 0 HcmV?d00001 diff --git a/testData/results/TestInheritanceChainCycle.dec b/testData/results/TestInheritanceChainCycle.dec new file mode 100644 index 0000000..f2490af --- /dev/null +++ b/testData/results/TestInheritanceChainCycle.dec @@ -0,0 +1,22 @@ +package pkg; + +public class TestInheritanceChainCycle extends TestInheritanceChainCycle { + public void printMessage() { + System.out.println("Hello, bug!");// 21 22 23 + }// 24 +} + +class 'pkg/TestInheritanceChainCycle' { + method 'printMessage ()V' { + 0 4 + 3 4 + 5 4 + 8 5 + } +} + +Lines mapping: +21 <-> 5 +22 <-> 5 +23 <-> 5 +24 <-> 6 diff --git a/testData/src/pkg/TestInheritanceChainCycle.jasm b/testData/src/pkg/TestInheritanceChainCycle.jasm new file mode 100644 index 0000000..b6ccdb6 --- /dev/null +++ b/testData/src/pkg/TestInheritanceChainCycle.jasm @@ -0,0 +1,27 @@ +/** + * This code can be assembled with asmtools + * using asmtools jasm -g *.jasm command line. + */ +package pkg; + +super public class TestInheritanceChainCycle + extends TestInheritanceChainCycle + version 52:0 +{ + public Method "":"()V" + stack 1 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } + public Method printMessage:"()V" + stack 2 locals 1 + { + getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; + ldc String "Hello, bug!"; + invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V"; + return; + } + +} // end Class TestInheritanceChainCycle From 4814e96e8faf7dc8a5966077dcadc101cadb2efa Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Tue, 20 Apr 2021 22:53:31 +0200 Subject: [PATCH 26/27] [java decompiler] migrating to Gradle 7.0 Obsoletes #1523 GitOrigin-RevId: b1d2b2723f95eaffdbf2e1c1f055de77ab538fe5 --- build.gradle | 6 +-- gradle/wrapper/gradle-wrapper.jar | Bin 54706 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 53 ++++++++++++++--------- gradlew.bat | 43 ++++++++++-------- 5 files changed, 61 insertions(+), 44 deletions(-) diff --git a/build.gradle b/build.gradle index 9ce3662..d158b9c 100644 --- a/build.gradle +++ b/build.gradle @@ -13,12 +13,12 @@ sourceSets { repositories { mavenCentral() } dependencies { - testCompile 'junit:junit:4.12' - testCompile 'org.assertj:assertj-core:3.12.2' + testImplementation 'junit:junit:4.12' + testImplementation 'org.assertj:assertj-core:3.12.2' } jar { - archiveName 'fernflower.jar' + archiveFileName = 'fernflower.jar' manifest { attributes 'Main-Class': 'org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index cd13592d068005179bd9e3c58ef9024b2a14337f..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644 GIT binary patch literal 59203 zcma&O1CT9Y(k9%tZQHhO+qUh#ZQHhO+qmuS+qP|E@9xZO?0h@l{(r>DQ>P;GjjD{w zH}lENr;dU&FbEU?00aa80D$0M0RRB{U*7-#kbjS|qAG&4l5%47zyJ#WrfA#1$1Ctx zf&Z_d{GW=lf^w2#qRJ|CvSJUi(^E3iv~=^Z(zH}F)3Z%V3`@+rNB7gTVU{Bb~90p|f+0(v;nz01EG7yDMX9@S~__vVgv%rS$+?IH+oZ03D5zYrv|^ zC1J)SruYHmCki$jLBlTaE5&dFG9-kq3!^i>^UQL`%gn6)jz54$WDmeYdsBE9;PqZ_ zoGd=P4+|(-u4U1dbAVQrFWoNgNd;0nrghPFbQrJctO>nwDdI`Q^i0XJDUYm|T|RWc zZ3^Qgo_Qk$%Fvjj-G}1NB#ZJqIkh;kX%V{THPqOyiq)d)0+(r9o(qKlSp*hmK#iIY zA^)Vr$-Hz<#SF=0@tL@;dCQsm`V9s1vYNq}K1B)!XSK?=I1)tX+bUV52$YQu*0%fnWEukW>mxkz+%3-S!oguE8u#MGzST8_Dy^#U?fA@S#K$S@9msUiX!gd_ow>08w5)nX{-KxqMOo7d?k2&?Vf z&diGDtZr(0cwPe9z9FAUSD9KC)7(n^lMWuayCfxzy8EZsns%OEblHFSzP=cL6}?J| z0U$H!4S_TVjj<`6dy^2j`V`)mC;cB%* z8{>_%E1^FH!*{>4a7*C1v>~1*@TMcLK{7nEQ!_igZC}ikJ$*<$yHy>7)oy79A~#xE zWavoJOIOC$5b6*q*F_qN1>2#MY)AXVyr$6x4b=$x^*aqF*L?vmj>Mgv+|ITnw_BoW zO?jwHvNy^prH{9$rrik1#fhyU^MpFqF2fYEt(;4`Q&XWOGDH8k6M=%@fics4ajI;st# zCU^r1CK&|jzUhRMv;+W~6N;u<;#DI6cCw-otsc@IsN3MoSD^O`eNflIoR~l4*&-%RBYk@gb^|-JXs&~KuSEmMxB}xSb z@K76cXD=Y|=I&SNC2E+>Zg?R6E%DGCH5J1nU!A|@eX9oS(WPaMm==k2s_ueCqdZw| z&hqHp)47`c{BgwgvY2{xz%OIkY1xDwkw!<0veB#yF4ZKJyabhyyVS`gZepcFIk%e2 zTcrmt2@-8`7i-@5Nz>oQWFuMC_KlroCl(PLSodswHqJ3fn<;gxg9=}~3x_L3P`9Sn zChIf}8vCHvTriz~T2~FamRi?rh?>3bX1j}%bLH+uFX+p&+^aXbOK7clZxdU~6Uxgy z8R=obwO4dL%pmVo*Ktf=lH6hnlz_5k3cG;m8lgaPp~?eD!Yn2kf)tU6PF{kLyn|oI@eQ`F z3IF7~Blqg8-uwUuWZScRKn%c2_}dXB6Dx_&xR*n9M9LXasJhtZdr$vBY!rP{c@=)& z#!?L$2UrkvClwQO>U*fSMs67oSj2mxiJ$t;E|>q%Kh_GzzWWO&3;ufU%2z%ucBU8H z3WIwr$n)cfCXR&>tyB7BcSInK>=ByZA%;cVEJhcg<#6N{aZC4>K41XF>ZgjG`z_u& zGY?;Ad?-sgiOnI`oppF1o1Gurqbi*;#x2>+SSV6|1^G@ooVy@fg?wyf@0Y!UZ4!}nGuLeC^l)6pwkh|oRY`s1Pm$>zZ3u-83T|9 zGaKJIV3_x+u1>cRibsaJpJqhcm%?0-L;2 zitBrdRxNmb0OO2J%Y&Ym(6*`_P3&&5Bw157{o7LFguvxC$4&zTy#U=W*l&(Q2MNO} zfaUwYm{XtILD$3864IA_nn34oVa_g^FRuHL5wdUd)+W-p-iWCKe8m_cMHk+=? zeKX)M?Dt(|{r5t7IenkAXo%&EXIb-i^w+0CX0D=xApC=|Xy(`xy+QG^UyFe z+#J6h_&T5i#sV)hj3D4WN%z;2+jJcZxcI3*CHXGmOF3^)JD5j&wfX)e?-|V0GPuA+ zQFot%aEqGNJJHn$!_}#PaAvQ^{3-Ye7b}rWwrUmX53(|~i0v{}G_sI9uDch_brX&6 zWl5Ndj-AYg(W9CGfQf<6!YmY>Ey)+uYd_JNXH=>|`OH-CDCmcH(0%iD_aLlNHKH z7bcW-^5+QV$jK?R*)wZ>r9t}loM@XN&M-Pw=F#xn(;u3!(3SXXY^@=aoj70;_=QE9 zGghsG3ekq#N||u{4We_25U=y#T*S{4I{++Ku)> zQ!DZW;pVcn>b;&g2;YE#+V`v*Bl&Y-i@X6D*OpNA{G@JAXho&aOk(_j^weW{#3X5Y z%$q_wpb07EYPdmyH(1^09i$ca{O<}7) zRWncXdSPgBE%BM#by!E>tdnc$8RwUJg1*x($6$}ae$e9Knj8gvVZe#bLi!<+&BkFj zg@nOpDneyc+hU9P-;jmOSMN|*H#>^Ez#?;%C3hg_65leSUm;iz)UkW)jX#p)e&S&M z1|a?wDzV5NVnlhRBCd_;F87wp>6c<&nkgvC+!@KGiIqWY4l}=&1w7|r6{oBN8xyzh zG$b#2=RJp_iq6)#t5%yLkKx(0@D=C3w+oiXtSuaQ%I1WIb-eiE$d~!)b@|4XLy!CZ z9p=t=%3ad@Ep+<9003D2KZ5VyP~_n$=;~r&YUg5UZ0KVD&tR1DHy9x)qWtKJp#Kq# zP*8p#W(8JJ_*h_3W}FlvRam?<4Z+-H77^$Lvi+#vmhL9J zJ<1SV45xi;SrO2f=-OB(7#iNA5)x1uNC-yNxUw|!00vcW2PufRm>e~toH;M0Q85MQLWd?3O{i8H+5VkR@l9Dg-ma ze2fZ%>G(u5(k9EHj2L6!;(KZ8%8|*-1V|B#EagbF(rc+5iL_5;Eu)L4Z-V;0HfK4d z*{utLse_rvHZeQ>V5H=f78M3Ntg1BPxFCVD{HbNA6?9*^YIq;B-DJd{Ca2L#)qWP? zvX^NhFmX?CTWw&Ns}lgs;r3i+Bq@y}Ul+U%pzOS0Fcv9~aB(0!>GT0)NO?p=25LjN z2bh>6RhgqD7bQj#k-KOm@JLgMa6>%-ok1WpOe)FS^XOU{c?d5shG(lIn3GiVBxmg`u%-j=)^v&pX1JecJics3&jvPI)mDut52? z3jEA)DM%}BYbxxKrizVYwq?(P&19EXlwD9^-6J+4!}9{ywR9Gk42jjAURAF&EO|~N z)?s>$Da@ikI4|^z0e{r`J8zIs>SpM~Vn^{3fArRu;?+43>lD+^XtUcY1HidJwnR6+ z!;oG2=B6Z_=M%*{z-RaHc(n|1RTKQdNjjV!Pn9lFt^4w|AeN06*j}ZyhqZ^!-=cyGP_ShV1rGxkx8t zB;8`h!S{LD%ot``700d0@Grql(DTt4Awgmi+Yr0@#jbe=2#UkK%rv=OLqF)9D7D1j z!~McAwMYkeaL$~kI~90)5vBhBzWYc3Cj1WI0RS`z000R8-@ET0dA~*r(gSiCJmQMN&4%1D zyVNf0?}sBH8zNbBLn>~(W{d3%@kL_eQ6jEcR{l>C|JK z(R-fA!z|TTRG40|zv}7E@PqCAXP3n`;%|SCQ|ZS%ym$I{`}t3KPL&^l5`3>yah4*6 zifO#{VNz3)?ZL$be;NEaAk9b#{tV?V7 zP|wf5YA*1;s<)9A4~l3BHzG&HH`1xNr#%){4xZ!jq%o=7nN*wMuXlFV{HaiQLJ`5G zBhDi#D(m`Q1pLh@Tq+L;OwuC52RdW7b8}~60WCOK5iYMUad9}7aWBuILb({5=z~YF zt?*Jr5NG+WadM{mDL>GyiByCuR)hd zA=HM?J6l1Xv0Dl+LW@w$OTcEoOda^nFCw*Sy^I@$sSuneMl{4ys)|RY#9&NxW4S)9 zq|%83IpslTLoz~&vTo!Ga@?rj_kw{|k{nv+w&Ku?fyk4Ki4I?);M|5Axm)t+BaE)D zm(`AQ#k^DWrjbuXoJf2{Aj^KT zFb1zMSqxq|vceV+Mf-)$oPflsO$@*A0n0Z!R{&(xh8s}=;t(lIy zv$S8x>m;vQNHuRzoaOo?eiWFe{0;$s`Bc+Osz~}Van${u;g(su`3lJ^TEfo~nERfP z)?aFzpDgnLYiERsKPu|0tq4l2wT)Atr6Qb%m-AUn6HnCue*yWICp7TjW$@sO zm5rm4aTcPQ(rfi7a`xP7cKCFrJD}*&_~xgLyr^-bmsL}y;A5P|al8J3WUoBSjqu%v zxC;mK!g(7r6RRJ852Z~feoC&sD3(6}^5-uLK8o)9{8L_%%rItZK9C){UxB|;G>JbP zsRRtS4-3B*5c+K2kvmgZK8472%l>3cntWUOVHxB|{Ay~aOg5RN;{PJgeVD*H%ac+y!h#wi%o2bF2Ca8IyMyH{>4#{E_8u^@+l-+n=V}Sq?$O z{091@v%Bd*3pk0^2UtiF9Z+(a@wy6 zUdw8J*ze$K#=$48IBi1U%;hmhO>lu!uU;+RS}p&6@rQila7WftH->*A4=5W|Fmtze z)7E}jh@cbmr9iup^i%*(uF%LG&!+Fyl@LFA-}Ca#bxRfDJAiR2dt6644TaYw1Ma79 zt8&DYj31j^5WPNf5P&{)J?WlCe@<3u^78wnd(Ja4^a>{^Tw}W>|Cjt^If|7l^l)^Q zbz|7~CF(k_9~n|h;ysZ+jHzkXf(*O*@5m zLzUmbHp=x!Q|!9NVXyipZ3)^GuIG$k;D)EK!a5=8MFLI_lpf`HPKl=-Ww%z8H_0$j ztJ||IfFG1lE9nmQ0+jPQy zCBdKkjArH@K7jVcMNz);Q(Q^R{d5G?-kk;Uu_IXSyWB)~KGIizZL(^&qF;|1PI7!E zTP`%l)gpX|OFn&)M%txpQ2F!hdA~hX1Cm5)IrdljqzRg!f{mN%G~H1&oqe`5eJCIF zHdD7O;AX-{XEV(a`gBFJ9ews#CVS2y!&>Cm_dm3C8*n3MA*e67(WC?uP@8TXuMroq z{#w$%z@CBIkRM7?}Xib+>hRjy?%G!fiw8! z8(gB+8J~KOU}yO7UGm&1g_MDJ$IXS!`+*b*QW2x)9>K~Y*E&bYMnjl6h!{17_8d!%&9D`a7r&LKZjC<&XOvTRaKJ1 zUY@hl5^R&kZl3lU3njk`3dPzxj$2foOL26r(9zsVF3n_F#v)s5vv3@dgs|lP#eylq62{<-vczqP!RpVBTgI>@O6&sU>W|do17+#OzQ7o5A$ICH z?GqwqnK^n2%LR;$^oZM;)+>$X3s2n}2jZ7CdWIW0lnGK-b#EG01)P@aU`pg}th&J-TrU`tIpb5t((0eu|!u zQz+3ZiOQ^?RxxK4;zs=l8q!-n7X{@jSwK(iqNFiRColuEOg}!7cyZi`iBX4g1pNBj zAPzL?P^Ljhn;1$r8?bc=#n|Ed7wB&oHcw()&*k#SS#h}jO?ZB246EGItsz*;^&tzp zu^YJ0=lwsi`eP_pU8}6JA7MS;9pfD;DsSsLo~ogzMNP70@@;Fm8f0^;>$Z>~}GWRw!W5J3tNX*^2+1f3hz{~rIzJo z6W%J(H!g-eI_J1>0juX$X4Cl6i+3wbc~k146UIX&G22}WE>0ga#WLsn9tY(&29zBvH1$`iWtTe zG2jYl@P!P)eb<5DsR72BdI7-zP&cZNI{7q3e@?N8IKc4DE#UVr->|-ryuJXk^u^>4 z$3wE~=q390;XuOQP~TNoDR?#|NSPJ%sTMInA6*rJ%go|=YjGe!B>z6u$IhgQSwoV* zjy3F2#I>uK{42{&IqP59)Y(1*Z>>#W8rCf4_eVsH)`v!P#^;BgzKDR`ARGEZzkNX+ zJUQu=*-ol=Xqqt5=`=pA@BIn@6a9G8C{c&`i^(i+BxQO9?YZ3iu%$$da&Kb?2kCCo zo7t$UpSFWqmydXf@l3bVJ=%K?SSw)|?srhJ-1ZdFu*5QhL$~-IQS!K1s@XzAtv6*Y zl8@(5BlWYLt1yAWy?rMD&bwze8bC3-GfNH=p zynNFCdxyX?K&G(ZZ)afguQ2|r;XoV^=^(;Cku#qYn4Lus`UeKt6rAlFo_rU`|Rq z&G?~iWMBio<78of-2X(ZYHx~=U0Vz4btyXkctMKdc9UM!vYr~B-(>)(Hc|D zMzkN4!PBg%tZoh+=Gba!0++d193gbMk2&krfDgcbx0jI92cq?FFESVg0D$>F+bil} zY~$)|>1HZsX=5sAZ2WgPB5P=8X#TI+NQ(M~GqyVB53c6IdX=k>Wu@A0Svf5#?uHaF zsYn|koIi3$(%GZ2+G+7Fv^lHTb#5b8sAHSTnL^qWZLM<(1|9|QFw9pnRU{svj}_Al zL)b9>fN{QiA($8peNEJyy`(a{&uh-T4_kdZFIVsKKVM(?05}76EEz?#W za^fiZOAd14IJ4zLX-n7Lq0qlQ^lW8Cvz4UKkV9~P}>sq0?xD3vg+$4vLm~C(+ zM{-3Z#qnZ09bJ>}j?6ry^h+@PfaD7*jZxBEY4)UG&daWb??6)TP+|3#Z&?GL?1i+280CFsE|vIXQbm| zM}Pk!U`U5NsNbyKzkrul-DzwB{X?n3E6?TUHr{M&+R*2%yOiXdW-_2Yd6?38M9Vy^ z*lE%gA{wwoSR~vN0=no}tP2Ul5Gk5M(Xq`$nw#ndFk`tcpd5A=Idue`XZ!FS>Q zG^0w#>P4pPG+*NC9gLP4x2m=cKP}YuS!l^?sHSFftZy{4CoQrb_ z^20(NnG`wAhMI=eq)SsIE~&Gp9Ne0nD4%Xiu|0Fj1UFk?6avDqjdXz{O1nKao*46y zT8~iA%Exu=G#{x=KD;_C&M+Zx4+n`sHT>^>=-1YM;H<72k>$py1?F3#T1*ef9mLZw z5naLQr?n7K;2l+{_uIw*_1nsTn~I|kkCgrn;|G~##hM;9l7Jy$yJfmk+&}W@JeKcF zx@@Woiz8qdi|D%aH3XTx5*wDlbs?dC1_nrFpm^QbG@wM=i2?Zg;$VK!c^Dp8<}BTI zyRhAq@#%2pGV49*Y5_mV4+OICP|%I(dQ7x=6Ob}>EjnB_-_18*xrY?b%-yEDT(wrO z9RY2QT0`_OpGfMObKHV;QLVnrK%mc?$WAdIT`kJQT^n%GuzE7|9@k3ci5fYOh(287 zuIbg!GB3xLg$YN=n)^pHGB0jH+_iIiC=nUcD;G6LuJsjn2VI1cyZx=a?ShCsF==QK z;q~*m&}L<-cb+mDDXzvvrRsybcgQ;Vg21P(uLv5I+eGc7o7tc6`;OA9{soHFOz zT~2?>Ts}gprIX$wRBb4yE>ot<8+*Bv`qbSDv*VtRi|cyWS>)Fjs>fkNOH-+PX&4(~ z&)T8Zam2L6puQl?;5zg9h<}k4#|yH9czHw;1jw-pwBM*O2hUR6yvHATrI%^mvs9q_ z&ccT0>f#eDG<^WG^q@oVqlJrhxH)dcq2cty@l3~|5#UDdExyXUmLQ}f4#;6fI{f^t zDCsgIJ~0`af%YR%Ma5VQq-p21k`vaBu6WE?66+5=XUd%Ay%D$irN>5LhluRWt7 zov-=f>QbMk*G##&DTQyou$s7UqjjW@k6=!I@!k+S{pP8R(2=e@io;N8E`EOB;OGoI zw6Q+{X1_I{OO0HPpBz!X!@`5YQ2)t{+!?M_iH25X(d~-Zx~cXnS9z>u?+If|iNJbx zyFU2d1!ITX64D|lE0Z{dLRqL1Ajj=CCMfC4lD3&mYR_R_VZ>_7_~|<^o*%_&jevU+ zQ4|qzci=0}Jydw|LXLCrOl1_P6Xf@c0$ieK2^7@A9UbF{@V_0p%lqW|L?5k>bVM8|p5v&2g;~r>B8uo<4N+`B zH{J)h;SYiIVx@#jI&p-v3dwL5QNV1oxPr8J%ooezTnLW>i*3Isb49%5i!&ac_dEXv zvXmVUck^QHmyrF8>CGXijC_R-y(Qr{3Zt~EmW)-nC!tiH`wlw5D*W7Pip;T?&j%kX z6DkZX4&}iw>hE(boLyjOoupf6JpvBG8}jIh!!VhnD0>}KSMMo{1#uU6kiFcA04~|7 zVO8eI&x1`g4CZ<2cYUI(n#wz2MtVFHx47yE5eL~8bot~>EHbevSt}LLMQX?odD{Ux zJMnam{d)W4da{l7&y-JrgiU~qY3$~}_F#G7|MxT)e;G{U`In&?`j<5D->}cb{}{T(4DF0BOk-=1195KB-E*o@c?`>y#4=dMtYtSY=&L{!TAjFVcq0y@AH`vH! z$41+u!Ld&}F^COPgL(EE{0X7LY&%D7-(?!kjFF7=qw<;`V{nwWBq<)1QiGJgUc^Vz ztMUlq1bZqKn17|6x6iAHbWc~l1HcmAxr%$Puv!znW)!JiukwIrqQ00|H$Z)OmGG@= zv%A8*4cq}(?qn4rN6o`$Y))(MyXr8R<2S^J+v(wmFmtac!%VOfN?&(8Nr!T@kV`N; z*Q33V3t`^rN&aBiHet)18wy{*wi1=W!B%B-Q6}SCrUl$~Hl{@!95ydml@FK8P=u4s z4e*7gV2s=YxEvskw2Ju!2%{8h01rx-3`NCPc(O zH&J0VH5etNB2KY6k4R@2Wvl^Ck$MoR3=)|SEclT2ccJ!RI9Nuter7u9@;sWf-%um;GfI!=eEIQ2l2p_YWUd{|6EG ze{yO6;lMc>;2tPrsNdi@&1K6(1;|$xe8vLgiouj%QD%gYk`4p{Ktv9|j+!OF-P?@p z;}SV|oIK)iwlBs+`ROXkhd&NK zzo__r!B>tOXpBJMDcv!Mq54P+n4(@dijL^EpO1wdg~q+!DT3lB<>9AANSe!T1XgC=J^)IP0XEZ()_vpu!!3HQyJhwh?r`Ae%Yr~b% zO*NY9t9#qWa@GCPYOF9aron7thfWT`eujS4`t2uG6)~JRTI;f(ZuoRQwjZjp5Pg34 z)rp$)Kr?R+KdJ;IO;pM{$6|2y=k_siqvp%)2||cHTe|b5Ht8&A{wazGNca zX$Ol?H)E_R@SDi~4{d-|8nGFhZPW;Cts1;08TwUvLLv&_2$O6Vt=M)X;g%HUr$&06 zISZb(6)Q3%?;3r~*3~USIg=HcJhFtHhIV(siOwV&QkQe#J%H9&E21!C*d@ln3E@J* zVqRO^<)V^ky-R|%{(9`l-(JXq9J)1r$`uQ8a}$vr9E^nNiI*thK8=&UZ0dsFN_eSl z(q~lnD?EymWLsNa3|1{CRPW60>DSkY9YQ;$4o3W7Ms&@&lv9eH!tk~N&dhqX&>K@} zi1g~GqglxkZ5pEFkllJ)Ta1I^c&Bt6#r(QLQ02yHTaJB~- zCcE=5tmi`UA>@P=1LBfBiqk)HB4t8D?02;9eXj~kVPwv?m{5&!&TFYhu>3=_ zsGmYZ^mo*-j69-42y&Jj0cBLLEulNRZ9vXE)8~mt9C#;tZs;=#M=1*hebkS;7(aGf zcs7zH(I8Eui9UU4L--))yy`&d&$In&VA2?DAEss4LAPCLd>-$i?lpXvn!gu^JJ$(DoUlc6wE98VLZ*z`QGQov5l4Fm_h?V-;mHLYDVOwKz7>e4+%AzeO>P6v}ndPW| zM>m#6Tnp7K?0mbK=>gV}=@k*0Mr_PVAgGMu$j+pWxzq4MAa&jpCDU&-5eH27Iz>m^ zax1?*HhG%pJ((tkR(V(O(L%7v7L%!_X->IjS3H5kuXQT2!ow(;%FDE>16&3r){!ex zhf==oJ!}YU89C9@mfDq!P3S4yx$aGB?rbtVH?sHpg?J5C->!_FHM%Hl3#D4eplxzQ zRA+<@LD%LKSkTk2NyWCg7u=$%F#;SIL44~S_OGR}JqX}X+=bc@swpiClB`Zbz|f!4 z7Ysah7OkR8liXfI`}IIwtEoL}(URrGe;IM8%{>b1SsqXh)~w}P>yiFRaE>}rEnNkT z!HXZUtxUp1NmFm)Dm@-{FI^aRQqpSkz}ZSyKR%Y}YHNzBk)ZIp} zMtS=aMvkgWKm9&oTcU0?S|L~CDqA+sHpOxwnswF-fEG)cXCzUR?ps@tZa$=O)=L+5 zf%m58cq8g_o}3?Bhh+c!w4(7AjxwQ3>WnVi<{{38g7yFboo>q|+7qs<$8CPXUFAN< zG&}BHbbyQ5n|qqSr?U~GY{@GJ{(Jny{bMaOG{|IkUj7tj^9pa9|FB_<+KHLxSxR;@ zHpS$4V)PP+tx}22fWx(Ku9y+}Ap;VZqD0AZW4gCDTPCG=zgJmF{|x;(rvdM|2|9a}cex6xrMkERnkE;}jvU-kmzd%_J50$M`lIPCKf+^*zL=@LW`1SaEc%=m zQ+lT06Gw+wVwvQ9fZ~#qd430v2HndFsBa9WjD0P}K(rZYdAt^5WQIvb%D^Q|pkVE^ zte$&#~zmULFACGfS#g=2OLOnIf2Of-k!(BIHjs77nr!5Q1*I9 z1%?=~#Oss!rV~?-6Gm~BWJiA4mJ5TY&iPm_$)H1_rTltuU1F3I(qTQ^U$S>%$l z)Wx1}R?ij0idp@8w-p!Oz{&*W;v*IA;JFHA9%nUvVDy7Q8woheC#|8QuDZb-L_5@R zOqHwrh|mVL9b=+$nJxM`3eE{O$sCt$UK^2@L$R(r^-_+z?lOo+me-VW=Zw z-Bn>$4ovfWd%SPY`ab-u9{INc*k2h+yH%toDHIyqQ zO68=u`N}RIIs7lsn1D){)~%>ByF<>i@qFb<-axvu(Z+6t7v<^z&gm9McRB~BIaDn$ z#xSGT!rzgad8o>~kyj#h1?7g96tOcCJniQ+*#=b7wPio>|6a1Z?_(TS{)KrPe}(8j z!#&A=k(&Pj^F;r)CI=Z{LVu>uj!_W1q4b`N1}E(i%;BWjbEcnD=mv$FL$l?zS6bW!{$7j1GR5ocn94P2u{ z70tAAcpqtQo<@cXw~@i-@6B23;317|l~S>CB?hR5qJ%J3EFgyBdJd^fHZu7AzHF(BQ!tyAz^L0`X z23S4Fe{2X$W0$zu9gm%rg~A>ijaE#GlYlrF9$ds^QtaszE#4M(OLVP2O-;XdT(XIC zatwzF*)1c+t~c{L=fMG8Z=k5lv>U0;C{caN1NItnuSMp)6G3mbahu>E#sj&oy94KC zpH}8oEw{G@N3pvHhp{^-YaZeH;K+T_1AUv;IKD<=mv^&Ueegrb!yf`4VlRl$M?wsl zZyFol(2|_QM`e_2lYSABpKR{{NlxlDSYQNkS;J66aT#MSiTx~;tUmvs-b*CrR4w=f z8+0;*th6kfZ3|5!Icx3RV11sp=?`0Jy3Fs0N4GZQMN=8HmT6%x9@{Dza)k}UwL6JT zHRDh;%!XwXr6yuuy`4;Xsn0zlR$k%r%9abS1;_v?`HX_hI|+EibVnlyE@3aL5vhQq zlIG?tN^w@0(v9M*&L+{_+RQZw=o|&BRPGB>e5=ys7H`nc8nx)|-g;s7mRc7hg{GJC zAe^vCIJhajmm7C6g! zL&!WAQ~5d_5)00?w_*|*H>3$loHrvFbitw#WvLB!JASO?#5Ig5$Ys10n>e4|3d;tS zELJ0|R4n3Az(Fl3-r^QiV_C;)lQ1_CW{5bKS15U|E9?ZgLec@%kXr84>5jV2a5v=w z?pB1GPdxD$IQL4)G||B_lI+A=08MUFFR4MxfGOu07vfIm+j=z9tp~5i_6jb`tR>qV z$#`=BQ*jpCjm$F0+F)L%xRlnS%#&gro6PiRfu^l!EVan|r3y}AHJQOORGx4~ z&<)3=K-tx518DZyp%|!EqpU!+X3Et7n2AaC5(AtrkW>_57i}$eqs$rupubg0a1+WO zGHZKLN2L0D;ab%{_S1Plm|hx8R?O14*w*f&2&bB050n!R2by zw!@XOQx$SqZ5I<(Qu$V6g>o#A!JVwErWv#(Pjx=KeS0@hxr4?13zj#oWwPS(7Ro|v z>Mp@Kmxo79q|}!5qtX2-O@U&&@6s~!I&)1WQIl?lTnh6UdKT_1R640S4~f=_xoN3- zI+O)$R@RjV$F=>Ti7BlnG1-cFKCC(t|Qjm{SalS~V-tX#+2ekRhwmN zZr`8{QF6y~Z!D|{=1*2D-JUa<(1Z=;!Ei!KiRNH?o{p5o3crFF=_pX9O-YyJchr$~ zRC`+G+8kx~fD2k*ZIiiIGR<8r&M@3H?%JVOfE>)})7ScOd&?OjgAGT@WVNSCZ8N(p zuQG~76GE3%(%h1*vUXg$vH{ua0b`sQ4f0*y=u~lgyb^!#CcPJa2mkSEHGLsnO^kb$ zru5_l#nu=Y{rSMWiYx?nO{8I!gH+?wEj~UM?IrG}E|bRIBUM>UlY<`T1EHpRr36vv zBi&dG8oxS|J$!zoaq{+JpJy+O^W(nt*|#g32bd&K^w-t>!Vu9N!k9eA8r!Xc{utY> zg9aZ(D2E0gL#W0MdjwES-7~Wa8iubPrd?8-$C4BP?*wok&O8+ykOx{P=Izx+G~hM8 z*9?BYz!T8~dzcZr#ux8kS7u7r@A#DogBH8km8Ry4slyie^n|GrTbO|cLhpqgMdsjX zJ_LdmM#I&4LqqsOUIXK8gW;V0B(7^$y#h3h>J0k^WJfAMeYek%Y-Dcb_+0zPJez!GM zAmJ1u;*rK=FNM0Nf}Y!!P9c4)HIkMnq^b;JFd!S3?_Qi2G#LIQ)TF|iHl~WKK6JmK zbv7rPE6VkYr_%_BT}CK8h=?%pk@3cz(UrZ{@h40%XgThP*-Oeo`T0eq9 zA8BnWZKzCy5e&&_GEsU4*;_k}(8l_&al5K-V*BFM=O~;MgRkYsOs%9eOY6s6AtE*<7GQAR2ulC3RAJrG_P1iQK5Z~&B z&f8X<>yJV6)oDGIlS$Y*D^Rj(cszTy5c81a5IwBr`BtnC6_e`ArI8CaTX_%rx7;cn zR-0?J_LFg*?(#n~G8cXut(1nVF0Oka$A$1FGcERU<^ggx;p@CZc?3UB41RY+wLS`LWFNSs~YP zuw1@DNN3lTd|jDL7gjBsd9}wIw}4xT2+8dBQzI00m<@?c2L%>}QLfK5%r!a-iII`p zX@`VEUH)uj^$;7jVUYdADQ2k*!1O3WdfgF?OMtUXNpQ1}QINamBTKDuv19^{$`8A1 zeq%q*O0mi@(%sZU>Xdb0Ru96CFqk9-L3pzLVsMQ`Xpa~N6CR{9Rm2)A|CI21L(%GW zh&)Y$BNHa=FD+=mBw3{qTgw)j0b!Eahs!rZnpu)z!!E$*eXE~##yaXz`KE5(nQM`s zD!$vW9XH)iMxu9R>r$VlLk9oIR%HxpUiW=BK@4U)|1WNQ=mz9a z^!KkO=>GaJ!GBXm{KJj^;kh-MkUlEQ%lza`-G&}C5y1>La1sR6hT=d*NeCnuK%_LV zOXt$}iP6(YJKc9j-Fxq~*ItVUqljQ8?oaysB-EYtFQp9oxZ|5m0^Hq(qV!S+hq#g( z?|i*H2MIr^Kxgz+3vIljQ*Feejy6S4v~jKEPTF~Qhq!(ms5>NGtRgO5vfPPc4Z^AM zTj!`5xEreIN)vaNxa|q6qWdg>+T`Ol0Uz)ckXBXEGvPNEL3R8hB3=C5`@=SYgAju1 z!)UBr{2~=~xa{b8>x2@C7weRAEuatC)3pkRhT#pMPTpSbA|tan%U7NGMvzmF?c!V8 z=pEWxbdXbTAGtWTyI?Fml%lEr-^AE}w#l(<7OIw;ctw}imYax&vR4UYNJZK6P7ZOd zP87XfhnUHxCUHhM@b*NbTi#(-8|wcv%3BGNs#zRCVV(W?1Qj6^PPQa<{yaBwZ`+<`w|;rqUY_C z&AeyKwwf*q#OW-F()lir=T^<^wjK65Lif$puuU5+tk$;e_EJ;Lu+pH>=-8=PDhkBg z8cWt%@$Sc#C6F$Vd+0507;{OOyT7Hs%nKS88q-W!$f~9*WGBpHGgNp}=C*7!RiZ5s zn1L_DbKF@B8kwhDiLKRB@lsXVVLK|ph=w%_`#owlf@s@V(pa`GY$8h%;-#h@TsO|Y8V=n@*!Rog7<7Cid%apR|x zOjhHCyfbIt%+*PCveTEcuiDi%Wx;O;+K=W?OFUV%)%~6;gl?<0%)?snDDqIvkHF{ zyI02)+lI9ov42^hL>ZRrh*HhjF9B$A@=H94iaBESBF=eC_KT$8A@uB^6$~o?3Wm5t1OIaqF^~><2?4e3c&)@wKn9bD? zoeCs;H>b8DL^F&>Xw-xjZEUFFTv>JD^O#1E#)CMBaG4DX9bD(Wtc8Rzq}9soQ8`jf zeSnHOL}<+WVSKp4kkq&?SbETjq6yr@4%SAqOG=9E(3YeLG9dtV+8vmzq+6PFPk{L; z(&d++iu=^F%b+ea$i2UeTC{R*0Isk;vFK!no<;L+(`y`3&H-~VTdKROkdyowo1iqR zbVW(3`+(PQ2>TKY>N!jGmGo7oeoB8O|P_!Ic@ zZ^;3dnuXo;WJ?S+)%P>{Hcg!Jz#2SI(s&dY4QAy_vRlmOh)QHvs_7c&zkJCmJGVvV zX;Mtb>QE+xp`KyciG$Cn*0?AK%-a|=o!+7x&&yzHQOS>8=B*R=niSnta^Pxp1`=md z#;$pS$4WCT?mbiCYU?FcHGZ#)kHVJTTBt^%XE(Q};aaO=Zik0UgLcc0I(tUpt(>|& zcxB_|fxCF7>&~5eJ=Dpn&5Aj{A^cV^^}(7w#p;HG&Q)EaN~~EqrE1qKrMAc&WXIE;>@<&)5;gD2?={Xf@Mvn@OJKw=8Mgn z!JUFMwD+s==JpjhroT&d{$kQAy%+d`a*XxDEVxy3`NHzmITrE`o!;5ClXNPb4t*8P zzAivdr{j_v!=9!^?T3y?gzmqDWX6mkzhIzJ-3S{T5bcCFMr&RPDryMcdwbBuZbsgN zGrp@^i?rcfN7v0NKGzDPGE#4yszxu=I_`MI%Z|10nFjU-UjQXXA?k8Pk|OE<(?ae) zE%vG#eZAlj*E7_3dx#Zz4kMLj>H^;}33UAankJiDy5ZvEhrjr`!9eMD8COp}U*hP+ zF}KIYx@pkccIgyxFm#LNw~G&`;o&5)2`5aogs`1~7cMZQ7zj!%L4E`2yzlQN6REX20&O<9 zKV6fyr)TScJPPzNTC2gL+0x#=u>(({{D7j)c-%tvqls3#Y?Z1m zV5WUE)zdJ{$p>yX;^P!UcXP?UD~YM;IRa#Rs5~l+*$&nO(;Ers`G=0D!twR(0GF@c zHl9E5DQI}Oz74n zfKP>&$q0($T4y$6w(p=ERAFh+>n%iaeRA%!T%<^+pg?M)@ucY<&59$x9M#n+V&>}=nO9wCV{O~lg&v#+jcUj(tQ z`0u1YH)-`U$15a{pBkGyPL0THv1P|4e@pf@3IBZS4dVJPo#H>pWq%Lr0YS-SeWash z8R7=jb28KPMI|_lo#GEO|5B?N_e``H*23{~a!AmUJ+fb4HX-%QI@lSEUxKlGV7z7Q zSKw@-TR>@1RL%w{x}dW#k1NgW+q4yt2Xf1J62Bx*O^WG8OJ|FqI4&@d3_o8Id@*)4 zYrk=>@!wv~mh7YWv*bZhxqSmFh2Xq)o=m;%n$I?GSz49l1$xRpPu_^N(vZ>*>Z<04 z2+rP70oM=NDysd!@fQdM2OcyT?3T^Eb@lIC-UG=Bw{BjQ&P`KCv$AcJ;?`vdZ4){d z&gkoUK{$!$$K`3*O-jyM1~p-7T*qb)Ys>Myt^;#1&a%O@x8A+E>! zY8=eD`ZG)LVagDLBeHg>=atOG?Kr%h4B%E6m@J^C+U|y)XX@f z8oyJDW|9g=<#f<{JRr{y#~euMnv)`7j=%cHWLc}ngjq~7k**6%4u>Px&W%4D94(r* z+akunK}O0DC2A%Xo9jyF;DobX?!1I(7%}@7F>i%&nk*LMO)bMGg2N+1iqtg+r(70q zF5{Msgsm5GS7DT`kBsjMvOrkx&|EU!{{~gL4d2MWrAT=KBQ-^zQCUq{5PD1orxlIL zq;CvlWx#f1NWvh`hg011I%?T_s!e38l*lWVt|~z-PO4~~1g)SrJ|>*tXh=QfXT)%( z+ex+inPvD&O4Ur;JGz>$sUOnWdpSLcm1X%aQDw4{dB!cnj`^muI$CJ2%p&-kULVCE z>$eMR36kN$wCPR+OFDM3-U(VOrp9k3)lI&YVFqd;Kpz~K)@Fa&FRw}L(SoD z9B4a+hQzZT-BnVltst&=kq6Y(f^S4hIGNKYBgMxGJ^;2yrO}P3;r)(-I-CZ)26Y6? z&rzHI_1GCvGkgy-t1E;r^3Le30|%$ebDRu2+gdLG)r=A~Qz`}~&L@aGJ{}vVs_GE* zVUjFnzHiXfKQbpv&bR&}l2bzIjAooB)=-XNcYmrGmBh(&iu@o!^hn0^#}m2yZZUK8 zufVm7Gq0y`Mj;9b>`c?&PZkU0j4>IL=UL&-Lp3j&47B5pAW4JceG{!XCA)kT<%2nqCxj<)uy6XR_uws~>_MEKPOpAQ!H zkn>FKh)<9DwwS*|Y(q?$^N!6(51O0 z^JM~Ax{AI1Oj$fs-S5d4T7Z_i1?{%0SsIuQ&r8#(JA=2iLcTN+?>wOL532%&dMYkT z*T5xepC+V6zxhS@vNbMoi|i)=rpli@R9~P!39tWbSSb904ekv7D#quKbgFEMTb48P zuq(VJ+&L8aWU(_FCD$3^uD!YM%O^K(dvy~Wm2hUuh6bD|#(I39Xt>N1Y{ZqXL`Fg6 zKQ?T2htHN!(Bx;tV2bfTtIj7e)liN-29s1kew>v(D^@)#v;}C4-G=7x#;-dM4yRWm zyY`cS21ulzMK{PoaQ6xChEZ}o_#}X-o}<&0)$1#3we?+QeLt;aVCjeA)hn!}UaKt< zat1fHEx13y-rXNMvpUUmCVzocPmN~-Y4(YJvQ#db)4|%B!rBsgAe+*yor~}FrNH08 z3V!97S}D7d$zbSD{$z;@IYMxM6aHdypIuS*pr_U6;#Y!_?0i|&yU*@16l z*dcMqDQgfNBf}?quiu4e>H)yTVfsp#f+Du0@=Kc41QockXkCkvu>FBd6Q+@FL!(Yx z2`YuX#eMEiLEDhp+9uFqME_E^faV&~9qjBHJkIp~%$x^bN=N)K@kvSVEMdDuzA0sn z88CBG?`RX1@#hQNd`o^V{37)!w|nA)QfiYBE^m=yQKv-fQF+UCMcuEe1d4BH7$?>b zJl-r9@0^Ie=)guO1vOd=i$_4sz>y3x^R7n4ED!5oXL3@5**h(xr%Hv)_gILarO46q+MaDOF%ChaymKoI6JU5Pg;7#2n9-18|S1;AK+ zgsn6;k6-%!QD>D?cFy}8F;r@z8H9xN1jsOBw2vQONVqBVEbkiNUqgw~*!^##ht>w0 zUOykwH=$LwX2j&nLy=@{hr)2O&-wm-NyjW7n~Zs9UlH;P7iP3 zI}S(r0YFVYacnKH(+{*)Tbw)@;6>%=&Th=+Z6NHo_tR|JCI8TJiXv2N7ei7M^Q+RM z?9o`meH$5Yi;@9XaNR#jIK^&{N|DYNNbtdb)XW1Lv2k{E>;?F`#Pq|&_;gm~&~Zc9 zf+6ZE%{x4|{YdtE?a^gKyzr}dA>OxQv+pq|@IXL%WS0CiX!V zm$fCePA%lU{%pTKD7|5NJHeXg=I0jL@$tOF@K*MI$)f?om)D63K*M|r`gb9edD1~Y zc|w7N)Y%do7=0{RC|AziW7#am$)9jciRJ?IWl9PE{G3U+$%FcyKs_0Cgq`=K3@ttV z9g;M!3z~f_?P%y3-ph%vBMeS@p7P&Ea8M@97+%XEj*(1E6vHj==d zjsoviB>j^$_^OI_DEPvFkVo(BGRo%cJeD){6Uckei=~1}>sp299|IRjhXe)%?uP0I zF5+>?0#Ye}T^Y$u_rc4=lPcq4K^D(TZG-w30-YiEM=dcK+4#o*>lJ8&JLi+3UcpZk z!^?95S^C0ja^jwP`|{<+3cBVog$(mRdQmadS+Vh~z zS@|P}=|z3P6uS+&@QsMp0no9Od&27O&14zHXGAOEy zh~OKpymK5C%;LLb467@KgIiVwYbYd6wFxI{0-~MOGfTq$nBTB!{SrWmL9Hs}C&l&l#m?s*{tA?BHS4mVKHAVMqm63H<|c5n0~k)-kbg zXidai&9ZUy0~WFYYKT;oe~rytRk?)r8bptITsWj(@HLI;@=v5|XUnSls7$uaxFRL+ zRVMGuL3w}NbV1`^=Pw*0?>bm8+xfeY(1PikW*PB>>Tq(FR`91N0c2&>lL2sZo5=VD zQY{>7dh_TX98L2)n{2OV=T10~*YzX27i2Q7W86M4$?gZIXZaBq#sA*{PH8){|GUi;oM>e?ua7eF4WFuFYZSG| zze?srg|5Ti8Og{O zeFxuw9!U+zhyk?@w zjsA6(oKD=Ka;A>Ca)oPORxK+kxH#O@zhC!!XS4@=swnuMk>t+JmLmFiE^1aX3f<)D@`%K0FGK^gg1a1j>zi z2KhV>sjU7AX3F$SEqrXSC}fRx64GDoc%!u2Yag68Lw@w9v;xOONf@o)Lc|Uh3<21ctTYu-mFZuHk*+R{GjXHIGq3p)tFtQp%TYqD=j1&y)>@zxoxUJ!G@ zgI0XKmP6MNzw>nRxK$-Gbzs}dyfFzt>#5;f6oR27ql!%+{tr+(`(>%51|k`ML} zY4eE)Lxq|JMas(;JibNQds1bUB&r}ydMQXBY4x(^&fY_&LlQC)3hylc$~8&~|06-D z#T+%66rYbHX%^KuqJED_wuGB+=h`nWA!>1n0)3wZrBG3%`b^Ozv6__dNa@%V14|!D zQ?o$z5u0^8`giv%qE!BzZ!3j;BlDlJDk)h@9{nSQeEk!z9RGW) z${RSF3phEM*ce*>Xdp}585vj$|40=&S{S-GTiE?Op*vY&Lvr9}BO$XWy80IF+6@%n z5*2ueT_g@ofP#u5pxb7n*fv^Xtt7&?SRc{*2Ka-*!BuOpf}neHGCiHy$@Ka1^Dint z;DkmIL$-e)rj4o2WQV%Gy;Xg(_Bh#qeOsTM2f@KEe~4kJ8kNLQ+;(!j^bgJMcNhvklP5Z6I+9Fq@c&D~8Fb-4rmDT!MB5QC{Dsb;BharP*O;SF4& zc$wj-7Oep7#$WZN!1nznc@Vb<_Dn%ga-O#J(l=OGB`dy=Sy&$(5-n3zzu%d7E#^8`T@}V+5B;PP8J14#4cCPw-SQTdGa2gWL0*zKM z#DfSXs_iWOMt)0*+Y>Lkd=LlyoHjublNLefhKBv@JoC>P7N1_#> zv=mLWe96%EY;!ZGSQDbZWb#;tzqAGgx~uk+-$+2_8U`!ypbwXl z^2E-FkM1?lY@yt8=J3%QK+xaZ6ok=-y%=KXCD^0r!5vUneW>95PzCkOPO*t}p$;-> ze5j-BLT_;)cZQzR2CEsm@rU7GZfFtdp*a|g4wDr%8?2QkIGasRfDWT-Dvy*U{?IHT z*}wGnzdlSptl#ZF^sf)KT|BJs&kLG91^A6ls{CzFprZ6-Y!V0Xysh%9p%iMd7HLsS zN+^Un$tDV)T@i!v?3o0Fsx2qI(AX_$dDkBzQ@fRM%n zRXk6hb9Py#JXUs+7)w@eo;g%QQ95Yq!K_d=z{0dGS+pToEI6=Bo8+{k$7&Z zo4>PH(`ce8E-Ps&uv`NQ;U$%t;w~|@E3WVOCi~R4oj5wP?%<*1C%}Jq%a^q~T7u>K zML5AKfQDv6>PuT`{SrKHRAF+^&edg6+5R_#H?Lz3iGoWo#PCEd0DS;)2U({{X#zU^ zw_xv{4x7|t!S)>44J;KfA|DC?;uQ($l+5Vp7oeqf7{GBF9356nx|&B~gs+@N^gSdd zvb*>&W)|u#F{Z_b`f#GVtQ`pYv3#||N{xj1NgB<#=Odt6{eB%#9RLt5v zIi|0u70`#ai}9fJjKv7dE!9ZrOIX!3{$z_K5FBd-Kp-&e4(J$LD-)NMTp^_pB`RT; zftVVlK2g@+1Ahv2$D){@Y#cL#dUj9*&%#6 zd2m9{1NYp>)6=oAvqdCn5#cx{AJ%S8skUgMglu2*IAtd+z1>B&`MuEAS(D(<6X#Lj z?f4CFx$)M&$=7*>9v1ER4b6!SIz-m0e{o0BfkySREchp?WdVPpQCh!q$t>?rL!&Jg zd#heM;&~A}VEm8Dvy&P|J*eAV&w!&Nx6HFV&B8jJFVTmgLaswn!cx$&%JbTsloz!3 zMEz1d`k==`Ueub_JAy_&`!ogbwx27^ZXgFNAbx=g_I~5nO^r)}&myw~+yY*cJl4$I znNJ32M&K=0(2Dj_>@39`3=FX!v3nZHno_@q^!y}%(yw0PqOo=);6Y@&ylVe>nMOZ~ zd>j#QQSBn3oaWd;qy$&5(5H$Ayi)0haAYO6TH>FR?rhqHmNOO+(})NB zLI@B@v0)eq!ug`>G<@htRlp3n!EpU|n+G+AvXFrWSUsLMBfL*ZB`CRsIVHNTR&b?K zxBgsN0BjfB>UVcJ|x%=-zb%OV7lmZc& zxiupadZVF7)6QuhoY;;FK2b*qL0J-Rn-8!X4ZY$-ZSUXV5DFd7`T41c(#lAeLMoeT z4%g655v@7AqT!i@)Edt5JMbN(=Q-6{=L4iG8RA%}w;&pKmtWvI4?G9pVRp|RTw`g0 zD5c12B&A2&P6Ng~8WM2eIW=wxd?r7A*N+&!Be7PX3s|7~z=APxm=A?5 zt>xB4WG|*Td@VX{Rs)PV0|yK`oI3^xn(4c_j&vgxk_Y3o(-`_5o`V zRTghg6%l@(qodXN;dB#+OKJEEvhfcnc#BeO2|E(5df-!fKDZ!%9!^BJ_4)9P+9Dq5 zK1=(v?KmIp34r?z{NEWnLB3Px{XYwy-akun4F7xTRr2^zeYW{gcK9)>aJDdU5;w5@ zak=<+-PLH-|04pelTb%ULpuuuJC7DgyT@D|p{!V!0v3KpDnRjANN12q6SUR3mb9<- z>2r~IApQGhstZ!3*?5V z8#)hJ0TdZg0M-BK#nGFP>$i=qk82DO z7h;Ft!D5E15OgW)&%lej*?^1~2=*Z5$2VX>V{x8SC+{i10BbtUk9@I#Vi&hX)q
Q!LwySI{Bnv%Sm)yh{^sSVJ8&h_D-BJ_YZe5eCaAWU9b$O2c z$T|{vWVRtOL!xC0DTc(Qbe`ItNtt5hr<)VijD0{U;T#bUEp381_y`%ZIav?kuYG{iyYdEBPW=*xNSc;Rlt6~F4M`5G+VtOjc z*0qGzCb@gME5udTjJA-9O<&TWd~}ysBd(eVT1-H82-doyH9RST)|+Pb{o*;$j9Tjs zhU!IlsPsj8=(x3bAKJTopW3^6AKROHR^7wZ185wJGVhA~hEc|LP;k7NEz-@4p5o}F z`AD6naG3(n=NF9HTH81=F+Q|JOz$7wm9I<+#BSmB@o_cLt2GkW9|?7mM;r!JZp89l zbo!Hp8=n!XH1{GwaDU+k)pGp`C|cXkCU5%vcH)+v@0eK>%7gWxmuMu9YLlChA|_D@ zi#5zovN_!a-0?~pUV-Rj*1P)KwdU-LguR>YM&*Nen+ln8Q$?WFCJg%DY%K}2!!1FE zDv-A%Cbwo^p(lzac&_TZ-l#9kq`mhLcY3h9ZTUVCM(Ad&=EriQY5{jJv<5K&g|*Lk zgV%ILnf1%8V2B0E&;Sp4sYbYOvvMebLwYwzkRQ#F8GpTQq#uv=J`uaSJ34OWITeSGo6+-8Xw znCk*n{kdDEi)Hi&u^)~cs@iyCkFWB2SWZU|Uc%^43ZIZQ-vWNExCCtDWjqHs;;tWf$v{}0{p0Rvxkq``)*>+Akq%|Na zA`@~-Vfe|+(AIlqru+7Ceh4nsVmO9p9jc8}HX^W&ViBDXT+uXbT#R#idPn&L>+#b6 zflC-4C5-X;kUnR~L>PSLh*gvL68}RBsu#2l`s_9KjUWRhiqF`j)`y`2`YU(>3bdBj z?>iyjEhe-~$^I5!nn%B6Wh+I`FvLNvauve~eX<+Ipl&04 zT}};W&1a3%W?dJ2=N#0t?e+aK+%t}5q%jSLvp3jZ%?&F}nOOWr>+{GFIa%wO_2`et z=JzoRR~}iKuuR+azPI8;Gf9)z3kyA4EIOSl!sRR$DlW}0>&?GbgPojmjmnln;cTqCt=ADbE zZ8GAnoM+S1(5$i8^O4t`ue;vO4i}z0wz-QEIVe5_u03;}-!G1NyY8;h^}y;tzY}i5 zqQr#Ur3Fy8sSa$Q0ys+f`!`+>9WbvU_I`Sj;$4{S>O3?#inLHCrtLy~!s#WXV=oVP zeE93*Nc`PBi4q@%Ao$x4lw9vLHM!6mn3-b_cebF|n-2vt-zYVF_&sDE--J-P;2WHo z+@n2areE0o$LjvjlV2X7ZU@j+`{*8zq`JR3gKF#EW|#+{nMyo-a>nFFTg&vhyT=b} zDa8+v0(Dgx0yRL@ZXOYIlVSZ0|MFizy0VPW8;AfA5|pe!#j zX}Py^8fl5SyS4g1WSKKtnyP+_PoOwMMwu`(i@Z)diJp~U54*-miOchy7Z35eL>^M z4p<-aIxH4VUZgS783@H%M7P9hX>t{|RU7$n4T(brCG#h9e9p! z+o`i;EGGq3&pF;~5V~eBD}lC)>if$w%Vf}AFxGqO88|ApfHf&Bvu+xdG)@vuF}Yvk z)o;~k-%+0K0g+L`Wala!$=ZV|z$e%>f0%XoLib%)!R^RoS+{!#X?h-6uu zF&&KxORdZU&EwQFITIRLo(7TA3W}y6X{?Y%y2j0It!ekU#<)$qghZtpcS>L3uh`Uj z7GY;6f$9qKynP#oS3$$a{p^{D+0oJQ71`1?OAn_m8)UGZmj3l*ZI)`V-a>MKGGFG< z&^jg#Ok%(hhm>hSrZ5;Qga4u(?^i>GiW_j9%_7M>j(^|Om$#{k+^*ULnEgzW_1gCICtAD^WpC`A z{9&DXkG#01Xo)U$OC(L5Y$DQ|Q4C6CjUKk1UkPj$nXH##J{c8e#K|&{mA*;b$r0E4 zUNo0jthwA(c&N1l=PEe8Rw_8cEl|-eya9z&H3#n`B$t#+aJ03RFMzrV@gowbe8v(c zIFM60^0&lCFO10NU4w@|61xiZ4CVXeaKjd;d?sv52XM*lS8XiVjgWpRB;&U_C0g+`6B5V&w|O6B*_q zsATxL!M}+$He)1eOWECce#eS@2n^xhlB4<_Nn?yCVEQWDs(r`|@2GqLe<#(|&P0U? z$7V5IgpWf09uIf_RazRwC?qEqRaHyL?iiS05UiGesJy%^>-C{{ypTBI&B0-iUYhk> zIk<5xpsuV@g|z(AZD+C-;A!fTG=df1=<%nxy(a(IS+U{ME4ZbDEBtcD_3V=icT6*_ z)>|J?>&6%nvHhZERBtjK+s4xnut*@>GAmA5m*OTp$!^CHTr}vM4n(X1Q*;{e-Rd2BCF-u@1ZGm z!S8hJ6L=Gl4T_SDa7Xx|-{4mxveJg=ctf`BJ*fy!yF6Dz&?w(Q_6B}WQVtNI!BVBC zKfX<>7vd6C96}XAQmF-Jd?1Q4eTfRB3q7hCh0f!(JkdWT5<{iAE#dKy*Jxq&3a1@~ z8C||Dn2mFNyrUV|<-)C^_y7@8c2Fz+2jrae9deBDu;U}tJ{^xAdxCD248(k;dCJ%o z`y3sADe>U%suxwwv~8A1+R$VB=Q?%U?4joI$um;aH+eCrBqpn- z%79D_7rb;R-;-9RTrwi9dPlg8&@tfWhhZ(Vx&1PQ+6(huX`;M9x~LrW~~#3{j0Bh2kDU$}@!fFQej4VGkJv?M4rU^x!RU zEwhu$!CA_iDjFjrJa`aocySDX16?~;+wgav;}Zut6Mg%C4>}8FL?8)Kgwc(Qlj{@#2Pt0?G`$h7P#M+qoXtlV@d}%c&OzO+QYKK`kyXaK{U(O^2DyIXCZlNQjt0^8~8JzNGrIxhj}}M z&~QZlbx%t;MJ(Vux;2tgNKGlAqphLq%pd}JG9uoVHUo?|hN{pLQ6Em%r*+7t^<);X zm~6=qChlNAVXNN*Sow->*4;}T;l;D1I-5T{Bif@4_}=>l`tK;qqDdt5zvisCKhMAH z#r}`)7VW?LZqfdmXQ%zo5bJ00{Xb9^YKrk0Nf|oIW*K@(=`o2Vndz}ZDyk{!u}PVx zzd--+_WC*U{~DH3{?GI64IB+@On&@9X>EUAo&L+G{L^dozaI4C3G#2wr~hseW@K&g zKWs{uHu-9Je!3;4pE>eBltKUXb^*hG8I&413)$J&{D4N%7PcloU6bn%jPxJyQL?g* z9g+YFFEDiE`8rW^laCNzQmi7CTnPfwyg3VDHRAl>h=In6jeaVOP@!-CP60j3+#vpL zEYmh_oP0{-gTe7Or`L6x)6w?77QVi~jD8lWN@3RHcm80iV%M1A!+Y6iHM)05iC64tb$X2lV_%Txk@0l^hZqi^%Z?#- zE;LE0uFx)R08_S-#(wC=dS&}vj6P4>5ZWjhthP=*Hht&TdLtKDR;rXEX4*z0h74FA zMCINqrh3Vq;s%3MC1YL`{WjIAPkVL#3rj^9Pj9Ss7>7duy!9H0vYF%>1jh)EPqvlr6h%R%CxDsk| z!BACz7E%j?bm=pH6Eaw{+suniuY7C9Ut~1cWfOX9KW9=H><&kQlinPV3h9R>3nJvK z4L9(DRM=x;R&d#a@oFY7mB|m8h4692U5eYfcw|QKwqRsshN(q^v$4$)HgPpAJDJ`I zkqjq(8Cd!K!+wCd=d@w%~e$=gdUgD&wj$LQ1r>-E=O@c ze+Z$x{>6(JA-fNVr)X;*)40Eym1TtUZI1Pwwx1hUi+G1Jlk~vCYeXMNYtr)1?qwyg zsX_e*$h?380O00ou?0R@7-Fc59o$UvyVs4cUbujHUA>sH!}L54>`e` zHUx#Q+Hn&Og#YVOuo*niy*GU3rH;%f``nk#NN5-xrZ34NeH$l`4@t);4(+0|Z#I>Y z)~Kzs#exIAaf--65L0UHT_SvV8O2WYeD>Mq^Y6L!Xu8%vnpofG@w!}R7M28?i1*T&zp3X4^OMCY6(Dg<-! zXmcGQrRgHXGYre7GfTJ)rhl|rs%abKT_Nt24_Q``XH{88NVPW+`x4ZdrMuO0iZ0g` z%p}y};~T5gbb9SeL8BSc`SO#ixC$@QhXxZ=B}L`tP}&k?1oSPS=4%{UOHe0<_XWln zwbl5cn(j-qK`)vGHY5B5C|QZd5)W7c@{bNVXqJ!!n$^ufc?N9C-BF2QK1(kv++h!>$QbAjq)_b$$PcJdV+F7hz0Hu@ zqj+}m0qn{t^tD3DfBb~0B36|Q`bs*xs|$i^G4uNUEBl4g;op-;Wl~iThgga?+dL7s zUP(8lMO?g{GcYpDS{NM!UA8Hco?#}eNEioRBHy4`mq!Pd-9@-97|k$hpEX>xoX+dY zDr$wfm^P&}Wu{!%?)U_(%Mn79$(ywvu*kJ9r4u|MyYLI_67U7%6Gd_vb##Nerf@>& z8W11z$$~xEZt$dPG}+*IZky+os5Ju2eRi;1=rUEeIn>t-AzC_IGM-IXWK3^6QNU+2pe=MBn4I*R@A%-iLDCOHTE-O^wo$sL_h{dcPl=^muAQb`_BRm};=cy{qSkui;`WSsj9%c^+bIDQ z0`_?KX0<-=o!t{u(Ln)v>%VGL z0pC=GB7*AQ?N7N{ut*a%MH-tdtNmNC+Yf$|KS)BW(gQJ*z$d{+{j?(e&hgTy^2|AR9vx1Xre2fagGv0YXWqtNkg*v%40v?BJBt|f9wX5 z{QTlCM}b-0{mV?IG>TW_BdviUKhtosrBqdfq&Frdz>cF~yK{P@(w{Vr7z2qKFwLhc zQuogKO@~YwyS9%+d-zD7mJG~@?EFJLSn!a&mhE5$_4xBl&6QHMzL?CdzEnC~C3$X@ zvY!{_GR06ep5;<#cKCSJ%srxX=+pn?ywDwtJ2{TV;0DKBO2t++B(tIO4)Wh`rD13P z4fE$#%zkd=UzOB74gi=-*CuID&Z3zI^-`4U^S?dHxK8fP*;fE|a(KYMgMUo`THIS1f!*6dOI2 zFjC3O=-AL`6=9pp;`CYPTdVX z8(*?V&%QoipuH0>WKlL8A*zTKckD!paN@~hh zmXzm~qZhMGVdQGd=AG8&20HW0RGV8X{$9LldFZYm zE?}`Q3i?xJRz43S?VFMmqRyvWaS#(~Lempg9nTM$EFDP(Gzx#$r)W&lpFKqcAoJh-AxEw$-bjW>`_+gEi z2w`99#UbFZGiQjS8kj~@PGqpsPX`T{YOj`CaEqTFag;$jY z8_{Wzz>HXx&G*Dx<5skhpETxIdhKH?DtY@b9l8$l?UkM#J-Snmts7bd7xayKTFJ(u zyAT&@6cAYcs{PBfpqZa%sxhJ5nSZBPji?Zlf&}#L?t)vC4X5VLp%~fz2Sx<*oN<7` z?ge=k<=X7r<~F7Tvp9#HB{!mA!QWBOf%EiSJ6KIF8QZNjg&x~-%e*tflL(ji_S^sO ztmib1rp09uon}RcsFi#k)oLs@$?vs(i>5k3YN%$T(5Or(TZ5JW9mA6mIMD08=749$ z!d+l*iu{Il7^Yu}H;lgw=En1sJpCKPSqTCHy4(f&NPelr31^*l%KHq^QE>z>Ks_bH zjbD?({~8Din7IvZeJ>8Ey=e;I?thpzD=zE5UHeO|neioJwG;IyLk?xOz(yO&0DTU~ z^#)xcs|s>Flgmp;SmYJ4g(|HMu3v7#;c*Aa8iF#UZo7CvDq4>8#qLJ|YdZ!AsH%^_7N1IQjCro

K7UpUK$>l@ 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 E2O(~oYXATM literal 54706 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 56a6b16..f371643 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Tue Nov 28 14:11:56 CET 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip diff --git a/gradlew b/gradlew index cccdd3d..4f906e0 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f955316..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell From 03a6b01b83491cfd03d3b3cbc09cf1f209c25a25 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Tue, 20 Apr 2021 23:07:06 +0200 Subject: [PATCH 27/27] [java decompiler] getting rid of deprecated API usage GitOrigin-RevId: 4b575f5001e391224bd77fc71483ac0ab0c42787 --- .../decompose/DominatorTreeExceptionFilter.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java index d8b587f..ec1181d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.decompose; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; @@ -127,16 +127,15 @@ public class DominatorTreeExceptionFilter { Set range = entry.getValue(); if (range.contains(id)) { - Integer exit; - if (!range.contains(childid)) { exit = childid; } + else if (map.containsKey(handler)) { + exit = -1; + } else { - // after replacing 'new Integer(-1)' with '-1' Eclipse throws a NullPointerException on the following line - // could be a bug in Eclipse or some obscure specification glitch, FIXME: needs further investigation - exit = map.containsKey(handler) ? new Integer(-1) : mapChild.get(handler); + exit = mapChild.get(handler); } if (exit != null) {