I changed the compilation of a project from Ant to Maven. We still use Java 1.7.0_80. The ByteCode of all classes stayed the same except for a Comparator class. I disassembled the class with javap -c. The only differences are in #-values, e.g.:
before: invokevirtual #2
after: invokevirtual #29
What do these #-values mean? Is this a functional difference or just a difference in naming?
EDIT: The first method in Bytecode before and after:
public de.continentale.kvrl.model.comp.KvRLRechBetragEuroComparator();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public de.continentale.kvrl.model.comp.KvRLRechBetragEuroComparator();
Code:
0: aload_0
1: invokespecial #21 // Method java/lang/Object."<init>":()V
4: return
The #29 refers to an index in the constant table that describes the method being invoked. It would have been better if you had pasted the full output of javap -c, because the comment at the end of the line indicates the actual method descriptor.
Example of javap -c on an invocation of an instance method test in class Z:
7: invokevirtual #19 // Method snippet/Z.test:()V
You can ignore the # number, as long as the method descriptor in the comment is the same.
Why
As to the "why" question, it is very likely caused by the debug in the ant and maven compiler task/plugin settings.
By default, the maven-compiler-plugin compiles with the debug configuration option set to true, which will attach among other the name of the source file, and thing like line number tables and local variable names.
https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html
By default, the ant java compiler has this set to false.
https://ant.apache.org/manual/Tasks/javac.html
To compare and check, set the debug parameter of the Ant Javac task to true and see if that makes the generated .class files the same.
Related
Currently I am tinkering with the jvm bytecode instructions. I made a simple compiler that given source code (C like style) generates valid jvm bytecode representation. For example, the following code:
float x = 3;
float y = 4.5;
float z = x + y;
print z;
Compiles to:
ldc 3
i2f
fstore 1
ldc 4.5
fstore 2
fload 1
fload 2
fadd
fstore 3
getstatic java/lang/System/out Ljava/io/PrintStream;
fload 3
invokevirtual java/io/PrintStream/println(F)V
return
(I know the generated java code is not the most efficient as of now, but that is not the point).
Using a Java Bytecode Editor, I loaded a compiled main class and replaced the main method code with my code. After that, I was able to run the class file with my code perfectly fine. My question is, is there a tool/script without UI that can take java bytecode and generate the appropriate headers for the class file (in other words, take the bytecode and make a valid class file out of it). I guess I can write a script myself, but that would take some time that I might not have now.
The Krakatau assembler allows you to write bytecode in a textual format and assembles it into a classfile, handling all the binary encoding details for you.
It's similar to the older Jasmin assembler, but with minor syntax changes in order to remove ambiguity and to support classfile features that Jasmin can't handle. Unlike Jasmin, it fully supports the entire Java 8 classfile format and optionally allows full control over the binary representation of the classfile.
For example, here's a class using lambdas in Krakatau assembly format.
.version 52 0
.class public super LambdaTest1
.super java/lang/Object
.method public <init> : ()V
.code stack 1 locals 1
aload_0
invokespecial Method java/lang/Object <init> ()V
return
.end code
.end method
.method public static varargs main : ([Ljava/lang/String;)V
.code stack 4 locals 2
invokedynamic InvokeDynamic invokeStatic Method java/lang/invoke/LambdaMetafactory metafactory (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; MethodType (J)J MethodHandle invokeStatic Method LambdaTest1 lambda$main$0 (J)J MethodType (J)J : applyAsLong ()Ljava/util/function/LongUnaryOperator;
astore_1
getstatic Field java/lang/System out Ljava/io/PrintStream;
aload_1
ldc2_w 42L
invokeinterface InterfaceMethod java/util/function/LongUnaryOperator applyAsLong (J)J 3
invokevirtual Method java/io/PrintStream println (J)V
return
.end code
.end method
.method private static synthetic lambda$main$0 : (J)J
.code stack 4 locals 2
lload_0
lload_0
l2i
lshl
lreturn
.end code
.end method
.innerclasses
java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup public static final
.end innerclasses
.end class
In the coverage result, it shows that I've covered 9 instructions while there are only 5 lines highlighted green. Which are the other 4 instructions?
Click the dropdown arrow at the top right of the Coverage box. It'll give you a couple different ways to measure your coverage. The default seems to be instructions (bytecode instructions), but you can manually select lines.
The reason you are seeing 9 instructions is because there are 9 bytecode instructions in Foo:
$ javap -c Foo.class
Compiled from "Foo.java"
public class Foo {
public Foo();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #22 // String Test
5: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: new #1 // class Foo
11: invokespecial #30 // Method "<init>":()V
14: return
}
As #schmosel says, it is counting bytecode instructions.
You can verify this by reading the EMMA reference documentation (EclEMMA is an Eclipse GUI wrapped around EMMA), in which the phrase "bytecode instructions" is used throughout.
I am currently writing my own compiler and I am trying to compile the following code:
List[String] list = List("a", "b", "c", "d")
list stream map((String s) => s.toUpperCase())
System out println list
The compiler has no problem parsing, linking or compiling the code, but when it comes to executing it, the JVM throws the following error:
java.lang.VerifyError: Illegal type at constant pool entry 40 in class dyvil.test.Main
Exception Details:
Location:
dyvil/test/Main.main([Ljava/lang/String;)V #29: invokevirtual
Reason:
Constant pool index 40 is invalid
Bytecode:
...
I tried to use javap to find the problem, and this is the instruction #29:
29: invokevirtual #40 // InterfaceMethod java/util/Collection.stream:()Ljava/util/stream/Stream;
And the entry in the Constant Pool (also using javap):
#37 = Utf8 stream
#38 = Utf8 ()Ljava/util/stream/Stream;
#39 = NameAndType #37:#38 // stream:()Ljava/util/stream/Stream;
#40 = InterfaceMethodref #36.#39 // java/util/Collection.stream:()Ljava/util/stream/Stream;
When opening the class with the Eclipse Class File Viewer, the line where #29 should be reads:
Class Format Exception
and all following instructions are not shown anymore (except for Locals, ...). However, the ASM Bytecode Plugin writes
INVOKEVIRTUAL java/util/Collection.stream ()Ljava/util/stream/Stream;
at that line, which appears to be valid. What am I doing wrong / missing here?
I figured out my mistake. The error lies here:
invokevirtual #40 // InterfaceMethod
^^^^^^^ ^^^^^^^^^
I am using invokevirtual on an interface method, which is generally not a good idea. However I think that the error thrown by the verifier should be a bit more clear about what is actually wrong.
With ASM you will see this error because of an incorrect isInterface flag. The argument isInterface of visitMethodInsn refers to the target/owner/destination and not the current context.
i.o.w when using INVOKEVIRTUAL, isInterface is false.
see issue here for more details.
I used MAT tool in Eclipse to investigate a memory leak issue. I found that the leak was caused by an anonymous inner class instance in my app. The class name displayed in MAT is com.mycompany.myproduct.MyActivity$3. There are many anonymous inner classes defined in MyActivity.java. How do I know the which inner class com.mycompany.myproduct.MyActivity$3 indicates?
Thanks.
On the Oracle compiler, they're numbered in order of occurrence in the class. I'm not sure if that's part of any specification or consistent with other implementations.
You could decompile the class--JD-GUI is a great tool for that--and then you'll see what you want to know. You could even just go with basic disassembly using javap -c. It'll give you a rough idea of where the classes occur.
Hint: debugger somehow knows which classes are where. So you can, too!
Try using javap on this example with two anonymous classes:
import java.util.*;
public class Test {
public static void main(String [] args) {
Map m = new HashMap(){{System.out.print(1);}};
Map m1 = new HashMap(){{System.out.print(2);}};
}
}
Compile it and run javap -c -l:
$ javap -c -l Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
Code:
0: new #2; //class Test$1
3: dup
4: invokespecial #3; //Method Test$1."<init>":()V
7: astore_1
8: new #4; //class Test$2
11: dup
12: invokespecial #5; //Method Test$2."<init>":()V
15: astore_2
16: return
LineNumberTable:
line 5: 0
line 7: 8
line 9: 16
}
As you can see, the first class got name Test$1, the second oneāTest$2. Hope tat helps.
For more specific information, decompile the specific classes you're interested in, e.g. javap -c -l Test\$2. Pay attention to line numbers: they will give you a hint on where in the source file the class was defined.
When you compile your code securely you have MyActivity$1.class, MyActivity$2.class, MyActivity$3.class, and so on. You can use a java decompiler (over your .class) in order to identify the anonymus class that is throwing the exception.
The whole point of anonymous classes is that they are just that. As you have found its not easy to figure out which one its come from. Usually the numbering starts at one, so my guess is that it will be the third declared anonymous class that is your problem.
In this situation, you maybe better off to refactor your code to not have any anonymous classes. Otherwise I suggest attaching the debugger and stepping the code.
I'm translating this code to Clojure. As you can see, I have to extend the class ArthurFrame, yet I'm getting an IllegalAccessError everytime I use (proxy [ArthurFrame] [] ...).
Any idea why? Here is the class's source.
Thanks!
EDIT: Here is the full error stack for running (proxy [ArthurFrame] []) on the REPL.
EDIT 2: Actually it seems that even instantiating the class yields an error. Here is the output from (ArthurFrame. wid):
tried to access class com.trolltech.demos.ArthurFrame from class user$eval__2205
[Thrown class java.lang.IllegalAccessError]
Restarts:
0: [ABORT] Return to SLIME's top level.
Backtrace:
0: user$eval__2205.invoke(NO_SOURCE_FILE:1)
1: clojure.lang.Compiler.eval(Compiler.java:4642)
2: clojure.core$eval__5254.invoke(core.clj:2031)
3: swank.commands.basic$eval_region__907.invoke(basic.clj:40)
4: swank.commands.basic$eval_region__907.invoke(basic.clj:31)
5: swank.commands.basic$eval__927$listener_eval__929.invoke(basic.clj:54)
6: clojure.lang.Var.invoke(Var.java:359)
7: user$eval__2202.invoke(NO_SOURCE_FILE)
8: clojure.lang.Compiler.eval(Compiler.java:4642)
9: clojure.core$eval__5254.invoke(core.clj:2031)
10: swank.core$eval_in_emacs_package__455.invoke(core.clj:59)
11: swank.core$eval_for_emacs__533.invoke(core.clj:128)
12: clojure.lang.Var.invoke(Var.java:367)
13: clojure.lang.AFn.applyToHelper(AFn.java:179)
14: clojure.lang.Var.applyTo(Var.java:476)
15: clojure.core$apply__4379.invoke(core.clj:434)
16: swank.core$eval_from_control__458.invoke(core.clj:66)
17: swank.core$eval_loop__461.invoke(core.clj:71)
18: swank.core$spawn_repl_thread__595$fn__627$fn__629.invoke(core.clj:183)
19: clojure.lang.AFn.applyToHelper(AFn.java:171)
20: clojure.lang.AFn.applyTo(AFn.java:164)
21: clojure.core$apply__4379.invoke(core.clj:434)
22: swank.core$spawn_repl_thread__595$fn__627.doInvoke(core.clj:180)
23: clojure.lang.RestFn.invoke(RestFn.java:402)
24: clojure.lang.AFn.run(AFn.java:37)
25: java.lang.Thread.run(Thread.java:619)
The problem is that ArthurFrame visibility is package not public so the proxy can't access it since the proxy doesn't belong to the package com.trolltech.demos. You have to make ArthurFrame public.
The most likely cause of your problem is that something has not been recompiled. Here's the javadoc description of the IllegalAccessError exception:
Thrown if an application attempts to access or modify a field, or to call a method that it does not have access to.
Normally, this error is caught by the compiler; this error can only occur at run time if the definition of a class has incompatibly changed.
To be more specific, this normally happens when you have classes A and B, where B depends on some members of A. Then you do something like this:
You compile A, then B.
Make an incompatible change to A and recompile it without recompiling B. In this case, the change would involve reducing the visibility of some member of A that B uses so that the member should no longer be visible to B.
Run an application that uses A and B and and you will get an IllegalAccessError.
EDIT
The proxy class that is trying to do the accessing looks like it must have been generated by the Clojure compiler. So maybe there is a Clojure compiler bug ... or maybe you changed the visibility of ArthurFrame after you ran the Clojure compiler. Either way, one possible fix is to change the visibility of the ArthurFrame to public.