{"id":16847822,"url":"https://github.com/stepancheg/mh-lang","last_synced_at":"2025-04-11T06:38:23.274Z","repository":{"id":66119950,"uuid":"277235666","full_name":"stepancheg/mh-lang","owner":"stepancheg","description":"DSL for constructing MethodHandles","archived":false,"fork":false,"pushed_at":"2020-10-13T23:18:50.000Z","size":150,"stargazers_count":5,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-25T04:33:06.659Z","etag":null,"topics":["java","reflection"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stepancheg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2020-07-05T05:13:20.000Z","updated_at":"2024-06-29T14:18:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"a9a48534-493f-4df1-95a4-35b9a650bfa4","html_url":"https://github.com/stepancheg/mh-lang","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepancheg%2Fmh-lang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepancheg%2Fmh-lang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepancheg%2Fmh-lang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepancheg%2Fmh-lang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stepancheg","download_url":"https://codeload.github.com/stepancheg/mh-lang/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248358547,"owners_count":21090401,"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":["java","reflection"],"created_at":"2024-10-13T13:09:10.185Z","updated_at":"2025-04-11T06:38:23.245Z","avatar_url":"https://github.com/stepancheg.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mh-lang: DSL for constructing Java MethodHandles\n\nReflection is great, but slow. Single reflection operation can be fast, but when invoking a sequence of operations,\na user has to pay for reflective invocation overhead in each operation, which is:\n* type checking of all arguments\n* accessibility\n* boxing/unboxing\n\nConsider this use case: check that none of the class fields are not `null`. Pseudo code:\n\n```\nvoid checkAllNotNull(T t) {\n  for (Field f in T.fields) {\n    Object v = f.get(t);\n    if (v == null) {\n      throw new IllegalStateException();\n    }\n  }\n}\n```\n\n`Field.get` is invoked multiple times, each time it performs a type check.\n\nJava `MethodHandle`s allow glueing multiple reflective operation together.\nEach reflective operation (field get/set, method call, constructor) is a `MethodHandle`,\nand `MethodHandles` class contains a lot of combinators to glue all of the operations together.\n\nType-checking happens only once, when combined `MethodHandle` is constructed, and only once\nwhen glued `MethodHandles` is invoked.\n\nThe example above would be written as:\n\n```\nMethodHandle checkAllNotNull = ...;\n\nvoid checkAllNotNull(T t) {\n  checkAllNotNull.invokeExact(t);\n}\n```\n\nThe question is how to construct resulting `MethodHandle`. Raw combine operations (e. g. `collectArguments`)\nare very hard in my experience, and writing stateful code\n\n* when a computation result need to be used in multiple operations\n* with the order of operations is important\n* with runtime-conditional invocations\n\nis very hard to a regular human.\n\nmh-lang library provides a DSL for writing method handles in a friendly semi-functional style.\n\nThe `mh` described above could be written like this:\n\n```\nMhBuilder b = new MhBuilder();\nVar\u003c?\u003e t = b.addParameter(tClass);\nfor (Field field : tClass.getDeclaredFields()) {\n  Closure\u003c?\u003e fieldExpr = Closure.getField(field, t);\n  b.assign(Closure.ifThen(\n    fieldExpr.isNull(),\n    Closure.throwException(void.class, Closure.supplier(IllegalStateException.class, () -\u003e new IllegalStateException()));\n  ));\n}\ncheckAllNotNull = b.buildReturnVoid();\n```\n\n## Examples\n\nExamples live in\n[examples folder](https://github.com/stepancheg/mh-lang/tree/master/src/main/java/com/github/stepancheg/mhlang/examples),\nand the most demonstrative one is an implementation of struct of arrays.\n\n## Struct of arrays\n\nUntil Java gets proper value types, efficient memory usage is an issue.\n\nOne possible way to deal with this issue it to store lists of objects in\n[structs of arrays](https://en.wikipedia.org/wiki/AoS_and_SoA):\nfor each field allocate an array, and store each struct fields in own arrays.\n\nAs a demo for this library I have written to implementation of struct of arrays array lists:\n* the one with `MethodHandle`s ([FlatArrayMhList](https://github.com/stepancheg/mh-lang/blob/master/src/main/java/com/github/stepancheg/mhlang/examples/FlatArrayMhList.java))\n* the one with regular reflection ([FlatArrayReflList](https://github.com/stepancheg/mh-lang/blob/master/src/main/java/com/github/stepancheg/mhlang/examples/FlatArrayReflList.java))\n\nMH version is\n[twenty times faster](https://github.com/stepancheg/mh-lang/blob/master/src/test/java/com/github/stepancheg/mhlang/examples/FlatArrayListBenchmark.java)\nthan reflective version.\n\n## What about bytecode generation?\n\nBytecode generation is equally hard for a regular user and a small project, but additionally, bytecode cannot easily\n(without using JVM hacks) perform restricted operatins like accessing private fields or invoking private methods.\n\nMethod handles do not have this limitation.\n\n## Maven and other feedback\n\nThis library is not published in Maven or elsewhere.\nFeel free to just copy sources to your project or to you private storage.\nIf you think that it need to be published on Maven,\nopen an issue in GitHub issue tracker and I'll try to do something about it.\n\nAny other feedback? Open an issue in GitHub issue tracker.\n\nThanks!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstepancheg%2Fmh-lang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstepancheg%2Fmh-lang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstepancheg%2Fmh-lang/lists"}