returning an "object" from JavaScript back out to C++... how?

1,779 views
Skip to first unread message

getify

unread,
Jan 5, 2011, 1:41:26 PM1/5/11
to v8-users
I know I can create an object inside of C++, and set keys and values,
and pass that into JavaScript. Something like this is what I currently
do:

v8::Handle<v8::Object> obj_inject = v8::Object::New();
obj_inject->Set(v8::String::New("foo"), v8::String::New("i'm foo"));
obj_inject->Set(v8::String::New("bar"), v8::String::New("i'm bar"));

v8::Handle<v8::Value> func_args[1] = {obj_inject};

myfunc->Call(my_context->Global(), 1, func_args);


So, my question is, how do I do the reverse? How do I have a
JavaScript function that returns an object (instead of a primitive
like a string or integer) with key/value pairs in it? I've tried:


v8::TryCatch try_catch;
v8::Handle<v8::Script> script = v8::Script::Compile("myfunc();",
"inline");
if (script.IsEmpty()) {
// Print errors that happened during compilation.
ReportException(&try_catch);
}
else {
v8::Handle<v8::Value> ret = script->Run(); // works!
v8::Handle<v8::Object> obj(ret); // cast fails :(

// want then to be able to do:
std::string foo = obj->Get(v8::String::New("foo"));
}


But the compiler complains about the cast from Value to Object. So how
do I do that properly and then access the key/value pairs?

bradley.meck

unread,
Jan 5, 2011, 3:30:17 PM1/5/11
to v8-u...@googlegroups.com
Check Value.IsObject() and then use Value.ToObject().

Søren Gjesse

unread,
Jan 6, 2011, 2:40:22 AM1/6/11
to v8-u...@googlegroups.com
Instead of using ToObject a case is probably more appropriate

v8::Handle<v8::Value> ret = script->Run();
if (ret.IsEmpty()) {
  // Handle exception.
} else {
  Handle<Object> result = Handle<Object>::Cast(ret);
  ...
}
v8::Handle<v8::Object> obj(ret);

ToObject performs the convertion in section 9.9 of the ECMAScript Language Specification, which for something that is already an object does nothing, so it also does the cast.

Regards,
Søren

On Wed, Jan 5, 2011 at 21:30, bradley.meck <bradle...@gmail.com> wrote:
Check Value.IsObject() and then use Value.ToObject().

getify

unread,
Jan 6, 2011, 9:09:42 PM1/6/11
to v8-users
Thanks! That seems to work. However, I'm still having trouble
converting some types of the properties on the object to something
useable in my C++.

For instance:

v8::String::Utf8Value my_str = v8::String::Utf8Value(obj-
>Get(v8::String::New("my_str")));

That works for getting a string property (`my_str`) value out. But now
that I have a v8::String::Utf8Value, is there a straightforward way to
get that value into something I can use, like a C++ std::string for
instance?

This is what I came up with from the sample code:

const char* ToCString(const v8::String::Utf8Value& value) {
return *value ? *value : "<string conversion failed>";
}

...

std::string my_str =
std::string::New(ToCString(v8::String::Utf8Value(obj-
>Get(v8::String::New("my_str")))));

That seems kinda convoluted, but works. However, is there another more
direct way?

And what about extracting a Boolean? Integer? Float? Array? Haven't
found any documentation about how to get at those other property
types. Any help is greatly appreciated.

Matthias Ernst

unread,
Jan 7, 2011, 4:57:22 AM1/7/11
to v8-u...@googlegroups.com
On Fri, Jan 7, 2011 at 3:09 AM, getify <get...@gmail.com> wrote:
> Thanks! That seems to work. However, I'm still having trouble
> converting some types of the properties on the object to something
> useable in my C++.
>
> For instance:
>
> v8::String::Utf8Value my_str = v8::String::Utf8Value(obj-
>>Get(v8::String::New("my_str")));
>
> That works for getting a string property (`my_str`) value out. But now
> that I have a v8::String::Utf8Value, is there a straightforward way to
> get that value into something I can use, like a C++ std::string for
> instance?

I think you're doing it quite right. I must admit I've never dealt
with failed conversion.
I'm not sure what reasons for failure exist other than
input->ToString() failing - which is not an issue if it's already a
string.

> This is what I came up with from the sample code:
>
> const char* ToCString(const v8::String::Utf8Value& value) {
>        return *value ? *value : "<string conversion failed>";
> }
>
> ...
>
> std::string my_str =
> std::string::New(ToCString(v8::String::Utf8Value(obj-
>>Get(v8::String::New("my_str")))));
>
> That seems kinda convoluted, but works. However, is there another more
> direct way?
>
> And what about extracting a Boolean? Integer? Float? Array? Haven't
> found any documentation about how to get at those other property
> types. Any help is greatly appreciated.

Same pattern. Either you can use the ECMA conversions via "ToNumber",
"ToBoolean", ... or Cast the handle after checking "IsXXX". Number has
"double Value()", Boolean has "bool Value()". Array has Length() and
Get(uint32).

Stephan Beal

unread,
Jan 7, 2011, 4:59:42 AM1/7/11
to v8-u...@googlegroups.com
On Fri, Jan 7, 2011 at 3:09 AM, getify <get...@gmail.com> wrote:
That seems kinda convoluted, but works. However, is there another more
direct way?

AFAIK, that is the way to do it. See String::AsciiValue and String::Utf8Value. However, be aware that the lifetimes of their string bytes is not well defined, so they must be copied if you want to use them safely from outside of v8. (The std::string ctor copies them, but when using the raw (char const *) one must be careful about the lifetime.)
 
And what about extracting a Boolean? Integer? Float? Array? Haven't
found any documentation about how to get at those other property
types. Any help is greatly appreciated.

See the API docs for the Value type. Value has methods which return a value as the various numeric types. For Object and Arrays, something like this:

if( handle->IsObject() ) {
  Local<Object> obj( Object::Cast(*handle) );
}

the approach is the same for Arrays.



--
----- stephan beal
http://wanderinghorse.net/home/stephan/

bradley.meck

unread,
Jan 7, 2011, 11:03:19 AM1/7/11
to v8-u...@googlegroups.com
http://bespin.cz/~ondras/html/classv8_1_1Value.html may help you out for grabbing the various values ...Value() methods in particular. And yes the UTF8 work is the standard way to do that. Basically if you can use ASCII do, it requires less work on encoding and will be nicer. For one I think this is fine as is exemplifies the possible performance issues of using utf8 (node.js has had a lot of issues with their work in UTF8).

getify

unread,
Jan 7, 2011, 11:58:46 AM1/7/11
to v8-users
> See the API docs for

I've tried many times to find "the API docs". All I found is this:
http://code.google.com/apis/v8/intro.html

I've read through all the pages there, and I don't see any
documentation about these different API functions. What documentation
am I missing?


>Same pattern. Either you can use the ECMA conversions via "ToNumber", "ToBoolean", ... or Cast the handle after checking "IsXXX". Number has "double Value()", Boolean has "bool Value()". Array has Length() and Get(uint32).

I tried:

bool logging = obj->Get(v8::String::New("my_bool"))->ToBoolean();

And the compiler complains:

error: cannot convert v8::Local<v8::Boolean> to bool in
initialization

Then I tried:

bool logging = (bool)obj->Get(v8::String::New("my_bool"))-
>ToBoolean();

And the compiler complains:

error: invalid cast from type v8::Local<v8::Boolean>to type bool

Sorry, I'm sure the documentation that I'm failing to find makes this
all perfectly clear, but I'm having an extrodinary amount of
difficulty getting a type from the V8 version of that type
(v8::Boolean) to the actual c/c++ type (bool) so that I can do
something with it. Again, any help or direction to specific
documentation is greatly appreciated.

Stephan Beal

unread,
Jan 7, 2011, 12:04:43 PM1/7/11
to v8-u...@googlegroups.com
On Fri, Jan 7, 2011 at 5:58 PM, getify <get...@gmail.com> wrote:
> See the API docs for

I've tried many times to find "the API docs". All I found is this:
http://code.google.com/apis/v8/intro.html

I've read through all the pages there, and I don't see any
documentation about these different API functions. What documentation
am I missing?

Attached is a doxygen file. Drop it in the top-most v8 source dir and run 'doxygen'. That creates HTML docs in the 'doxygen' subdir. The docs are pretty minimalistic, but being able to see their structure in doxygen is helpful.

    bool logging = (bool)obj->Get(v8::String::New("my_bool"))-
>ToBoolean();

And the compiler complains:

   error: invalid cast from type v8::Local<v8::Boolean>to type bool

You want Value::BooleanValue() in this case.
 
Sorry, I'm sure the documentation that I'm failing to find makes this
all perfectly clear,

LOL! Don't bet on it ;).
 
but I'm having an extrodinary amount of
difficulty getting a type from the V8 version of that type
(v8::Boolean) to the actual c/c++ type (bool) so that I can do
something with it.

See Value::BooleanValue()
 
Again, any help or direction to specific
documentation is greatly appreciated.

See attached. i hope that helps.
 
Doxyfile

Matthias Ernst

unread,
Jan 7, 2011, 12:32:00 PM1/7/11
to v8-u...@googlegroups.com
On Fri, Jan 7, 2011 at 5:58 PM, getify <get...@gmail.com> wrote:
>> See the API docs for
>
> I've tried many times to find "the API docs". All I found is this:
> http://code.google.com/apis/v8/intro.html
>
> I've read through all the pages there, and I don't see any
> documentation about these different API functions. What documentation
> am I missing?
>
>
>>Same pattern. Either you can use the ECMA conversions via "ToNumber", "ToBoolean", ... or Cast the handle after checking "IsXXX". Number has "double Value()", Boolean has "bool Value()". Array has Length() and Get(uint32).
>
> I tried:
>
>    bool logging = obj->Get(v8::String::New("my_bool"))->ToBoolean();

I fully agree with you that the documentation is lacking. Really, v8.h
is your friend. There are lots of third-party articles about specific
things, but in the end, you'll need to look at the header and try your
way through. You seem to have grokked the Handle<X> ~ smart pointer
idea, so given a Handle<X> you want to look at X's interface.

Let's see:

V8EXPORT Local<Boolean> ToBoolean() const;

(What I think I've learnt is that the To* methods perform conversions
as described in ECMA 262.
http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf.
E.g. String::New("3")->ToNumber() won't fail but convert to a Number
value. Other than the Casts mentioned above).

So now we have a Local<Boolean>.

...
class Boolean : public Primitive {
public:
V8EXPORT bool Value() const;
static inline Handle<Boolean> New(bool value);
};

i.e.

bool boolValue = v8Value->ToBoolean()->Value();

or there seems to be a shortcut:
V8EXPORT bool BooleanValue() const;


Matthias


>
> And the compiler complains:
>
>    error: cannot convert v8::Local<v8::Boolean> to bool in
> initialization
>
> Then I tried:
>
>    bool logging = (bool)obj->Get(v8::String::New("my_bool"))-
>>ToBoolean();
>
> And the compiler complains:
>
>    error: invalid cast from type v8::Local<v8::Boolean>to type bool
>
> Sorry, I'm sure the documentation that I'm failing to find makes this
> all perfectly clear, but I'm having an extrodinary amount of
> difficulty getting a type from the V8 version of that type
> (v8::Boolean) to the actual c/c++ type (bool) so that I can do
> something with it. Again, any help or direction to specific
> documentation is greatly appreciated.
>

getify

unread,
Jan 9, 2011, 1:04:08 AM1/9/11
to v8-users
OK, thank you all for the help with variable conversions. I think I
mostly have that part figured out.

However, I've now run into a frustrating problems with calling
JavaScript functions from c++. I made this gist to illustrate:

https://gist.github.com/771459

There's 4 snippets list there.

`A1_works` and `A2_broken` show one variation of the problem, and how
it works versus how it breaks.

Similarly, `B1_works` and `B2_broken` shows (I think the same??)
problem, and how it works versus how it breaks.

In `A2_broken`, I create a helper function that captures and stores
the JS function reference, and then returns that reference to be used
in another context.

In `B2_broken`, I do the reverse, and capture and store the JS
function reference in the global scope, and try to use that global
inside a function.

If I interpret what I'm seeing correctly, when I capture in my C++ a
reference to a JavaScript function, and store that in a variable, if I
do that process in a different context scope than where I use that
function reference to execute it (and get back its results), things go
haywire.

Can someone help me sort out what's going wrong here?

--Kyle

Matthias Ernst

unread,
Jan 9, 2011, 5:19:29 AM1/9/11
to v8-u...@googlegroups.com
Kyle, what you're basically doing is the equivalent of returning
references into stack frames. That'll fail once the frame is gone. If
you build v8 with mode=debug you should hopefully get more and better
errors on the spot.

While all objects live in the V8 heap, the GC needs your help to
identify which objects you are referencing from native code. So you
don't get to point directly into the heap but through the Handle
indirection. The "Local" kind of handles does live on a stack, namely
in HandleScopes. Whenever a Local handle is created, it is allocated
within the currently active HandleScope. When the HandleScope closes,
it doesn't necessarily mean that the target of the reference is gone,
but that your Local is no longer known to V8 and it might relocate the
object or overwrite your Handle with something else.

So: never let a Local escape outside of its HandleScope. But how do
deal with returning a Handle from a nested scope? Use
"HandleScope::Close". It takes the argument, and returns an identical
Handle allocated in the parent scope, so you can safely leave the
current (A2_broken: L10: return handle_scope.Close(script->Run());).

If that helps, this is the equivalent of the Push/PopLocalFrame
functions of JNI.


I think you're just lucky that the getObj in B1&B2 work.
B2 doesn't look like the actual code anyway
(printf("$s\n",s3_2.c_str()); certainly won't print "undefined")


PS: Context::Scopes are thread-local, you don't need to open them in
every function you call. One outer Context::Scope is typically enough.
The same does hold for HandleScopes but here it is a good idea to
stack them and release references as early as possible.

Reply all
Reply to author
Forward
0 new messages