Here it says:
Since 2.1 :
[..]
added the invokedynamic instruction
Thus I suppose that it is possible to write instruction code containing invokedynamics with jasmin. However I could not find any documentation on the jasmin syntax and I just figured out how to use invokedynamic to get VerifyErrors with Jasmin, but not how to create a working example.
How is this instruction correctly used in Jasmin?
Each invokedynamic bytecode should refer to a corresponding call site specifier (JVMS 6.5) which is actually a constant pool entry of CONSTANT_InvokeDynamic type (JVMS 4.4.10).
Jasmin (http://jasmin.sourceforge.net) does not support CONSTANT_InvokeDynamic, but Sable/jasmin does. Though using invokedynamic in hand-written assembly is ungrateful job.
Here is an example of dynamic method that returns a reference to System.out:
.class public HelloWorld
.super java/lang/Object
.method public <init>()V
aload_0
invokespecial java/lang/Object/<init>()V
return
.end method
.method public static main([Ljava/lang/String;)V
.limit stack 2
.limit locals 1
invokedynamic "getPrintStream" ()Ljava/io/PrintStream; HelloWorld/bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;()
ldc "Hello, world"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
return
.end method
.method private static bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
.limit stack 6
.limit locals 3
new java/lang/invoke/ConstantCallSite
dup
aload_0
ldc java/lang/System
ldc "out"
ldc java/io/PrintStream
invokevirtual java/lang/invoke/MethodHandles$Lookup/findStaticGetter(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;
invokespecial java/lang/invoke/ConstantCallSite/<init>(Ljava/lang/invoke/MethodHandle;)V
areturn
.end method
Related
I have an old application which refuses to start on versions of Java that it was not tested on. Unfortunately it is an abandonware, so there is little chance for it to be updated. It is complaining that JVM is made by Oracle rather than Sun.
Back in days of MS DOS there was a setver command which allowed one to set a version of the operating system that an application saw, is there something similar to that on JVM?
I would use the asmtools from the OpenJDK project to disassemble the files to a bytecode assembly format.
So for example a helloworld looks like this (jdis NameOfClass.class>NameOfClass.j):
super public class A
version 59:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
public static Method main:"([Ljava/lang/String;)V"
stack 2 locals 1
{
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Hello World!";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
return;
}
} // end Class A
After that you edit the file:
super public class A
version 59:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
public static Method main:"([Ljava/lang/String;)V"
stack 2 locals 1
{
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Hello Stackoverflow!";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
return;
}
} // end Class A
And then you can compile it to .class again:
jasm NameOfClass.j
and then you can run it normally with Java.
This was just an example.
You would just have to find the class with the method that checks for the version. For example you could unzip the jar files and if for example the message for a non-matching version is, for example:
"Bad version detected: "
You can just grep for it (grep -R Bad.version) and then you get the class.
There you will have to change the method responsible for checking the Java version.
An example:
A.java:
public class A{
static boolean hasMatchingJavaVersion() {
return System.getProperty("java.version").matches("1.4.*");//Just Java 1.4
}
public static void main(String[] args) {
if(hasMatchingJavaVersion()) {
System.out.println("Matches");
} else {
System.out.println("Bad version");
System.exit(-1);
}
}
}
Run it: false (On Java 15, e.g.)
As assembly.
super public class A
version 59:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
static Method hasMatchingJavaVersion:"()Z"
stack 2 locals 0
{
ldc String "java.version";
invokestatic Method java/lang/System.getProperty:"(Ljava/lang/String;)Ljava/lang/String;";
ldc String "1.4.*";
invokevirtual Method java/lang/String.matches:"(Ljava/lang/String;)Z";
ireturn;
}
public static Method main:"([Ljava/lang/String;)V"
stack 2 locals 1
{
invokestatic Method hasMatchingJavaVersion:"()Z";
ifeq L17;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Matches";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
goto L29;
L17: stack_frame_type same;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Bad version";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
iconst_m1;
invokestatic Method java/lang/System.exit:"(I)V";
L29: stack_frame_type same;
return;
}
} // end Class A
This method checks for the version:
static Method hasMatchingJavaVersion:"()Z"
stack 2 locals 0
{
ldc String "java.version";
invokestatic Method java/lang/System.getProperty:"(Ljava/lang/String;)Ljava/lang/String;";
ldc String "1.4.*";
invokevirtual Method java/lang/String.matches:"(Ljava/lang/String;)Z";
ireturn;
}
So you could for example change it to:
static Method hasMatchingJavaVersion:"()Z"
stack 2 locals 0
{
iconst_1;//Load true, always return true
ireturn;
}
And then compile it again with jasm.
Edit:
that JVM is made by Oracle rather than Sun.
I overread this, but it works the same way as described above, just another system property and some other minor changes,
I'm using ASM to modify the bytecode of any java class to add a null-check for each object.
For example, for this piece of code:
Object a = new Object();
a.doSomething();
I want to modify the bytecode so it looks like this:
Object a = new Object();
assertNotNull(a);
a.doSomething();
or this:
Object a = new Object();
if(a != null){
a.doSomething();}
else return 1 //or throw exception
I'm stuck because I don't know what number go with ALOAD. I think ALOAD always go with ALOAD 1 (or in this format ALOAD n). What can I do to get the number that goes with ALOAD? Here's my work so far (using Junit assertNotNull)
/*
* Use JUnit assertNotNull to check object/item for null
*/
private void addAssertNullMethod() {
//need to add ALOAD here, but I don't know the location of the object on the stack.
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "org/junit/Assert", "assertNull", "(Ljava/lang/Object;)V", false);
mv.visitEnd();
}
You need DUP bytecode instead.
ALOAD refers to local variables, but there can be be no local variable slot assigned for a given object. Before calling doSomething(), an object reference is already put on the expression stack (with ALOAD bytecode or some other - it does not matter). So all you need is to copy this object reference (with DUP) and then invoke your assert method.
I'm writing a compiler that generates Jasmin code and I want to invoke a method using an argument, as follows:
val test(val x) {
return x;
}
val main (string[] args) {
test(1);
}
This compiles to:
.class public helloworld
.super java/lang/Object
.method public <init>()V
aload_0
invokenonvirtual java/lang/Object/<init>()V
return
.end method
.method public test(I)I
.limit stack 4
.limit locals 3
iload 1
ireturn
.end method
.method public static main([Ljava/lang/String;)V
.limit stack 4
.limit locals 3
aload_0
ldc 1
invokevirtual helloworld/test(I)I
return
.end method
However, this results in the following error. What am I doing wrong here?
java.lang.VerifyError: (class: helloworld, method: main signature: ([Ljava/lang/String;)V) Incompatible object argument for function call
To begin with, the code you 'compile' is not Java, and I'm not sure what it is (pseudo-code?).
The problem with your jasmin code is that it seems you want to call test() as an instance method of the array, which it isn't, because its an instance method of helloworld, which you don't instantiate.
So either you should instantiate the class helloworld, or make test() a static method, so that it can be called without creating an object.
When fixing the jasmin code I also ran into another error: iload 1 should be iload 0. (Programmers like to start counting at 0).
In this code I assume you meant test() to be static.
.class public helloworld
.super java/lang/Object
.method public <init>()V
aload_0
invokenonvirtual java/lang/Object/<init>()V
return
.end method
.method public static test([Ljava/lang/String;)[Ljava/lang/String;
.limit stack 4
.limit locals 3
aload 0
areturn
.end method
.method public static main([Ljava/lang/String;)V
.limit stack 4
.limit locals 3
ldc 1
anewarray java/lang/String
astore 2
aload 2
ldc 0
ldc "bar"
aastore
aload_0
aload 2
invokestatic helloworld/test([Ljava/lang/String;)[Ljava/lang/String;
return
.end method
I managed to isolate my problem in this test case:
.bytecode 50.0
.class public test
.super java/lang/Object
.field public static final foo1 J = 1
.method public <init>()V
.limit stack 1
.limit locals 1
.var 0 is this Ltest; from init_start to init_end
init_start:
aload_0
invokespecial java/lang/Object/<init>()V
init_end:
return
.end method
.method public static main([Ljava/lang/String;)V
.limit stack 1
.limit locals 1
main_start:
new test
invokespecial test/<init>()V
main_end:
return
.end method
When I try to run this, I get this:
$ jasmin test.j
$ java test
Exception in thread "main" java.lang.ClassFormatError: Inconsistent constant value type in class file test
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:277)
at java.net.URLClassLoader.access$000(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:212)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
Could not find the main class: test. Program will exit.
Some investigation(with javap) reveals this:
public static final long foo1;
Constant value: int 1
In other words, the value of the long constant is being stored in the class file as an integer.
Is there some way to force Jasmin to store the constant in the pool as a long?
I've tried 1L but it doesn't work.
Setting the value of foo1 to be a number greater than INT_MAX (such as 2147483648) does fix the error, but I was hoping for a solution that wouldn't require me to change the constant values.
Does Jasmin provide any way to do this or will this require me to change the source code of jasmin?
I've released a modified version of Jasmin that allows me to do this. It is available on https://github.com/luiscubal/jasmin
To use long constants on this modified version, do this:
.field public static foo J = 1l
same issue with exists with public static final double constants:
.field public static PHASE_1 D = 5.0
also seems to crash with the same Java runtime error.
I worked around it by removing the 'final' flag and moving the initialization to the constructor ;-)
Examining bytecode, I've noticed javac seems to duplicate checkcast instructions when casting to array types.
Cast.java:
class Cast {
void test(Object a) {
Object[] b = (Object[]) a;
}
}
javap disassembly of the javac compiled version
void test(java.lang.Object);
Code:
0: aload_1
1: checkcast #2; //class "[Ljava/lang/Object;"
4: checkcast #2; //class "[Ljava/lang/Object;"
7: astore_2
8: return
Testing jikes shows the expected single cast
void test(java.lang.Object);
Code:
0: aload_1
1: checkcast #10; //class "[Ljava/lang/Object;"
4: astore_2
5: return
checkcast is supposed to raise an exception if the object can't be treated as the requested type and otherwise does nothing, so I don't see why it might help to double the cast. I haven't looked at the JDK sources to see how it's produced, and if that helps explain the why (maybe it's meant as a hint).
It is a known bug of javac. But it is mostly harmless.