Skip to content

Setting up Vineflower development

zml edited this page Aug 9, 2023 · 1 revision

Setting up Vineflower development

Want to set up an environment to compile and test Vineflower? This document has instructions on the best way to set up your environment to it easy to develop and test your changes.

  • Cloning and building

    • Clone the same as any other repository, https://github.com/Vineflower/vineflower.git. This will give you a copy of the working tree. Do note that the default branch contains the last stable release, while the latest in-development changes are found in the develop/x.xx branch. When submitting pull requests, targeting the development branch is highly encouraged.
    • Building is as easy as ./gradlew build (or .\gradlew.bat build on Windows). This will produce a compiled jar under ./build/libs.
  • Making changes

    • Most of Vineflower's Java code generation resides in MethodProcessor.java. This class contains the control flow graph parser, statement structure finder, and more.
    • For more information on how every step of Vineflower works, you can refer to the ARCHITECTURE.md file.
  • Testing

    • Vineflower is a codebase that relies heavily on unit tests to validate decompiler output and internal state. Whenever submitting changes that add a new feature or fix a bug, it is expected that a unit test be added to ensure the behavior stays consistent in the future.
    • To run the test suite, you simply need to run ./gradlew test. This will run all of the unit tests and report any failing ones.
      • Intellij specific information: Running via ./gradlew test (as a run configuration) is not recommended as it does not allow you to focus execution on a specific unit test, making it difficult to debug issues. Instead, it is recommended to set up a new JUnit run configuration with the selection "Class" and the value org.jetbrains.java.decompiler.SingleClassesTest. Make sure the module is vineflower.test. This will run all Java unit tests. Do note that to run this you will need to specify the -Djava.8.home and -Djava.16.home variables in the VM Options text field, setting them to your Java 8 and Java 16 installations respectively.
    • To add a new unit test, you must first choose the "domain" of the test class. This corresponds to a subdirectory in the ./testData/src/ directory. Examples include Java 8, Java 16, JASM, Java 8 (No debug symbols), Kotlin, and more. When in doubt, use the Java 8 folder. That's where the largest amount of tests are housed. After writing a test class, head over to ./src/test/org/jetbrains/java/decompiler/SingleClassesTest.java to add your unit test to the list. Head to the end of the registerDefault method, add a line registering your test class (with the format register(DOMAIN, "MyClassName");). This will add it to the list of unit tests. Now execute the tests again, and you're done!
    • The unit tests are evaluated by comparing the decompiled output to a known output, housed in the ./testData/results folder. This folder contains .dec (DECompiled output) files. When first running your new unit test, there won't be a matching .dec file so a new one will be created automatically. On subsequent runs, the test will be correctly checked against.
  • Debugging

    • As Vineflower contains many moving parts and complicated data structures, debugging issues can be a daunting task. However, there are many helpful debugging methods that may help you.
    • Vineflower contains many "validation" checks, which are essentially assertions. Vineflower expects a certain graph structure for the central statement graph, and this can be checked with the validation system. However, the validation system is disabled by default- you can enable it via passing -DVALIDATE_DECOMPILED_CODE=true to the unit test configuration. As soon as an invalid structure is found, it'll throw an error- helping isolate the invalid change to the graph.
    • The internal graph structure can be dumped to a GraphViz .dot file via the DotExporter class. To enable it, you must provide a directory to either -DDOT_ERROR_EXPORT_DIR=... or -DDOT_EXPORT_DIR=.... The directory must have a / at the end! Exporting error dots will dump the graph structure if an error is thrown, whereas exporting all dots will export a dot file for every single step of the decompile process. It's best to only export all dot files when focusing execution on a single class, as otherwise execution will become very slow and consume incredible amounts of disk space.
    • A single method can be focused for execution through changing the DEBUG_METHOD_FILTER constant in ClassWrapper.java. When set, it'll skip all methods that have a name different from the given one. Filtering on signature is currently not supported.
    • Using the Intellij Coverage Runner is very helpful in seeing code execution paths.
    • Statements can be dumped to console using <statement>.toJava().convertToStringAndAllowDataDiscard().
  • Submitting changes

    • After you ensure all tests pass, changes can be submitted via opening a PR.
    • Take a look at the Contribution guidelines for more information.
    • If you have any problems setting up your environment or making changes, please don't hesitate to reach out through the GitHub discussion page or our socials
Clone this wiki locally