> 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
> [...]
>
[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