Wednesday, June 8, 2022

Updating `dx.jar` library to fix `Dx unsupported class file version 52.0`

If you're still developing with Eclipse & Google ADT plugin you may encountered this error when trying to use Java libraries compiled with JDK 1.8:

[2022-05-11 08:55:53 - demo] Using default Build Tools revision 25.0.3
[2022-05-11 08:55:53 - demo] Starting full Post Compiler.
[2022-05-11 08:55:53 - demo] Dx Using Pre-Dexed androidx-versionedparcelable-2b1eb8b8b275a1d7c02af96379c6a5af.jar <- R:\Workspace\androidx-versionedparcelable\libs\androidx-versionedparcelable.jar
[2022-05-11 08:55:53 - demo] Dx Using Pre-Dexed androidx-lifecycle-common-eb8ba09572c04143f806576a1b143ada.jar <- R:\Workspace\androidx-lifecycle-common\libs\androidx-lifecycle-common.jar
[2022-05-11 08:55:53 - demo] Dx Pre-Dexing R:\Workspace\androidx-annotation-experimental\libs\androidx-annotation-experimental.jar -> androidx-annotation-experimental-b634477a1038eeb097daf1a0fd5faecf.jar
[2022-05-11 08:55:53 - demo] Dx processing archive R:\Workspace\androidx-annotation-experimental\libs\androidx-annotation-experimental.jar...
[2022-05-11 08:55:53 - demo] Dx processing META-INF/androidx.annotation_annotation-experimental.version...
[2022-05-11 08:55:53 - demo] Dx processing META-INF/annotation-experimental_release.kotlin_module...
[2022-05-11 08:55:53 - demo] Dx processing androidx/annotation/OptIn.class...
[2022-05-11 08:55:53 - demo] Dx
PARSE ERROR:
[2022-05-11 08:55:53 - demo] Dx unsupported class file version 52.0
...while parsing androidx/annotation/OptIn.class
[2022-05-11 08:55:53 - demo] Dx 1 error; aborting
[2022-05-11 08:55:53 - demo] Conversion to Dalvik format failed with error 1
 

Looking at DexWrapper.java (Google ADT Plugin) it calls classes from android-sdk\build-tools\25.0.3\lib\dx.jar through reflection. 

public final class DexWrapper {

private final static String DEX_MAIN = "com.android.dx.command.dexer.Main"; //$NON-NLS-1$
private final static String DEX_CONSOLE = "com.android.dx.command.DxConsole"; //$NON-NLS-1$
private final static String DEX_ARGS = "com.android.dx.command.dexer.Main$Arguments"; //$NON-NLS-1$
...
public synchronized IStatus loadDex(String osFilepath) {
...
// get the classes.
Class<?> mainClass = loader.loadClass(DEX_MAIN);
Class<?> consoleClass = loader.loadClass(DEX_CONSOLE);
Class<?> argClass = loader.loadClass(DEX_ARGS);
...
// now get the fields/methods we need
mRunMethod = mainClass.getMethod(MAIN_RUN, argClass);

mArgConstructor = argClass.getConstructor();
mArgOutName = argClass.getField("outName"); //$NON-NLS-1$
mArgJarOutput = argClass.getField("jarOutput"); //$NON-NLS-1$
mArgFileNames = argClass.getField("fileNames"); //$NON-NLS-1$
mArgVerbose = argClass.getField("verbose"); //$NON-NLS-1$
mArgForceJumbo = argClass.getField("forceJumbo"); //$NON-NLS-1$

mConsoleOut = consoleClass.getField("out"); //$NON-NLS-1$
mConsoleErr = consoleClass.getField("err"); //$NON-NLS-1$

That dx.jar is an old version of Google Dex library (compiled 3 Apr 2017, version 1.12 if you decompile dx.jar\com\android\dx\Version.class) which didn't support Java 1.8 compiled classes back then. The support was introduced in 21 Feb 2017 but wasn't included in that release back then:

Commit: Allow version 52.0 class files (21 Feb 2017)

-   private static final int CLASS_FILE_MAX_MAJOR_VERSION = 51;
+ private static final int CLASS_FILE_MAX_MAJOR_VERSION = 52;

/**
* Does the actual parsing.
*/
private void parse0() {
...
if (!isGoodVersion(getMinorVersion0(), getMajorVersion0())) {
throw new ParseException("unsupported class file version " +
getMajorVersion0() + "." +
getMinorVersion0());
}

Since 25.0.3 is the last version of Android SDK Build Tools to work with Google ADT plugin for Eclipse in terms of having libraries and structure in a way that the plugin knows and understands, we update dx.jar in-place with a newer version that does support Java 1.8 binaries. Now this will not solve all the problems with newer Java 1.8 language constructs, but at least will give us a try at using newer Android library, maybe there have parts that we need and don't use newer language features.

Google DEX library sources are available on GitHub at aosp-mirror/platform_dalvik/dx/ and did gain support for version 52 class files, so we can in theory just compile it and drop it in. Unfortunately as support for Eclipse was dropped around 2017 some classes needed by Google ADT plugin (e.g. DxConsole) were shed with this commit: Make dx code re-entrant. Remove static state, clear biggest caches after running.

To reintroduce some of the support for the Google ADT plugin we forked the project on GitHub ( dandar3/platform_dalvik) and made some small changes to bring back DxConsole which is called from ADT plugin through reflection to supply stdout and stderr streams.
https://github.com/dandar3/platform_dalvik/commit/64e9c9b6dbb5606d2d61d0c512f3f7cc80b6788f    

You can clone and build the project yourself or download the JAR from the Release tab and place into your android-sdk\build-tools\xxx\lib

git clone https://github.com/dandar3/platform_dalvik.git
mvn clean package copy "target\dx-1.16-ADT.jar" "%ANDROID_HOME%\build-tools\25.0.3\lib\dx.jar*" 

Simply rebuild your project in Eclipse, it should work now.