Issue 879 in v8: Why does Handle<T>::operator*() return T* instead of T& ?

1 view
Skip to first unread message

codesite...@google.com

unread,
Sep 24, 2010, 7:00:02 PM9/24/10
to v8-...@googlegroups.com
Status: New
Owner: ----

New issue 879 by spen...@jacknife.org: Why does Handle<T>::operator*()
return T* instead of T& ?
http://code.google.com/p/v8/issues/detail?id=879

On classes aiming to act like pointers, such as v8::Handle, operator->()
usually returns T*, but doesn't operator*() usually return T&?

By returning T*, you have to do a double-dereference to get a reference to
the actual type:

void foo(const String &arg) {}
//...
Handle<String> x = someFunction();
foo(**x); // unfortunate

Am I missing something obvious? :)

Of course, changing this probably breaks a bunch of code :(

Thanks.

codesite...@google.com

unread,
Sep 25, 2010, 4:50:04 AM9/25/10
to v8-...@googlegroups.com
Updates:
Status: WorkingAsIntended

Comment #1 on issue 879 by kmil...@chromium.org: Why does

Handle<T>::operator*() return T* instead of T& ?
http://code.google.com/p/v8/issues/detail?id=879

You really want to pass a String* to function foo.

String* is sort of a fiction. It is a (slightly mangled) address of an
object in V8's heap. It is not really a pointer to a C++ object of type
String.

To have a reference to a String, there would have to be a String.

The v8-users and v8-dev mailing lists are better places than the bug
tracker to ask questions like this :)

codesite...@google.com

unread,
Sep 25, 2010, 7:02:33 AM9/25/10
to v8-...@googlegroups.com

Comment #2 on issue 879 by mikhail.naganov: Why does Handle<T>::operator*()

I want to add that you should never work with pointers and refs to V8
objects, but use Handles instead. See the comment around line 160 in
include/v8.h:

All objects returned from v8 have to be tracked by the garbage
collector so that it knows that the objects are still alive. Also,
because the garbage collector may move objects, it is unsafe to
point directly to an object. Instead, all objects are stored in
handles which are known by the garbage collector and updated
whenever an object moves. Handles should always be passed by value
(except in cases like out-parameters) and they should never be
allocated on the heap.

Brad

unread,
Sep 25, 2010, 11:45:16 AM9/25/10
to v8-dev
Thanks for the responses on what I recognize is a "nit pick" on an
excellent library, despite my not using the mailing lists. :) I've
moved the discussion there (I think).

The Handle<T> class works to manage interaction with the GC and that
the String class is just C++ veneer to allow access to the underlying
GC-managed JavaScript strings. However, there is a C++ class String
with real C++ functions on it. And a Handle<String> is just a (smart)
pointer to an instance of that class.

The API actually guarantees that you can dereference a Handle<T> and
use the pointed-to instance directly. The documentation of Handle<T>
states:

* It is safe to extract the object stored in the handle by
* dereferencing the handle (for instance, to extract the Object* from
* an Handle<Object>); the value will still be governed by a handle
* behind the scenes and the same rules apply to these values as to
* their handles.

So, it is safe to pass a String*, because that's all that happens when
you pass a Handle<String> (since it's just a smart, wrapped up
String*), and the API documentation says you can. (Clearly, you still
need some kind of Handle managing the pointer during all this.)

If it is safe to pass String*, then it is safe to pass String&. They
are the same thing: a "pointer" to the address of a C++ String
instance. (After all, when you dereference a String*, you get a
String&.) Using String* instead of String& doesn't do anything to
guarantee that the pointed-to object still exists; it is the fact that
the String is ultimately managed by a Handle<String> that takes care
of that.

For these reasons, I don't understand how using String* is any safer
with regards to object lifetimes or interaction than String&, so I'm
not sure why it wouldn't be okay for operator*() to return the
conventional T* and thus offer the usual symmetry between p->f() and
(*p).f().

codesite...@google.com

unread,
Sep 25, 2010, 11:47:39 AM9/25/10
to v8-...@googlegroups.com

Comment #3 on issue 879 by spen...@jacknife.org: Why does
Handle<T>::operator*() return T* instead of T& ?
http://code.google.com/p/v8/issues/detail?id=879

I moved this discussion to the mailing list:
http://groups.google.com/group/v8-dev/browse_thread/thread/bee1a38f63a44dc8

Brad

unread,
Sep 25, 2010, 12:07:37 PM9/25/10
to v8-dev
On Sep 25, 12:45 pm, Brad <spen...@jacknife.org> wrote:
> The Handle<T> class works to manage interaction with the GC and that
> the String class is just C++ veneer to allow access to the underlying
> GC-managed JavaScript strings.  However, there is a C++ class String
> with real C++ functions on it.  And a Handle<String> is just a (smart)
> pointer to an instance of that class.

My last sentence above is wrong. The "this" pointer isn't really
pointing to an instance of String as allocated by, say, a String
constructor; it's (properly) cheating and using the this pointer
itself as the sole data member of the class. I get it.
Notwithstanding, the equivalence of String* and String& still stands,
as far as I can tell.

Kevin Millikin

unread,
Sep 25, 2010, 12:31:16 PM9/25/10
to v8-...@googlegroups.com
On Sat, Sep 25, 2010 at 5:45 PM, Brad <spe...@jacknife.org> wrote:
However, there is a C++ class String
with real C++ functions on it.  And a Handle<String> is just a (smart)
pointer to an instance of that class.

Not really.  Handle<String> is a smart pointer to String*.

There is no instance of String.  V8 never created an instance of class String.  There is nothing that guarantees that what one finds in memory at the address represented by a (tagged) String* corresponds to what a C++ compiler thinks should be an instance of String.

From the standard: "a reference shall be initialized to refer to a valid object or function".  

There is no way for us to get a reference to a String in the first place other than dereferencing a String*, which seems kind of dodgy because it isn't really a pointer to a String.

We're cheating ("leveraging") the C++ type system to impose a hierarchy on V8's heap objects.  To actually imagine that there are C++ objects would be carrying it a step to far.

In other words, it's not operator* that does the wrong thing, but operator->.
 
 * It is safe to extract the object stored in the handle by
 * dereferencing the handle (for instance, to extract the Object* from
 * an Handle<Object>); the value will still be governed by a handle
 * behind the scenes and the same rules apply to these values as to
 * their handles.

Caveats: the raw pointer value is still governed by a handle so long as it does not escape the handle scope of the handle.  And the pointer will be possibly invalid after a GC occurs.
 
(After all, when you dereference a String*, you get a
String&.)

We never dereference a String* :)
Reply all
Reply to author
Forward
0 new messages