[java decompiler] restores thread-conscious context access (EA-116270)
This commit is contained in:
@@ -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);
|
||||
private static final ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<>();
|
||||
|
||||
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() {
|
||||
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,27 +12,69 @@ 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);
|
||||
}
|
||||
else {
|
||||
helper = null;
|
||||
converter = null;
|
||||
}
|
||||
|
||||
DecompilerContext.initContext(options, logger, structContext, classProcessor, interceptor);
|
||||
DecompilerContext context = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor);
|
||||
DecompilerContext.setCurrentContext(context);
|
||||
}
|
||||
|
||||
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 e) {
|
||||
logger.writeMessage("Cannot load renamer '" + className + "'", IFernflowerLogger.Severity.WARN, e);
|
||||
}
|
||||
}
|
||||
|
||||
return new ConverterHelper();
|
||||
}
|
||||
|
||||
public void addSource(File source) {
|
||||
structContext.addSpace(source, true);
|
||||
}
|
||||
|
||||
public void addLibrary(File library) {
|
||||
structContext.addSpace(library, false);
|
||||
}
|
||||
|
||||
public void decompileContext() {
|
||||
@@ -47,24 +87,8 @@ public class Fernflower implements IDecompiledData {
|
||||
structContext.saveContext();
|
||||
}
|
||||
|
||||
private static IIdentifierRenamer loadHelper(String className) {
|
||||
if (className != null) {
|
||||
try {
|
||||
Class<?> renamerClass = Fernflower.class.getClassLoader().loadClass(className);
|
||||
return (IIdentifierRenamer) renamerClass.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
catch (Exception ignored) { }
|
||||
}
|
||||
|
||||
return new ConverterHelper();
|
||||
}
|
||||
|
||||
public void clearContext() {
|
||||
DecompilerContext.clearContext();
|
||||
}
|
||||
|
||||
public StructContext getStructContext() {
|
||||
return structContext;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user