Skip to content

Commit c09c4f5

Browse files
author
John Haley
committed
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.
1 parent 262d2fa commit c09c4f5

File tree

9 files changed

+286
-10
lines changed

9 files changed

+286
-10
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef CALLBACK_WRAPPER_H
2+
#define CALLBACK_WRAPPER_H
3+
4+
#include <v8.h>
5+
#include <node.h>
6+
7+
#include "nan.h"
8+
9+
using namespace v8;
10+
using namespace node;
11+
12+
struct CallbackWrapper {
13+
NanCallback* jsCallback;
14+
void * payload;
15+
};
16+
17+
#endif

generate/combyne/partials/async_function.cc

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
77
return NanThrowError("Callback is required and must be a Function.");
88
}
99

10+
{%each args|argsInfo as arg %}
11+
{%if arg.isCallbackFunction %}
12+
CallbackWrapper* {{ arg.name }}_cbWrapper = malloc(sizeof(CallbackWrapper));
13+
{{ arg.name }}_cbWrapper->jsCallback = args[{{ arg.jsArg }}];
14+
{{ arg.name }}_cbWrapper->payload = {{ args|payloadFor arg.name }};
15+
{%%endif%}
16+
{%endeach%}
17+
1018
{{ cppFunctionName }}Baton* baton = new {{ cppFunctionName }}Baton;
1119

1220
baton->error_code = GIT_OK;
@@ -16,6 +24,10 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
1624
{%if not arg.isReturn %}
1725
{%if arg.isSelf %}
1826
baton->{{ arg.name }} = ObjectWrap::Unwrap<{{ arg.cppClassName }}>(args.This())->GetValue();
27+
{%elsif arg.isCallbackFunction %}
28+
baton->{{ arg.name}} = {{ cppFunctionName }}_{{ arg.name }}_cppCallback
29+
{%elsif arg.payloadFor %}
30+
baton->{{ arg.name }} = {{ arg.payloadFor }}_cbWrapper
1931
{%elsif arg.name %}
2032
{%partial convertFromV8 arg%}
2133
{%if not arg.isPayload %}
@@ -36,7 +48,7 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
3648
{%if not arg.isReturn %}
3749
{%if arg.isSelf %}
3850
worker->SaveToPersistent("{{ arg.name }}", args.This());
39-
{%else%}
51+
{%elsif not arg.isCallbackFunction %}
4052
if (!args[{{ arg.jsArg }}]->IsUndefined() && !args[{{ arg.jsArg }}]->IsNull())
4153
worker->SaveToPersistent("{{ arg.name }}", args[{{ arg.jsArg }}]->ToObject());
4254
{%endif%}
@@ -125,6 +137,8 @@ void {{ cppClassName }}::{{ cppFunctionName }}Worker::HandleOKCallback() {
125137
{%else%}
126138
free((void*)baton->{{ arg.name }});
127139
{%endif%}
140+
{%elsif arg.payloadFor%}
141+
free(baton->{{ arg.name }});
128142
{%endif%}
129143
{%endeach%}
130144
}
@@ -145,8 +159,12 @@ void {{ cppClassName }}::{{ cppFunctionName }}Worker::HandleOKCallback() {
145159
baton->{{ arg.name}}NeedsFree = false;
146160
free((void *)baton->{{ arg.name }});
147161
}
162+
{%elsif arg.payloadFor%}
163+
free(baton->{{ arg.name }});
148164
{%endif%}
149165
{%endeach%}
150166

151167
delete baton;
152168
}
169+
170+
{%partial callbackHelpers .%}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
{%each args as cbArg %}
2+
{%if cbArg.isCallbackFunction %}
3+
4+
{{ cbArg.returnType }} {{ cppClassName }}::{{ cppFunctionName }}_{{ cbArg.name }}_cppCallback (
5+
{% each cbArg.args|argsInfo as arg %}
6+
{{ arg.cType }} {{ arg.name}}{% if not arg.lastArg %},{% endif %}
7+
{% endeach %}
8+
) {
9+
{{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton* baton = new {{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton();
10+
11+
{% each cbArg.args|argsInfo as arg %}
12+
baton->{{ arg.name }} = {{ arg.name }};
13+
{% endeach %}
14+
15+
baton->req.data = baton;
16+
baton->done = false;
17+
18+
uv_queue_work(uv_default_loop(), &baton->req, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncAfter);
19+
20+
while(!baton->done) {
21+
this_thread::sleep_for(chrono::milliseconds(1));
22+
}
23+
24+
{% each cbArg|returnsInfo true false as _return %}
25+
*{{ _return.name }} = *baton->{{ _return.name }};
26+
{% endeach %}
27+
28+
return baton->result;
29+
}
30+
31+
void {{ cppClassName }}::{{ function.cppFunctionName }}_{{ cbArg.name }}_asyncWork(uv_work_t* req) {
32+
// We aren't doing any work on a seperate thread, just need to
33+
// access the main node thread in the async after method.
34+
// However, this worker method is still needed
35+
}
36+
37+
void {{ cppClassName }}::{{ function.cppFunctionName }}_{{ cbArg.name }}_asyncAfter(uv_work_t* req, int status) {
38+
NanScope();
39+
40+
{{ function.cppFunctionName }}_{{ cbArg.name|titleCase }}Baton* baton = static_cast<{{ function.cppFunctionName }}_{{ cbArg.name|titleCase }}Baton*>(req->data);
41+
{{ cppClassName }}* instance = static_cast<{{ cppClassName }}*>(baton->payload);
42+
43+
if (instance->{{ cbArg.name }}->IsEmpty()) {
44+
{% if cbArg.returnType == "int" %}
45+
baton->result = {{ cbArg.returnNoResults }}; // no results acquired
46+
{% endif %}
47+
48+
baton->done = true;
49+
return;
50+
}
51+
52+
CallbackWrapper* cbWrapper = baton->payload;
53+
54+
Local<Value> argv[{{ cbArg.args|jsArgsCount }}] = {
55+
{% each cbArg.args|argsInfo as arg %}
56+
{% if arg.name == "payload" %}
57+
{%-- payload is always the last arg --%}
58+
NanNew(cbWrapper->payload)
59+
{% elsif arg.isJsArg %}
60+
{% if arg.isEnum %}
61+
NanNew((int)baton->{{ arg.name }}),
62+
{% elsif arg.isLibgitType %}
63+
NanNew({{ arg.cppClassName }}::New(&baton->{{ arg.name }}, false)),
64+
{% elsif arg.cType == "size_t" %}
65+
// HACK: NAN should really have an overload for NanNew to support size_t
66+
NanNew((unsigned int)baton->{{ arg.name }}),
67+
{% else %}
68+
NanNew(baton->{{ arg.name }}),
69+
{% endif %}
70+
{% endif %}
71+
{% endeach %}
72+
};
73+
74+
TryCatch tryCatch;
75+
Handle<Value> result = cbWrapper->jsFunction->Call({{ cbArg.args|jsArgsCount }}, argv);
76+
77+
if (result->IsObject() && result->ToObject()->Has(NanNew("then"))) {
78+
Handle<Value> thenProp = result->ToObject()->Get(NanNew("then"));
79+
80+
if (thenProp->IsFunction()) {
81+
// we can be reasonbly certain that the result is a promise
82+
Local<Object> promise = result->ToObject();
83+
84+
NanAssignPersistent(baton->promise, promise);
85+
86+
uv_queue_work(uv_default_loop(), &baton->req, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling);
87+
return;
88+
}
89+
}
90+
91+
{{ cbArg.returnType }} resultStatus;
92+
93+
{% each cbArg|returnsInfo true false as _return %}
94+
if (result.IsEmpty() || result->IsNativeError()) {
95+
baton->result = {{ cbArg.returnError }};
96+
}
97+
else if (!result->IsNull() && !result->IsUndefined()) {
98+
{{ _return.cppClassName }}* wrapper = ObjectWrap::Unwrap<{{ _return.cppClassName }}>(result->ToObject());
99+
wrapper->selfFreeing = false;
100+
101+
baton->{{ _return.name }} = wrapper->GetRefValue();
102+
baton->result = {{ cbArg.returnSuccess }};
103+
}
104+
else {
105+
baton->result = {{ cbArg.returnNoResults }};
106+
}
107+
{% endeach %}
108+
baton->done = true;
109+
}
110+
111+
void {{ cppClassName }}::{{ function.cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling(uv_work_t* req, int status) {
112+
NanScope();
113+
114+
{{ function.cppFunctionName }}_{{ cbArg.name|titleCase }}Baton* baton = static_cast<{{ function.cppFunctionName }}_{{ cbArg.name|titleCase }}Baton*>(req->data);
115+
Local<Object> promise = NanNew<Object>(baton->promise);
116+
NanCallback* isPendingFn = new NanCallback(promise->Get(NanNew("isPending")).As<Function>());
117+
Local<Value> argv[1]; // MSBUILD won't assign an array of length 0
118+
Local<Boolean> isPending = isPendingFn->Call(0, argv)->ToBoolean();
119+
120+
if (isPending->Value()) {
121+
uv_queue_work(uv_default_loop(), &baton->req, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling);
122+
return;
123+
}
124+
125+
NanCallback* isFulfilledFn = new NanCallback(promise->Get(NanNew("isFulfilled")).As<Function>());
126+
Local<Boolean> isFulfilled = isFulfilledFn->Call(0, argv)->ToBoolean();
127+
128+
if (isFulfilled->Value()) {
129+
NanCallback* resultFn = new NanCallback(promise->Get(NanNew("value")).As<Function>());
130+
Handle<Value> result = resultFn->Call(0, argv);
131+
{{ cbArg.returnType }} resultStatus;
132+
133+
{% each cbArg|returnsInfo true false as _return %}
134+
if (result.IsEmpty() || result->IsNativeError()) {
135+
baton->result = {{ cbArg.returnError }};
136+
}
137+
else if (!result->IsNull() && !result->IsUndefined()) {
138+
{{ _return.cppClassName }}* wrapper = ObjectWrap::Unwrap<{{ _return.cppClassName }}>(result->ToObject());
139+
wrapper->selfFreeing = false;
140+
141+
baton->{{ _return.name }} = wrapper->GetRefValue();
142+
baton->result = {{ cbArg.returnSuccess }};
143+
}
144+
else {
145+
baton->result = {{ cbArg.returnNoResults }};
146+
}
147+
{% endeach %}
148+
baton->done = true;
149+
}
150+
else {
151+
// promise was rejected
152+
baton->result = {{ cbArg.returnError }};
153+
baton->done = false;
154+
}
155+
}
156+
{%endif%}
157+
{%endeach%}

generate/combyne/partials/guard_arguments.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
|| (!args[{{arg.jsArg}}]->IsObject() && !args[{{arg.jsArg}}]->IsString())) {
88
return NanThrowError("{{arg.jsClassName}} {{arg.name}} is required.");
99
}
10-
10+
{%elsif arg.isCallbackFunction %}
11+
if (args.Length() == {{arg.jsArg}} || !args[{{arg.jsArg}}]->IsFunction()) {
12+
return NanThrowError("{{arg.jsClassName}} {{arg.name}} is required.");
13+
}
1114
{%else%}
1215
if (args.Length() == {{arg.jsArg}} || !args[{{arg.jsArg}}]->Is{{arg.cppClassName|cppToV8}}()) {
1316
return NanThrowError("{{arg.jsClassName}} {{arg.name}} is required.");

generate/combyne/partials/sync_function.cc

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,22 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
1515
{%each args|argsInfo as arg %}
1616
{%if not arg.isSelf %}
1717
{%if not arg.isReturn %}
18-
{%partial convertFromV8 arg %}
18+
{%if not arg.isCallbackFunction %}
19+
{%if not arg.payloadFor %}
20+
{%partial convertFromV8 arg %}
21+
{%endif%}
22+
{%endif%}
1923
{%endif%}
2024
{%endif%}
2125
{%endeach%}
2226

27+
{%each args|argsInfo as arg %}
28+
{%if arg.isCallbackFunction %}
29+
CallbackWrapper* {{ arg.name }}_cbWrapper = malloc(sizeof(CallbackWrapper));
30+
{{ arg.name }}_cbWrapper->jsCallback = args[{{ arg.jsArg }}];
31+
{{ arg.name }}_cbWrapper->payload = {{ args|payloadFor arg.name }};
32+
{%%endif%}
33+
{%endeach%}
2334
{%if .|hasReturns %}
2435
{{ return.cType }} result = {%endif%}{{ cFunctionName }}(
2536
{%each args|argsInfo as arg %}
@@ -30,12 +41,23 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
3041
ObjectWrap::Unwrap<{{ arg.cppClassName }}>(args.This())->GetValue()
3142
{%elsif arg.isReturn %}
3243
{{ arg.name }}
44+
{%elsif arg.isCallbackFunction %}
45+
{{ cppFunctionName }}_{{ arg.name }}_cppCallback
46+
{%elsif arg.payloadFor %}
47+
{{ arg.payloadFor }}_cbWrapper
3348
{%else%}
3449
from_{{ arg.name }}
3550
{%endif%}
3651
{%if not arg.lastArg %},{%endif%}
3752
{%endeach%}
3853
);
54+
55+
{%each args|argsInfo as arg %}
56+
{%if arg.isCallbackFunction %}
57+
free({{ arg.name }}_cbWrapper);
58+
{%%endif%}
59+
{%endeach%}
60+
3961
{%if return.isErrorCode %}
4062
if (result != GIT_OK) {
4163
{%each args|argsInfo as arg %}
@@ -91,3 +113,5 @@ from_{{ arg.name }}
91113
{%endif%}
92114
{%endif%}
93115
}
116+
117+
{%partial callbackHelpers .%}

generate/combyne/templates/class_content.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ extern "C" {
99
{% endeach %}
1010
}
1111

12-
12+
#include "../include/callbackWrapper.h"
1313
#include "../include/functions/copy.h"
1414
#include "../include/macros.h"
1515
#include "../include/{{ filename }}.h"

generate/combyne/templates/class_header.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ extern "C" {
1515
#include "{{ dependency }}"
1616
{%endeach%}
1717

18+
#include "../include/callbackWrapper.h"
19+
1820
{%if needsForwardDeclaration %}
1921
// Forward declaration.
2022
struct {{ cType }} {
@@ -43,6 +45,36 @@ class {{ cppClassName }} : public ObjectWrap {
4345
{%endif%}
4446
bool selfFreeing;
4547

48+
{% each functions as function %}
49+
{% if not function.ignore %}
50+
{%each function.args as arg %}
51+
{%if arg.isCallbackFunction %}
52+
static {{ arg.returnType }} {{ function.cppFunctionName }}_{{ arg.name }}_cppCallback (
53+
{% each arg.args|argsInfo as cbArg %}
54+
{{ cbArg.cType }} {{ cbArg.name }}
55+
{% if not cbArg.lastArg %}
56+
,
57+
{% endif %}
58+
{% endeach %}
59+
);
60+
61+
static void {{ function.cppFunctionName }}_{{ arg.name }}_asyncWork(uv_work_t* req);
62+
static void {{ function.cppFunctionName }}_{{ arg.name }}_asyncAfter(uv_work_t* req, int status);
63+
static void {{ function.cppFunctionName }}_{{ arg.name }}_asyncPromisePolling(uv_work_t* req, int status);
64+
struct {{ function.cppFunctionName }}_{{ arg.name|titleCase }}Baton {
65+
{% each arg.args|argsInfo as cbArg %}
66+
{{ cbArg.cType }} {{ cbArg.name }};
67+
{% endeach %}
68+
69+
uv_work_t* req;
70+
{{ arg.returnType }} result;
71+
Persistent<Object> promise;
72+
bool done;
73+
};
74+
{% endif %}
75+
{% endeach %}
76+
{% endif %}
77+
{% endeach %}
4678
private:
4779
{%if cType%}
4880
{{ cppClassName }}({{ cType }} *raw, bool selfFreeing);
@@ -91,6 +123,13 @@ class {{ cppClassName }} : public ObjectWrap {
91123
};
92124
{%endif%}
93125

126+
{%each function.args as arg %}
127+
{%if arg.payloadFor %}
128+
129+
Persistent<Value> {{ function.cppFunctionName }}_{{ arg.name }};
130+
{%endif%}
131+
{%endeach%}
132+
94133
static NAN_METHOD({{ function.cppFunctionName }});
95134
{%endif%}
96135
{%endeach%}

generate/scripts/generateNativeCode.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ module.exports = function generateNativeCode() {
2828

2929
var partials = {
3030
asyncFunction: utils.readFile("combyne/partials/async_function.cc"),
31+
callbackHelpers: utils.readFile("combyne/partials/callback_helpers.cc"),
3132
convertFromV8: utils.readFile("combyne/partials/convert_from_v8.cc"),
3233
convertToV8: utils.readFile("combyne/partials/convert_to_v8.cc"),
3334
doc: utils.readFile("combyne/partials/doc.cc"),

0 commit comments

Comments
 (0)