Giter VIP home page Giter VIP logo

jasm's Introduction

JASM - A JVM Assembler for the modern age

What?

JASM is an assembler/disassembler for JVM bytecode. It provides a nice syntax for writing JVM classes in a bytecode-focused assembly language, and can also disassemble any Java .class file to JASM source code.

JASM has a Gradle plugin and a (WIP) Plugin for IntelliJ.

See the Example Gradle project for an example of how JASM might fit in to your project.

Let's just get this out of the way, shall we?

public class com/example/HelloWorld {
    public static main([java/lang/String)V {
        getstatic java/lang/System.out java/io/PrintStream
        ldc "Hello, World"
        invokevirtual java/io/PrintStream.println(java/lang/String)V
        return
    }
}

See the Examples for more examples of JASM code.

How?

Requirements

  • Java 11 or higher is required to run the tool and/or gradle plugin
    • (However, JASM can assemble classes targeted at any JVM version!)

Using the Gradle plugin

If you just want to use some JASM code in your own Gradle project, the easiest way to get started is via the Gradle plugin.

For more information and some documentation about the plugin, see the Github repo

Using the command-line tool

If you downloaded a binary distribution then you should be all set. Inside the archive you'll find a `bin/jasm' script that will take care of running the command-line tool for you.

To see usage details:

bin/jasm --help

To simply assemble a file src/com/example/MyClass.jasm to the classes directory:

bin/jasm -i src -o classes com/example/MyClass.jasm

Or to disassemble a .class file classes/com/example/MyClass.class to the src directory:

bin/jasm -d -i classes -o src com/example/MyClass.class

Notice that you set the source and destination directories, and just pass the relative path to the files within them - this is how the assembler creates the output files in the appropriate place (in a com/example directory under the destination directory in the example above).

When disassembling, you can optionally specify the -l flag, which will cause JASM to output comments in the disassembly with the original line number (if this information is present in the .class file).

Building the tool with Gradle

If you grabbed the source from Github you can easily build a binary distribution with ./gradlew clean assemble (pun not intended).

Using JASM as a library

If you want to use this as a library (with Maven or Gradle), you'll want to pull in the dependency from Maven central.

E.g. (for Gradle):

dependencies {
  implementation("com.roscopeco.jasm:jasm:0.7.0")
}

Why??

Well, why not?

I wrote this for fun, which I had both in writing it and playing with it.

If you really need some use-cases to justify the electrons squandered in pursuit of this project, how about these (some lifted from Jasmin's README):

  • Curious People - Want to understand more about the way JVM bytecode works or the things that are possible at the bytecode level? Always wondered what invokedynamic is for? Curious as to how @SneakyThrows can possibly work? This might help!

  • System Implementors - If you're writing a JVM or runtime this could be useful for generating test classes...

  • Advanced Programmers - You could hand-generate critical classes or methods if you think Javac isn't doing the right thing (but spoiler alert: by this point, it almost certainly is).

  • Language Implementors - You could use this as an IL if you liked, rather than getting involved in the nuts and bolts of the binary .class format.

  • Security Researchers - Create hostile classes and see if you can sneak them past the class verifier.

  • Teachers - Perhaps you're teaching a compiler course, maybe you could use this to introduce students to JVM bytecode, or even as an IL for the compilers.

Why not just use Jasmin?

The venerable Jasmin project has been around for years, and has the advantage of being mature, stable, and well supported everywhere (for example, Github does syntax highlighting for it). So why not just use that?

Of course it's totally personal choice which you use, but there are a few reasons to choose JASM over Jasmin:

  • JASM supports all the modern features of the latest JVMs, such as
    • invokedynamic and dynamic constants
    • record classes etc
  • JASM has some nice "quality of life" features, such as automatically computing stack map frames / maxlocals for you
  • JASM is built on modern tooling, whereas Jasmin's code is showing its age a bit
    • This makes it easy, for example, to build Gradle and IntelliJ integration for JASM
  • JASM comes with a full-featured built-in disassembler
  • JASM has (IMHO) a cleaner syntax than Jasmin

Who?

JASM is copyright 2022 Ross Bamford (roscopeco AT gmail DOT com).

See LICENSE.md for the gory legal stuff (spoiler: MIT).

jasm's People

Contributors

roscopeco avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jasm's Issues

Instruction mnemonics are not usable as member names

Currently it is not possible to use instruction mnemonics (e.g. invokespecial, d2i, fconst etc) as member names.

These are perfectly valid JVM member names. The grammar should be extended to allow this.

Likely it'll need to allow the individual instruction tokens in the membername rule, which is a bit verbose, but probably the cleanest way to do it with Antlr.

gradlew check fails

$ ./gradlew check

> Task :compileKotlin
w: E:\msys64\home\topkek\t\a\jasm\src\main\kotlin\com\roscopeco\jasm\JasmDisassemblingVisitor.kt: (385, 27): This class shouldn't be used in Kotlin. Use kotlin.Int instead.
w: E:\msys64\home\topkek\t\a\jasm\src\main\kotlin\com\roscopeco\jasm\JasmDisassemblingVisitor.kt: (387, 27): This class shouldn't be used in Kotlin. Use kotlin.Long instead.
w: E:\msys64\home\topkek\t\a\jasm\src\main\kotlin\com\roscopeco\jasm\JasmDisassemblingVisitor.kt: (389, 27): This class shouldn't be used in Kotlin. Use kotlin.Float instead.
w: E:\msys64\home\topkek\t\a\jasm\src\main\kotlin\com\roscopeco\jasm\JasmDisassemblingVisitor.kt: (391, 27): This class shouldn't be used in Kotlin. Use kotlin.Double instead.

> Task :compileTestKotlin
w: E:\msys64\home\topkek\t\a\jasm\src\test\kotlin\com\roscopeco\jasm\asserts\AnnotatableItemAssert.kt: (23, 60): Unchecked cast: AnnotatableItemAssert<Self, Actual> to Self
w: E:\msys64\home\topkek\t\a\jasm\src\test\kotlin\com\roscopeco\jasm\asserts\AnnotatableItemAssert.kt: (31, 91): Unchecked cast: AnnotatableItemAssert<Self, Actual> to Self
w: E:\msys64\home\topkek\t\a\jasm\src\test\kotlin\com\roscopeco\jasm\asserts\AnnotationAssert.kt: (34, 70): Unchecked cast: Any? to Array<String>

> Task :compileTestJava FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileTestJava'.
> java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid'

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 42s
9 actionable tasks: 9 executed

Annotations?

There doesn't currently seem to be any way to apply annotation to things is jasm files. Some frameworks and libraries depend on runtime annotations and would thus require class files intended for use with them to have it.

If this is a feature then examples of the syntax would be useful

Improve String escapes

Currently, JASM supports only basic escaping in Strings (using "" to allow strings to contain double-quotes).

Going forward, we should probably have better support for special characters in strings, using the standard backslash escapes.

How do I run this from the command line?

The BAT file you included depends a ton on environment variables that are NOT existing as part of Windows, or even as part of any JDK installation. They are only set by certain IDEs like Eclipse. As such, the provided BAT file to run this assembler is useless for running in a command line. Nor are any of your instructions on the readme here helpful for helping me to get it running from a command line.

jasm reporting [BUG]: <Unknown> [java.lang.NullPointerException

Hello! I'm using jasm in a "toy compiler". It's an amazing project :-)

I assembled a lot of jasm files, without any problem. But, with the example bellow, I got a "[BUG]: [java.lang.NullPointerException".

I'm using jasm 0.7.0, installed from the binary distribution.

public class if_with_integers_with_and_or {
	public static main([java/lang/String)V {
		sipush 2
		sipush 1
		if_icmple L3
		iconst_1 
		goto L4
		L3:
		iconst_0 
		L4:
		ifeq L1
		getstatic java/lang/System.out java/io/PrintStream
		ldc "true"
		invokevirtual java/io/PrintStream.print(java/lang/String)V
		getstatic java/lang/System.out java/io/PrintStream
		invokevirtual java/io/PrintStream.println()V
		goto L2
		L1:
		getstatic java/lang/System.out java/io/PrintStream
		ldc "false"
		invokevirtual java/io/PrintStream.print(java/lang/String)V
		getstatic java/lang/System.out java/io/PrintStream
		invokevirtual java/io/PrintStream.println()V
		L2:
		return
	}
}

Could someone help me?

Regards.

Runtime invisible annotation support, aka "class"-level annotations

Hi,

I'm currently looking for an assembler to use for writing tests for a transpiler I am developing, and came across this project.

The transpiler searches for methods to transpile based on an annotation either present on the declaring type of a method, or on the method itself. The annotation has retention level CLASS; it is invisible at runtime, but still present in the class file.

Objectweb's ASM represents these annotations as entries in the "invisible annotation" list, and my transpiler checks for the annotation in that list.

Your project seems to treat every annotation in the source file as a runtime visible annotation tho, which results in all annotations being present in the "visible annotation" list, instead of the "invisible annotation" list.

Example:

Source:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
@interface Annot {}

@Annot
public class Hi {}

Jasm:

@Annot
public class Hi extends java/lang/Object {
  public <init>()V {
    aload 0
    invokespecial java/lang/Object.<init>()V
    return
  }
}

Compiling and loading the source version, there is one annotation in the "invisible annotation" list, that being Annot. Doing the same thing for the jasm version results in the Annot annotation being placed in the "visible annotation" list instead.

Code responsible: JasmAssemblingVisitor at line 83: visitor.visitAnnotation(..., true). true means "runtime visible".

I understand this is just a minor nitpick, but I still wanted to make sure you know about this. Is runtime invisible annotation support planned?

Disassemble NPE

catchType can be null to catch all exceptions (finally blocks) according to the asm docs

java.lang.NullPointerException: Parameter specified as non-null is null: method com.roscopeco.jasm.JasmDisassemblingVisitor$JasmDisassemblingMethodVisitor.visitTryCatchBlock, parameter type
	at com.roscopeco.jasm.JasmDisassemblingVisitor$JasmDisassemblingMethodVisitor.visitTryCatchBlock(JasmDisassemblingVisitor.kt)
	at org.objectweb.asm.ClassReader.readCode(ClassReader.java:1857)
	at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1514)
	at org.objectweb.asm.ClassReader.accept(ClassReader.java:744)
	at org.objectweb.asm.ClassReader.accept(ClassReader.java:424)
	at com.roscopeco.jasm.JasmDisassembler.disassemble(JasmDisassembler.kt:42)

Convert Class Files to Jasm

Please create a tool to convert .class files into .jasm files. This will make reading .class files much easier than the output from javap.

Cannot read field "outgoingEdges" because "handlerRangeBlock" is null

When trying to disassemble and re-assemble RasterPrinterJob.class from Eclipse Temurin's JRE build of OpenJDK 17.0.5+8, I get the following error:

$ jasm -d java.desktop/sun/print/RasterPrinterJob.class
$ jasm java.desktop/sun/print/RasterPrinterJob.jasm -o out
ERROR: There were failed tasks: 

RasterPrinterJob.jasm : Cannot read field "outgoingEdges" because "handlerRangeBlock" is null

I have uploaded RasterPrinterJob.class and RasterPrinterJob.jasm to https://gist.github.com/tyilo/ffeb20c26cc619a319abfb7ef3448990

Support literal names in disassembler

Literal names are not currently emitted by the disassembler. Going forward, where the disassembler detects that a name needs escaping as a literal name (enclosing in backticks) it should do so.

Error running README.md example

public class com/example/HelloWorld {
  public static main([java/lang/String)V {
    getstatic java/lang/System.out
    ldc "Hello, World"
    invokevirtual java/io/PrintStream.println(java/lang/String)V
    return
  }
}

Error Message

Index 0 out of bounds for length 0

I suspect the error is on the getstatic java/lang/System.out line, due to the missing import.
How do I write import java.lang.*; in JASM?

Parser issues with names and string escape

The nature of this issue is that there are alot of open problems about dealing with technially jvm valid input but invalid input regarding to antlr or jasm

public class java/lang/Example {
    public test()V {
        getstatic 1.0 I
    }
}

Results in: test.j : Cannot invoke "org.antlr.v4.runtime.tree.RuleNode.getChildCount()" because "node" is null

This should work because 1.0 is a valid path descriptor

Any class, extends, implements, method or field declaration fails when the access modifiers are used as names, but they should because they are valid jvm names
Example

public class public
extends private
implements final, static, private {

}

Is technically valid by the JVM but jasm errors with: test.j : Cannot invoke "org.antlr.v4.runtime.tree.TerminalNode.getText()" because the return value of "com.roscopeco.jasm.antlr.JasmParser$ClassnameContext.QNAME()" is null

It is impossible to name lables, variables, methods after any instruction and it will fail with: test.j : Cannot read field "inputLocals" because "dstFrame" is null in the goto case.

Example:

public class java/lang/Example {
	
    public test()V {
        goto ldc
		
		ldc:
			return
    }
}

It is also impossible for invokex descriptors to contain any keywords

Example:

public class java/lang/Example {
	
    public test()V {
        invokevirtual ldc/private.final(goto)V
    }
}

Also string escape is not working on example:

public class java/lang/Example {
	
    public test()V {
        ldc "\"\\\\\\\""
    }
	
}

Which should just result in the string '"\"' but instead errors with: test.j : Errors: test.j:[4:22]: extraneous input '""'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.