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

master
Roman Shevchenko 7 years ago
parent bc55d8d9ec
commit 429b667031
  1. 74
      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; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper; 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.modules.renamer.PoolInterceptor;
import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.StructContext;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; 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_CLASS_NODE = "CURRENT_CLASS_NODE";
public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER"; public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER";
private static volatile DecompilerContext currentContext = null;
private final Map<String, Object> properties; private final Map<String, Object> properties;
private final IFernflowerLogger logger; private final IFernflowerLogger logger;
private final StructContext structContext; private final StructContext structContext;
@ -33,11 +29,16 @@ public class DecompilerContext {
private CounterContainer counterContainer; private CounterContainer counterContainer;
private BytecodeSourceMapper bytecodeSourceMapper; private BytecodeSourceMapper bytecodeSourceMapper;
private DecompilerContext(Map<String, Object> properties, public DecompilerContext(Map<String, Object> properties,
IFernflowerLogger logger, IFernflowerLogger logger,
StructContext structContext, StructContext structContext,
ClassesProcessor classProcessor, ClassesProcessor classProcessor,
PoolInterceptor interceptor) { PoolInterceptor interceptor) {
Objects.requireNonNull(properties);
Objects.requireNonNull(logger);
Objects.requireNonNull(structContext);
Objects.requireNonNull(classProcessor);
this.properties = properties; this.properties = properties;
this.logger = logger; this.logger = logger;
this.structContext = structContext; this.structContext = structContext;
@ -50,48 +51,31 @@ public class DecompilerContext {
// context setup and update // context setup and update
// ***************************************************************************** // *****************************************************************************
public static void initContext(Map<String, Object> customProperties, private static final ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<>();
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) { }
}
currentContext = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor); public static DecompilerContext getCurrentContext() {
return currentContext.get();
} }
public static void clearContext() { public static void setCurrentContext(DecompilerContext context) {
currentContext = null; currentContext.set(context);
} }
public static void setProperty(String key, Object value) { public static void setProperty(String key, Object value) {
currentContext.properties.put(key, value); getCurrentContext().properties.put(key, value);
} }
public static void startClass(ImportCollector importCollector) { public static void startClass(ImportCollector importCollector) {
currentContext.importCollector = importCollector; DecompilerContext context = getCurrentContext();
currentContext.counterContainer = new CounterContainer(); context.importCollector = importCollector;
currentContext.bytecodeSourceMapper = new BytecodeSourceMapper(); context.counterContainer = new CounterContainer();
context.bytecodeSourceMapper = new BytecodeSourceMapper();
} }
public static void startMethod(VarProcessor varProcessor) { public static void startMethod(VarProcessor varProcessor) {
currentContext.varProcessor = varProcessor; DecompilerContext context = getCurrentContext();
currentContext.counterContainer = new CounterContainer(); context.varProcessor = varProcessor;
context.counterContainer = new CounterContainer();
} }
// ***************************************************************************** // *****************************************************************************
@ -99,7 +83,7 @@ public class DecompilerContext {
// ***************************************************************************** // *****************************************************************************
public static Object getProperty(String key) { public static Object getProperty(String key) {
return currentContext.properties.get(key); return getCurrentContext().properties.get(key);
} }
public static boolean getOption(String key) { public static boolean getOption(String key) {
@ -112,34 +96,34 @@ public class DecompilerContext {
} }
public static IFernflowerLogger getLogger() { public static IFernflowerLogger getLogger() {
return currentContext.logger; return getCurrentContext().logger;
} }
public static StructContext getStructContext() { public static StructContext getStructContext() {
return currentContext.structContext; return getCurrentContext().structContext;
} }
public static ClassesProcessor getClassProcessor() { public static ClassesProcessor getClassProcessor() {
return currentContext.classProcessor; return getCurrentContext().classProcessor;
} }
public static PoolInterceptor getPoolInterceptor() { public static PoolInterceptor getPoolInterceptor() {
return currentContext.poolInterceptor; return getCurrentContext().poolInterceptor;
} }
public static ImportCollector getImportCollector() { public static ImportCollector getImportCollector() {
return currentContext.importCollector; return getCurrentContext().importCollector;
} }
public static VarProcessor getVarProcessor() { public static VarProcessor getVarProcessor() {
return currentContext.varProcessor; return getCurrentContext().varProcessor;
} }
public static CounterContainer getCounterContainer() { public static CounterContainer getCounterContainer() {
return currentContext.counterContainer; return getCurrentContext().counterContainer;
} }
public static BytecodeSourceMapper getBytecodeSourceMapper() { public static BytecodeSourceMapper getBytecodeSourceMapper() {
return currentContext.bytecodeSourceMapper; return getCurrentContext().bytecodeSourceMapper;
} }
} }

@ -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-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.main; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; 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.struct.lazy.LazyLoader;
import org.jetbrains.java.decompiler.util.TextBuffer; import org.jetbrains.java.decompiler.util.TextBuffer;
import java.io.File;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map; import java.util.Map;
public class Fernflower implements IDecompiledData { public class Fernflower implements IDecompiledData {
private final StructContext structContext; private final StructContext structContext;
private final ClassesProcessor classProcessor; private final ClassesProcessor classProcessor;
private IIdentifierRenamer helper; private final IIdentifierRenamer helper;
private IdentifierConverter converter; 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)); structContext = new StructContext(saver, this, new LazyLoader(provider));
classProcessor = new ClassesProcessor(structContext); classProcessor = new ClassesProcessor(structContext);
PoolInterceptor interceptor = null; PoolInterceptor interceptor = null;
Object rename = options.get(IFernflowerPreferences.RENAME_ENTITIES); if ("1".equals(properties.get(IFernflowerPreferences.RENAME_ENTITIES))) {
if ("1".equals(rename) || rename == null && "1".equals(IFernflowerPreferences.DEFAULTS.get(IFernflowerPreferences.RENAME_ENTITIES))) { helper = loadHelper((String)properties.get(IFernflowerPreferences.USER_RENAMER_CLASS), logger);
helper = loadHelper((String)options.get(IFernflowerPreferences.USER_RENAMER_CLASS));
interceptor = new PoolInterceptor(); interceptor = new PoolInterceptor();
converter = new IdentifierConverter(structContext, helper, interceptor); converter = new IdentifierConverter(structContext, helper, interceptor);
} }
else {
DecompilerContext.initContext(options, logger, structContext, classProcessor, interceptor); helper = null;
} converter = null;
public void decompileContext() {
if (converter != null) {
converter.rename();
} }
classProcessor.loadClasses(helper); DecompilerContext context = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor);
DecompilerContext.setCurrentContext(context);
structContext.saveContext();
} }
private static IIdentifierRenamer loadHelper(String className) { private static IIdentifierRenamer loadHelper(String className, IFernflowerLogger logger) {
if (className != null) { if (className != null) {
try { try {
Class<?> renamerClass = Fernflower.class.getClassLoader().loadClass(className); Class<?> renamerClass = Fernflower.class.getClassLoader().loadClass(className);
return (IIdentifierRenamer) renamerClass.getDeclaredConstructor().newInstance(); return (IIdentifierRenamer) renamerClass.getDeclaredConstructor().newInstance();
} }
catch (Exception ignored) { } catch (Exception e) {
logger.writeMessage("Cannot load renamer '" + className + "'", IFernflowerLogger.Severity.WARN, e);
}
} }
return new ConverterHelper(); return new ConverterHelper();
} }
public void clearContext() { public void addSource(File source) {
DecompilerContext.clearContext(); structContext.addSpace(source, true);
}
public void addLibrary(File library) {
structContext.addSpace(library, false);
} }
public StructContext getStructContext() { public void decompileContext() {
return structContext; if (converter != null) {
converter.rename();
}
classProcessor.loadClasses(helper);
structContext.saveContext();
}
public void clearContext() {
DecompilerContext.setCurrentContext(null);
} }
@Override @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; package org.jetbrains.java.decompiler.main.decompiler;
import org.jetbrains.java.decompiler.main.Fernflower; import org.jetbrains.java.decompiler.main.Fernflower;
@ -11,22 +11,22 @@ import java.util.Map;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class BaseDecompiler { public class BaseDecompiler {
private final Fernflower fernflower; private final Fernflower engine;
public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> options, IFernflowerLogger logger) { 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) { public void addSource(File source) {
fernflower.getStructContext().addSpace(file, isOwn); engine.addSource(source);
} }
public void decompileContext() { public void decompileContext() {
try { try {
fernflower.decompileContext(); engine.decompileContext();
} }
finally { 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; package org.jetbrains.java.decompiler.main.decompiler;
import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.DecompilerContext;
@ -27,8 +27,8 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
} }
Map<String, Object> mapOptions = new HashMap<>(); Map<String, Object> mapOptions = new HashMap<>();
List<File> lstSources = new ArrayList<>(); List<File> sources = new ArrayList<>();
List<File> lstLibraries = new ArrayList<>(); List<File> libraries = new ArrayList<>();
boolean isOption = true; boolean isOption = true;
for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination
@ -49,15 +49,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
isOption = false; isOption = false;
if (arg.startsWith("-e=")) { if (arg.startsWith("-e=")) {
addPath(lstLibraries, arg.substring(3)); addPath(libraries, arg.substring(3));
} }
else { else {
addPath(lstSources, arg); addPath(sources, arg);
} }
} }
} }
if (lstSources.isEmpty()) { if (sources.isEmpty()) {
System.out.println("error: no sources given"); System.out.println("error: no sources given");
return; return;
} }
@ -71,11 +71,11 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
PrintStreamLogger logger = new PrintStreamLogger(System.out); PrintStreamLogger logger = new PrintStreamLogger(System.out);
ConsoleDecompiler decompiler = new ConsoleDecompiler(destination, mapOptions, logger); ConsoleDecompiler decompiler = new ConsoleDecompiler(destination, mapOptions, logger);
for (File source : lstSources) { for (File source : sources) {
decompiler.addSpace(source, true); decompiler.addSource(source);
} }
for (File library : lstLibraries) { for (File library : libraries) {
decompiler.addSpace(library, false); decompiler.addLibrary(library);
} }
decompiler.decompileContext(); decompiler.decompileContext();
@ -97,25 +97,29 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
// ******************************************************************* // *******************************************************************
private final File root; private final File root;
private final Fernflower fernflower; private final Fernflower engine;
private final Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<>(); private final Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<>();
private final Map<String, Set<String>> mapArchiveEntries = new HashMap<>(); private final Map<String, Set<String>> mapArchiveEntries = new HashMap<>();
protected ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) { protected ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) {
root = destination; root = destination;
fernflower = new Fernflower(this, this, options, logger); engine = new Fernflower(this, this, options, logger);
} }
public void addSpace(File file, boolean isOwn) { public void addSource(File source) {
fernflower.getStructContext().addSpace(file, isOwn); engine.addSource(source);
}
public void addLibrary(File library) {
engine.addLibrary(library);
} }
public void decompileContext() { public void decompileContext() {
try { try {
fernflower.decompileContext(); engine.decompileContext();
} }
finally { 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; package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -62,7 +62,7 @@ public class ClassWrapper {
root = MethodProcessorRunnable.codeToJava(mt, md, varProc); root = MethodProcessorRunnable.codeToJava(mt, md, varProc);
} }
else { else {
MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, md, varProc); MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, md, varProc, DecompilerContext.getCurrentContext());
Thread mtThread = new Thread(mtProc, "Java decompiler"); Thread mtThread = new Thread(mtProc, "Java decompiler");
long stopAt = System.currentTimeMillis() + maxSec * 1000; 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; package org.jetbrains.java.decompiler.main.rels;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -25,15 +25,20 @@ public class MethodProcessorRunnable implements Runnable {
private final StructMethod method; private final StructMethod method;
private final MethodDescriptor methodDescriptor; private final MethodDescriptor methodDescriptor;
private final VarProcessor varProc; private final VarProcessor varProc;
private final DecompilerContext parentContext;
private volatile RootStatement root; private volatile RootStatement root;
private volatile Throwable error; private volatile Throwable error;
private volatile boolean finished = false; 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.method = method;
this.methodDescriptor = methodDescriptor; this.methodDescriptor = methodDescriptor;
this.varProc = varProc; this.varProc = varProc;
this.parentContext = parentContext;
} }
@Override @Override
@ -42,6 +47,7 @@ public class MethodProcessorRunnable implements Runnable {
root = null; root = null;
try { try {
DecompilerContext.setCurrentContext(parentContext);
root = codeToJava(method, methodDescriptor, varProc); root = codeToJava(method, methodDescriptor, varProc);
} }
catch (ThreadDeath ex) { catch (ThreadDeath ex) {
@ -50,6 +56,9 @@ public class MethodProcessorRunnable implements Runnable {
catch (Throwable ex) { catch (Throwable ex) {
error = ex; error = ex;
} }
finally {
DecompilerContext.setCurrentContext(null);
}
finished = true; finished = true;
synchronized (lock) { 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; package org.jetbrains.java.decompiler;
import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler;
@ -36,7 +36,7 @@ public class BulkDecompilationTest {
unpack(new File(fixture.getTestDataDir(), "bulk.jar"), classes); unpack(new File(fixture.getTestDataDir(), "bulk.jar"), classes);
ConsoleDecompiler decompiler = fixture.getDecompiler(); ConsoleDecompiler decompiler = fixture.getDecompiler();
decompiler.addSpace(classes, true); decompiler.addSource(classes);
decompiler.decompileContext(); decompiler.decompileContext();
assertFilesEqual(new File(fixture.getTestDataDir(), "bulk"), fixture.getTargetDir()); assertFilesEqual(new File(fixture.getTestDataDir(), "bulk"), fixture.getTargetDir());
@ -55,7 +55,7 @@ public class BulkDecompilationTest {
private void doTestJar(String name) { private void doTestJar(String name) {
ConsoleDecompiler decompiler = fixture.getDecompiler(); ConsoleDecompiler decompiler = fixture.getDecompiler();
String jarName = name + ".jar"; String jarName = name + ".jar";
decompiler.addSpace(new File(fixture.getTestDataDir(), jarName), true); decompiler.addSource(new File(fixture.getTestDataDir(), jarName));
decompiler.decompileContext(); decompiler.decompileContext();
File unpacked = new File(fixture.getTempDir(), "unpacked"); 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; package org.jetbrains.java.decompiler;
import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler;
@ -125,14 +123,14 @@ public class SingleClassesTest {
File classFile = new File(fixture.getTestDataDir(), "/classes/" + testFile + ".class"); File classFile = new File(fixture.getTestDataDir(), "/classes/" + testFile + ".class");
assertTrue(classFile.isFile()); assertTrue(classFile.isFile());
for (File file : collectClasses(classFile)) { for (File file : collectClasses(classFile)) {
decompiler.addSpace(file, true); decompiler.addSource(file);
} }
for (String companionFile : companionFiles) { for (String companionFile : companionFiles) {
File companionClassFile = new File(fixture.getTestDataDir(), "/classes/" + companionFile + ".class"); File companionClassFile = new File(fixture.getTestDataDir(), "/classes/" + companionFile + ".class");
assertTrue(companionClassFile.isFile()); assertTrue(companionClassFile.isFile());
for (File file : collectClasses(companionClassFile)) { for (File file : collectClasses(companionClassFile)) {
decompiler.addSpace(file, true); decompiler.addSource(file);
} }
} }

Loading…
Cancel
Save