Skip to content

char/Koffee

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
src
 
 
 
 
 
 
 
 
 
 
 
 

Koffee

A Kotlin DSL wrapping ObjectWeb's ASM bytecode manipulation library. (In particular, asm-tree.)

Limitations

Since the DSL is in Kotlin, there are a few reserved words that get in our way. In order to get around this, we provide properties with the literal name and use backtick-escaping, and the preferred approach of a leading underscore before the reserved name.

  • Since 'interface' is a globally reserved keyword in Kotlin, the access flag interface can be referenced using `interface` or _interface
  • Since 'super' is a globally reserved keyword in Kotlin, the access flag super can be referenced using `super` or _super
  • Since 'return' is a globally reserved keyword in Kotlin, the instruction return can be referenced using `return` or _return

Extras

Kotlin makes life a lot easier for simplifying the user's experience. It allows Koffee to do some useful things:

  • All bytecode instructions are represented as simple methods (e.g. getfield(...)) or properties (e.g. aconst_null)
  • Fields are created with a single function call (e.g. field(...))
  • Methods are created with an easy way to add individual bytecode (see the example)
  • Lots of sugar exists for commonly used constructs:
    • init and clinit methods
    • Object creation (with construct)
    • Try/catch blocks (with guard and its returned object to add exception handlers)

Simple Example

val helloWorld: ClassNode = assembleClass(public, "com/example/HelloWorld") {
    method(public + static, "main", void, Array<String>::class) {
        getstatic(System::class, "out", PrintStream::class)
        ldc("Hello, world!")
        invokevirtual(PrintStream::class, "println", void, String::class)
        _return
    }
}

Complex Example

val myClass = assembleClass(public, "com/example/MyClass") {
    field(public + static + final, "theAnswer", int, value = 42)
    field(private + static + final, "instance", MyClass::class)

    method(public + static, "getInstance", MyClass::class) {
        getstatic(MyClass::class, "instance", MyClass::class)
        dup
        ifnull(L["oh noes"])
        _return
        +L["oh noes"]
        construct(IOException::class, String::class) {
            ldc("no instance exists whatever shall we do?")
        }
        athrow
    }

    init(public) {
        // nothing extra: the superclass is called automatically, and no fields are initialized
    }

    clinit {
        construct(MyClass::class)
        putstatic(MyClass::class, "instance", MyClass::class)
    }
}

Goals

  • Koffee is designed to be used in projects that already make use of ASM, so we don't need to abstract too far away.
  • Despite this, Koffee should aim to make the use of ASM easier - High-level abstractions are acceptable if they are common. (e.g. 'load int constant' can be made uniform)

About

Java bytecode assembler as a Kotlin DSL

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages