|
Quick Lists
|
|
Bug ID:
|
4381996
|
|
Votes
|
0
|
|
Synopsis
|
Java Bytecode Verification impossible [2]
|
|
Category
|
java:compiler
|
|
Reported Against
|
merlin-beta
, kest-sol-fcs
, merlin-beta2
, merlin-beta3
|
|
Release Fixed
|
1.4.2(mantis)
|
|
State
|
10-Fix Delivered,
Verified,
bug
|
|
Priority:
|
4-Low
|
|
Related Bugs
|
4494152
|
|
Submit Date
|
23-OCT-2000
|
|
Description
|
javac no longer generates the jsr instruction, instead inlining the finally
clause. Consequently, code with deeply nested try-finally blocks may generate
more byte code than before. In the extreme case, synthetic Java source code
that deeply nests try-finally blocks may generate more bytecode in a single
method than can be expressed in the bytecode file format, even though such code
used to compile without problems. This particular problem may be seen in some
JSP implementations. In general, you should avoid the try-finally language
construct in synthetic Java source code.
If you do run into this problem but you cannot modify the generator of your
synthetic code, you can use the following flag to cause javac to once again
generate jsr instructions for try-finally blocks
-XDjsrlimit=0
java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0)
Java HotSpot(TM) Client VM (build 1.3.0, mixed mode)
The following legal Java program is rejected by any Java Bytecode
verifier I have tried. The program shows that Java Bytecode Verification
is not possible as described in the Java Virtual Machine Specification.
Remark: The example program shows a different problem than in my
previous bug report. There are no "break" statements and labels involved
in this program.
public class Test {
int test(boolean b) {
int i;
try {
if (b) return 1;
i = 2;
} finally {
if (b) i = 3;
}
return i;
}
}
tomis> java -version
java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0)
Java HotSpot(TM) Client VM (build 1.3.0, mixed mode)
tomis> javac Test.java
tomis> java Test
Exception in thread "main" java.lang.VerifyError: (class: Test, method: test
signature: (Z)I) Register 2 contains wrong type
What is the problem? The variable i is modified by the subroutine S
corresponding to the finally block. The variable i, however, is
"unusable" at the end of S, since S is called before the "return 1" and
after the try block. When S is called before the "return 1" variable i
is "unusable" and it remains "unusable", since there is a path from the
beginning to the end of S which does not have an "istore i"
instruction. Hence, at the of the try block, the variable "i" is
unusable in the eyes of the bytecode verifier and therefore also at the
end of the method at the "return i" instruction.
Solution? Restrict the "rules of definite assignment" in the JLS for
16.2.14 "try statement" such that the above program is no longer legal.
http://java.sun.com/docs/books/jls/second_edition/html/defAssign.doc.html#26242
V is definitely assigned after the try statement iff the following
is true:
V is definitely assigned after the finally block.
(Review ID: 111235)
======================================================================
|
|
Work Around
|
N/A
|
|
Evaluation
|
I believe this is true.
xxxxx@xxxxx 2000-12-04
I believe these two bugs are caused by the interaction of jsr
generated by javac and the verifier. In a sense, they are
compiler bugs and not spec bugs because we could fix these
problems by not using jsr.
We are considering a new verifier scheme for 1.5 that would
require the compiler to stop generating jsr.
See also 4494152.
xxxxx@xxxxx 2002-01-17
The Mantis (1.4.2) javac compiler avoids using jsr instructions most
of the time. This test case will not fail in Mantis, though
with some effort one could construct a test case that does.
xxxxx@xxxxx 2002-07-08
Yes, compilers may choose not to generate jsrs (or generate them only in
simple cases). I would encourage compilers to stop the use of jsrs altogether.
Future versions of the class file format may deprecate jsrs.
So I am reclassifying this as a compiler bug.
xxxxx@xxxxx 2002-07-08
|
|
Comments
|
Submitted On 08-JUL-2003
svachalek
This is faulty logic: just because you can make a problem go
away by dropping a bomb on it doesn't make it a military
problem. It is clearly a verifier bug, code should not
verify differently just because you inlined a jsr. The
ramifications of this solution are much worse than the
original problem--possible exponential explosion of bytecode
( #4739388) and more importantly the breakage of many
bytecode based tools such as for coverage and metrics.
Submitted On 15-SEP-2003
rob_d_clark
+1 for svachalek's comment... how can you call this a compiler
bug?!? JSR serves a purpose (ie. not causing the method size
to explode with all the duplicated bytecode). PLEASE DO NOT
DEPRECATE JSR or JSR_W unless you are planning a reasonable
alternative (and inlining the finally is not reasonable)!
Submitted On 09-MAR-2004
astid
Consider this example:
int i = 1;
try
{
if(b == 0) j = j/k;
}
catch(ArithmeticException e)
{
// do something
}
finally
{
i = 2;
}
Regardless of exception occuring or not, dynamic code
analysis tools that operate on bytecode can correctly
infer that i=2 does not depend on any value inside the
try block - IF there is no inlining, and the finally block is
generated using JSR/RET.
with inlining this example gets equivalent to this on
bytecode level:
int i = 1;
try
{
if(b == 0) j = j/k;
i = 2;
}
catch(ArithmeticException e)
{
i = 2;
}
If b == k == 0, an analysis tool can no longer determine
this, because there is no longer a single i=2 statement
that is reachable from all paths in the code, but rather
there are now *two* statements, one for each path.
Therefore, if b==k==0 and an exception is thrown, i=2
in the catch block gets executed, and a runtime
analysis tool will incorrectly determine that the value of
i depends on the value of b (since the statement would
not execute if b weren't 0). Such runtime analysis tools
are becoming mainstream (dynamic slicing etc.) as
debugging and reengineering aids, and inlining finally
blocks will make their accuracy worse.
Submitted On 17-MAR-2004
staerk
Another solution for the problem would have been that the
javac compiler
inserts code that initializes local variables in certain
situations.
Consider the following try-finally statement:
try TryBlock finally FinallyBlock
If a variable x is definitely assigned after TryBlock but not
after FinallyBlock and if there is an assignment to x in
FinallyBlock, then the compiler has to insert code that
initializes x.
In the original example code, the variable `i' would be such a
candidate.
BTW: Microsofts .NET runtime system initializes *all* local
variables
with default values in verified code ...
Submitted On 03-JUL-2004
themis25
It's a valid java program rejected by the bytecode verifier
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|
|
|
 |