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 ;-)
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,
Using ByteBuddy how can I create enum with constructors such as this one :
public enum EnumConstructorSample {
STATE1(10),
STATE2(15);
public int count;
EnumConstructorSample(int count){
this.count = count;
}
}
I tried this code and it gives me error.
Class enumClass = new ByteBuddy().makeEnumeration("STATE1", "STATE2")
.name("DynamicEnum")
.defineConstructor(Visibility.PACKAGE_PRIVATE)
.withParameters(int.class)
.intercept(FixedValue.value(1))
.make()
.load(EnumWithConstructor.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
System.out.println(enumClass.getDeclaredConstructors()[0]);
This is the Error and it is happening in enumClass.getDeclaredConstructors()
Exception in thread "main" java.lang.VerifyError: Constructor must call super() or this() before return
Exception Details:
Location:
DynamicEnum.<init>(I)V #2: return
Reason:
Error exists in the bytecode
Bytecode:
0x0000000: 0457 b1
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getDeclaredConstructors(Class.java:2020)
at EnumWithConstructor.main(EnumWithConstructor.java:19)
For constructors, it is required to invoke the super method within the constructor. For enumerations, you'd need to invoke the Enum(String, int) constructor. You can implement this using MethodCall.invoke(...).onSuper().
If you wanted to achieve this, I'd recommend you to subclass Enum manually since you'd otherwise define multiple constructors for the enum you are creating where Byte Buddy would invoke its own enum constructor and the fields would all have its default value.
Rather, implement the method and return the value based on its name. You can for example use a MethodDelegation and then use a #This Enum<?> val injection where you switch over the name to return the correct value as if it was stored in a field.
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
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
/clr:pure switch generates pure MSIL but it is not verifible. Native array and pointer can be used in this mode. Does that mean that there is a structure in MSIL to hold native arrays and pointers? If yes, I would like to ask how can I code MSIL native array and pointer?
Yes, there is a type in CIL to represent unmanaged pointers. They are similar to managed pointers (ref and out in C#, & in CIL), except that GC ignores them and you can do some arithmetic operations on them (those that make sense with pointers).
Interestingly, the pointer type does contain information about the target type (so it's e.g. int32*), but all arithmetic operations are byte based.
As an example, the following C++/CLI method:
void Bar(int *a)
{
a[5] = 15;
}
produces the following CIL when it's inside a ref class (as reported by Reflector):
.method private hidebysig instance void Bar(int32* a) cil managed
{
.maxstack 2
L_0000: ldarg.1 // load the value of a pointer to the stack
L_0001: ldc.i4.s 20 // load the number 20 (= 4 * 5) to the stack
L_0003: add // add 20 to the pointer
L_0004: ldc.i4.s 15 // load the number 15 to the stack
L_0006: stind.i4 // store the value of 15 at the computed address
L_0007: ret // return from the method
}