Skip to content

Commit

Permalink
Added Vineflower decompiler, refactored DecompilerResult and JvmDecom…
Browse files Browse the repository at this point in the history
…piler
  • Loading branch information
therathatter committed Jan 13, 2024
1 parent 40d2a1a commit 4e34d74
Show file tree
Hide file tree
Showing 30 changed files with 651 additions and 82 deletions.
2 changes: 2 additions & 0 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@ project.ext {
richtextfx = 'org.fxmisc.richtext:richtextfx:0.11.2'

treemapfx = 'software.coley:treemap-fx:1.1.0'

vineflower = 'org.vineflower:vineflower:1.9.3'
}
1 change: 1 addition & 0 deletions recaf-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ dependencies {
api regex
api jasm_core
api jasm_composistion_jvm
api vineflower
}

// Force generation of gversion data class when the version information is not up-to-date
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class BasicConfigContainer implements ConfigContainer {
private final String group;
private final String id;


/**
* @param group
* Container group.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,19 @@ public AbstractDecompiler(@Nonnull String name, @Nonnull String version, @Nonnul
this.config = config;
}

@Nonnull
@Override
public String getName() {
return name;
}

@Nonnull
@Override
public String getVersion() {
return version;
}

@Nonnull
@Override
public DecompilerConfig getConfig() {
return config;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package software.coley.recaf.services.decompile;

import jakarta.annotation.Nonnull;
import org.objectweb.asm.ClassReader;
import software.coley.recaf.info.JvmClassInfo;
import software.coley.recaf.info.properties.builtin.CachedDecompileProperty;
import software.coley.recaf.workspace.model.Workspace;
Expand Down Expand Up @@ -33,13 +34,14 @@ public void addJvmInputFilter(@Nonnull JvmInputFilter filter) {
inputFilters.add(filter);
}

@Nonnull
@Override
public final DecompileResult decompile(@Nonnull Workspace workspace, @Nonnull JvmClassInfo classInfo) {
// Check for cached result, returning the cached result if found
// and only if the current config matches the one that yielded the cached result.
DecompileResult cachedResult = CachedDecompileProperty.get(classInfo, this);
if (cachedResult != null) {
if (cachedResult.getConfigHash() == getConfig().getConfigHash())
if (cachedResult.getConfigHash() == getConfig().getHash())
return cachedResult;

// Config changed, void the cache.
Expand All @@ -50,15 +52,19 @@ public final DecompileResult decompile(@Nonnull Workspace workspace, @Nonnull Jv
byte[] bytecode = classInfo.getBytecode();
for (JvmInputFilter filter : inputFilters)
bytecode = filter.filter(bytecode);
JvmClassInfo filteredBytecode = classInfo.toJvmClassBuilder().adaptFrom(new ClassReader(bytecode)).build();

// Pass to implementation.
DecompileResult result = decompile(workspace, classInfo.getName(), bytecode);
DecompileResult result = decompileInternal(workspace, filteredBytecode);

// Cache result.
CachedDecompileProperty.set(classInfo, this, result);
return result;
}

@Nonnull
protected abstract DecompileResult decompileInternal(@Nonnull Workspace workspace, @Nonnull JvmClassInfo classInfo);

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package software.coley.recaf.services.decompile;

import jakarta.annotation.Nonnull;
import software.coley.recaf.config.BasicConfigContainer;

/**
* Base class for fields needed by all decompiler configurations
*
* @author therathatter
*/
public class BaseDecompilerConfig extends BasicConfigContainer implements DecompilerConfig {
private int hash = 0;

/**
* @param group Container group.
* @param id Container ID.
*/
public BaseDecompilerConfig(@Nonnull String group, @Nonnull String id) {
super(group, id);
}

@Override
public int getHash() {
return hash;
}

@Override
public void setHash(int hash) {
this.hash = hash;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import software.coley.recaf.info.properties.builtin.CachedDecompileProperty;
import software.coley.recaf.util.StringUtil;

import java.util.Objects;

Expand All @@ -20,19 +21,52 @@ public class DecompileResult {
/**
* @param text
* Decompiled text.
* @param configHash
* Value of {@link DecompilerConfig#getHash()} of associated decompiler.
* Used to determine if cached value in {@link CachedDecompileProperty} is up-to-date with current config.
*/
public DecompileResult(@Nonnull String text, int configHash) {
this.text = text;
this.type = ResultType.SUCCESS;
this.configHash = configHash;
this.exception = null;
}

/**
* @param exception
* Failure reason.
* @param type
* Result type.
* @param configHash
* Value of {@link DecompilerConfig#getConfigHash()} of associated decompiler.
* Value of {@link DecompilerConfig#getHash()} of associated decompiler.
* Used to determine if cached value in {@link CachedDecompileProperty} is up-to-date with current config.
*/
public DecompileResult(@Nullable String text, @Nullable Throwable exception, @Nonnull ResultType type, int configHash) {
this.text = text;
public DecompileResult(@Nonnull Throwable exception, int configHash) {
this.text = "// " + StringUtil.traceToString(exception).replace("\n", "\n// ");
this.type = ResultType.FAILURE;
this.configHash = configHash;

Check warning on line 45 in recaf-core/src/main/java/software/coley/recaf/services/decompile/DecompileResult.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/decompile/DecompileResult.java#L42-L45

Added lines #L42 - L45 were not covered by tests
this.exception = exception;
this.type = type;
}

Check warning on line 47 in recaf-core/src/main/java/software/coley/recaf/services/decompile/DecompileResult.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/decompile/DecompileResult.java#L47

Added line #L47 was not covered by tests

/**
* @param configHash
* Value of {@link DecompilerConfig#getHash()} of associated decompiler.
* Used to determine if cached value in {@link CachedDecompileProperty} is up-to-date with current config.
*/
public DecompileResult(int configHash) {
this.text = null;
this.type = ResultType.SKIPPED;

Check warning on line 56 in recaf-core/src/main/java/software/coley/recaf/services/decompile/DecompileResult.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/decompile/DecompileResult.java#L54-L56

Added lines #L54 - L56 were not covered by tests
this.configHash = configHash;
this.exception = null;
}

Check warning on line 59 in recaf-core/src/main/java/software/coley/recaf/services/decompile/DecompileResult.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/decompile/DecompileResult.java#L58-L59

Added lines #L58 - L59 were not covered by tests

/**
* @param text
* Decompiled text.
*/
public DecompileResult(@Nonnull String text) {
this.text = text;
this.type = ResultType.SKIPPED;
this.configHash = 0;
this.exception = null;

Check warning on line 69 in recaf-core/src/main/java/software/coley/recaf/services/decompile/DecompileResult.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/decompile/DecompileResult.java#L65-L69

Added lines #L65 - L69 were not covered by tests
}

/**
Expand Down Expand Up @@ -62,7 +96,7 @@ public ResultType getType() {
}

/**
* @return Value of {@link DecompilerConfig#getConfigHash()} of associated decompiler.
* @return Value of {@link DecompilerConfig#getHash()} of associated decompiler.
* Used to determine if cached value in {@link CachedDecompileProperty} is up-to-date with current config.
*/
public int getConfigHash() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package software.coley.recaf.services.decompile;

import jakarta.annotation.Nonnull;
import software.coley.recaf.info.properties.builtin.CachedDecompileProperty;

/**
Expand All @@ -15,15 +16,18 @@ public interface Decompiler {
/**
* @return Decompiler name.
*/
@Nonnull
String getName();

/**
* @return Decompiler version.
*/
@Nonnull
String getVersion();

/**
* @return Decompiler config.
*/
@Nonnull
DecompilerConfig getConfig();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* <br>
* Tracks the hash of all contained {@link ConfigValue} so that when decompilers check for
* {@link CachedDecompileProperty} they can see if the {@link DecompileResult#getConfigHash()}
* matches the current one of {@link #getConfigHash()}.
* matches the current one of {@link #getHash()}.
*
* @author Matt Coley
*/
Expand All @@ -24,20 +24,20 @@ public interface DecompilerConfig extends ConfigContainer {
*
* @return Unique hash of all contained {@link ConfigValue}.
*/
int getConfigHash();
int getHash();

/**
* @param hash
* New hash value.
*
* @see #getConfigHash() For more detail.
* @see #getHash() For more detail.
*/
void setConfigHash(int hash);
void setHash(int hash);

/**
* Called by implementations after they add all their values to the container.
*
* @see #getConfigHash() For more detail.
* @see #getHash() For more detail.
*/
default void registerConfigValuesHashUpdates() {
// Initial value computation.
Expand All @@ -53,6 +53,6 @@ private void update() {
.map(ConfigValue::getValue)
.mapToInt(value -> value == null ? 0 : value.hashCode())
.reduce((a, b) -> (31 * a) + b)
.ifPresent(this::setConfigHash);
.ifPresent(this::setHash);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ public DecompilerManager(@Nonnull DecompilerManagerConfig config,
*
* @return Future of decompilation result.
*/
public CompletableFuture<DecompileResult> decompile(Workspace workspace, JvmClassInfo classInfo) {
@Nonnull
public CompletableFuture<DecompileResult> decompile(@Nonnull Workspace workspace, @Nonnull JvmClassInfo classInfo) {
return decompile(getTargetJvmDecompiler(), workspace, classInfo);
}

Expand All @@ -113,7 +114,8 @@ public CompletableFuture<DecompileResult> decompile(Workspace workspace, JvmClas
*
* @return Future of decompilation result.
*/
public CompletableFuture<DecompileResult> decompile(JvmDecompiler decompiler, Workspace workspace, JvmClassInfo classInfo) {
@Nonnull
public CompletableFuture<DecompileResult> decompile(@Nonnull JvmDecompiler decompiler, @Nonnull Workspace workspace, @Nonnull JvmClassInfo classInfo) {
return CompletableFuture.supplyAsync(() -> decompiler.decompile(workspace, classInfo), decompileThreadPool);
}

Expand All @@ -127,7 +129,8 @@ public CompletableFuture<DecompileResult> decompile(JvmDecompiler decompiler, Wo
*
* @return Future of decompilation result.
*/
public CompletableFuture<DecompileResult> decompile(Workspace workspace, AndroidClassInfo classInfo) {
@Nonnull
public CompletableFuture<DecompileResult> decompile(@Nonnull Workspace workspace, @Nonnull AndroidClassInfo classInfo) {
return decompile(getTargetAndroidDecompiler(), workspace, classInfo);
}

Expand All @@ -143,20 +146,23 @@ public CompletableFuture<DecompileResult> decompile(Workspace workspace, Android
*
* @return Future of decompilation result.
*/
public CompletableFuture<DecompileResult> decompile(AndroidDecompiler decompiler, Workspace workspace, AndroidClassInfo classInfo) {
@Nonnull
public CompletableFuture<DecompileResult> decompile(@Nonnull AndroidDecompiler decompiler, @Nonnull Workspace workspace, @Nonnull AndroidClassInfo classInfo) {
return CompletableFuture.supplyAsync(() -> decompiler.decompile(workspace, classInfo), decompileThreadPool);
}

/**
* @return Preferred JVM decompiler.
*/
@Nonnull
public JvmDecompiler getTargetJvmDecompiler() {
return targetJvmDecompiler.getValue();
}

/**
* @return Preferred Android decompiler.
*/
@Nonnull
public AndroidDecompiler getTargetAndroidDecompiler() {
return targetAndroidDecompiler.getValue();
}
Expand All @@ -165,15 +171,15 @@ public AndroidDecompiler getTargetAndroidDecompiler() {
* @param decompiler
* JVM decompiler to add.
*/
public void register(JvmDecompiler decompiler) {
public void register(@Nonnull JvmDecompiler decompiler) {
jvmDecompilers.put(decompiler.getName(), decompiler);
}

/**
* @param decompiler
* Android decompiler to add.
*/
public void register(AndroidDecompiler decompiler) {
public void register(@Nonnull AndroidDecompiler decompiler) {
androidDecompilers.put(decompiler.getName(), decompiler);
}

Expand All @@ -184,7 +190,7 @@ public void register(AndroidDecompiler decompiler) {
* @return Decompiler instance, or {@code null} if nothing by the ID was found.
*/
@Nullable
public JvmDecompiler getJvmDecompiler(String name) {
public JvmDecompiler getJvmDecompiler(@Nonnull String name) {
return jvmDecompilers.get(name);
}

Expand All @@ -195,20 +201,22 @@ public JvmDecompiler getJvmDecompiler(String name) {
* @return Decompiler instance, or {@code null} if nothing by the ID was found.
*/
@Nullable
public AndroidDecompiler getAndroidDecompiler(String name) {
public AndroidDecompiler getAndroidDecompiler(@Nonnull String name) {
return androidDecompilers.get(name);
}

/**
* @return Available JVM class decompilers.
*/
@Nonnull
public Collection<JvmDecompiler> getJvmDecompilers() {
return jvmDecompilers.values();
}

/**
* @return Available android class decompilers.
*/
@Nonnull
public Collection<AndroidDecompiler> getAndroidDecompilers() {
return androidDecompilers.values();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,6 @@ public interface JvmDecompiler extends Decompiler {
*
* @return Decompilation result.
*/
@Nonnull
DecompileResult decompile(@Nonnull Workspace workspace, @Nonnull JvmClassInfo classInfo);

/**
* @param workspace
* Workspace to pull data from.
* @param name
* Class name.
* @param bytecode
* Class bytecode.
*
* @return Decompilation result.
*/
DecompileResult decompile(@Nonnull Workspace workspace, @Nonnull String name, @Nonnull byte[] bytecode);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ public static NoopAndroidDecompiler getInstance() {

@Override
public DecompileResult decompile(@Nonnull Workspace workspace, @Nonnull AndroidClassInfo classInfo) {
return new DecompileResult(null, null, DecompileResult.ResultType.SKIPPED, getConfig().getConfigHash());
return new DecompileResult(getConfig().getHash());
}

Check warning on line 29 in recaf-core/src/main/java/software/coley/recaf/services/decompile/NoopAndroidDecompiler.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/decompile/NoopAndroidDecompiler.java#L28-L29

Added lines #L28 - L29 were not covered by tests
}
Loading

0 comments on commit 4e34d74

Please sign in to comment.