From 59b359c191d74965752438c0e516cbfd6e7ec6f7 Mon Sep 17 00:00:00 2001 From: Mikhail Pyltsin Date: Wed, 14 Jun 2023 12:11:19 +0200 Subject: [PATCH] IJ-CR-108265 [java-decompiler] IDEA-198397 add cancelled checks for java-decompiler. Refresh api GitOrigin-RevId: 0edb31b19b393df6981c3785d2a7368226c70114 --- .../decompiler/main/CancellationManager.java | 47 ++++++++--------- .../decompiler/main/DecompilerContext.java | 51 ++++++++++--------- .../java/decompiler/main/Fernflower.java | 10 ++-- .../main/decompiler/BaseDecompiler.java | 9 ++-- .../decompiler/main/rels/ClassWrapper.java | 15 ++---- .../java/decompiler/CancellableTest.java | 8 +-- .../decompiler/IdeaCancellationManager.java | 8 ++- .../java/decompiler/IdeaDecompiler.kt | 3 +- 8 files changed, 73 insertions(+), 78 deletions(-) diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/CancellationManager.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/CancellationManager.java index 859dd3221d71..ae7deebb6078 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/CancellationManager.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/CancellationManager.java @@ -7,25 +7,19 @@ import org.jetbrains.annotations.NotNull; @ApiStatus.Experimental public interface CancellationManager { /** - * @throws CanceledException if the process has been canceled. - * @throws TimeExceedException if limit timeout is exceeded. + * @throws CanceledException if the process has been canceled. */ - void checkCanceled() throws CanceledException, TimeExceedException; + void checkCanceled() throws CanceledException; /** - * @param sec - limit timeout (seconds) + * Called every time the body of a new method is started to be decompiled */ - void setMaxSec(int sec); + void startMethod(String className, String methodName); /** - * Call to start counting down the timeout + * Called every time the method decompilation is finished */ - void startMethod(); - - /** - * Call to reset timer - */ - void finishMethod(); + void finishMethod(String className, String methodName); @ApiStatus.Experimental class CanceledException extends RuntimeException { @@ -33,22 +27,30 @@ public interface CancellationManager { public CanceledException(@NotNull Throwable cause) { super(cause); } + + public CanceledException() { + super(); + } } @ApiStatus.Experimental - class TimeExceedException extends RuntimeException { + class TimeExceedException extends CanceledException { } - static CancellationManager getSimpleWithTimeout() { - return new SimpleWithTimeoutCancellationManager(); + static CancellationManager getSimpleWithTimeout(int maxMethodTimeoutSec) { + return new TimeoutCancellationManager(maxMethodTimeoutSec); } - class SimpleWithTimeoutCancellationManager implements CancellationManager { - private long maxMilis = 0; + class TimeoutCancellationManager implements CancellationManager { + private final long maxMilis; private long startMilis = 0; + protected TimeoutCancellationManager(int maxMethodTimeoutSec) { + this.maxMilis = maxMethodTimeoutSec * 1000L; + } + @Override - public void checkCanceled() throws CanceledException, TimeExceedException { + public void checkCanceled() throws CanceledException { if (maxMilis <= 0 || startMilis <= 0) { return; } @@ -59,17 +61,12 @@ public interface CancellationManager { } @Override - public void setMaxSec(int sec) { - maxMilis = sec * 1_000L; - } - - @Override - public void startMethod() { + public void startMethod(String className, String methodName) { startMilis = System.currentTimeMillis(); } @Override - public void finishMethod() { + public void finishMethod(String className, String methodName) { startMilis = 0; } } diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/DecompilerContext.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/DecompilerContext.java index 5eb9086f65db..a1c3fd00ba52 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/DecompilerContext.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/DecompilerContext.java @@ -1,6 +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. package org.jetbrains.java.decompiler.main; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.ImportCollector; @@ -19,36 +21,46 @@ public class DecompilerContext { public static final String CURRENT_CLASS_NODE = "CURRENT_CLASS_NODE"; public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER"; + @NotNull private final Map properties; + @NotNull private final IFernflowerLogger logger; + @NotNull private final StructContext structContext; + @NotNull private final ClassesProcessor classProcessor; + @Nullable private final PoolInterceptor poolInterceptor; + @NotNull private final CancellationManager cancellationManager; private ImportCollector importCollector; private VarProcessor varProcessor; private CounterContainer counterContainer; private BytecodeSourceMapper bytecodeSourceMapper; - public DecompilerContext(Map properties, - IFernflowerLogger logger, - StructContext structContext, - ClassesProcessor classProcessor, - PoolInterceptor interceptor) { - this(properties, logger, structContext, classProcessor, interceptor, CancellationManager.getSimpleWithTimeout()); - } + public DecompilerContext(@NotNull Map properties, + @NotNull IFernflowerLogger logger, + @NotNull StructContext structContext, + @NotNull ClassesProcessor classProcessor, + @Nullable PoolInterceptor interceptor) { + this(properties, logger, structContext, classProcessor, interceptor, null); + } - public DecompilerContext(Map properties, - IFernflowerLogger logger, - StructContext structContext, - ClassesProcessor classProcessor, - PoolInterceptor interceptor, - CancellationManager cancellationManager) { + public DecompilerContext(@NotNull Map properties, + @NotNull IFernflowerLogger logger, + @NotNull StructContext structContext, + @NotNull ClassesProcessor classProcessor, + @Nullable PoolInterceptor interceptor, + @Nullable CancellationManager cancellationManager) { Objects.requireNonNull(properties); Objects.requireNonNull(logger); Objects.requireNonNull(structContext); Objects.requireNonNull(classProcessor); - Objects.requireNonNull(cancellationManager); + if (cancellationManager == null) { + Object object = properties.get(IFernflowerPreferences.MAX_PROCESSING_METHOD); + object = object == null ? "0" : object; + cancellationManager = CancellationManager.getSimpleWithTimeout(Integer.parseInt(object.toString())); + } this.properties = properties; this.logger = logger; @@ -118,16 +130,9 @@ public class DecompilerContext { public static ClassesProcessor getClassProcessor() { return getCurrentContext().classProcessor; } - public static CancellationManager getCancellationManager() { - DecompilerContext context = getCurrentContext(); - if (context != null) { - return context.cancellationManager; - } - CancellationManager simpleWithTimeout = CancellationManager.getSimpleWithTimeout(); - int maxSec = Integer.parseInt(getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString()); - simpleWithTimeout.setMaxSec(maxSec); - return simpleWithTimeout; + public static CancellationManager getCancellationManager() { + return getCurrentContext().cancellationManager; } public static PoolInterceptor getPoolInterceptor() { diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/Fernflower.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/Fernflower.java index a355b17c4673..f3769b1f59a1 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/Fernflower.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/Fernflower.java @@ -1,6 +1,7 @@ // 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. package org.jetbrains.java.decompiler.main; +import org.jetbrains.annotations.Nullable; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.extern.*; import org.jetbrains.java.decompiler.modules.renamer.ConverterHelper; @@ -25,12 +26,9 @@ public class Fernflower implements IDecompiledData { public Fernflower(IBytecodeProvider provider, IResultSaver saver, - Map customProperties, + @Nullable Map customProperties, IFernflowerLogger logger, - CancellationManager cancellationManager) { - if (cancellationManager == null) { - cancellationManager = CancellationManager.getSimpleWithTimeout(); - } + @Nullable CancellationManager cancellationManager) { Map properties = new HashMap<>(IFernflowerPreferences.DEFAULTS); if (customProperties != null) { properties.putAll(customProperties); @@ -63,7 +61,7 @@ public class Fernflower implements IDecompiledData { } public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map customProperties, IFernflowerLogger logger) { - this(provider, saver, customProperties, logger, CancellationManager.getSimpleWithTimeout()); + this(provider, saver, customProperties, logger, null); } private static IIdentifierRenamer loadHelper(String className, IFernflowerLogger logger) { diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java index 01abe64802c9..3abd62c448c6 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java @@ -1,6 +1,7 @@ // 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. package org.jetbrains.java.decompiler.main.decompiler; +import org.jetbrains.annotations.Nullable; import org.jetbrains.java.decompiler.main.CancellationManager; import org.jetbrains.java.decompiler.main.Fernflower; import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; @@ -14,12 +15,12 @@ import java.util.Map; public class BaseDecompiler { private final Fernflower engine; - public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, Map options, IFernflowerLogger logger) { - this(provider, saver, options, logger, CancellationManager.getSimpleWithTimeout()); + public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, @Nullable Map options, IFernflowerLogger logger) { + this(provider, saver, options, logger, null); } - public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, Map options, IFernflowerLogger logger, - CancellationManager cancellationManager) { + public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, @Nullable Map options, IFernflowerLogger logger, + @Nullable CancellationManager cancellationManager) { engine = new Fernflower(provider, saver, options, logger, cancellationManager); } diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index 52bbd9f4f88e..ad30feea9598 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -1,4 +1,4 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.java.decompiler.main.rels; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -42,15 +42,8 @@ public class ClassWrapper { DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_WRAPPER, this); DecompilerContext.getLogger().startClass(classStruct.qualifiedName); - int maxSec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString()); boolean testMode = DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE); CancellationManager cancellationManager = DecompilerContext.getCancellationManager(); - if (testMode) { - cancellationManager.setMaxSec(0); - } - else { - cancellationManager.setMaxSec(maxSec); - } for (StructMethod mt : classStruct.getMethods()) { cancellationManager.checkCanceled(); DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor()); @@ -68,13 +61,13 @@ public class ClassWrapper { try { if (mt.containsCode()) { - if (maxSec == 0 || testMode) { + if (testMode) { root = MethodProcessorRunnable.codeToJava(classStruct, mt, md, varProc); } else { DecompilerContext context = DecompilerContext.getCurrentContext(); try { - cancellationManager.startMethod(); + cancellationManager.startMethod(classStruct.qualifiedName, mt.getName()); MethodProcessorRunnable mtProc = new MethodProcessorRunnable(classStruct, mt, md, varProc, DecompilerContext.getCurrentContext()); mtProc.run(); @@ -83,7 +76,7 @@ public class ClassWrapper { } finally { DecompilerContext.setCurrentContext(context); - cancellationManager.finishMethod(); + cancellationManager.finishMethod(classStruct.qualifiedName, mt.getName()); } } } diff --git a/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/CancellableTest.java b/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/CancellableTest.java index 5487d6c0be1f..b3ac62b28873 100644 --- a/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/CancellableTest.java +++ b/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/CancellableTest.java @@ -39,19 +39,15 @@ public class CancellableTest { } @Override - public void setMaxSec(int sec) { + public void startMethod(String className, String methodName) { } @Override - public void startMethod() { + public void finishMethod(String className, String methodName) { } - @Override - public void finishMethod() { - - } private void check() { if (myAtomicInteger.incrementAndGet() > MIN_CALL_NUMBERS) { diff --git a/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaCancellationManager.java b/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaCancellationManager.java index 5d9c5f812857..17fecd0cd97f 100644 --- a/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaCancellationManager.java +++ b/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaCancellationManager.java @@ -7,10 +7,14 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.java.decompiler.main.CancellationManager; @ApiStatus.Experimental -public class IdeaCancellationManager extends CancellationManager.SimpleWithTimeoutCancellationManager { +public class IdeaCancellationManager extends CancellationManager.TimeoutCancellationManager { + + public IdeaCancellationManager(int maxMethodTimeoutSec) { + super(maxMethodTimeoutSec); + } @Override - public void checkCanceled() throws CanceledException, TimeExceedException { + public void checkCanceled() throws CanceledException { try { ProgressManager.checkCanceled(); } diff --git a/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaDecompiler.kt b/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaDecompiler.kt index 6da8a7ac562e..95c36dc00561 100644 --- a/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaDecompiler.kt +++ b/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaDecompiler.kt @@ -147,8 +147,9 @@ class IdeaDecompiler : ClassFileDecompilers.Light() { val provider = MyBytecodeProvider(files) val saver = MyResultSaver() + val maxSecProcessingMethod = options[IFernflowerPreferences.MAX_PROCESSING_METHOD]?.toString()?.toIntOrNull() ?: 0 val decompiler = BaseDecompiler(provider, saver, options, myLogger.value, - IdeaCancellationManager()) + IdeaCancellationManager(maxSecProcessingMethod)) files.forEach { decompiler.addSource(File(it.path)) } try { decompiler.decompileContext()