On Nov 3, 2021, at 4:05 PM, Dan Smith 
<daniel.sm...@oracle.com<mailto:daniel.sm...@oracle.com>> wrote:

(It is, I suppose, part of the model that objects of a given class all have a 
finite, matching layout when accessed by value, even if the details of that 
layout are kept abstract. Which is why value types are monomorphic and you need 
reference types for polymorphism.)

The fact that the VM often discards object headers at runtime is a pure 
optimization.

Let’s see what happens if we say that (a) bare values have headers and (b) 
Object::getClass allows the user to observe part of the header contents.

It follows then that the expression aPointVal.getClass() will show the contents 
of aPointVal’s header, even if it is a compile-time constant.

Point pv = new Point(42,42);  // “class Point” is the definition of Point
assert pv.getClass() == Point.class;  // ok, that’s certainly the class
assert pv.getClass() != Point.ref.class;  // and it’s not a ref, so good

That is all fine.  There’s a little hiccup when you “box” the point and get the 
same Class mirror even though the “header” is a very real-heap resident value 
now:

Point.ref pr = pv;  // same object… now it’s on the heap, though, with a real 
live heap header
assert pr.getClass() == Point.class;  // same class, but...
assert pr.getClass() != Point.ref.class;  // we suppress any distinction the 
heap header might provide

There’s a bigger hiccup when you compare all that with good old int:

int iv = 42;  // “class int” is NOT a thing, but “class Integer” is
assert iv.getClass() != int.class;  // because int is not a class
assert iv.getClass() == Integer.class;  // ah, there’s the class!
assert iv.getClass() == int.ref.class;  // this works differently from Point
assert ((Object)iv).getClass() == pr.getClass();  // this should be true also, 
right?

And to finish out the combinations:

int.ref ir = iv;  // same object… now it’s on the heap, though, with a real 
live heap header
assert ir.getClass() == Integer.class;  // same class
assert ir.getClass() == int.ref.class;  // and this time it’s a ref-class (only 
for classic primitives)
assert ir.getClass() != int.class;

All this has some odd irregularities when you compare what Point does and what 
int does.  And yet it’s probably the least-bad thing we can do.

A bad response would be to follow the bad precedent of ir.getClass() == 
Integer.class off the cliff, and have pv.getClass() and pr.getClass() return 
Point.ref.class.  That way, getClass() only returns a ref.  Get it, see, 
getClass() can only return reference types.  The rejoinder (which Brian made to 
me when I aired it) is devastating:  Point.class is the class, not 
Point.ref.class, and the method is named “get-class”.

Another approach would be to fiddle with the definitions of val.getClass(), so 
as to align iv.getClass() with pv.getClass() with their non-ref types.  But 
that still leaves pv.getClass() unaligned (in its non-ref-ness) with 
ir.getClass() (in its ref-ness).  We still expect Point.class as the answer 
from *both* pr.getClass() and pv.getClass().

Or we could try to make the problem go away by simply outlawing (statically) 
instances of expr.getClass() that expose inconvenient answers.  Such moves 
score high on the “Those Idiots” score card.  And they still doesn’t align the 
ref-ness of pr.getClass() vs. ir.getClass().

Maybe we only earn partial Idiot Points if we outlaw iv.getClass() but allow 
pv.getClass()?  Same amount of seam, different shape of seam, IMO.

Another source of constraint is that we expect that up-casting anything to 
Object and then re-querying should not change the answer.  (This is another way 
of saying that the header should stay the same whether it is in the heap or 
not.)  It is one of the reasons that iv.getClass() should not return int.class.

assert ((Object)pv).getClass() == pv.getClass();  // this should be true also, 
right?
assert ((Object)pr).getClass() == pr.getClass();  // this should be true also, 
right?
assert ((Object)iv).getClass() == iv.getClass();  // this should be true also, 
right?
assert ((Object)ir).getClass() == ir.getClass();  // this should be true also, 
right?

This is an over-constrained problem.  I don’t know how to make it look more 
regular, and I think (after doing some more exhaustive analysis off-line) there 
aren’t any other ideas we haven’t examined.

(I’m saying that partly in a superstitious hope that, having said it, someone 
will of course prove me wrong.)


I'm claiming this picture makes explaining the feature harder, unnecessarily. 
An unhoused value floating around somewhere that I can somehow have a reference 
to strikes me as quite exotic. Tell me it's just an object and I feel calmer.

Yes, it's just an object. :-)

But not quite how you mean. The new feature here is working with objects 
*directly*, without references. I think one thing you're struggling with is 
that your concept of "object" includes the reference, and if we take that away, 
it doesn't quite seem like an object anymore.

The lack of “null” in the value set is a small but persistent hint that 
something has changed in the object representation.

We can double down on the model that a val-object has a header.  It’s not in 
the heap; it has a statically defined value; it exists (if at all) to assist 
with Object::getClass and the other methods as needed.  It feeds getClass with 
the val-projection, not the ref-projection.

We are so sorry, Mr. int.  You don’t really pass as a primitive class.  If an 
int has a header (on stack or on heap), it feeds getClass with the 
ref-projection Integer.class, not the val-projection int.class, because your 
class is Integer, a ref-type (one of 8 or 9 such types).  It’s a seam.

BTW, here’s another look at the difference between Mr. int and Mr. Point:

var pv = new Point(42,42);  // var infers Point (a val type)
assert new Point(42,42).getClass() == Point.class;  // OK

//var pr = new Point.ref(42,42);  // nope, Point.ref is not “class Point”
//assert new Point.ref(42,42).getClass() == Point.ref.class;  // cannot ask 
this question

var ir = new Integer(42);  // var infers Integer (a ref type)
assert new Integer(42).getClass() == Integer.class;  // OK, but I don’t like 
Integer as much as Point

//var iv = new int(42);  // sorry, Mr. int, you don’t get to play there
//assert new int(42).getClass() == int.class;  // cannot ask this question

Did I get all the details right, Dan and Brian?

— John

Reply via email to