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.