Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Started getting callback functions working in classes
Unlike structs, classes don't store the callbacks on the object.
Instead they are passed into the function and have to be hanled
differently. Currently this is breaking the combyne templating
and after that is fixed this should build. But this is what we
need to get going for status and diff to start working.
  • Loading branch information
John Haley committed Feb 1, 2015
commit c09c4f5d1e83f42c0fbc0ae1c9b5f9f65a3329de
17 changes: 17 additions & 0 deletions generate/combyne/manual/include/callbackWrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef CALLBACK_WRAPPER_H
#define CALLBACK_WRAPPER_H

#include <v8.h>
#include <node.h>

#include "nan.h"

using namespace v8;
using namespace node;

struct CallbackWrapper {
NanCallback* jsCallback;
void * payload;
};

#endif
20 changes: 19 additions & 1 deletion generate/combyne/partials/async_function.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
return NanThrowError("Callback is required and must be a Function.");
}

{%each args|argsInfo as arg %}
{%if arg.isCallbackFunction %}
CallbackWrapper* {{ arg.name }}_cbWrapper = malloc(sizeof(CallbackWrapper));
{{ arg.name }}_cbWrapper->jsCallback = args[{{ arg.jsArg }}];
{{ arg.name }}_cbWrapper->payload = {{ args|payloadFor arg.name }};
{%%endif%}
{%endeach%}

{{ cppFunctionName }}Baton* baton = new {{ cppFunctionName }}Baton;

baton->error_code = GIT_OK;
Expand All @@ -16,6 +24,10 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
{%if not arg.isReturn %}
{%if arg.isSelf %}
baton->{{ arg.name }} = ObjectWrap::Unwrap<{{ arg.cppClassName }}>(args.This())->GetValue();
{%elsif arg.isCallbackFunction %}
baton->{{ arg.name}} = {{ cppFunctionName }}_{{ arg.name }}_cppCallback
{%elsif arg.payloadFor %}
baton->{{ arg.name }} = {{ arg.payloadFor }}_cbWrapper
{%elsif arg.name %}
{%partial convertFromV8 arg%}
{%if not arg.isPayload %}
Expand All @@ -36,7 +48,7 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
{%if not arg.isReturn %}
{%if arg.isSelf %}
worker->SaveToPersistent("{{ arg.name }}", args.This());
{%else%}
{%elsif not arg.isCallbackFunction %}
if (!args[{{ arg.jsArg }}]->IsUndefined() && !args[{{ arg.jsArg }}]->IsNull())
worker->SaveToPersistent("{{ arg.name }}", args[{{ arg.jsArg }}]->ToObject());
{%endif%}
Expand Down Expand Up @@ -125,6 +137,8 @@ void {{ cppClassName }}::{{ cppFunctionName }}Worker::HandleOKCallback() {
{%else%}
free((void*)baton->{{ arg.name }});
{%endif%}
{%elsif arg.payloadFor%}
free(baton->{{ arg.name }});
{%endif%}
{%endeach%}
}
Expand All @@ -145,8 +159,12 @@ void {{ cppClassName }}::{{ cppFunctionName }}Worker::HandleOKCallback() {
baton->{{ arg.name}}NeedsFree = false;
free((void *)baton->{{ arg.name }});
}
{%elsif arg.payloadFor%}
free(baton->{{ arg.name }});
{%endif%}
{%endeach%}

delete baton;
}

{%partial callbackHelpers .%}
157 changes: 157 additions & 0 deletions generate/combyne/partials/callback_helpers.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
{%each args as cbArg %}
{%if cbArg.isCallbackFunction %}

{{ cbArg.returnType }} {{ cppClassName }}::{{ cppFunctionName }}_{{ cbArg.name }}_cppCallback (
{% each cbArg.args|argsInfo as arg %}
{{ arg.cType }} {{ arg.name}}{% if not arg.lastArg %},{% endif %}
{% endeach %}
) {
{{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton* baton = new {{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton();

{% each cbArg.args|argsInfo as arg %}
baton->{{ arg.name }} = {{ arg.name }};
{% endeach %}

baton->req.data = baton;
baton->done = false;

uv_queue_work(uv_default_loop(), &baton->req, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncAfter);

while(!baton->done) {
this_thread::sleep_for(chrono::milliseconds(1));
}

{% each cbArg|returnsInfo true false as _return %}
*{{ _return.name }} = *baton->{{ _return.name }};
{% endeach %}

return baton->result;
}

void {{ cppClassName }}::{{ function.cppFunctionName }}_{{ cbArg.name }}_asyncWork(uv_work_t* req) {
// We aren't doing any work on a seperate thread, just need to
// access the main node thread in the async after method.
// However, this worker method is still needed
}

void {{ cppClassName }}::{{ function.cppFunctionName }}_{{ cbArg.name }}_asyncAfter(uv_work_t* req, int status) {
NanScope();

{{ function.cppFunctionName }}_{{ cbArg.name|titleCase }}Baton* baton = static_cast<{{ function.cppFunctionName }}_{{ cbArg.name|titleCase }}Baton*>(req->data);
{{ cppClassName }}* instance = static_cast<{{ cppClassName }}*>(baton->payload);

if (instance->{{ cbArg.name }}->IsEmpty()) {
{% if cbArg.returnType == "int" %}
baton->result = {{ cbArg.returnNoResults }}; // no results acquired
{% endif %}

baton->done = true;
return;
}

CallbackWrapper* cbWrapper = baton->payload;

Local<Value> argv[{{ cbArg.args|jsArgsCount }}] = {
{% each cbArg.args|argsInfo as arg %}
{% if arg.name == "payload" %}
{%-- payload is always the last arg --%}
NanNew(cbWrapper->payload)
{% elsif arg.isJsArg %}
{% if arg.isEnum %}
NanNew((int)baton->{{ arg.name }}),
{% elsif arg.isLibgitType %}
NanNew({{ arg.cppClassName }}::New(&baton->{{ arg.name }}, false)),
{% elsif arg.cType == "size_t" %}
// HACK: NAN should really have an overload for NanNew to support size_t
NanNew((unsigned int)baton->{{ arg.name }}),
{% else %}
NanNew(baton->{{ arg.name }}),
{% endif %}
{% endif %}
{% endeach %}
};

TryCatch tryCatch;
Handle<Value> result = cbWrapper->jsFunction->Call({{ cbArg.args|jsArgsCount }}, argv);

if (result->IsObject() && result->ToObject()->Has(NanNew("then"))) {
Handle<Value> thenProp = result->ToObject()->Get(NanNew("then"));

if (thenProp->IsFunction()) {
// we can be reasonbly certain that the result is a promise
Local<Object> promise = result->ToObject();

NanAssignPersistent(baton->promise, promise);

uv_queue_work(uv_default_loop(), &baton->req, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling);
return;
}
}

{{ cbArg.returnType }} resultStatus;

{% each cbArg|returnsInfo true false as _return %}
if (result.IsEmpty() || result->IsNativeError()) {
baton->result = {{ cbArg.returnError }};
}
else if (!result->IsNull() && !result->IsUndefined()) {
{{ _return.cppClassName }}* wrapper = ObjectWrap::Unwrap<{{ _return.cppClassName }}>(result->ToObject());
wrapper->selfFreeing = false;

baton->{{ _return.name }} = wrapper->GetRefValue();
baton->result = {{ cbArg.returnSuccess }};
}
else {
baton->result = {{ cbArg.returnNoResults }};
}
{% endeach %}
baton->done = true;
}

void {{ cppClassName }}::{{ function.cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling(uv_work_t* req, int status) {
NanScope();

{{ function.cppFunctionName }}_{{ cbArg.name|titleCase }}Baton* baton = static_cast<{{ function.cppFunctionName }}_{{ cbArg.name|titleCase }}Baton*>(req->data);
Local<Object> promise = NanNew<Object>(baton->promise);
NanCallback* isPendingFn = new NanCallback(promise->Get(NanNew("isPending")).As<Function>());
Local<Value> argv[1]; // MSBUILD won't assign an array of length 0
Local<Boolean> isPending = isPendingFn->Call(0, argv)->ToBoolean();

if (isPending->Value()) {
uv_queue_work(uv_default_loop(), &baton->req, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling);
return;
}

NanCallback* isFulfilledFn = new NanCallback(promise->Get(NanNew("isFulfilled")).As<Function>());
Local<Boolean> isFulfilled = isFulfilledFn->Call(0, argv)->ToBoolean();

if (isFulfilled->Value()) {
NanCallback* resultFn = new NanCallback(promise->Get(NanNew("value")).As<Function>());
Handle<Value> result = resultFn->Call(0, argv);
{{ cbArg.returnType }} resultStatus;

{% each cbArg|returnsInfo true false as _return %}
if (result.IsEmpty() || result->IsNativeError()) {
baton->result = {{ cbArg.returnError }};
}
else if (!result->IsNull() && !result->IsUndefined()) {
{{ _return.cppClassName }}* wrapper = ObjectWrap::Unwrap<{{ _return.cppClassName }}>(result->ToObject());
wrapper->selfFreeing = false;

baton->{{ _return.name }} = wrapper->GetRefValue();
baton->result = {{ cbArg.returnSuccess }};
}
else {
baton->result = {{ cbArg.returnNoResults }};
}
{% endeach %}
baton->done = true;
}
else {
// promise was rejected
baton->result = {{ cbArg.returnError }};
baton->done = false;
}
}
{%endif%}
{%endeach%}
5 changes: 4 additions & 1 deletion generate/combyne/partials/guard_arguments.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
|| (!args[{{arg.jsArg}}]->IsObject() && !args[{{arg.jsArg}}]->IsString())) {
return NanThrowError("{{arg.jsClassName}} {{arg.name}} is required.");
}

{%elsif arg.isCallbackFunction %}
if (args.Length() == {{arg.jsArg}} || !args[{{arg.jsArg}}]->IsFunction()) {
return NanThrowError("{{arg.jsClassName}} {{arg.name}} is required.");
}
{%else%}
if (args.Length() == {{arg.jsArg}} || !args[{{arg.jsArg}}]->Is{{arg.cppClassName|cppToV8}}()) {
return NanThrowError("{{arg.jsClassName}} {{arg.name}} is required.");
Expand Down
26 changes: 25 additions & 1 deletion generate/combyne/partials/sync_function.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,22 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
{%each args|argsInfo as arg %}
{%if not arg.isSelf %}
{%if not arg.isReturn %}
{%partial convertFromV8 arg %}
{%if not arg.isCallbackFunction %}
{%if not arg.payloadFor %}
{%partial convertFromV8 arg %}
{%endif%}
{%endif%}
{%endif%}
{%endif%}
{%endeach%}

{%each args|argsInfo as arg %}
{%if arg.isCallbackFunction %}
CallbackWrapper* {{ arg.name }}_cbWrapper = malloc(sizeof(CallbackWrapper));
{{ arg.name }}_cbWrapper->jsCallback = args[{{ arg.jsArg }}];
{{ arg.name }}_cbWrapper->payload = {{ args|payloadFor arg.name }};
{%%endif%}
{%endeach%}
{%if .|hasReturns %}
{{ return.cType }} result = {%endif%}{{ cFunctionName }}(
{%each args|argsInfo as arg %}
Expand All @@ -30,12 +41,23 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
ObjectWrap::Unwrap<{{ arg.cppClassName }}>(args.This())->GetValue()
{%elsif arg.isReturn %}
{{ arg.name }}
{%elsif arg.isCallbackFunction %}
{{ cppFunctionName }}_{{ arg.name }}_cppCallback
{%elsif arg.payloadFor %}
{{ arg.payloadFor }}_cbWrapper
{%else%}
from_{{ arg.name }}
{%endif%}
{%if not arg.lastArg %},{%endif%}
{%endeach%}
);

{%each args|argsInfo as arg %}
{%if arg.isCallbackFunction %}
free({{ arg.name }}_cbWrapper);
{%%endif%}
{%endeach%}

{%if return.isErrorCode %}
if (result != GIT_OK) {
{%each args|argsInfo as arg %}
Expand Down Expand Up @@ -91,3 +113,5 @@ from_{{ arg.name }}
{%endif%}
{%endif%}
}

{%partial callbackHelpers .%}
2 changes: 1 addition & 1 deletion generate/combyne/templates/class_content.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ extern "C" {
{% endeach %}
}


#include "../include/callbackWrapper.h"
#include "../include/functions/copy.h"
#include "../include/macros.h"
#include "../include/{{ filename }}.h"
Expand Down
39 changes: 39 additions & 0 deletions generate/combyne/templates/class_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ extern "C" {
#include "{{ dependency }}"
{%endeach%}

#include "../include/callbackWrapper.h"

{%if needsForwardDeclaration %}
// Forward declaration.
struct {{ cType }} {
Expand Down Expand Up @@ -43,6 +45,36 @@ class {{ cppClassName }} : public ObjectWrap {
{%endif%}
bool selfFreeing;

{% each functions as function %}
{% if not function.ignore %}
{%each function.args as arg %}
{%if arg.isCallbackFunction %}
static {{ arg.returnType }} {{ function.cppFunctionName }}_{{ arg.name }}_cppCallback (
{% each arg.args|argsInfo as cbArg %}
{{ cbArg.cType }} {{ cbArg.name }}
{% if not cbArg.lastArg %}
,
{% endif %}
{% endeach %}
);

static void {{ function.cppFunctionName }}_{{ arg.name }}_asyncWork(uv_work_t* req);
static void {{ function.cppFunctionName }}_{{ arg.name }}_asyncAfter(uv_work_t* req, int status);
static void {{ function.cppFunctionName }}_{{ arg.name }}_asyncPromisePolling(uv_work_t* req, int status);
struct {{ function.cppFunctionName }}_{{ arg.name|titleCase }}Baton {
{% each arg.args|argsInfo as cbArg %}
{{ cbArg.cType }} {{ cbArg.name }};
{% endeach %}

uv_work_t* req;
{{ arg.returnType }} result;
Persistent<Object> promise;
bool done;
};
{% endif %}
{% endeach %}
{% endif %}
{% endeach %}
private:
{%if cType%}
{{ cppClassName }}({{ cType }} *raw, bool selfFreeing);
Expand Down Expand Up @@ -91,6 +123,13 @@ class {{ cppClassName }} : public ObjectWrap {
};
{%endif%}

{%each function.args as arg %}
{%if arg.payloadFor %}

Persistent<Value> {{ function.cppFunctionName }}_{{ arg.name }};
{%endif%}
{%endeach%}

static NAN_METHOD({{ function.cppFunctionName }});
{%endif%}
{%endeach%}
Expand Down
1 change: 1 addition & 0 deletions generate/scripts/generateNativeCode.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = function generateNativeCode() {

var partials = {
asyncFunction: utils.readFile("combyne/partials/async_function.cc"),
callbackHelpers: utils.readFile("combyne/partials/callback_helpers.cc"),
convertFromV8: utils.readFile("combyne/partials/convert_from_v8.cc"),
convertToV8: utils.readFile("combyne/partials/convert_to_v8.cc"),
doc: utils.readFile("combyne/partials/doc.cc"),
Expand Down
Loading