double x = 0.2F;
double x = 0.20000000298023224D;
double x = 0.2F;
Fernflower isn't able to resugar the float->double inaccuracy correctly.
public void test(String directionStr) {
String axis = switch (directionStr.toLowerCase()) {
case "north":
case "south":
yield "y";
case "east":
case "west":
yield "x";
case "up":
case "down":
yield "z";
default:
throw new IllegalStateException("Unexpected value: " + directionStr);
};
System.out.println(axis);
}
public void test(String directionStr) {
String var10000;
switch (directionStr.toLowerCase()) {
case "north":
case "south":
var10000 = "y";
break;
case "east":
case "west":
var10000 = "x";
break;
case "up":
case "down":
var10000 = "z";
break;
default:
throw new IllegalStateException("Unexpected value: " + directionStr);
}
String axis = var10000;
System.out.println(axis);
}
public void test(String directionStr) {
String var3 = directionStr.toLowerCase();
String axis = switch(var3) {
case "north", "south" -> "y";
case "east", "west" -> "x";
case "up", "down" -> "z";
default -> throw new IllegalStateException("Unexpected value: " + directionStr);
};
System.out.println(axis);
}
Fernflower is unable to resugar the switch expression at all, and instead makes a standard switch statement.
while (i > 10) {
i++;
if (i == 15) {
break;
}
System.out.println(0);
}
while(true) {
if (i > 10) {
++i;
if (i != 15) {
System.out.println(0);
continue;
}
}
return;
}
while(i > 10 && ++i != 15) {
System.out.println(0);
}
Fernflower extends the infinite loop to the return, which produces much more complex code. While Quiltflower is a bit aggressive on the loop merging, it's also significantly more concise.
public void test() {
while (true) {
}
}
public void test() {
// $FF: Couldn't be decompiled
}
public void test() {
while(true) {
}
}
Fernflower is entirely unable to decompile the empty loop, which is due to a minor bug in the goto instruction resolver.
try (Scanner scanner = new Scanner(file)) {
scanner.next();
}
Scanner scanner = new Scanner(file);
try {
scanner.next();
} catch (Throwable var6) {
try {
scanner.close();
} catch (Throwable var5) {
var6.addSuppressed(var5);
}
throw var6;
}
scanner.close();
try (Scanner scanner = new Scanner(file)) {
scanner.next();
}
Fernflower is not able to resugar the output, and is left to display the try-with-resources directly.
int[] array = new int[]{1, 2, 3, 4};
for (int i : array) {
System.out.println(i);
}
int[] array = new int[]{1, 2, 3, 4};
int[] var2 = array;
int var3 = array.length;
for(int var4 = 0; var4 < var3; ++var4) {
int i = var2[var4];
System.out.println(i);
}
int[] array = new int[]{1, 2, 3, 4};
for(int i : array) {
System.out.println(i);
}
Fernflower doesn't recognize the foreach loop and represents the iteration with a for loop instead.
public class Inner<T> {
public Class<? super T> get() {
return null;
}
}
public <T> Class<T> test(Inner<T> inner) {
Class<T> t = (Class<T>) inner.get();
return (Class<T>) inner.get();
}
public class Inner<T> {
public Inner() {
}
public Class<? super T> get() {
return null;
}
}
public <T> Class<T> test(Inner<T> inner) {
Class<T> t = inner.get();
return inner.get();
}
public class Inner<T> {
public Class<? super T> get() {
return null;
}
}
public <T> Class<T> test(Inner<T> inner) {
Class<T> t = (Class<T>)inner.get();
return (Class<T>)inner.get();
}
Fernflower doesn't cast the contravariant type to the regular type, which would fail to compile.
@Override
public void visitDependencies(TaskDependencyResolveContext context) {
Set<Object> mutableValues = this.getMutableValues();
if (!mutableValues.isEmpty() || !this.immutableValues.isEmpty()) {
Deque<Object> queue = new ArrayDeque(mutableValues.size() + this.immutableValues.size());
queue.addAll(this.immutableValues);
queue.addAll(mutableValues);
while(!queue.isEmpty()) {
Object dependency = queue.removeFirst();
if (dependency instanceof Buildable) {
context.add(dependency);
} else if (dependency instanceof Task) {
context.add(dependency);
} else if (dependency instanceof TaskDependency) {
context.add(dependency);
} else if (dependency instanceof ProviderInternal) {
ProviderInternal<?> provider = (ProviderInternal)dependency;
ValueSupplier.ValueProducer producer = provider.getProducer();
if (producer.isKnown()) {
producer.visitProducerTasks(context);
} else {
queue.addFirst(provider.get());
}
} else if (dependency instanceof TaskDependencyContainer) {
((TaskDependencyContainer)dependency).visitDependencies(context);
} else if (dependency instanceof Closure) {
Closure closure = (Closure)dependency;
Object closureResult = closure.call(context.getTask());
if (closureResult != null) {
queue.addFirst(closureResult);
}
} else if (dependency instanceof List) {
List<?> list = (List)dependency;
if (list instanceof RandomAccess) {
for(int i = list.size() - 1; i >= 0; --i) {
queue.addFirst(list.get(i));
}
} else {
ListIterator<?> iterator = list.listIterator(list.size());
while(iterator.hasPrevious()) {
Object item = iterator.previous();
queue.addFirst(item);
}
}
} else if (dependency instanceof Iterable && !(dependency instanceof Path)) {
Iterable<?> iterable = Cast.uncheckedNonnullCast(dependency);
addAllFirst(queue, Iterables.toArray(iterable, Object.class));
} else if (dependency instanceof Map) {
Map<?, ?> map = Cast.uncheckedNonnullCast(dependency);
addAllFirst(queue, map.values().toArray());
} else if (dependency instanceof Object[]) {
Object[] array = dependency;
addAllFirst(queue, array);
} else if (!(dependency instanceof Callable)) {
if (this.resolver == null || !(dependency instanceof CharSequence)) {
List<String> formats = new ArrayList();
if (this.resolver != null) {
formats.add("A String or CharSequence task name or path");
}
formats.add("A Task instance");
formats.add("A TaskReference instance");
formats.add("A Buildable instance");
formats.add("A TaskDependency instance");
formats.add("A Provider that represents a task output");
formats.add("A Provider instance that returns any of these types");
formats.add("A Closure instance that returns any of these types");
formats.add("A Callable instance that returns any of these types");
formats.add("An Iterable, Collection, Map or array instance that contains any of these types");
throw new UnsupportedNotationException(dependency, String.format("Cannot convert %s to a task.", dependency), null, formats);
}
context.add(this.resolver.resolveTask(dependency.toString()));
} else {
Callable<?> callable = Cast.uncheckedNonnullCast(dependency);
Object callableResult = GUtil.uncheckedCall(Cast.uncheckedNonnullCast(callable));
if (callableResult != null) {
queue.addFirst(callableResult);
}
}
}
}
}
..... yeah.
That'll likely be a separate gist, but yes definitely! Will do when I have time.