{"id":19764092,"url":"https://github.com/rikonardo/cafebabe","last_synced_at":"2025-05-12T20:46:43.124Z","repository":{"id":38836790,"uuid":"496376704","full_name":"Rikonardo/CafeBabe","owner":"Rikonardo","description":"Java .class files parser for Kotlin","archived":false,"fork":false,"pushed_at":"2023-03-30T11:13:13.000Z","size":97,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-01T03:25:21.679Z","etag":null,"topics":["bytecode","bytecode-manipulation","bytecode-parser","java","kotlin","library","multiplatform","parser"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Rikonardo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-05-25T20:14:25.000Z","updated_at":"2024-11-03T16:05:42.000Z","dependencies_parsed_at":"2022-09-18T12:41:40.738Z","dependency_job_id":null,"html_url":"https://github.com/Rikonardo/CafeBabe","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rikonardo%2FCafeBabe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rikonardo%2FCafeBabe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rikonardo%2FCafeBabe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rikonardo%2FCafeBabe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Rikonardo","download_url":"https://codeload.github.com/Rikonardo/CafeBabe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253819028,"owners_count":21969266,"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","bytecode-manipulation","bytecode-parser","java","kotlin","library","multiplatform","parser"],"created_at":"2024-11-12T04:12:20.475Z","updated_at":"2025-05-12T20:46:43.097Z","avatar_url":"https://github.com/Rikonardo.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\u003ch1\u003eCafeBabe - Java .class files parser for Kotlin\u003c/h1\u003e\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\u003cimg alt=\"Logo\" src=\"logo.svg\" width=\"512\"/\u003e\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n    \u003cimg alt=\"Open issues\" src=\"https://img.shields.io/github/issues-raw/Rikonardo/CafeBabe\"/\u003e\n    \u003cimg alt=\"Version\" src=\"https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fmaven.rikonardo.com%2Freleases%2Fcom%2Frikonardo%2Fcafebabe%2FCafeBabe%2Fmaven-metadata.xml\"/\u003e\n    \u003cimg alt=\"Code size\" src=\"https://img.shields.io/github/languages/code-size/Rikonardo/CafeBabe\"/\u003e\n\u003c/div\u003e\n\n\u003cbr\u003e\n\n\u003chr\u003e\n\n**CafeBabe** is a Java .class file parser/serializer written in pure Kotlin multiplatform. It doesn't provide any tools for working with Java bytecode, but it does allow you to manipulate the class structure and metadata.\n\n💼 **This readme contains full library documentation/tutorial!**\n\n## Install\n\nGradle Kotlin:\n```kotlin\nrepositories {\n    maven {\n        url = uri(\"https://maven.rikonardo.com/releases\")\n    }\n}\n\ndependencies {\n    implementation(\"com.rikonardo.cafebabe:CafeBabe:1.0.3\")\n}\n```\n\n## Documentation\n\n| Content                                    |\n|--------------------------------------------|\n| **1. [Parsing classes](#parsing-classes)** |\n| **2. [Constant pool](#constant-pool)**     |\n| **3. [Editing classes](#editing-classes)** |\n| **4. [Interfaces](#interfaces)**           |\n| **5. [Fields](#fields)**                   |\n| **6. [Methods](#methods)**                 |\n| **7. [Attributes](#attributes)**           |\n\n## Parsing classes\nTo parse a class, call the `ClassFile` constructor with a ByteArray parameter:\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    val classBytes = File(\"./path/to/class.class\").readBytes()\n    val classFile = ClassFile(classBytes)\n    println(classFile.name) // Prints class name (e.g. \"com/example/MyClass\")\n    println(classFile.parent) // Prints parent class name (e.g. \"java/lang/Object\")\n    println(classFile.version.toString()) // Prints class version (e.g. \"52.0\")\n    println(classFile.access.toString()) // Prints class access flags (e.g. \"PUBLIC, SYNCHRONIZED\")\n    println(classFile) // Prints all class metadata\n}\n```\n\nYou can also retrieve raw class or class members' metadata by accessing the `data` property:\n    \n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    val classBytes = File(\"./path/to/class.class\").readBytes()\n    val classFile = ClassFile(classBytes)\n    println(classFile.data) // Prints all class raw metadata\n    println(classFile.data.fields) // Prints all class fields raw metadata\n    println(classFile.data.methods) // Prints all class methods raw metadata\n}\n```\n\n## Constant pool\nThe constant pool is a list of all constants used in the class. Constants are all values or value literals that are used in the class. Their peculiarity is that they are numerated starting from 1, not 0. CafeBabe provides wrapper for constant list, so you can access them directly by index:\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    val classBytes = File(\"./path/to/class.class\").readBytes()\n    val classFile = ClassFile(classBytes)\n    val constant = classFile.constantPool[1]\n    println(constant) // Prints constant at index 1, which is the first constant in constant pool\n}\n```\n\nThis wrapper also has `add` method, which allows you to add new constant to the constant pool. This method returns index of the new constant:\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    val classBytes = File(\"./path/to/class.class\").readBytes()\n    val classFile = ClassFile(classBytes)\n    val constant = classFile.constantPool.add(ConstantUtf8(\"new_constant\"))\n    println(constant) // Prints index of the new constant\n}\n```\n\n## Editing classes\nCafeBabe allows you not only read classes, but also edit them. For example, you can rename class members, or class itself; copy methods from other classes; change class members' visibility; etc.\n\nHere is an example of renaming class and copying method from another class to it:\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    val classFile = ClassFile(File(\"./path/to/class.class\").readBytes()) // Read original class\n    \n    classFile.name = \"com/example/MyClass\" // Rename class\n    \n    val donorClass = ClassFile(File(\"./path/to/donor.class\").readBytes()) // Read donor class\n    val sourceMethodName = \"donorMethod\" // Name of method we are copying\n    val targetMethodName = \"targetMethod\" // New name of copied method inside our class\n\n    val nameIndex = classInfo.constantPool.add(ConstantUtf8(targetMethodName)) // Add new name to constant pool\n    val method = classInfo.methods.find { it.name == sourceMethodName }\n        ?: throw IllegalStateException(\"No method found\") // Find method in donor class\n    \n    val newMethodData = MethodData( // Create new method data\n        accessFlags = method.data.accessFlags,\n        nameIndex = nameIndex, // Copy everything except name, which we changed\n        descriptorIndex = method.data.descriptorIndex,\n        attributes = method.data.attributes\n    )\n    \n    val newMethod = Method(classInfo, newMethodData) // Create new method\n    classInfo.methods.add(newMethod) // Add new method to class\n    \n    File(\"./path/to/class.class\").writeBytes(classFile.compile()) // Write class back to file\n}\n```\n\n❗ Note that copied method won't work as expected because it's bytecode references to the constant pool entries in source class.\n\nChanging class or class member name will automatically change value in the constant pool on related index. This can lead to unexpected behavior when single Utf8 constant used in multiple places, so it's recommended to create separate constant pool entry when renaming:\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    val classBytes = File(\"./path/to/class.class\").readBytes()\n    val classFile = ClassFile(classBytes)\n    \n    val constantClass = classFile.constantPool[classFile.data.thisClass] as ConstantClass // Get class constant\n    constantClass.nameIndex = classFile.constantPool.add(ConstantUtf8(\"\")) // Replace linked name constant\n    classFile.name = \"com/example/MyClass\" // Rename class\n    \n    File(\"./path/to/class.class\").writeBytes(classFile.compile())\n}\n```\n\n## Interfaces\nYou can get list of interfaces implemented by a class by calling `classFile.interfaces` property:\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    val classBytes = File(\"./path/to/class.class\").readBytes()\n    val classFile = ClassFile(classBytes)\n    val i = classFile.interfaces[0]\n    println(i.name) // Prints interface name (e.g. \"java/lang/Runnable\")\n}\n```\n\n## Fields\nYou can get list of fields by calling `classFile.fields` property:\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    val classBytes = File(\"./path/to/class.class\").readBytes()\n    val classFile = ClassFile(classBytes)\n    val f = classFile.fields[0]\n    println(f.name) // Prints field name (e.g. \"fieldName\")\n    println(f.access) // Prints field access flags (e.g. \"PUBLIC\")\n    println(f.descriptor) // Prints field descriptor (e.g. \"Ljava/lang/String;\")\n    println(f.attributes) // Print attributes (more on them later)\n}\n```\n\n## Methods\nYou can get list of methods by calling `classFile.methods` property:\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    val classBytes = File(\"./path/to/class.class\").readBytes()\n    val classFile = ClassFile(classBytes)\n    val m = classFile.methods[0]\n    println(m.name) // Prints method name (e.g. \"methodName\")\n    println(m.access) // Prints method access flags (e.g. \"PUBLIC\")\n    println(m.descriptor) // Prints method descriptor (e.g. \"()V\")\n    println(m.attributes) // Print attributes (more on them later)\n}\n```\n\n## Attributes\nAttributes contain data, that is related to JVM runtime. For example, they can contain method code, annotations data, etc. CafeBabe doesn't parse attributes, but provides their names and ByteArray body, so you can manually parse and modify them if you need.\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    val classBytes = File(\"./path/to/class.class\").readBytes()\n    val classFile = ClassFile(classBytes)\n    val m = classFile.methods[0]\n    val a = m.attributes[0]\n    println(a.name) // Prints attribute name (e.g. \"Code\")\n    println(a.info.joinToString(\"\") { \"%02x\".format(it) }) // Prints attribute body as hex string\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frikonardo%2Fcafebabe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frikonardo%2Fcafebabe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frikonardo%2Fcafebabe/lists"}