diff --git a/src/main/java/jd/core/process/analyzer/classfile/visitor/SearchInstructionByTypeVisitor.java b/src/main/java/jd/core/process/analyzer/classfile/visitor/SearchInstructionByTypeVisitor.java
new file mode 100644
index 00000000..813c9f9d
--- /dev/null
+++ b/src/main/java/jd/core/process/analyzer/classfile/visitor/SearchInstructionByTypeVisitor.java
@@ -0,0 +1,460 @@
+/*******************************************************************************
+ * Copyright (C) 2007-2023 Emmanuel Dupuy GPLv3 and other contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ ******************************************************************************/
+package jd.core.process.analyzer.classfile.visitor;
+
+import org.apache.bcel.Const;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import jd.core.model.instruction.bytecode.ByteCodeConstants;
+import jd.core.model.instruction.bytecode.instruction.ANewArray;
+import jd.core.model.instruction.bytecode.instruction.AThrow;
+import jd.core.model.instruction.bytecode.instruction.ArrayLength;
+import jd.core.model.instruction.bytecode.instruction.ArrayStoreInstruction;
+import jd.core.model.instruction.bytecode.instruction.AssertInstruction;
+import jd.core.model.instruction.bytecode.instruction.BinaryOperatorInstruction;
+import jd.core.model.instruction.bytecode.instruction.CheckCast;
+import jd.core.model.instruction.bytecode.instruction.ComplexConditionalBranchInstruction;
+import jd.core.model.instruction.bytecode.instruction.ConvertInstruction;
+import jd.core.model.instruction.bytecode.instruction.DupStore;
+import jd.core.model.instruction.bytecode.instruction.GetField;
+import jd.core.model.instruction.bytecode.instruction.IfCmp;
+import jd.core.model.instruction.bytecode.instruction.IfInstruction;
+import jd.core.model.instruction.bytecode.instruction.IncInstruction;
+import jd.core.model.instruction.bytecode.instruction.InitArrayInstruction;
+import jd.core.model.instruction.bytecode.instruction.InstanceOf;
+import jd.core.model.instruction.bytecode.instruction.Instruction;
+import jd.core.model.instruction.bytecode.instruction.InvokeInstruction;
+import jd.core.model.instruction.bytecode.instruction.InvokeNoStaticInstruction;
+import jd.core.model.instruction.bytecode.instruction.LookupSwitch;
+import jd.core.model.instruction.bytecode.instruction.MonitorEnter;
+import jd.core.model.instruction.bytecode.instruction.MonitorExit;
+import jd.core.model.instruction.bytecode.instruction.MultiANewArray;
+import jd.core.model.instruction.bytecode.instruction.NewArray;
+import jd.core.model.instruction.bytecode.instruction.Pop;
+import jd.core.model.instruction.bytecode.instruction.PutField;
+import jd.core.model.instruction.bytecode.instruction.PutStatic;
+import jd.core.model.instruction.bytecode.instruction.ReturnInstruction;
+import jd.core.model.instruction.bytecode.instruction.StoreInstruction;
+import jd.core.model.instruction.bytecode.instruction.TableSwitch;
+import jd.core.model.instruction.bytecode.instruction.TernaryOpStore;
+import jd.core.model.instruction.bytecode.instruction.TernaryOperator;
+import jd.core.model.instruction.bytecode.instruction.UnaryOperatorInstruction;
+import jd.core.model.instruction.fast.FastConstants;
+import jd.core.model.instruction.fast.instruction.FastDeclaration;
+import jd.core.model.instruction.fast.instruction.FastFor;
+import jd.core.model.instruction.fast.instruction.FastForEach;
+import jd.core.model.instruction.fast.instruction.FastInstruction;
+import jd.core.model.instruction.fast.instruction.FastLabel;
+import jd.core.model.instruction.fast.instruction.FastList;
+import jd.core.model.instruction.fast.instruction.FastSwitch;
+import jd.core.model.instruction.fast.instruction.FastSwitch.Pair;
+import jd.core.model.instruction.fast.instruction.FastSynchronized;
+import jd.core.model.instruction.fast.instruction.FastTest2Lists;
+import jd.core.model.instruction.fast.instruction.FastTestList;
+import jd.core.model.instruction.fast.instruction.FastTry;
+import jd.core.model.instruction.fast.instruction.FastTry.FastCatch;
+
+public final class SearchInstructionByTypeVisitor
+{
+ private final Class type;
+ private final Predicate predicate;
+
+ public SearchInstructionByTypeVisitor(Class type, Predicate predicate) {
+ this.type = type;
+ this.predicate = predicate;
+ }
+
+ public T visit(Instruction instruction)
+ throws RuntimeException
+ {
+ Objects.requireNonNull(instruction, "Null instruction");
+
+ if (type.isInstance(instruction)) {
+ T convertedInstruction = type.cast(instruction);
+ if (predicate.test(convertedInstruction)) {
+ return convertedInstruction;
+ }
+ }
+
+ switch (instruction.getOpcode())
+ {
+ case Const.ARRAYLENGTH:
+ return visit(((ArrayLength)instruction).getArrayref());
+ case Const.AASTORE,
+ ByteCodeConstants.ARRAYSTORE:
+ return visit(((ArrayStoreInstruction)instruction).getArrayref());
+ case ByteCodeConstants.ASSERT:
+ {
+ AssertInstruction ai = (AssertInstruction)instruction;
+ T tmp = visit(ai.getTest());
+ if (tmp != null) {
+ return tmp;
+ }
+ if (ai.getMsg() == null) {
+ return null;
+ }
+ return visit(ai.getMsg());
+ }
+ case Const.ATHROW:
+ return visit(((AThrow)instruction).getValue());
+ case ByteCodeConstants.UNARYOP:
+ return visit(((UnaryOperatorInstruction)instruction).getValue());
+ case ByteCodeConstants.BINARYOP,
+ ByteCodeConstants.ASSIGNMENT:
+ {
+ BinaryOperatorInstruction boi =
+ (BinaryOperatorInstruction)instruction;
+ T tmp = visit(boi.getValue1());
+ if (tmp != null) {
+ return tmp;
+ }
+ return visit(boi.getValue2());
+ }
+ case Const.CHECKCAST:
+ return visit(((CheckCast)instruction).getObjectref());
+ case ByteCodeConstants.STORE,
+ Const.ASTORE,
+ Const.ISTORE:
+ return visit(((StoreInstruction)instruction).getValueref());
+ case ByteCodeConstants.DUPSTORE:
+ return visit(((DupStore)instruction).getObjectref());
+ case ByteCodeConstants.CONVERT,
+ ByteCodeConstants.IMPLICITCONVERT:
+ return visit(((ConvertInstruction)instruction).getValue());
+ case ByteCodeConstants.IFCMP:
+ {
+ IfCmp ifCmp = (IfCmp)instruction;
+ T tmp = visit(ifCmp.getValue1());
+ if (tmp != null) {
+ return tmp;
+ }
+ return visit(ifCmp.getValue2());
+ }
+ case ByteCodeConstants.IF,
+ ByteCodeConstants.IFXNULL:
+ return visit(((IfInstruction)instruction).getValue());
+ case ByteCodeConstants.COMPLEXIF:
+ {
+ List branchList =
+ ((ComplexConditionalBranchInstruction)instruction).getInstructions();
+ for (int i=branchList.size()-1; i>=0; --i)
+ {
+ T tmp = visit(branchList.get(i));
+ if (tmp != null) {
+ return tmp;
+ }
+ }
+ }
+ break;
+ case Const.INSTANCEOF:
+ return visit(((InstanceOf)instruction).getObjectref());
+ case Const.INVOKEINTERFACE,
+ Const.INVOKESPECIAL,
+ Const.INVOKEVIRTUAL:
+ {
+ T result = visit(
+ ((InvokeNoStaticInstruction)instruction).getObjectref());
+ if (result != null) {
+ return result;
+ }
+ }
+ // intended fall through
+ case Const.INVOKESTATIC,
+ ByteCodeConstants.INVOKENEW:
+ {
+ List list = ((InvokeInstruction)instruction).getArgs();
+ for (int i=list.size()-1; i>=0; --i)
+ {
+ T tmp = visit(list.get(i));
+ if (tmp != null) {
+ return tmp;
+ }
+ }
+ }
+ break;
+ case Const.LOOKUPSWITCH:
+ return visit(((LookupSwitch)instruction).getKey());
+ case Const.MONITORENTER:
+ return visit(((MonitorEnter)instruction).getObjectref());
+ case Const.MONITOREXIT:
+ return visit(((MonitorExit)instruction).getObjectref());
+ case Const.MULTIANEWARRAY:
+ {
+ Instruction[] dimensions = ((MultiANewArray)instruction).getDimensions();
+ for (int i=dimensions.length-1; i>=0; --i)
+ {
+ T tmp = visit(dimensions[i]);
+ if (tmp != null) {
+ return tmp;
+ }
+ }
+ }
+ break;
+ case Const.NEWARRAY:
+ return visit(((NewArray)instruction).getDimension());
+ case Const.ANEWARRAY:
+ return visit(((ANewArray)instruction).getDimension());
+ case Const.POP:
+ return visit(((Pop)instruction).getObjectref());
+ case Const.PUTFIELD:
+ {
+ PutField putField = (PutField)instruction;
+ T tmp = visit(putField.getObjectref());
+ if (tmp != null) {
+ return tmp;
+ }
+ return visit(putField.getValueref());
+ }
+ case Const.PUTSTATIC:
+ return visit(((PutStatic)instruction).getValueref());
+ case ByteCodeConstants.XRETURN:
+ return visit(((ReturnInstruction)instruction).getValueref());
+ case Const.TABLESWITCH:
+ return visit(((TableSwitch)instruction).getKey());
+ case ByteCodeConstants.TERNARYOPSTORE:
+ return visit(((TernaryOpStore)instruction).getObjectref());
+ case ByteCodeConstants.PREINC,
+ ByteCodeConstants.POSTINC:
+ return visit(((IncInstruction)instruction).getValue());
+ case Const.GETFIELD:
+ return visit(((GetField)instruction).getObjectref());
+ case ByteCodeConstants.INITARRAY,
+ ByteCodeConstants.NEWANDINITARRAY:
+ {
+ InitArrayInstruction iai = (InitArrayInstruction)instruction;
+ T tmp = visit(iai.getNewArray());
+ if (tmp != null) {
+ return tmp;
+ }
+ if (iai.getValues() != null) {
+ return visit(iai.getValues());
+ }
+ }
+ break;
+ case ByteCodeConstants.TERNARYOP:
+ {
+ TernaryOperator to = (TernaryOperator)instruction;
+ T tmp = visit(to.getValue1());
+ if (tmp != null) {
+ return tmp;
+ }
+ return visit(to.getValue2());
+ }
+ case FastConstants.TRY:
+ {
+ FastTry ft = (FastTry)instruction;
+ T tmp = visit(ft.getInstructions());
+ if (tmp != null) {
+ return tmp;
+ }
+ List catches = ft.getCatches();
+ for (int i=catches.size()-1; i>=0; --i)
+ {
+ tmp = visit(catches.get(i).instructions());
+ if (tmp != null) {
+ return tmp;
+ }
+ }
+ if (ft.getFinallyInstructions() != null) {
+ return visit(ft.getFinallyInstructions());
+ }
+ }
+ break;
+ case FastConstants.SYNCHRONIZED:
+ {
+ FastSynchronized fsy = (FastSynchronized)instruction;
+ T tmp = visit(fsy.getMonitor());
+ if (tmp != null) {
+ return tmp;
+ }
+ return visit(fsy.getInstructions());
+ }
+ case FastConstants.FOR:
+ {
+ FastFor ff = (FastFor)instruction;
+ if (ff.getInit() != null)
+ {
+ T tmp = visit(ff.getInit());
+ if (tmp != null) {
+ return tmp;
+ }
+ }
+ if (ff.getInc() != null)
+ {
+ T tmp = visit(ff.getInc());
+ if (tmp != null) {
+ return tmp;
+ }
+ }
+ }
+ // intended fall through
+ case FastConstants.WHILE,
+ FastConstants.DO_WHILE,
+ FastConstants.IF_SIMPLE:
+ {
+ FastTestList ftl = (FastTestList)instruction;
+ if (ftl.getTest() != null)
+ {
+ T tmp = visit(ftl.getTest());
+ if (tmp != null) {
+ return tmp;
+ }
+ }
+ }
+ // intended fall through
+ case FastConstants.INFINITE_LOOP:
+ {
+ List instructions =
+ ((FastList)instruction).getInstructions();
+ if (instructions != null) {
+ return visit(instructions);
+ }
+ }
+ break;
+ case FastConstants.FOREACH:
+ {
+ FastForEach ffe = (FastForEach)instruction;
+ T tmp = visit(ffe.getVariable());
+ if (tmp != null) {
+ return tmp;
+ }
+ tmp = visit(ffe.getValues());
+ if (tmp != null) {
+ return tmp;
+ }
+ return visit(ffe.getInstructions());
+ }
+ case FastConstants.IF_ELSE:
+ {
+ FastTest2Lists ft2l = (FastTest2Lists)instruction;
+ T tmp = visit(ft2l.getTest());
+ if (tmp != null) {
+ return tmp;
+ }
+ tmp = visit(ft2l.getInstructions());
+ if (tmp != null) {
+ return tmp;
+ }
+ return visit(ft2l.getInstructions2());
+ }
+ case FastConstants.IF_CONTINUE,
+ FastConstants.IF_BREAK,
+ FastConstants.IF_LABELED_BREAK,
+ FastConstants.GOTO_CONTINUE,
+ FastConstants.GOTO_BREAK,
+ FastConstants.GOTO_LABELED_BREAK:
+ {
+ FastInstruction fi = (FastInstruction)instruction;
+ if (fi.getInstruction() != null) {
+ return visit(fi.getInstruction());
+ }
+ }
+ break;
+ case FastConstants.DECLARE:
+ {
+ FastDeclaration fd = (FastDeclaration)instruction;
+ if (fd.getInstruction() != null) {
+ return visit(fd.getInstruction());
+ }
+ }
+ break;
+ case FastConstants.SWITCH,
+ FastConstants.SWITCH_ENUM,
+ FastConstants.SWITCH_STRING:
+ {
+ FastSwitch fs = (FastSwitch)instruction;
+ T tmp = visit(fs.getTest());
+ if (tmp != null) {
+ return tmp;
+ }
+
+ Pair[] pairs = fs.getPairs();
+ for (int i=pairs.length-1; i>=0; --i)
+ {
+ List instructions = pairs[i].getInstructions();
+ if (instructions != null)
+ {
+ tmp = visit(instructions);
+ if (tmp != null) {
+ return tmp;
+ }
+ }
+ }
+ }
+ break;
+ case FastConstants.LABEL:
+ {
+ FastLabel fla = (FastLabel)instruction;
+ if (fla.getInstruction() != null) {
+ return visit(fla.getInstruction());
+ }
+ }
+ break;
+ case Const.ACONST_NULL,
+ ByteCodeConstants.ARRAYLOAD,
+ ByteCodeConstants.LOAD,
+ Const.ALOAD,
+ Const.ILOAD,
+ Const.BIPUSH,
+ ByteCodeConstants.ICONST,
+ ByteCodeConstants.LCONST,
+ ByteCodeConstants.FCONST,
+ ByteCodeConstants.DCONST,
+ ByteCodeConstants.DUPLOAD,
+ ByteCodeConstants.EXCEPTIONLOAD,
+ Const.GETSTATIC,
+ ByteCodeConstants.OUTERTHIS,
+ Const.GOTO,
+ Const.IINC,
+ Const.JSR,
+ Const.LDC,
+ Const.LDC2_W,
+ Const.NEW,
+ Const.NOP,
+ Const.RET,
+ Const.RETURN,
+ Const.INVOKEDYNAMIC,
+ ByteCodeConstants.RETURNADDRESSLOAD,
+ Const.SIPUSH:
+ break;
+ default:
+ System.err.println(
+ "Can not search instruction in " +
+ instruction.getClass().getName() +
+ "=" + instruction.getOpcode());
+ }
+
+ return null;
+ }
+
+ private T visit(List instructions)
+ throws RuntimeException
+ {
+ for (int i=instructions.size()-1; i>=0; --i)
+ {
+ T instruction = visit(instructions.get(i));
+ if (instruction != null) {
+ return instruction;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/jd/core/process/analyzer/instruction/fast/FastInstructionListBuilder.java b/src/main/java/jd/core/process/analyzer/instruction/fast/FastInstructionListBuilder.java
index 7407d813..15bf295b 100644
--- a/src/main/java/jd/core/process/analyzer/instruction/fast/FastInstructionListBuilder.java
+++ b/src/main/java/jd/core/process/analyzer/instruction/fast/FastInstructionListBuilder.java
@@ -105,6 +105,7 @@
import jd.core.process.analyzer.classfile.reconstructor.AssignmentOperatorReconstructor;
import jd.core.process.analyzer.classfile.visitor.RemoveCheckCastVisitor;
import jd.core.process.analyzer.classfile.visitor.SearchInstructionByOpcodeVisitor;
+import jd.core.process.analyzer.classfile.visitor.SearchInstructionByTypeVisitor;
import jd.core.process.analyzer.instruction.bytecode.ComparisonInstructionAnalyzer;
import jd.core.process.analyzer.instruction.bytecode.reconstructor.AssertInstructionReconstructor;
import jd.core.process.analyzer.instruction.bytecode.util.ByteCodeUtil;
@@ -140,8 +141,10 @@
* AnalyzeXXXXSwitch -------------------------------------------------+
*/
public final class FastInstructionListBuilder {
+
private FastInstructionListBuilder() {
}
+
/** Declaration constants. */
private static final boolean DECLARED = true;
private static final boolean NOT_DECLARED = false;
@@ -1574,17 +1577,15 @@ private static Set addDeclarations(List list, Loca
if (length > 0) {
// 1) Ajout de declaration sur les instructions 'store' et 'for'
- LocalVariable lv;
int lastOffset = list.get(length - 1).getOffset();
- Instruction instruction;
for (int i = 0; i < length; i++)
{
- instruction = list.get(i);
+ Instruction instruction = list.get(i);
if (instruction instanceof StoreInstruction) {
StoreInstruction si = (StoreInstruction) instruction;
- lv = localVariables.getLocalVariableWithIndexAndOffset(si.getIndex(), si.getOffset());
+ LocalVariable lv = localVariables.getLocalVariableWithIndexAndOffset(si.getIndex(), si.getOffset());
if (lv != null && lv.hasDeclarationFlag() == NOT_DECLARED) {
ReturnInstruction returnInstruction = findReturnInstructionForStore(list, length, i, si);
if (returnInstruction == null || returnInstruction.getLineNumber() != si.getLineNumber()) {
@@ -1611,7 +1612,7 @@ private static Set addDeclarations(List list, Loca
if (ff.getInit() != null && (ff.getInit().getOpcode() == Const.ASTORE || ff.getInit().getOpcode() == Const.ISTORE
|| ff.getInit().getOpcode() == ByteCodeConstants.STORE)) {
StoreInstruction si = (StoreInstruction) ff.getInit();
- lv = localVariables.getLocalVariableWithIndexAndOffset(si.getIndex(), si.getOffset());
+ LocalVariable lv = localVariables.getLocalVariableWithIndexAndOffset(si.getIndex(), si.getOffset());
if (lv != null && lv.hasDeclarationFlag() == NOT_DECLARED
&& beforeListOffset < lv.getStartPc()
&& lv.getStartPc() + lv.getLength() - 1 <= lastOffset
@@ -1626,7 +1627,7 @@ private static Set addDeclarations(List list, Loca
instruction, ByteCodeConstants.ASSIGNMENT);
if (asi != null && asi.getValue1() instanceof LoadInstruction) {
LoadInstruction li = (LoadInstruction) asi.getValue1();
- lv = localVariables.getLocalVariableWithIndexAndOffset(li.getIndex(), li.getOffset());
+ LocalVariable lv = localVariables.getLocalVariableWithIndexAndOffset(li.getIndex(), li.getOffset());
if (lv != null && lv.hasDeclarationFlag() == NOT_DECLARED && beforeListOffset < lv.getStartPc()
&& lv.getStartPc() + lv.getLength() - 1 <= lastOffset) {
FastDeclaration fastDeclaration = new FastDeclaration(lv.getStartPc(),
@@ -1635,13 +1636,16 @@ private static Set addDeclarations(List list, Loca
lv.setDeclarationFlag(DECLARED);
}
} else {
- StoreInstruction si = (StoreInstruction) SearchInstructionByOpcodeVisitor.visit(
- instruction, ByteCodeConstants.STORE, Const.ISTORE, Const.ASTORE);
+ SearchInstructionByTypeVisitor visitor = new SearchInstructionByTypeVisitor<>(
+ StoreInstruction.class, si -> {
+ LocalVariable lv = localVariables.getLocalVariableWithIndexAndOffset(si.getIndex(), si.getOffset());
+ return lv != null && lv.hasDeclarationFlag() == NOT_DECLARED && beforeListOffset < lv.getStartPc()
+ && lv.getStartPc() + lv.getLength() - 1 <= lastOffset;
+ });
+ StoreInstruction si = visitor.visit(instruction);
if (si != null) {
- lv = localVariables.getLocalVariableWithIndexAndOffset(si.getIndex(), si.getOffset());
- if (lv != null && lv.hasDeclarationFlag() == NOT_DECLARED && beforeListOffset < lv.getStartPc()
- && lv.getStartPc() + lv.getLength() - 1 <= lastOffset
- && !declarationFound(list, lv) && CheckLocalVariableUsedVisitor.visit(lv, list)) {
+ LocalVariable lv = localVariables.getLocalVariableWithIndexAndOffset(si.getIndex(), si.getOffset());
+ if (!declarationFound(list, lv)) {
FastDeclaration fastDeclaration = new FastDeclaration(lv.getStartPc(),
Instruction.UNKNOWN_LINE_NUMBER, lv, null);
list.add(i, fastDeclaration);
@@ -1678,7 +1682,7 @@ private static Set addDeclarations(List list, Loca
// }
final int lvLength = Optional.ofNullable(localVariables).map(LocalVariables::size).orElse(0);
for (int i = 0; i < lvLength; i++) {
- lv = localVariables.getLocalVariableAt(i);
+ LocalVariable lv = localVariables.getLocalVariableAt(i);
if (lv.hasDeclarationFlag() == NOT_DECLARED && !lv.isToBeRemoved() && beforeListOffset < lv.getStartPc()
&& lv.getStartPc() + lv.getLength() - 1 <= lastOffset && !INTERNAL_OBJECT_SIGNATURE.equals(lv.getName(classFile.getConstantPool()))) {
int indexForNewDeclaration = InstructionUtil.getIndexForOffset(list, lv.getStartPc());
@@ -1696,7 +1700,18 @@ private static Set addDeclarations(List list, Loca
Instruction.UNKNOWN_LINE_NUMBER, lv, null);
if (addDeclarations) {
if (!declarationFound(list, lv) && CheckLocalVariableUsedVisitor.visit(lv, list)) {
- list.add(indexForNewDeclaration, fastDeclaration);
+ Instruction instruction = list.get(indexForNewDeclaration);
+ if (instruction instanceof StoreInstruction) {
+ StoreInstruction si = (StoreInstruction) instruction;
+ if (lv == localVariables.getLocalVariableWithIndexAndOffset(si.getIndex(), si.getOffset())) {
+ fastDeclaration = new FastDeclaration(si.getOffset(), si.getLineNumber(), lv, si);
+ }
+ }
+ if (fastDeclaration.getInstruction() == null) {
+ list.add(indexForNewDeclaration, fastDeclaration);
+ } else {
+ list.set(indexForNewDeclaration, fastDeclaration);
+ }
}
} else {
outerDeclarations.add(fastDeclaration);
diff --git a/src/test/java/jd/core/test/EventCountCircuitBreakerTest.java b/src/test/java/jd/core/test/EventCountCircuitBreakerTest.java
new file mode 100644
index 00000000..010c269b
--- /dev/null
+++ b/src/test/java/jd/core/test/EventCountCircuitBreakerTest.java
@@ -0,0 +1,18 @@
+package jd.core.test;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+
+public class EventCountCircuitBreakerTest extends AbstractTestCase {
+
+ @Test
+ public void test() throws IOException {
+ String output = decompile("org/apache/commons/lang3/concurrent/EventCountCircuitBreaker");
+ assertEquals(IOUtils.toString(getClass().getResource("EventCountCircuitBreaker.txt"), StandardCharsets.UTF_8), output);
+ }
+}
diff --git a/src/test/resources/jd/core/test/EventCountCircuitBreaker.txt b/src/test/resources/jd/core/test/EventCountCircuitBreaker.txt
new file mode 100644
index 00000000..7e491138
--- /dev/null
+++ b/src/test/resources/jd/core/test/EventCountCircuitBreaker.txt
@@ -0,0 +1,564 @@
+/* */ package org.apache.commons.lang3.concurrent;
+/* */
+/* */ import java.util.EnumMap;
+/* */ import java.util.Map;
+/* */ import java.util.concurrent.TimeUnit;
+/* */ import java.util.concurrent.atomic.AtomicReference;
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public class EventCountCircuitBreaker
+/* */ extends AbstractCircuitBreaker
+/* */ {
+/* */ private final AtomicReference checkIntervalData;
+/* */ private final int openingThreshold;
+/* */ private final long openingInterval;
+/* */ private final int closingThreshold;
+/* */ private final long closingInterval;
+/* 141 */ private static final Map STRATEGY_MAP = createStrategyMap();
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public EventCountCircuitBreaker(int openingThreshold, long openingInterval, TimeUnit openingUnit, int closingThreshold, long closingInterval, TimeUnit closingUnit)
+/* */ {
+/* 177 */ this.checkIntervalData = new AtomicReference<>(new CheckIntervalData(0, 0L));
+/* 178 */ this.openingThreshold = openingThreshold;
+/* 179 */ this.openingInterval = openingUnit.toNanos(openingInterval);
+/* 180 */ this.closingThreshold = closingThreshold;
+/* 181 */ this.closingInterval = closingUnit.toNanos(closingInterval);
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public EventCountCircuitBreaker(int openingThreshold, long checkInterval, TimeUnit checkUnit, int closingThreshold)
+/* */ {
+/* 199 */ this(openingThreshold, checkInterval, checkUnit, closingThreshold, checkInterval, checkUnit);
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public EventCountCircuitBreaker(int threshold, long checkInterval, TimeUnit checkUnit)
+/* */ {
+/* 214 */ this(threshold, checkInterval, checkUnit, threshold);
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public int getOpeningThreshold()
+/* */ {
+/* 225 */ return this.openingThreshold;
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public long getOpeningInterval()
+/* */ {
+/* 234 */ return this.openingInterval;
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public int getClosingThreshold()
+/* */ {
+/* 245 */ return this.closingThreshold;
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public long getClosingInterval()
+/* */ {
+/* 254 */ return this.closingInterval;
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ @Override
+/* */ public boolean checkState()
+/* */ {
+/* 264 */ return performStateCheck(0);
+/* */ }
+/* */
+/* */
+/* */
+/* */ @Override
+/* */ public boolean incrementAndCheckState(Integer increment)
+/* */ {
+/* 272 */ return performStateCheck(increment.intValue());
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public boolean incrementAndCheckState()
+/* */ {
+/* 284 */ return incrementAndCheckState(Integer.valueOf(1));
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ @Override
+/* */ public void open()
+/* */ {
+/* 295 */ super.open();
+/* 296 */ this.checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ @Override
+/* */ public void close()
+/* */ {
+/* 307 */ super.close();
+/* 308 */ this.checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
+/* */ }
+/* */
+/* */
+/* */
+/* */ private boolean performStateCheck(int increment)
+/* */ {
+/* */ CheckIntervalData nextData;
+/* */
+/* */
+/* */ CheckIntervalData currentData;
+/* */
+/* */ AbstractCircuitBreaker.State currentState;
+/* */
+/* */ do
+/* */ {
+/* 324 */ long time = nanoTime();
+/* 325 */ currentState = (AbstractCircuitBreaker.State)this.state.get();
+/* 326 */ currentData = (CheckIntervalData)this.checkIntervalData.get();
+/* 327 */ nextData = nextCheckIntervalData(increment, currentData, currentState, time);
+/* 328 */ } while (!updateCheckIntervalData(currentData, nextData));
+/* */
+/* */
+/* */
+/* 332 */ if (stateStrategy(currentState).isStateTransition(this, currentData, nextData)) {
+/* 333 */ currentState = currentState.oppositeState();
+/* 334 */ changeStateAndStartNewCheckInterval(currentState);
+/* */ }
+/* 336 */ return !isOpen(currentState);
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ private boolean updateCheckIntervalData(CheckIntervalData currentData, CheckIntervalData nextData)
+/* */ {
+/* 351 */ return currentData == nextData ||
+/* 352 */ this.checkIntervalData.compareAndSet(currentData, nextData);
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ private void changeStateAndStartNewCheckInterval(AbstractCircuitBreaker.State newState)
+/* */ {
+/* 362 */ changeState(newState);
+/* 363 */ this.checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ private CheckIntervalData nextCheckIntervalData(int increment, CheckIntervalData currentData, AbstractCircuitBreaker.State currentState, long time)
+/* */ {
+/* */ CheckIntervalData nextData;
+/* */
+/* */
+/* */
+/* */
+/* */
+/* 380 */ if (stateStrategy(currentState).isCheckIntervalFinished(this, currentData, time)) {
+/* 381 */ nextData = new CheckIntervalData(increment, time);
+/* */ } else {
+/* 383 */ nextData = currentData.increment(increment);
+/* */ }
+/* 385 */ return nextData;
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ long nanoTime()
+/* */ {
+/* 395 */ return System.nanoTime();
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ private static StateStrategy stateStrategy(AbstractCircuitBreaker.State state)
+/* */ {
+/* 406 */ return (StateStrategy)STRATEGY_MAP.get(state);
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ private static Map createStrategyMap()
+/* */ {
+/* 416 */ Map map = new EnumMap<>(AbstractCircuitBreaker.State.class);
+/* 417 */ map.put(AbstractCircuitBreaker.State.CLOSED, new StateStrategyClosed());
+/* 418 */ map.put(AbstractCircuitBreaker.State.OPEN, new StateStrategyOpen());
+/* 419 */ return map;
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ private static class CheckIntervalData
+/* */ {
+/* */ private final int eventCount;
+/* */
+/* */
+/* */
+/* */
+/* */ private final long checkIntervalStart;
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ CheckIntervalData(int count, long intervalStart)
+/* */ {
+/* 441 */ this.eventCount = count;
+/* 442 */ this.checkIntervalStart = intervalStart;
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public int getEventCount()
+/* */ {
+/* 451 */ return this.eventCount;
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public long getCheckIntervalStart()
+/* */ {
+/* 460 */ return this.checkIntervalStart;
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public CheckIntervalData increment(int delta)
+/* */ {
+/* 471 */ return delta == 0 ? this : new CheckIntervalData(getEventCount() + delta,
+/* 472 */ getCheckIntervalStart());
+/* */ }
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ private static abstract class StateStrategy
+/* */ {
+/* */ public boolean isCheckIntervalFinished(EventCountCircuitBreaker breaker, EventCountCircuitBreaker.CheckIntervalData currentData, long now)
+/* */ {
+/* 492 */ return now - currentData.getCheckIntervalStart() > fetchCheckInterval(breaker);
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ public abstract boolean isStateTransition(EventCountCircuitBreaker paramEventCountCircuitBreaker, EventCountCircuitBreaker.CheckIntervalData paramCheckIntervalData1, EventCountCircuitBreaker.CheckIntervalData paramCheckIntervalData2);
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ protected abstract long fetchCheckInterval(EventCountCircuitBreaker paramEventCountCircuitBreaker);
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ private static class StateStrategyClosed
+/* */ extends EventCountCircuitBreaker.StateStrategy
+/* */ {
+/* */ @Override
+/* */ public boolean isStateTransition(EventCountCircuitBreaker breaker, EventCountCircuitBreaker.CheckIntervalData currentData, EventCountCircuitBreaker.CheckIntervalData nextData)
+/* */ {
+/* 529 */ return nextData.getEventCount() > breaker.getOpeningThreshold();
+/* */ }
+/* */
+/* */
+/* */
+/* */ @Override
+/* */ protected long fetchCheckInterval(EventCountCircuitBreaker breaker)
+/* */ {
+/* 537 */ return breaker.getOpeningInterval();
+/* */ }
+/* */ }
+/* */
+/* */
+/* */
+/* */
+/* */
+/* */ private static class StateStrategyOpen
+/* */ extends EventCountCircuitBreaker.StateStrategy
+/* */ {
+/* */ @Override
+/* */ public boolean isStateTransition(EventCountCircuitBreaker breaker, EventCountCircuitBreaker.CheckIntervalData currentData, EventCountCircuitBreaker.CheckIntervalData nextData)
+/* */ {
+/* 551 */ return
+/* 552 */ nextData.getCheckIntervalStart() != currentData.getCheckIntervalStart() &&
+/* 553 */ currentData.getEventCount() < breaker.getClosingThreshold();
+/* */ }
+/* */
+/* */
+/* */
+/* */ @Override
+/* */ protected long fetchCheckInterval(EventCountCircuitBreaker breaker)
+/* */ {
+/* 561 */ return breaker.getClosingInterval();
+/* */ }
+/* */ }
+/* */ }
diff --git a/src/test/resources/jd/core/test/Pass2Verifier.txt b/src/test/resources/jd/core/test/Pass2Verifier.txt
index c2244fe0..4259a06a 100644
--- a/src/test/resources/jd/core/test/Pass2Verifier.txt
+++ b/src/test/resources/jd/core/test/Pass2Verifier.txt
@@ -176,7 +176,7 @@
/* */
/* */
/* 178 */ CodeException[] excTable = obj.getExceptionTable();
-/* 179 */ int excIndex; ConstantClass cc; String cname; JavaClass t; JavaClass o; for (CodeException element : excTable) {
+/* 179 */ JavaClass o; JavaClass t; String cname; ConstantClass cc; int excIndex; for (CodeException element : excTable) {
/* 180 */ excIndex = element.getCatchType();
/* 181 */ if (excIndex != 0) {
/* 182 */ checkIndex(obj, excIndex, this.CONST_Class);
@@ -806,8 +806,8 @@
/* 806 */ Type act = t;
/* 807 */ if ((act instanceof ArrayType))
/* 808 */ act = ((ArrayType)act).getBasicType();
-/* */ Verifier v;
-/* 810 */ VerificationResult vr; if ((act instanceof ObjectType)) {
+/* */ VerificationResult vr;
+/* 810 */ Verifier v; if ((act instanceof ObjectType)) {
/* 811 */ v = VerifierFactory.getVerifier(((ObjectType)act).getClassName());
/* 812 */ vr = v.doPass1();
/* 813 */ if (vr != VerificationResult.VR_OK)