[java decompiler] restores thread-conscious context access (EA-116270)

master
Roman Shevchenko 6 years ago
parent bc55d8d9ec
commit 429b667031
  1. 82
      src/org/jetbrains/java/decompiler/main/DecompilerContext.java
  2. 74
      src/org/jetbrains/java/decompiler/main/Fernflower.java
  3. 14
      src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java
  4. 36
      src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java
  5. 4
      src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java
  6. 13
      src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java
  7. 6
      test/org/jetbrains/java/decompiler/BulkDecompilationTest.java
  8. 8
      test/org/jetbrains/java/decompiler/SingleClassesTest.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-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.java.decompiler.main.collectors.BytecodeSourceMapper;
@ -10,8 +10,6 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;
import org.jetbrains.java.decompiler.struct.StructContext;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@ -21,8 +19,6 @@ public class DecompilerContext {
public static final String CURRENT_CLASS_NODE = "CURRENT_CLASS_NODE";
public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER";
private static volatile DecompilerContext currentContext = null;
private final Map<String, Object> properties;
private final IFernflowerLogger logger;
private final StructContext structContext;
@ -33,11 +29,16 @@ public class DecompilerContext {
private CounterContainer counterContainer;
private BytecodeSourceMapper bytecodeSourceMapper;
private DecompilerContext(Map<String, Object> properties,
IFernflowerLogger logger,
StructContext structContext,
ClassesProcessor classProcessor,
PoolInterceptor interceptor) {
public DecompilerContext(Map<String, Object> properties,
IFernflowerLogger logger,
StructContext structContext,
ClassesProcessor classProcessor,
PoolInterceptor interceptor) {
Objects.requireNonNull(properties);
Objects.requireNonNull(logger);
Objects.requireNonNull(structContext);
Objects.requireNonNull(classProcessor);
this.properties = properties;
this.logger = logger;
this.structContext = structContext;
@ -50,48 +51,31 @@ public class DecompilerContext {
// context setup and update
// *****************************************************************************
public static void initContext(Map<String, Object> customProperties,
IFernflowerLogger logger,
StructContext structContext,
ClassesProcessor classProcessor,
PoolInterceptor interceptor) {
Objects.requireNonNull(logger);
Objects.requireNonNull(structContext);
Objects.requireNonNull(classProcessor);
Map<String, Object> properties = new HashMap<>(IFernflowerPreferences.DEFAULTS);
if (customProperties != null) {
properties.putAll(customProperties);
}
String level = (String)properties.get(IFernflowerPreferences.LOG_LEVEL);
if (level != null) {
try {
logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.US)));
}
catch (IllegalArgumentException ignore) { }
}
private static final ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<>();
currentContext = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor);
public static DecompilerContext getCurrentContext() {
return currentContext.get();
}
public static void clearContext() {
currentContext = null;
public static void setCurrentContext(DecompilerContext context) {
currentContext.set(context);
}
public static void setProperty(String key, Object value) {
currentContext.properties.put(key, value);
getCurrentContext().properties.put(key, value);
}
public static void startClass(ImportCollector importCollector) {
currentContext.importCollector = importCollector;
currentContext.counterContainer = new CounterContainer();
currentContext.bytecodeSourceMapper = new BytecodeSourceMapper();
DecompilerContext context = getCurrentContext();
context.importCollector = importCollector;
context.counterContainer = new CounterContainer();
context.bytecodeSourceMapper = new BytecodeSourceMapper();
}
public static void startMethod(VarProcessor varProcessor) {
currentContext.varProcessor = varProcessor;
currentContext.counterContainer = new CounterContainer();
DecompilerContext context = getCurrentContext();
context.varProcessor = varProcessor;
context.counterContainer = new CounterContainer();
}
// *****************************************************************************
@ -99,7 +83,7 @@ public class DecompilerContext {
// *****************************************************************************
public static Object getProperty(String key) {
return currentContext.properties.get(key);
return getCurrentContext().properties.get(key);
}
public static boolean getOption(String key) {
@ -112,34 +96,34 @@ public class DecompilerContext {
}
public static IFernflowerLogger getLogger() {
return currentContext.logger;
return getCurrentContext().logger;
}
public static StructContext getStructContext() {
return currentContext.structContext;
return getCurrentContext().structContext;
}
public static ClassesProcessor getClassProcessor() {
return currentContext.classProcessor;
return getCurrentContext().classProcessor;
}
public static PoolInterceptor getPoolInterceptor() {
return currentContext.poolInterceptor;
return getCurrentContext().poolInterceptor;
}
public static ImportCollector getImportCollector() {
return currentContext.importCollector;
return getCurrentContext().importCollector;
}
public static VarProcessor getVarProcessor() {
return currentContext.varProcessor;
return getCurrentContext().varProcessor;
}
public static CounterContainer getCounterContainer() {
return currentContext.counterContainer;
return getCurrentContext().counterContainer;
}
public static BytecodeSourceMapper getBytecodeSourceMapper() {
return currentContext.bytecodeSourceMapper;
return getCurrentContext().bytecodeSourceMapper;
}
}

@ -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-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.java.decompiler.main.ClassesProcessor.ClassNode;
@ -14,57 +12,83 @@ import org.jetbrains.java.decompiler.struct.StructContext;
import org.jetbrains.java.decompiler.struct.lazy.LazyLoader;
import org.jetbrains.java.decompiler.util.TextBuffer;
import java.io.File;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class Fernflower implements IDecompiledData {
private final StructContext structContext;
private final ClassesProcessor classProcessor;
private IIdentifierRenamer helper;
private IdentifierConverter converter;
private final IIdentifierRenamer helper;
private final IdentifierConverter converter;
public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> customProperties, IFernflowerLogger logger) {
Map<String, Object> properties = new HashMap<>(IFernflowerPreferences.DEFAULTS);
if (customProperties != null) {
properties.putAll(customProperties);
}
String level = (String)properties.get(IFernflowerPreferences.LOG_LEVEL);
if (level != null) {
try {
logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.US)));
}
catch (IllegalArgumentException ignore) { }
}
public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> options, IFernflowerLogger logger) {
structContext = new StructContext(saver, this, new LazyLoader(provider));
classProcessor = new ClassesProcessor(structContext);
PoolInterceptor interceptor = null;
Object rename = options.get(IFernflowerPreferences.RENAME_ENTITIES);
if ("1".equals(rename) || rename == null && "1".equals(IFernflowerPreferences.DEFAULTS.get(IFernflowerPreferences.RENAME_ENTITIES))) {
helper = loadHelper((String)options.get(IFernflowerPreferences.USER_RENAMER_CLASS));
if ("1".equals(properties.get(IFernflowerPreferences.RENAME_ENTITIES))) {
helper = loadHelper((String)properties.get(IFernflowerPreferences.USER_RENAMER_CLASS), logger);
interceptor = new PoolInterceptor();
converter = new IdentifierConverter(structContext, helper, interceptor);
}
DecompilerContext.initContext(options, logger, structContext, classProcessor, interceptor);
}
public void decompileContext() {
if (converter != null) {
converter.rename();
else {
helper = null;
converter = null;
}
classProcessor.loadClasses(helper);
structContext.saveContext();
DecompilerContext context = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor);
DecompilerContext.setCurrentContext(context);
}
private static IIdentifierRenamer loadHelper(String className) {
private static IIdentifierRenamer loadHelper(String className, IFernflowerLogger logger) {
if (className != null) {
try {
Class<?> renamerClass = Fernflower.class.getClassLoader().loadClass(className);
return (IIdentifierRenamer) renamerClass.getDeclaredConstructor().newInstance();
}
catch (Exception ignored) { }
catch (Exception e) {
logger.writeMessage("Cannot load renamer '" + className + "'", IFernflowerLogger.Severity.WARN, e);
}
}
return new ConverterHelper();
}
public void clearContext() {
DecompilerContext.clearContext();
public void addSource(File source) {
structContext.addSpace(source, true);
}
public void addLibrary(File library) {
structContext.addSpace(library, false);
}
public StructContext getStructContext() {
return structContext;
public void decompileContext() {
if (converter != null) {
converter.rename();
}
classProcessor.loadClasses(helper);
structContext.saveContext();
}
public void clearContext() {
DecompilerContext.setCurrentContext(null);
}
@Override

@ -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-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.java.decompiler.main.Fernflower;
@ -11,22 +11,22 @@ import java.util.Map;
@SuppressWarnings("unused")
public class BaseDecompiler {
private final Fernflower fernflower;
private final Fernflower engine;
public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> options, IFernflowerLogger logger) {
fernflower = new Fernflower(provider, saver, options, logger);
engine = new Fernflower(provider, saver, options, logger);
}
public void addSpace(File file, boolean isOwn) {
fernflower.getStructContext().addSpace(file, isOwn);
public void addSource(File source) {
engine.addSource(source);
}
public void decompileContext() {
try {
fernflower.decompileContext();
engine.decompileContext();
}
finally {
fernflower.clearContext();
engine.clearContext();
}
}
}

@ -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-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.java.decompiler.main.DecompilerContext;
@ -27,8 +27,8 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
}
Map<String, Object> mapOptions = new HashMap<>();
List<File> lstSources = new ArrayList<>();
List<File> lstLibraries = new ArrayList<>();
List<File> sources = new ArrayList<>();
List<File> libraries = new ArrayList<>();
boolean isOption = true;
for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination
@ -49,15 +49,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
isOption = false;
if (arg.startsWith("-e=")) {
addPath(lstLibraries, arg.substring(3));
addPath(libraries, arg.substring(3));
}
else {
addPath(lstSources, arg);
addPath(sources, arg);
}
}
}
if (lstSources.isEmpty()) {
if (sources.isEmpty()) {
System.out.println("error: no sources given");
return;
}
@ -71,11 +71,11 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
PrintStreamLogger logger = new PrintStreamLogger(System.out);
ConsoleDecompiler decompiler = new ConsoleDecompiler(destination, mapOptions, logger);
for (File source : lstSources) {
decompiler.addSpace(source, true);
for (File source : sources) {
decompiler.addSource(source);
}
for (File library : lstLibraries) {
decompiler.addSpace(library, false);
for (File library : libraries) {
decompiler.addLibrary(library);
}
decompiler.decompileContext();
@ -97,25 +97,29 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
// *******************************************************************
private final File root;
private final Fernflower fernflower;
private final Fernflower engine;
private final Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<>();
private final Map<String, Set<String>> mapArchiveEntries = new HashMap<>();
protected ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) {
root = destination;
fernflower = new Fernflower(this, this, options, logger);
engine = new Fernflower(this, this, options, logger);
}
public void addSpace(File file, boolean isOwn) {
fernflower.getStructContext().addSpace(file, isOwn);
public void addSource(File source) {
engine.addSource(source);
}
public void addLibrary(File library) {
engine.addLibrary(library);
}
public void decompileContext() {
try {
fernflower.decompileContext();
engine.decompileContext();
}
finally {
fernflower.clearContext();
engine.clearContext();
}
}

@ -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-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.rels;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -62,7 +62,7 @@ public class ClassWrapper {
root = MethodProcessorRunnable.codeToJava(mt, md, varProc);
}
else {
MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, md, varProc);
MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, md, varProc, DecompilerContext.getCurrentContext());
Thread mtThread = new Thread(mtProc, "Java decompiler");
long stopAt = System.currentTimeMillis() + maxSec * 1000;

@ -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-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.rels;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -25,15 +25,20 @@ public class MethodProcessorRunnable implements Runnable {
private final StructMethod method;
private final MethodDescriptor methodDescriptor;
private final VarProcessor varProc;
private final DecompilerContext parentContext;
private volatile RootStatement root;
private volatile Throwable error;
private volatile boolean finished = false;
public MethodProcessorRunnable(StructMethod method, MethodDescriptor methodDescriptor, VarProcessor varProc) {
public MethodProcessorRunnable(StructMethod method,
MethodDescriptor methodDescriptor,
VarProcessor varProc,
DecompilerContext parentContext) {
this.method = method;
this.methodDescriptor = methodDescriptor;
this.varProc = varProc;
this.parentContext = parentContext;
}
@Override
@ -42,6 +47,7 @@ public class MethodProcessorRunnable implements Runnable {
root = null;
try {
DecompilerContext.setCurrentContext(parentContext);
root = codeToJava(method, methodDescriptor, varProc);
}
catch (ThreadDeath ex) {
@ -50,6 +56,9 @@ public class MethodProcessorRunnable implements Runnable {
catch (Throwable ex) {
error = ex;
}
finally {
DecompilerContext.setCurrentContext(null);
}
finished = true;
synchronized (lock) {

@ -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-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;
import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler;
@ -36,7 +36,7 @@ public class BulkDecompilationTest {
unpack(new File(fixture.getTestDataDir(), "bulk.jar"), classes);
ConsoleDecompiler decompiler = fixture.getDecompiler();
decompiler.addSpace(classes, true);
decompiler.addSource(classes);
decompiler.decompileContext();
assertFilesEqual(new File(fixture.getTestDataDir(), "bulk"), fixture.getTargetDir());
@ -55,7 +55,7 @@ public class BulkDecompilationTest {
private void doTestJar(String name) {
ConsoleDecompiler decompiler = fixture.getDecompiler();
String jarName = name + ".jar";
decompiler.addSpace(new File(fixture.getTestDataDir(), jarName), true);
decompiler.addSource(new File(fixture.getTestDataDir(), jarName));
decompiler.decompileContext();
File unpacked = new File(fixture.getTempDir(), "unpacked");

@ -1,6 +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-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;
import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler;
@ -125,14 +123,14 @@ public class SingleClassesTest {
File classFile = new File(fixture.getTestDataDir(), "/classes/" + testFile + ".class");
assertTrue(classFile.isFile());
for (File file : collectClasses(classFile)) {
decompiler.addSpace(file, true);
decompiler.addSource(file);
}
for (String companionFile : companionFiles) {
File companionClassFile = new File(fixture.getTestDataDir(), "/classes/" + companionFile + ".class");
assertTrue(companionClassFile.isFile());
for (File file : collectClasses(companionClassFile)) {
decompiler.addSpace(file, true);
decompiler.addSource(file);
}
}

Loading…
Cancel
Save