FunctionTemplate data getting lost during instantiation?

217 views
Skip to first unread message

Ryan Ingram

unread,
May 16, 2016, 6:22:34 PM5/16/16
to v8-users
What am I doing wrong here?  Does FunctionTemplate only support primitives for callback data?  (Its type should suggest that, if so...)

I'm trying to register native functions to be called back in my embedder, but my function callback data is getting corrupted.  I tried saving off the data passed to FunctionTemplate::New and I'm not getting the same data in my callback!

Here's a code snippet:

v8::Persistent<v8::Value> gDebugData;

void RegisterTestFunction( v8::Handle<v8::String> name, v8::Isolate* isolate, v8::Context* context )
{
    v8::Local<v8::Object> data = v8::Object::New(isolate); // In the actual code this is SomeFunctionTemplate->InstanceTemplate()->NewInstance()
    gDebugData.Reset(isolate, data); // save off pointer for debugging

    v8::Local<v8::FunctionTemplate> template = FunctionTemplate::New(isolate, &TestFunctionCallback, data);
    context->Enter();
    context->Global()->Set( name, template->GetFunction() );
    context->Exit();
}

void TestFunctionCallback(const v8::FunctionCallbackInfo<v8::Value> &arguments)
{
    v8::Isolate* isolate = arguments.GetIsolate();

    // Let's get our context
    v8::Local<v8::Value> dataValue = arguments.Data();
    assert( dataValue->IsObject() ); // Fails??

    // Hmm, let's look at our original context variable...
    v8::Local<v8::Value> expectedDataValue = v8::Local<v8::Value>::New( isolate, gDebugData );
    assert( dataValue == expectedDataValue ); // Also fails...

    assert( expectedDataValue->IsObject() ); // The original pointer is still valid...

    // Lets find out what we are getting passed
    v8::TryCatch tryCatch;

    // This function crashes...
    v8::Local<v8::String> whatIsIt = dataValue->ToString();

    if(tryCatch.HasCaught() { tryCatch.ReThrow(); return; }
}

Crash callstack:
  3eb50f46()
  3eb483e1()
  3eb2fa55()
  3eb2b8ea()
  v8.dll!v8::internal::Invoke(bool is_construct, v8::internal::Handle<v8::internal::JSFunction> function, v8::internal::Handle<v8::internal::Object> receiver, int argc, v8::internal::Handle<v8::internal::Object> * args)  Line 108 C++
  v8.dll!v8::internal::Execution::Call(v8::internal::Isolate * isolate, v8::internal::Handle<v8::internal::Object> callable, v8::internal::Handle<v8::internal::Object> receiver, int argc, v8::internal::Handle<v8::internal::Object> * argv, bool convert_receiver)  Line 157 + 0x13 bytes C++
  v8.dll!v8::internal::Execution::ToString(v8::internal::Isolate * isolate, v8::internal::Handle<v8::internal::Object> obj)  Line 517 + 0x95 bytes C++
  v8.dll!v8::Value::ToString()  Line 2594 + 0x9 bytes C++

If I use "expectedDataValue" in my callback, everything works as desired.  But that's not getting passed through to the FunctionCallbackInfo.

I am on a relatively old version of v8 (3.30).  Is there a way to tell what is inside of dataValue's handle more safely?  In the debugger it has a pointer value, although it doesn't look particularly valid...

dataValue
    v8::Handle<v8::Value> {val_=0x0023d608 }
expectedDataValue
    v8::Handle<v8::Value> {val_=0x128daab0 }
gDebugData
    v8::PersistentBase<v8::Value> {val_=0x128d6a90 }

Ryan Ingram

unread,
May 16, 2016, 6:24:45 PM5/16/16
to v8-users
More details: Stepping through HandleApiCallHelper and the constructor for FunctionCallbackArguments, I get to this line:
    values[T::kDataIndex] = data;
with data = 0x0baddeaf

which seems like a sentinel value for 'something went wrong'.

Ben Noordhuis

unread,
May 17, 2016, 4:18:18 AM5/17/16
to v8-users
I see you've logged [0]. I can't comment on V8 3.30 (too old) but
newer versions forbid anything that is not a primitive or a template
through a run-time check.

It makes sense in a way because objects are owned by their creating
context while templates are context-independent.

[0] https://bugs.chromium.org/p/v8/issues/detail?id=1503#c4

Ryan Ingram

unread,
May 17, 2016, 2:06:48 PM5/17/16
to v8-users
Actually it turns out it was my error, misusing HandleScope.  It turns out the function that constructed the callback data had a HandleScope and the returned handle didn't survive the scope closure.

I think the Embedder's Guide should go into more detail about the design of HandleScope as it seems critical to understanding how to use the library properly.  In particular, you need to be very careful that any handle you construct doesn't outlive the HandleScope it is constructed in, and how EscapableHandleScope solves the problem of wanting to return a handle from a function that has a HandleScope.

  -- ryan


On Monday, May 16, 2016 at 3:22:34 PM UTC-7, Ryan Ingram wrote:

Ryan Ingram

unread,
May 17, 2016, 2:17:34 PM5/17/16
to v8-users
(Egg on face)

Actually, re-reading the guide, it does go into pretty good detail.  I think I just didn't have the context to understand it at that point on first reading.  Now that I've had time to write a bunch of code and internalize the concepts, it's crystal clear.

  -- ryan
Reply all
Reply to author
Forward
0 new messages