{"id":22822026,"url":"https://github.com/olivki/asmkt","last_synced_at":"2025-10-31T09:50:48.467Z","repository":{"id":57737566,"uuid":"487863279","full_name":"Olivki/asmkt","owner":"Olivki","description":"Kotlin library providing DSL and utilities for Java ASM library","archived":false,"fork":false,"pushed_at":"2025-09-19T20:33:58.000Z","size":445,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-19T23:16:08.118Z","etag":null,"topics":["asm","bytecode","dsl","java","jvm","kotlin"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Olivki.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-05-02T13:52:02.000Z","updated_at":"2025-09-19T20:34:01.000Z","dependencies_parsed_at":"2023-11-14T11:25:49.223Z","dependency_job_id":"5895e4fc-1998-4d75-bba7-50a6c70fec67","html_url":"https://github.com/Olivki/asmkt","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Olivki/asmkt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Olivki%2Fasmkt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Olivki%2Fasmkt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Olivki%2Fasmkt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Olivki%2Fasmkt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Olivki","download_url":"https://codeload.github.com/Olivki/asmkt/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Olivki%2Fasmkt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281969175,"owners_count":26591719,"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","status":"online","status_checked_at":"2025-10-31T02:00:07.401Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["asm","bytecode","dsl","java","jvm","kotlin"],"created_at":"2024-12-12T16:10:13.206Z","updated_at":"2025-10-31T09:50:48.455Z","avatar_url":"https://github.com/Olivki.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"## asmkt\n\n![Maven Central](https://img.shields.io/maven-central/v/net.ormr.asmkt/asmkt?label=release\u0026style=for-the-badge)\n\n`asmkt` is a Kotlin library that provides a DSL and a set of utilities for generating bytecode using\nthe [ASM](https://asm.ow2.io/) library.\n\nThis library requires at least Java 8.\n\nThe library is *only* intended for generating JVM classes as of right now, parsing JVM classes is *not* supported.\n\nHeavily inspired by the new `ClassFile` API being worked on for the JVM. Motivation is to have something that looks\nsimilar for generating JVM bytecode that can run on older JVM versions.\n\n## Installation\n\n```kotlin\ndependencies {\n    implementation(group = \"net.ormr.asmkt\", name = \"asmkt\", version = \"${RELEASE_VERSION}\")\n}\n```\n\n## Notes\n\nCurrently the following features aren't supported:\n\n- Modules\n- Record components\n\n## Examples\n\nFor the below examples we'll be using this basic `ClassLoader` implementation:\n\n```kotlin\nclass ClassFileLoader : ClassLoader() {\n    fun loadClassFile(classFile: ClassFile): Class\u003c*\u003e =\n        defineClass(classFile.type.name, classFile.bytes, 0, classFile.bytes.size)\n\n    companion object {\n        @Suppress(\"UNCHECKED_CAST\")\n        fun \u003cT : Any\u003e load(classFile: ClassFile): Class\u003cT\u003e = ClassFileLoader().loadClassFile(classFile) as Class\u003cT\u003e\n    }\n}\n```\n\nNote that this implementation of a `ClassLoader` is *not* the best, as it's only made for the purpose of quickly running\nour compiled classes.\n\n### Hello, World!\n\nLet's say we want to generate the equivalent of this Java code:\n\n```java\npackage foo.bar;\n\npublic final class HelloWorld {\n    public static void main(final String[] args) {\n        System.out.println(\"Hello, World!\");\n    }\n}\n```\n\nWe could write something like this:\n\n```kotlin\nval element = ClassElement(\n    // Sets the minimum JDK version required to load the class to JDK 8\n    version = ClassFileVersion.RELEASE_8,\n    // Defines a class located in the package 'foo.bar' with the name 'HelloWorld' \n    type = ReferenceType(\"foo/bar/HelloWorld\"),\n    // Sets the modifiers of the class to 'public' and 'final'\n    flags = AccessFlag.PUBLIC + AccessFlag.FINAL,\n) {\n    // public static void main(final String[] args)\n    method(\n        name = \"main\",\n        // A method with return type of 'void', that accepts 1 argument of type 'String[]'\n        type = MethodType(VoidType, ArrayType.STRING),\n        flags = AccessFlag.PUBLIC + AccessFlag.STATIC,\n    ) {\n        // Sets the name of the parameter at index 0 to 'args'\n        // We can optionally also pass in a block here if we want to add annotations to the parameter\n        parameter(index = 0, name = \"args\", flags = AccessFlag.FINAL)\n        // Scopes us into the higher level instruction builder, 'withCodeChunk' can be used to access\n        // a more low level instruction builder DSL\n        withBody {\n            // Pushes a 'getstatic' instruction for the 'System.out' field onto the stack\n            getStaticField(\n                owner = ReferenceType\u003cSystem\u003e(),\n                name = \"out\",\n                type = ReferenceType\u003cPrintStream\u003e(),\n            )\n            // Pushes a string constant onto the stack\n            pushString(\"Hello, World!\")\n            // Pushes a 'invokevirtual' instruction onto the stack\n            // this will pop the top-most value on the stack\n            // as the pointer to the instance to invoke on which is our 'System.out' field\n            // and then it will pop our string constant off the stack\n            // as we gave it a MethodType expecting 1 argument\n            invokeVirtual(\n                // The class that the method belongs to\n                owner = ReferenceType\u003cPrintStream\u003e(),\n                name = \"println\",\n                type = MethodType(VoidType, ReferenceType.STRING),\n            )\n            // return an appropriately typed value\n            // all methods need to end with a `RETURN` instruction\n            // or some other non-branching instruction, even 'void' methods\n            returnValue()\n        }\n    }\n}\n```\n\n`element` will now contain a `ClassElement` that has all the elements required to generate JVM bytecode similar to that\nwe would get from the Java code example.\n\nTo actually run this code we need to do some \"plumbing\":\n\n```kotlin\n// Compiles 'element' to a 'ClassFile' with the default compiler\nval classFile = ClassElementCompiler.compileToClassFile(element)\n// Loads the compiled 'element' into the runtime with our  custom ClassLoader\nval loadedClass = ClassFileLoader.load\u003cAny\u003e(classFile)\n// Retrieves the 'main' method and invokes it via reflection\nloadedClass.getDeclaredMethod(\"main\", Array\u003cString\u003e::class.java).invoke(null, arrayOf\u003cString\u003e())\n// out: Hello, World!\n```\n\nThe `ClassElementCompiler` is how one turns a `ClassElement` into actual JVM bytecode. It comes with two\nfunctions, `compileToBytes` and `compileToClassFile`, a `ClassFile` is just a wrapper around JVM bytecode that contains\nthe `ClassFileVersion` and the `ReferenceType` of the compiled class, along with the actual compiled JVM bytecode.\n\n### Generating an interface\n\nThe \"kind\" of a class is determined by the `ClassKind` enum, which we pass in when we create the builder.\n\nIf we wanted to say, create an interface that looks something like this Java code:\n\n```java\npackage foo.bar;\n\npublic interface Foo {\n    void bar(String fooBar);\n}\n```\n\nWe could do something like this:\n\n```kotlin\nClassElement(\n    version = ClassFileVersion.RELEASE_8,\n    type = ReferenceType(\"foo/bar/Foo\"),\n    flags = AccessFlag.PUBLIC,\n    kind = ClassKind.INTERFACE,\n) {\n    method(\n        name = \"bar\",\n        flags = AccessFlag.PUBLIC + AccessFlag.ABSTRACT,\n        type = MethodType(VoidType, ReferenceType.STRING),\n    ) {\n        parameter(index = 0, name = \"fooBar\")\n        // Abstract methods are *not* allowed to have any instructions\n    }\n}\n```\n\n### `if`s, implementing interfaces and instances\n\nA quick and dirty example showcasing the `if` builders, how to implement interfaces, and default constructors.\n\nWe want to generate something similar to this Java code:\n\n```java\npackage foo.bar;\n\npublic class IfTestImpl implements IfTest {\n    public final void test(String name) {\n        if (name.equals(\"Dave\")) {\n            System.out.println(\"Hello, Dave.\");\n        } else {\n            System.out.println(\"Hello, unknown.\");\n        }\n    }\n}\n```\n\nWhere `IfTest` is an existing interface that looks like this:\n\n```java\npackage foo.bar;\n\npublic interface IfTest {\n    void test(String name);\n}\n```\n\nNote that we're *not* going to be generating the JVM bytecode for the `IfTest` interface itself. If you want to see how\nto generate an interface, see the previous chapter.\n\nThe following code will generate a similar result:\n\n```kotlin\nval element = ClassElement(\n    version = ClassFileVersion.RELEASE_8,\n    type = ReferenceType(\"foo/bar/IfTestImpl\"),\n    flags = AccessFlag.PUBLIC,\n    kind = ClassKind.CLASS,\n    interfaces = listOf(ReferenceType\u003cIfTest\u003e()),\n) {\n    // Generate a default no arguments constructor\n    // If we didn't generate one, we would not be able to properly create an instance of 'IfTestImpl'\n    defaultConstructor()\n    method(\n        name = \"test\",\n        flags = AccessFlag.PUBLIC + AccessFlag.FINAL,\n        type = MethodType(VoidType, ReferenceType.STRING),\n    ) {\n        parameter(index = 0, name = \"name\")\n        withBody {\n            // Index = 1 here because the local at Index = 0 is the 'this' pointer\n            loadLocal(index = 1, type = ReferenceType.STRING)\n            pushString(\"Dave\")\n            invokeVirtual(\n                owner = ReferenceType.STRING,\n                name = \"equals\",\n                type = MethodType(BooleanType, ReferenceType.OBJECT),\n            )\n            ifThenElse(\n                thenBuilder = {\n                    getStaticField(\n                        owner = ReferenceType\u003cSystem\u003e(),\n                        name = \"out\",\n                        type = ReferenceType\u003cPrintStream\u003e(),\n                    )\n                    pushString(\"Hello, Dave.\")\n                    invokeVirtual(\n                        owner = ReferenceType\u003cPrintStream\u003e(),\n                        name = \"println\",\n                        type = MethodType(VoidType, ReferenceType.STRING),\n                    )\n                },\n                elseBuilder = {\n                    getStaticField(\n                        owner = ReferenceType\u003cSystem\u003e(),\n                        name = \"out\",\n                        type = ReferenceType\u003cPrintStream\u003e(),\n                    )\n                    pushString(\"Hello, unknown.\")\n                    invokeVirtual(\n                        owner = ReferenceType\u003cPrintStream\u003e(),\n                        name = \"println\",\n                        type = MethodType(VoidType, ReferenceType.STRING),\n                    )\n                },\n            )\n            returnValue()\n        }\n    }\n}\n```\n\nAnd then the required \"plumbing\" to test if our code runs like it should:\n\n```kotlin\nval classFile = ClassElementCompiler.compileToClassFile(element)\n// Load the classFile as an instance of 'IfTest'\n// This is so we can easily call the 'test' method\nval loadedClass = ClassFileLoader.load\u003cIfTest\u003e(classFile)\n// Create a new instance of the newly loaded 'IfTestImpl' class\nval instance = loadedClass.newInstance()\ninstance.test(\"Dave\") // out: Hello, Dave.\ninstance.test(\"Not Dave\") // out: Hello, unknown.\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folivki%2Fasmkt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Folivki%2Fasmkt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folivki%2Fasmkt/lists"}