{"id":13760369,"url":"https://github.com/clojure-goes-fast/clj-java-decompiler","last_synced_at":"2025-11-25T16:01:26.981Z","repository":{"id":50575169,"uuid":"119350071","full_name":"clojure-goes-fast/clj-java-decompiler","owner":"clojure-goes-fast","description":"REPL-integrated Clojure-to-Java decompiler","archived":false,"fork":false,"pushed_at":"2024-10-03T20:30:38.000Z","size":57,"stargazers_count":266,"open_issues_count":2,"forks_count":8,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-11-14T17:11:51.992Z","etag":null,"topics":["bytecode","clojure","decompiler","disassembly","java"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clojure-goes-fast.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-29T07:52:22.000Z","updated_at":"2024-11-02T02:08:22.000Z","dependencies_parsed_at":"2024-05-02T14:31:58.667Z","dependency_job_id":"dfea47ee-a7f0-4781-a1d3-579790aab277","html_url":"https://github.com/clojure-goes-fast/clj-java-decompiler","commit_stats":{"total_commits":29,"total_committers":2,"mean_commits":14.5,"dds":0.03448275862068961,"last_synced_commit":"9526549e8194901ac30b3223f239498bffcceb02"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure-goes-fast%2Fclj-java-decompiler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure-goes-fast%2Fclj-java-decompiler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure-goes-fast%2Fclj-java-decompiler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure-goes-fast%2Fclj-java-decompiler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clojure-goes-fast","download_url":"https://codeload.github.com/clojure-goes-fast/clj-java-decompiler/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224949785,"owners_count":17397235,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bytecode","clojure","decompiler","disassembly","java"],"created_at":"2024-08-03T13:01:08.823Z","updated_at":"2025-11-25T16:01:26.885Z","avatar_url":"https://github.com/clojure-goes-fast.png","language":"Clojure","readme":"# clj-java-decompiler [![CircleCI](https://img.shields.io/circleci/build/github/clojure-goes-fast/clj-java-decompiler/master.svg)](https://dl.circleci.com/status-badge/redirect/gh/clojure-goes-fast/clj-java-decompiler/tree/master) [![](https://img.shields.io/clojars/dt/com.clojure-goes-fast/clj-java-decompiler?color=teal)](https://clojars.org/com.clojure-goes-fast/clj-java-decompiler) [![](https://img.shields.io/badge/-changelog-blue.svg)](CHANGELOG.md)\n\n_You can read the motivation behind clj-java-decompiler and the usage example in\nthe\n[blog post](http://clojure-goes-fast.com/blog/introspection-tools-java-decompilers/)._\n\nThis library is an integrated Clojure-to-Java decompiler usable from the REPL.\nUnder the hood, it uses [Procyon](https://github.com/mstrobel/procyon) to\ndecompile the bytecode generated by Clojure compiler into the equivalent Java\nsource code.\n\nQuick demo:\n\n```java\nuser\u003e (clj-java-decompiler.core/decompile\n        (loop [i 100, sum 0]\n          (if (\u003c i 0)\n            sum\n            (recur (unchecked-dec i) (unchecked-add sum i)))))\n\n// Decompiling class: user$fn__13332\nimport clojure.lang.*;\n\npublic final class user$fn__13332 extends AFunction\n{\n    public static Object invokeStatic() {\n        long i = 100L;\n        long sum = 0L;\n        while (i \u003e= 0L) {\n            final long n = i - 1L;\n            sum += i;\n            i = n;\n        }\n        return Numbers.num(sum);\n    }\n\n    public Object invoke() {\n        return invokeStatic();\n    }\n}\n```\n\n## Why?\n\nThere are several usecases when you may want to use a Java decompiler:\n\n- To get a general understanding how Clojure compiler works: how functions are\n  compiled into classes, how functions are invoked, etc.\n- To optimize performance bottlenecks when using low-level constructs like\n  loops, primitive math, and type hints.\n- To investigate how Java interop facilities are implemented (`reify`, `proxy`,\n  `gen-class`).\n\n## Usage\n\nAdd `com.clojure-goes-fast/clj-java-decompiler` to your dependencies:\n\n[![](https://clojars.org/com.clojure-goes-fast/clj-java-decompiler/latest-version.svg)](https://clojars.org/com.clojure-goes-fast/clj-java-decompiler)\n\nThen, at the REPL:\n\n```clojure\nuser\u003e (require '[clj-java-decompiler.core :refer [decompile]])\nnil\nuser\u003e (decompile (fn [] (println \"Hello, decompiler!\")))\n```\n\n```java\n// Decompiling class: clj_java_decompiler/core$fn__13257\nimport clojure.lang.*;\n\npublic final class core$fn__13257 extends AFunction\n{\n    public static final Var __println;\n\n    public static Object invokeStatic() {\n        return __println.invoke(\"Hello, decompiler!\");\n    }\n\n    public Object invoke() {\n        return invokeStatic();\n    }\n\n    static {\n        __println = RT.var(\"clojure.core\", \"println\");\n    }\n}\n```\n\nYou can also disassemble to bytecode, with the output being similar to the one\nof `javap`.\n\n```\nuser\u003e (disassemble (fn [] (println \"Hello, decompiler!\")))\n\n;;; Redacted\n\n    public static java.lang.Object invokeStatic();\n        Flags: PUBLIC, STATIC\n        Code:\n                  linenumber      1\n               0: getstatic       clj_java_decompiler/core$fn__17004.const__0:Lclojure/lang/Var;\n               3: invokevirtual   clojure/lang/Var.getRawRoot:()Ljava/lang/Object;\n                  linenumber      1\n               6: checkcast       Lclojure/lang/IFn;\n               9: getstatic       clj_java_decompiler/core$fn__17004.const__1:\nLclojure/lang/Var;\n              12: invokevirtual   clojure/lang/Var.getRawRoot:()Ljava/lang/Object;\n                  linenumber      1\n              15: checkcast       Lclojure/lang/IFn;\n              18: ldc             \"Hello, decompiler!\"\n                  linenumber      1\n              20: invokeinterface clojure/lang/IFn.invoke:(Ljava/lang/Object;)Ljava/lang/Object;\n                  linenumber      1\n              25: invokeinterface clojure/lang/IFn.invoke:(Ljava/lang/Object;)Ljava/lang/Object;\n              30: areturn\n```\n\n### Post-processing and de-cluttering\n\nTo make the output clearer, clj-java-decompiler by default disables [locals\nclearing](https://clojuredocs.org/clojure.core/*compiler-options*) for the code\nit compiles. You can re-enable it by setting this compiler option to false\nexplicitly, like this:\n\n```clj\n(binding [*compiler-options* {:disable-locals-clearing false}]\n  (decompile ...))\n```\n\nYou can also change other compiler options (static linking, metadata elision) in\nthe same way.\n\nBy default, clj-java-decompiler also performs additional post-processing of the\nProcyon output. This includes removing current class name from static\nreferences, and replacing opaque `const__` fields with more informative var\nnames. You can disable this post-processing by executing:\n\n```clj\n(reset! clj-java-decompier.core/postprocessing-enabled false)\n```\n\n### Usage from Emacs\n\nYou can use [clj-decompiler.el](https://github.com/bsless/clj-decompiler.el)\npackage (installable from MELPA) to fluidly invoke `clj-java-decompiler` right\nfrom your Clojure code buffer. Like with `cider-macroexpand`, you place your\ncursor at the end of the form you want to decompile and invoke `M-x\nclj-decompiler-decompile`. This will compile the form before the cursor, then\ndecompile it with `clj-java-decompiler`, and present you the Java output in a\nseparate syntax-highlighted buffer.\n\n`clj-decompiler.el` can also automatically inject `clj-java-decompiler`\ndependency at `cider-jack-in` time. Check its repository for more details.\n\n### How to decompile an already defined function\n\nShort answer: you can't do that. JVM doesn't retain the bytecode for classes it\nhas already loaded. When the Clojure compiler compiles a piece of Clojure code,\nit transforms it into bytecode in memory, then loads it with a classloader, and\ndiscards the bytecode. So, in order to decompile a function, you must pass its\nsource code to the `decompile` macro.\n\nFortunately, most Clojure libraries are distributed in the source form. If you\nuse CIDER or any other Clojure IDE, you can jump to the definition of the\nfunction you want to decompile, disable read-only mode (in Emacs, that is done\nwith \u003ckbd\u003eC-x C-q\u003c/kbd\u003e), wrap the `defn` form with\n`clj-java-decompiler.core/decompile` and recompile the form (\u003ckbd\u003eC-c C-c\u003c/kbd\u003e\nin Emacs). This becomes much simpler if you use\n[clj-decompiler.el](https://github.com/bsless/clj-decompiler.el), you just call\n`M-x clj-decompiler-decompile` on the function you've jumped to.\n\nIf you absolutely need to decompile a loaded function for which the source code\nis not available, you can consider trying the\n[no.disassemble](https://github.com/gtrak/no.disassemble) library. Note that it\nmust be loaded into the JVM at startup time as an agent and can only disassemble\nfunctions into bytecode representation (not decompile into Java code).\n\nAnother option for when you have no source code but compiled `.class` files is\nto use one of the available [Java\ndecompilers](http://clojure-goes-fast.com/blog/introspection-tools-java-decompilers/).\n\n## License\n\nclj-java-decompiler is distributed under the Eclipse Public License.\nSee [LICENSE](LICENSE).\n\nCopyright 2018-2025 Alexander Yakushev\n","funding_links":[],"categories":["Clojure"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclojure-goes-fast%2Fclj-java-decompiler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclojure-goes-fast%2Fclj-java-decompiler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclojure-goes-fast%2Fclj-java-decompiler/lists"}