Hello all

I am using my wrapper for embedding v8 into my application. I automated all 
parameter parsing and so on and so far everything worked fine. But now i 
have a problem. I have the following JS-Code:

var req = new IO.HttpRequest(IO.RequestType.get);
req.data({ i: 'jTKvNf9w' }).send('http://pastebin.com/raw.php', function 
(content) { console.log(content); });

This is mapped to my XmlHttpRequest-class in C++ and the send-function 
looks like this:
void XmlHttpRequest::open(const Utils::String& str, ::JS::FunctionObjPtr 
callback) {
mCallback = callback;
mRequest->open(str);
}

First lets have a look at JS::FunctionObj (the Ptr is just because its a 
typedef for a std::shared_ptr):
class FunctionObj
{
v8::Handle<v8::Value> mHandle;

public:
FunctionObj(v8::Handle<v8::Value>& fun) {
mHandle = fun;
}

FunctionObj() { }

operator bool () {
return mHandle.IsEmpty() == false;
}

template<typename... Args>
void callVoid(const Args&... args) {
std::vector<v8::Handle<v8::Value>> arguments;
addArgument(arguments, args...);

v8::TryCatch tc;
v8::Handle<v8::Function>::Cast(mHandle)->Call(mHandle, arguments.size(), 
arguments.data());
if (tc.HasCaught()) {
throw JS::Exception(tc.Exception(), tc.StackTrace());
}
}
};

And how i get there from the callback for the function. The argument gets 
unwrapped from the FunctionCallbackInfo in the following way:
template<uint32 index, typename T, typename... Rem, typename... CurArgs>
void expand(const v8::FunctionCallbackInfo<v8::Value>& args, const 
CurArgs&... curArgs) {
v8::TryCatch tc;
T arg = ObjectWrap::unwrap<typename std::remove_cv<typename 
std::remove_reference<T>::type>::type>(args[index]);
if (tc.HasCaught()) {
tc.ReThrow();
return;
}

expand<index + 1, Rem...>(args, curArgs..., arg);
}

In this case T would be ::JS::FunctionObjPtr. ObjectWrap::unwrap for the 
template FunctionObjPtr looks like this:
template<typename T>
static TYPE_RET(FunctionObjPtr) ObjectWrap::unwrap(v8::Handle<v8::Value>& 
value) {
if (value->IsFunction() == false) {
TYPE_ERR("Value is not a function");
}

return std::make_shared<FunctionObj>(value);
}

The TYPE_RET is merely some template metaprogramming stuff.

Ok, once im in XmlHttpRequest::send i can call the callback as many times 
as id like to, it allways works. But the http request is processed in a 
background thread and once its complete the following function gets called:
void XmlHttpRequest::onComplete(Utils::String content) {
sUIMgr->getDispatcher()->pushFrame(Gl::Dispatcher::Priority::Low, [this, 
content]() {
::JS::FunctionObjPtr f = mCallback;
f->callVoid(content);
});
}

The pushFrame function enqueues the lambda and calls it later in the main 
thread. And here it happens. I get an access violation on the following 
line:
inline Heap* heap() { return heap_; }

And the callstack:
  v8::internal::HeapObject::GetHeap() Line 1173 C++
  v8::internal::HeapObject::GetIsolate() Line 1180 C++
  v8::Function::Call(v8::Handle<v8::Value> recv, int argc, 
v8::Handle<v8::Value> * argv) Line 4175 C++
  JS::FunctionObj::callVoid<Utils::String>(const Utils::String & <args_0>) 
Line 58 C++
  UI::JS::XmlHttpRequest::onComplete::__l3::<lambda>() Line 24 C++

After a bit of debugging i have seen that in 
v8::internal::HeapObject::GetHeap the this-pointer of HeapObject is 
0xCCCCCCCC. I have investigated the following lines in the source code:
Local<v8::Value> Function::Call(v8::Handle<v8::Value> recv, int argc,
                                v8::Handle<v8::Value> argv[]) {
  i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();

this is correct, its the actual value in the handle.

#define MAKE_OPEN_HANDLE(From, To)                                         
 \
  v8::internal::Handle<v8::internal::To> Utils::OpenHandle(                 
\
    const v8::From* that, bool allow_empty_handle) {                       
 \
    EXTRA_CHECK(allow_empty_handle || that != NULL);                       
 \
    EXTRA_CHECK(that == NULL ||                                             
\
        !(*reinterpret_cast<v8::internal::To**>(                           
 \
            const_cast<v8::From*>(that)))->IsFailure());                   
 \
    return v8::internal::Handle<v8::internal::To>(                         
 \
        reinterpret_cast<v8::internal::To**>(const_cast<v8::From*>(that))); 
\
  }

with From being v8::Function and To being v8::internal::JSFunction. that is 
correct, but here: 
(*reinterpret_cast<v8::internal::To**>(                            \
            const_cast<v8::From*>(that)))->IsFailure())

in IsFailure this is 0xCCCCCCCC. That means that *(JSFunction**)that was 
0xCCCCCCCC. I can confirm this here:
::JS::FunctionObjPtr f = mCallback;
auto handle = f->getHandle();
auto fObj = v8::Handle<v8::Function>::Cast(handle).operator*();
void* ptr = *(void**) fObj;

ptr is 0xCCCCCCCC. in Visual Studio this means that its an uninitialized 
stack value.

*What have I tried?*
1. Creating a persistent in the constructor of FunctionObj -> No change
2. Setting a WeakCallback to the Persistent in FunctionObj to see if it 
gets GC'ed -> Not called
3. Using an object instead of a function and trying to call a method from 
the object -> Same crash as soon as I try to query a property from the 
object
4. Running a new script (unrelated to the callback) -> All fine (so 
context, isolate, handlescope, ... are all still valid)
5. Calling a function from the global context -> Works

The only thing that seems to be impossible is passing a value from JS to 
C++ and then later call it.

Greetings
Cromon

-- 
-- 
v8-users mailing list
[email protected]
http://groups.google.com/group/v8-users
--- 
You received this message because you are subscribed to the Google Groups 
"v8-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to