Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / Virtual Machine / May 2004

Tip: Looking for answers? Try searching our database.

Verifier problem

Thread view: 
Urs Keller - 30 Apr 2004 12:41 GMT
Hi

Suppose I have the following byte code:

public Simple0$();
  Code:
   0:   aload_0
   1:   putstatic       #25; //Field MODULE$:LSimple0$;
   4:   aload_0
   5:   invokespecial   #43; //Method java/lang/Object."<init>":()V
   8:   aload_0
   9:   iconst_1
   10:  putfield        #40; //Field Simple0$one$:I
   13:  iconst_0
   14:  return

For a constructor. Basically I put the reference to the instance of the
class (not initialized) in a static field in the same class.

The verifier says something like:
Exception in thread "main" java.lang.VerifyError: (class: Simple0$,
method: <init> signature: ()V) Expecting to find object/array on stack

Looking at the JVM specification section "4.8 Constraints on Java
Virtual Machine Code" in subsection structural and static constraints, I
can't really see that any of these apply.

So which one of the constraint is it, or what am I doing wrong.

Thanks
-Urs
Chris Uppal - 30 Apr 2004 13:42 GMT
> For a constructor. Basically I put the reference to the instance of the
> class (not initialized) in a static field in the same class.

I don't think it's intended to be legal to expose any instance before its
<init> method has been called.  In this case you are exposing the instance
before the super-class's <init> has been called, which is essentially the same
idea.  I can't find a /specific/ prohibition against it, but the general drift
of section 4.9.4 makes it pretty clear that it won't be tolerated.

I'm curious about why you are wanting to do this ?

> The verifier says something like:
> Exception in thread "main" java.lang.VerifyError: (class: Simple0$,
> method: <init> signature: ()V) Expecting to find object/array on stack

Presumably because the verifier treats the value in local 0 as being of the
"special" type representing an uninitialised instance (as described in the same
section) until the call to the superclass's <init> method.  Hence the
assignment to the static field cannot satisfy the type checking if it occurs
before the super <init>.

BTW, and FWIW, the last but one opcode:

>     13:  iconst_0

has no effect.

   -- chris
Urs Keller - 30 Apr 2004 16:07 GMT
Hi
Thanks for pointing me to section 4.9.4.
I think the part:
"... After an appropriate instance initialization method is invoked
(from the current class or the current superclass) on this object, all
occurrences of this special type on the verifier's model of the operand
stack and in the local variable array are replaced by the current class
type ... "

Describes pretty well what happens. It basically replaces one occurence
of a reference with a new one (so the reference changes after calling
super). And it will replace those references only on the operand stack
and in local variables. But not in other variables in particular static
fields in the Class.

The reason, why I wanted to do this, is that it is easier to add
instructions unconditionally at the beginning of a code array than to
search the code array for the first super.<init> call. In particular if
the compiler phase your working in has lost the notion of if a call is a
call to super or not.

-Urs

>>For a constructor. Basically I put the reference to the instance of the
>>class (not initialized) in a static field in the same class.
[quoted text clipped - 24 lines]
>
>     -- chris
Chris Uppal - 30 Apr 2004 19:45 GMT
> Thanks for pointing me to section 4.9.4.
> I think the part:
[quoted text clipped - 5 lines]
> and in local variables. But not in other variables in particular static
> fields in the Class.

Well, I may be misunderstanding you, but I feel I ought to point out that it's
not that the references are /really/ changed by the running JVM; it's just that
the verifier's type-checking algorithm pretends that happens because its an
easy way to prevent misuse of references to uninitialised objects.

> The reason, why I wanted to do this, is that it is easier to add
> instructions unconditionally at the beginning of a code array than to
> search the code array for the first super.<init> call.

Ah, yes, that makes sense.  Thanks for explaining.  Sadly, you are going to
have to do it the hard way after all :-(

   -- chris
David Hopwood - 01 May 2004 19:53 GMT
>>Thanks for pointing me to section 4.9.4.
>>I think the part:
[quoted text clipped - 10 lines]
> the verifier's type-checking algorithm pretends that happens because its an
> easy way to prevent misuse of references to uninitialised objects.

I wouldn't say that it was easy; a much simpler way would have been to have
constructors return a value of the correct type, and then it would not have
been necessary to treat them as a special case. It's an annoying design wart
(but not quite as annoying as, for example, the complexities of jsr/ret, or
double-sized stack items, or unification of interface types).

David Hopwood <david.nospam.hopwood@blueyonder.co.uk
Chris Uppal - 02 May 2004 11:54 GMT
> > [...] it's not that the references are /really/ changed by the running
> > JVM; it's just that the verifier's type-checking algorithm pretends
[quoted text clipped - 4 lines]
> have constructors return a value of the correct type, and then it would
> not have been necessary to treat them as a special case.

It would be simpler and cleaner, yes.  But, unless I am misunderstanding you,
it wouldn't have the same semantics as the current algorithm.  It would mean
that the "this" inside a constructor was of a different type to the "this"
inside any other method, and so would disallow Java code like:

   public SomeClass
   {
       public SomeClass()
       {
           this.resetToDefaults();
       }

       public void resetToDefaults()
       {
       }
   }

Alternatively, if the type of "this" inside a constructor was the same as
elsewhere, then Urs's code wouldn't have been forbidden.  Perhaps you mean
that his code "should" have been accepted ?

   -- chris
Boudewijn Dijkstra - 30 Apr 2004 17:52 GMT
> [...]
>
[quoted text clipped - 5 lines]
>
> So which one of the constraint is it, or what am I doing wrong.

I just got the same error, but this time in code that was compiled by Sun's
javac tool.  I wrote a program to test some things about overriding classes
and methods, at the same time experimenting with 68000-style arithmetic.
The program is this:

public class TestMethod
{
public static void main(String[] args)
{
 System.out.println("(-1) + (-1) = " + new I32(-1).addX(new I32(-1)));
}
}
abstract class I
{
abstract I.X startCascade();
abstract I add(I a);
abstract I.X addX(I a);

static interface X
{
 byte getCarry();
 I stopCascade();
}
}
class I32 extends I
{
int i;
I32(int i) {
 this.i = i;
}
I.X startCascade() {
 return new X(i);
}
I32 add(I32 a) {
 return new I32(i + a.i);
}
I add(I a) {
 if (a instanceof I32)
  return add((I32)a);
 return null;
}
I32.X addX(I32 a) {
 long s = (long) this.i + a.i;
 return new I32.X((byte) (s >> 32), (int) s);
}
I.X addX(I a) {
 if (a instanceof I32)
  return addX((I32)a);
 return null;
}
public String toString() {
 return String.valueOf(i);
}

class X extends I32 implements I.X
{
 byte carry;
 X(byte c, int i) {
  super(i);
  carry = c;
 }
 X(int i) {
  this((byte) 0, i);
 }
 public byte getCarry() {
  return carry;
 }
 public I stopCascade() {
  return I32.this;
 }
 public String toString() {
  return String.valueOf((long) carry << 32 + i);
 }
}
}

As I said, javac compiles this without problems.  But when I try to run it,
the following is printed:

java.lang.VerifyError: (class: I32$X, method: <init> signature: (LI32;I)V)
Expecting to find object/array on stack
at I32.addX(TestMethod.java:39)
at TestMethod.main(TestMethod.java:5)
Exception in thread "main"

As expected, the compiler inserts a private final I32 this$0 field into
I32$X and prepends an I32 reference to the I32$X constructor argument list.
Also, nothing in the bytecode looks abnormal to me, after a first glance.
Nonetheless, the compiler should produce valid code, or an error message.

What is going on?
Chris Uppal - 30 Apr 2004 19:44 GMT
> I just got the same error, but this time in code that was compiled by
> Sun's javac tool.

Wow!

> class I32 extends I
> {
[quoted text clipped - 9 lines]
>    this((byte) 0, i);
>   }

It looks as if the 1.4.x compiler is getting confused because the inner class
inherits from the outer.

The compiler generates code for the second constructor like this:

   1: aload_0
       aload_0
       iconst_0
       iload_2
       invokespecial I32$X/<init> (LI32;BI)V
   6: return

Notice that it is passing "this" as the added parameter to the call to <init>.
Which is wrong, the parameter should be set to the "outer" object that was
passed in.  Since the "this" isn't yet initialised at this point, the verifier
is refusing it.  But if it didn't then it'd still fail (and in bizarre ways, I
imagine) since the outer of the newly created object would end up pointing to
itself.  Fun ;-)

The code sequence /should/ be:

   1: aload_0
       aload_1                <-- the only difference
       iconst_0
       iload_2
       invokespecial I32$X/<init> (LI32;BI)V
   6: return

And that is indeed what the 1.5(beta) compiler generates.

   -- chris

P.S.  In case anyone's interested, a simplified example of the same bug is:

============
public class Test {
void doit() { new Nested(); }

class Nested
extends Test
{
 Nested(int x) { this(); }
 Nested() {}
}

public static void
main(String[] args)
{
 new Test().doit();
}
}
============
Boudewijn Dijkstra - 30 Apr 2004 21:13 GMT
> > I just got the same error, but this time in code that was compiled by
> > Sun's javac tool.
[quoted text clipped - 17 lines]
> It looks as if the 1.4.x compiler is getting confused because the inner class
> inherits from the outer.

That is not the sole problem.  If it were, then java.awt.geom.Point2D.Float
couldn't be compiled correctly either.  I looked into the source of that
class and I found out the the only difference is that none of the
Point2D.Float constructors use a this() constructor call.  So I rewrote the
single-argument I32.X constructor into this:

 X(int i) {
  super(i);
  carry = (byte) 0;
 }

and the program now runs without verifier problems.

> [...]
>
[quoted text clipped - 8 lines]
>
> And that is indeed what the 1.5(beta) compiler generates.

Good to hear that this bug is solved now.
Chris Uppal - 01 May 2004 12:35 GMT
> > It looks as if the 1.4.x compiler is getting confused because the inner
> > class inherits from the outer.
[quoted text clipped - 4 lines]
> difference is that none of the Point2D.Float constructors use a this()
> constructor call.

I agree; my little example will not work "work" without the explicit call of
this() either:

>   Nested(int x) { this(); }

   -- chris


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.