Can't access V8 Context in "callback" function - c++

I am writing a NodeJS addon where I use a C library that lets you register a callback at certain events. When the callback is fired I want to call a NodeJS callback function. The problem is that in my C callback function I get a segmentation fault when trying to do anything V8 related, like creating a HandleScope.
In test.js:
...
myaddon.register(function(data) {
console.log("data: " + JSON.stringify(data));
});
...
In test.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <node.h>
#include <v8.h>
using namespace v8;
void WINAPI myEvent(int num, void * context) {
HandleScope scope; // Segmentation fault here!
Local<Function> * cb = (Local<Function>*)(context);
Local<Object> obj = Object::New();
obj->Set(String::NewSymbol("id"), Number::New(num));
const int argc = 1;
Local<Value> argv[argc] = { obj };
(*cb)->Call(Context::GetCurrent()->Global(), argc, argv);
sleep(1);
}
Handle<Value> RegisterEvent(const Arguments& args) {
HandleScope scope;
Local<Function> cb = Local<Function>::Cast(args[0]);
int callbackId = registerEvent((Event)&myEvent, &cb );
printf("callback id: %i\n", callbackId);
init();
return scope.Close(Integer::New(callbackId));
}
void init(Handle<Object> exports) {
exports->Set(String::NewSymbol("register"),
FunctionTemplate::New(RegisterEvent)->GetFunction());
}
NODE_MODULE(test, init)
EDIT: Updated with real code.
EDIT: I just changed the title of this issue since the problem is probably that my callback function can't access the V8 Context. Since I get a segmentation fault when creating HandleScope instance I can't see what else it might be. In addition to this question I AM trying to find the answer in the V8 documentation, but it is huge and I don't have that much time to test and investigate.

Your handler function myEvent() must be called in V8 thread. If not, you have to post the event notification into the V8 thread:
https://stackoverflow.com/a/15701160/1355844
https://stackoverflow.com/a/22946062/1355844

It appears that you might have forgotten to create a HandleScope for your variable. This should work for you.
void callbackFunc() {
HandleScope scope;
Local<Object> obj = Object::New();
}

Related

NodeJS Addon calling Javascript callback from inside NAN AsyncWorker::Execute

I would like to call a nodejs callback from within my asynchronous addon function. I have seen the synchronous example (here)
and I am using a wonderful asynchronous example (here) as a starting base.
However, when I try to execute a callback that was given to the c++ AsyncWorker child class, I get a Segmentation fault.
Here is my code:
#include <nan.h>
#include <functional>
#include <iostream>
#include <exception>
using namespace Nan;
using namespace v8;
using namespace std;
class ScriptWorker : public AsyncWorker {
public:
ScriptWorker(Callback *callback, const std::map<std::string, Callback*>)
: AsyncWorker(callback), script(script), cbs(cbs) {}
~ScriptWorker() {}
void Execute () {
// ------------------------
// Segmentation fault after
// ------------------------
Local<Value> argv[] = {
New<v8::Number>(id)
};
// -------------------------
// Segmentation fault before
// -------------------------
cbs["getUser"]->Call(1, argv);
}
private:
std::string script;
std::map<std::string, Callback*> cbs;
};
NAN_METHOD(Method) {
Local<Object> array = info[0]->ToObject();
Callback *callback = new Callback(info[1].As<Function>());
// Build up callbacks passed in from javascript.
// Will be a dynamic loop, but for now, hard code the one getUser example.
std::map<std::string, Callback*> cbs;
cbs.insert(std::pair<std::string, Callback*>("getUser",
new Callback(
array->Get(
v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), "getUser")
).As<Function>()
)
));
AsyncQueueWorker(new ScriptWorker(callback, cbs));
}
NAN_MODULE_INIT(Init) {
Nan::Set(target, Nan::New<String>("hello").ToLocalChecked(), Nan::GetFunction(Nan::New<FunctionTemplate>(Method)).ToLocalChecked());
}
NODE_MODULE(hello, Init)
My questions:
Should I not use Nan's AsyncWorker and instead roll my own?
How do I setup the Execute function to call into Javascript?
EDIT:
See this repo:
https://github.com/xavero/node_addon_sample
it has a sample on how to work with callback functions and emitting events from C land.
You should not call v8/Nan functions in your Execute method of ScriptWorker, or you will get segment faults. Override the HandleOKCallback function to use the javascript callback.
To call from the javascript, in your c++ addon:
NAN_MODULE_INIT(Init) {
Nan::Set(target, Nan::New("myJsFunctionName").ToLocalChecked(),
Nan::GetFunction(Nan::New<FunctionTemplate>(Method)).ToLocalChecked());
}
NODE_MODULE(anyNameHere, Init)
And in your javascript:
// run "npm install bindings --save" in console first
var addon = require('bindings')('NativeExtension');
addon.myJsFunctionName({ foo: "bar"}, (arg1,arg2) => console.log(`${arg1} - ${arg2}`))

GetLastInputInfo fails in node addon

My goal is to make a module which provides access to the last time of user interaction (Client side app - not a server app). The Windows API has a function called GetLastInputInfo (https://msdn.microsoft.com/en-us/library/windows/desktop/ms646302(v=vs.85).aspx). Below is the code which should load the time information into last_input and it returns 0/1 for failure/success. Unfortunately, it fails every time.
Addon code:
#include <node.h>
#include <v8.h>
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
using namespace v8;
Handle<Value> TimeSinceInput(const Arguments& args) {
HandleScope scope;
LASTINPUTINFO last_input;
if (::GetLastInputInfo(&last_input)) {
return scope.Close(String::New("Success!"));
}
else {
return scope.Close(String::New("Failed for some reason!"));
}
}
void init(Handle<Object> exports) {
exports->Set(String::NewSymbol("time_since_input"), FunctionTemplate::New(TimeSinceInput)->GetFunction());
}
NODE_MODULE(addon, init)
Any thoughts?
LASTINPUTINFO structure has member cbSize, that should be initialized:
The size of the structure, in bytes. This member must be set to sizeof(LASTINPUTINFO).
It's a common way for versioning in Windows API.

Call a non-static method from no object using std::function

While doing some random-ish, weird-ish things to understand a little bit more C++ (just to say I'm still a newbie), I came across something I can't properly understand : I'm currently using SFML which provides a few methods to create and manage a window, namely this method to close it: void sf::RenderWindow::close(). It doesn't take any argument, and it can't be called directly but through an instantiated object.
sf::RenderWindow::close(); // error
My problem is the following code :
sf::RenderWindow window(sf::VideoMode(800, 600), "test"); // added after edit
const std::function <void(sf::RenderWindow &)> callback(sf::RenderWindow::close);
// callback();
callback(window); // closes the window
I'm not sure to understand what's happening here as I'm not using any object... even anonymous at first sight... I guess...
If someone can enlighten me, I'd really appreciate it.
Thanks you.
Edit:
My mistake, yes there's an object.
#include <iostream>
#include <functional>
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "test");
const std::function <void(sf::RenderWindow &)> call(sf::RenderWindow::close);
call(window);
return 0;
}
Actually, I understand this as if it were sf::RenderWindow::close(window), similarly to metatables in lua I suppose.
It's linked to the concept of Callable:
Since the stored target of the std::function (i.e. sf::RenderWindow::close) is a pointer to member function and the first argument (i.e. window) is a (reference to) an object of type RenderWindow, then the invocation of the function object is equivalent to window.close().
You could also have written something along those lines:
std::function<void(sf::RenderWindow*)> call = &sf::RenderWindow::close;
call(&window);
Below a code sample that doesn't involve SFML:
#include <iostream>
#include <functional>
class Window {
public:
void close() {std::cout << "close" << std::endl; }
};
int main(int argc, const char * argv[]) {
Window w;
std::function<void(Window&)> f = &Window::close;
f(w);
std::function<void(Window*)> g = &Window::close;
g(&w);
return 0;
}

runtime failure class wrapping C object

I am working on updating node-mapserver that wraps the mapserver library in the class-refactor branch to a more maintainable code structure (basing my changes on the organization of node-ogr).
Up to now, I have refactored the main module interface and started on the first class to be wrapped, a mapserver C struct called errorObj that I am wrapping with class MSError. The extension builds but I am facing is a runtime assertion error in the extension the first time I trigger a new MSError object to wrap an errorObj.
Fatal error in ../deps/v8/src/api.h, line 297
CHECK(allow_empty_handle || that != __null) failed
With node 0.10 there is an extremely lengthy stack trace after this which can be seen in travis build 27.2. I couldn't find anything particularly useful in the stack trace.
The extension builds and some tests pass (symbols and methods exported from the library itself), but fails when the library tries to create an Object that wraps a C object.
In javascript, I call mapserver.getError(). In the extension, this invokes a mapserver method errorObj* err = msGetErrorObj();and returned with return scope.Close(MSError::New(err));. The New method of MSError does this:
Handle<Value> MSError::New(errorObj *err) {
HandleScope scope;
MSError *wrapped = new MSError(err);
Handle<Value> ext = External::New(wrapped);
Handle<Object> obj = MSError::constructor->GetFunction()->NewInstance(1, &ext);
return scope.Close(obj);
}
I have attempted to debug this using gdb, the best I can get out of it with my limited skills is that the error happens at this call:
Handle<Object> obj = MSError::constructor->GetFunction()->NewInstance(1, &ext);
What I can gather from this is that ext is null which means that the External::New(wrapped) call is not returning a valid value. I have confirmed that errorObj* err does point to a valid, properly initialized, errorObj structure. To clarify, there is no actual error, mapserver always returns a valid errorObj but with a code of 0 if there is no error at this time.
Here is, hopefully, the relevant code.
ms_error.hpp
#ifndef __NODE_MS_ERROR_H__
#define __NODE_MS_ERROR_H__
#include <v8.h>
#include <node.h>
#include <node_object_wrap.h>
#include <mapserver.h>
using namespace v8;
using namespace node;
class MSError: public node::ObjectWrap {
public:
static Persistent<FunctionTemplate> constructor;
static void Initialize(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
static Handle<Value> New(errorObj *err);
MSError();
MSError(errorObj *err);
inline errorObj *get() { return this_; }
private:
~MSError();
errorObj *this_;
};
#endif
ms_error.cpp
#include "ms_error.hpp"
Persistent<FunctionTemplate> MSError::constructor;
void MSError::Initialize(Handle<Object> target) {
HandleScope scope;
constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(MSError::New));
constructor->InstanceTemplate()->SetInternalFieldCount(1);
constructor->SetClassName(String::NewSymbol("MSError"));
// constructor->InstanceTemplate()->SetNamedPropertyHandler(MSError::NamedPropertyGetter, NULL, MSError::NamedPropertyQuery, NULL, MSError::NamedPropertyEnumerator);
target->Set(String::NewSymbol("MSError"), constructor->GetFunction());
}
MSError::MSError(errorObj *err) : ObjectWrap(), this_(err) { }
MSError::MSError() : ObjectWrap(), this_(0) { }
MSError::~MSError() { }
Handle<Value> MSError::New(const Arguments& args)
{
HandleScope scope;
if (!args.IsConstructCall())
return ThrowException(String::New("Cannot call constructor as function, you need to use 'new' keyword"));
if (args[0]->IsExternal()) {
Local<External> ext = Local<External>::Cast(args[0]);
void *ptr = ext->Value();
MSError *f = static_cast<MSError *>(ptr);
f->Wrap(args.This());
return args.This();
}
return args.This();
}
The answer to this is that the code above is fine. The actual error was that the MSError::constructor was not initialized because I neglected to call it from the main module code. The actual null object was MSError::constructor->GetFunction().
Thanks to Ben Noordhuis for pointing us in the right direction.

AIR Native Extensions "The extension context does not have a method with the name"

I am attempting to make an AIR Native Extension and after successful compilation of all components, Flash Builder 4.6 logs "Error #3500: The extension context does not have a method with the name...".
Here's the C++ code for the native DLL:
#include "stdafx.h"
#include "TestANE.h"
#include "FlashRuntimeExtensions.h"
#include <string>
#include <iostream>
#include <iomanip>
#include <algorithm>
using namespace std;
FREObject isSupported(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
FREObject result;
uint32_t isSupportedSwitch = 1;
FRENewObjectFromBool(isSupportedSwitch, &result);
return result;
}
FREObject getString(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
FREObject result;
const char *testString = "Hello World from C++!";
FRENewObjectFromUTF8(strlen(testString)+1, (const uint8_t *) testString, &result);
return result;
}
void taneContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctions, const FRENamedFunction** functions) {
*numFunctions = 2;
FRENamedFunction* func = (FRENamedFunction*) malloc(sizeof(FRENamedFunction) * (*numFunctions));
func[0].name = (const uint8_t*) "isSupported";
func[0].functionData = NULL;
func[0].function = &isSupported;
func[1].name = (const uint8_t*) "getString";
func[1].functionData = NULL;
func[1].function = &getString;
*functions = func;
}
void taneContextFinalizer(FREContext ctx) {
return;
}
void taneInitializer(void** extData, FREContextInitializer* ctxInitializer, FREContextFinalizer* ctxFinalizer) {
*ctxInitializer = &taneContextInitializer;
*ctxFinalizer = &taneContextFinalizer;
}
void taneFinalizer(void* extData) {
return;
}
Here's the code for the ActionScript 3 interface:
package com.tests.TestANE {
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.external.ExtensionContext;
public class TestANE extends EventDispatcher {
private var _ExtensionContext:ExtensionContext;
public function TestANE(target:IEventDispatcher=null) {
this._ExtensionContext = ExtensionContext.createExtensionContext("com.tests.TestANE", null);
super(target);
}
public function isSupported():Boolean {
return _ExtensionContext.call("isSupported") as Boolean;
}
public function getString():String {
return _ExtensionContext.call("getString") as String;
}
public function dispose():void {
this._ExtensionContext.dispose();
}
}
}
Any help in solving this issue would be appreciated.
Please see before here: http://forums.adobe.com/thread/923158
If you got this error when you have more than one extension inside the same project, please be careful: EVERY initializer end finalizer methods MUST have different and unique names between ALL the extensions.
NB. Not only the methods specified in extension.xml but also the ones you delagate to initialize the context. To be clear: NOT only the extension initializer but also the context initializer inside the project MUST have unique names between ALL the extensions.
Eg.:
void UNIQUE_NAME_ExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet,
FREContextFinalizer* ctxFinalizerToSet) {
NSLog(#"Entering ExtInitializer()");
*extDataToSet = NULL;
*ctxInitializerToSet = &UNIQUE_NAME_ContextInitializer;
*ctxFinalizerToSet = &UNIQUE_NAME_ContextFinalizer;
NSLog(#"Exiting ExtInitializer()");
}
void UNIQUE_NAME_ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx,
uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet)
{
*numFunctionsToTest = 1;
FRENamedFunction* func = (FRENamedFunction*) malloc(sizeof(FRENamedFunction) * 1);
func[0].name = (const uint8_t*) "scan";
func[0].functionData = NULL;
func[0].function = &scan;
*functionsToSet = func;
}
And the same for the finalizer.
I hope this helps.
To resolve symbol name conflicts between ANEs, there is a new ADT option in AIR 3.4 i.e. hideANELibSymbols
Refer: http://help.adobe.com/en_US/air/build/WS901d38e593cd1bac1e63e3d128cdca935b-8000.html
Also, to ease the process of getting started with writing an ANE for iOS, I've written an xcode ANE template.
Refer: https://github.com/divijkumar/xcode-template-ane
Do read the instructions on the github wiki page before getting started.
Had the same issue but on concrete machine (others work just fine) and nothing help until i rebuild my dll with release flag. It seems like debug version request some addination dll's installed on user pc, so make sure you build with release flag!
It appears that the C++ function "getString" has required arguments. So when you are calling _ExtensionContext.call("getString") as String;
C++ does not have a getString() method. It wants you to pass through arguments also. Add these to teh call method like so:
_ExtensionContext.call("getString", arg, arg1, arg2) as String;
The arguments you pass must be of the appropriate type specified by the c++ getString method.
See the reference:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/external/ExtensionContext.html#call()
I've solved this problem. I am not completely certain how as I started with another ANE project and built from there. I suspect however that the problem had to do with wrong package names in the ActionScript part of the code and the extension descriptor.
This question is old, but there is no solid answer. I ran into the same issue with an Android ANE. In your FREExtension function/class, you must return a new instance of FREContext and in that instance of FREContext, you must return the function map. I made the mistake of not returning the map and running around like a chicken with its head cut off trying to figure out what was going on. Hopefully that will help someone in the future.
I had this same issue and none of the other answers here solved it for me. I was trying to create a simple c++ ANE for the Blackberry Playbook and would get an error #3500 any time I tried to run it. The same exact code worked fine in C. It turns out the c++ compiler was doing method name mangling so it could not find the method I was calling. I solved it by wrapping my function definitions in extern "C" like this:
extern "C" {
void ExtensionInitializer(void** extDataToSet,
FREContextInitializer* ctxInitializerToSet,
FREContextFinalizer* ctxFinalizerToSet);
void ExtensionFinalizer();
void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx,
uint32_t* numFunctionsToSet, const FRENamedFunction** functionsToSet);
void ContextFinalizer(FREContext ctx);
FREObject myMethod(FREContext ctx, void* functionData, uint32_t argc,
FREObject argv[]);
}