From 462675fa2dd113505028e900668327d8350b8a53 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Fri, 30 Jan 2015 11:55:54 -0500 Subject: [PATCH 01/20] WIP for status lists --- generate/input/descriptor.json | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json index 1301dbcfb..0f3280919 100644 --- a/generate/input/descriptor.json +++ b/generate/input/descriptor.json @@ -1274,6 +1274,10 @@ } }, "status": { + "dependencies": [ + "status_options.h", + "status_list.h" + ], "cDependencies": [ "git2/sys/diff.h" ], @@ -1289,9 +1293,16 @@ }, "git_status_init_options": { "ignore": true - }, + } + } + }, + "status_list": { + "functions": { "git_status_list_new": { - "ignore": true + "isAsync": true, + "return": { + "isErrorCode": true + } } } }, From f84ff247704eeed1aa0755ad73d982774ec9ecd3 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Fri, 30 Jan 2015 11:59:45 -0500 Subject: [PATCH 02/20] Adding status_options to the descriptor --- generate/input/descriptor.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json index 0f3280919..c18829131 100644 --- a/generate/input/descriptor.json +++ b/generate/input/descriptor.json @@ -1306,6 +1306,9 @@ } } }, + "status_options": { + "ignore": true + }, "submodule": { "functions": { "git_submodule_foreach": { From 6a5157370495b27336ebc713c28f170f1a6f1136 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Fri, 30 Jan 2015 12:25:01 -0500 Subject: [PATCH 03/20] Move git_status_options to suppliment.json --- generate/input/descriptor.json | 7 ------- generate/input/libgit2-supplement.json | 29 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json index c18829131..7e3e49001 100644 --- a/generate/input/descriptor.json +++ b/generate/input/descriptor.json @@ -1274,10 +1274,6 @@ } }, "status": { - "dependencies": [ - "status_options.h", - "status_list.h" - ], "cDependencies": [ "git2/sys/diff.h" ], @@ -1306,9 +1302,6 @@ } } }, - "status_options": { - "ignore": true - }, "submodule": { "functions": { "git_submodule_foreach": { diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index 693b2d865..4d6f0d9c9 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -248,6 +248,35 @@ { "type": "enum" } + ], + [ + "git_status_options", + { + "type": "struct", + "fields": [ + { + "type": "unsigned int", + "name": "version" + }, + { + "type": "git_status_show_t", + "name": "show" + }, + { + "type": "git_merge_tree_flag_t", + "name": "flags" + }, + { + "type": "git_strarray", + "name": "pathspec" + } + ], + "used": { + "needs": [ + "git_status_init_options" + ] + } + } ] ], "groups": [ From c89d4361967840d4f862364dce0e29eb753cb852 Mon Sep 17 00:00:00 2001 From: Maximiliano Korp Date: Fri, 30 Jan 2015 10:54:48 -0700 Subject: [PATCH 04/20] move status list methods off of status --- generate/input/libgit2-supplement.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index 4d6f0d9c9..0bdc25c87 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -300,6 +300,15 @@ "git_merge_head_from_ref", "git_merge_head_id" ] + ], + [ + "status_list", + [ + "git_status_list_entrycount", + "git_status_list_free", + "git_status_list_get_perfdata", + "git_status_list_new" + ] ] ] }, @@ -322,6 +331,14 @@ "git_merge_head_from_ref", "git_merge_head_id" ] + }, + "status": { + "functions": [ + "git_status_list_entrycount", + "git_status_list_free", + "git_status_list_get_perfdata", + "git_status_list_new" + ] } } } From b345eb519222d3830dfaf36cbf530e6790a1269a Mon Sep 17 00:00:00 2001 From: Maximiliano Korp Date: Fri, 30 Jan 2015 10:59:24 -0700 Subject: [PATCH 05/20] add other methods that refer to git_status_options --- generate/input/libgit2-supplement.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index 0bdc25c87..a593ad0d8 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -273,7 +273,9 @@ ], "used": { "needs": [ - "git_status_init_options" + "git_status_init_options", + "git_status_foreach_ext", + "git_status_list_new" ] } } From e5dbacb4f7f5ef401205d79cbc5443db86130121 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Fri, 30 Jan 2015 14:39:25 -0500 Subject: [PATCH 06/20] un-async git_status_list_new --- generate/input/descriptor.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json index 7e3e49001..46e2af667 100644 --- a/generate/input/descriptor.json +++ b/generate/input/descriptor.json @@ -1295,7 +1295,12 @@ "status_list": { "functions": { "git_status_list_new": { - "isAsync": true, + "isAsync": false, + "args": { + "opts": { + "isOptional": true + } + }, "return": { "isErrorCode": true } From da612cbfd261b128e4048f3615a4046aacfaf8e1 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Fri, 30 Jan 2015 14:39:51 -0500 Subject: [PATCH 07/20] Fix merge_tree_flat to status_opt --- generate/input/libgit2-supplement.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index a593ad0d8..f6ad7029f 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -263,7 +263,7 @@ "name": "show" }, { - "type": "git_merge_tree_flag_t", + "type": "git_status_opt_t", "name": "flags" }, { From 262d2fa44f7aa1615950d0cf7345417d37df9d64 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Fri, 30 Jan 2015 14:40:15 -0500 Subject: [PATCH 08/20] Enable git_status_foreach --- generate/input/callbacks.json | 20 ++++++++++++++++++++ generate/input/descriptor.json | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/generate/input/callbacks.json b/generate/input/callbacks.json index 3ca35e475..fb7d6439e 100644 --- a/generate/input/callbacks.json +++ b/generate/input/callbacks.json @@ -127,5 +127,25 @@ "returnNoResults": 1, "returnSuccess": 0, "returnError": -1 + }, + "git_status_cb": { + "args": [ + { + "name": "path", + "cType": "const char" + }, + { + "name": "status_flags", + "cType": "unsigned int" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "returnType": "int", + "returnNoResults": 1, + "returnSuccess": 0, + "returnError": -1 } } diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json index 46e2af667..1fe5d018b 100644 --- a/generate/input/descriptor.json +++ b/generate/input/descriptor.json @@ -1282,10 +1282,10 @@ "ignore": true }, "git_status_foreach": { - "ignore": true + "isAsync": true }, "git_status_foreach_ext": { - "ignore": true + "isAsync": true }, "git_status_init_options": { "ignore": true From c09c4f5d1e83f42c0fbc0ae1c9b5f9f65a3329de Mon Sep 17 00:00:00 2001 From: John Haley Date: Sun, 1 Feb 2015 14:25:00 -0700 Subject: [PATCH 09/20] 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. --- .../combyne/manual/include/callbackWrapper.h | 17 ++ generate/combyne/partials/async_function.cc | 20 ++- generate/combyne/partials/callback_helpers.cc | 157 ++++++++++++++++++ generate/combyne/partials/guard_arguments.cc | 5 +- generate/combyne/partials/sync_function.cc | 26 ++- generate/combyne/templates/class_content.cc | 2 +- generate/combyne/templates/class_header.h | 39 +++++ generate/scripts/generateNativeCode.js | 1 + generate/scripts/helpers.js | 29 +++- 9 files changed, 286 insertions(+), 10 deletions(-) create mode 100644 generate/combyne/manual/include/callbackWrapper.h create mode 100644 generate/combyne/partials/callback_helpers.cc diff --git a/generate/combyne/manual/include/callbackWrapper.h b/generate/combyne/manual/include/callbackWrapper.h new file mode 100644 index 000000000..e02182f09 --- /dev/null +++ b/generate/combyne/manual/include/callbackWrapper.h @@ -0,0 +1,17 @@ +#ifndef CALLBACK_WRAPPER_H +#define CALLBACK_WRAPPER_H + +#include +#include + +#include "nan.h" + +using namespace v8; +using namespace node; + +struct CallbackWrapper { + NanCallback* jsCallback; + void * payload; +}; + +#endif diff --git a/generate/combyne/partials/async_function.cc b/generate/combyne/partials/async_function.cc index ac0e0d1ee..98ea4779d 100644 --- a/generate/combyne/partials/async_function.cc +++ b/generate/combyne/partials/async_function.cc @@ -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; @@ -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 %} @@ -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%} @@ -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%} } @@ -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 .%} diff --git a/generate/combyne/partials/callback_helpers.cc b/generate/combyne/partials/callback_helpers.cc new file mode 100644 index 000000000..aac2b66d6 --- /dev/null +++ b/generate/combyne/partials/callback_helpers.cc @@ -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 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 result = cbWrapper->jsFunction->Call({{ cbArg.args|jsArgsCount }}, argv); + + if (result->IsObject() && result->ToObject()->Has(NanNew("then"))) { + Handle thenProp = result->ToObject()->Get(NanNew("then")); + + if (thenProp->IsFunction()) { + // we can be reasonbly certain that the result is a promise + Local 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 promise = NanNew(baton->promise); + NanCallback* isPendingFn = new NanCallback(promise->Get(NanNew("isPending")).As()); + Local argv[1]; // MSBUILD won't assign an array of length 0 + Local 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()); + Local isFulfilled = isFulfilledFn->Call(0, argv)->ToBoolean(); + + if (isFulfilled->Value()) { + NanCallback* resultFn = new NanCallback(promise->Get(NanNew("value")).As()); + Handle 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%} diff --git a/generate/combyne/partials/guard_arguments.cc b/generate/combyne/partials/guard_arguments.cc index 5179ce908..2a52b20a9 100644 --- a/generate/combyne/partials/guard_arguments.cc +++ b/generate/combyne/partials/guard_arguments.cc @@ -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."); diff --git a/generate/combyne/partials/sync_function.cc b/generate/combyne/partials/sync_function.cc index 243c1654e..9bd08497b 100644 --- a/generate/combyne/partials/sync_function.cc +++ b/generate/combyne/partials/sync_function.cc @@ -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 %} @@ -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 %} @@ -91,3 +113,5 @@ from_{{ arg.name }} {%endif%} {%endif%} } + +{%partial callbackHelpers .%} diff --git a/generate/combyne/templates/class_content.cc b/generate/combyne/templates/class_content.cc index b534ea4c2..b6ef84079 100644 --- a/generate/combyne/templates/class_content.cc +++ b/generate/combyne/templates/class_content.cc @@ -9,7 +9,7 @@ extern "C" { {% endeach %} } - +#include "../include/callbackWrapper.h" #include "../include/functions/copy.h" #include "../include/macros.h" #include "../include/{{ filename }}.h" diff --git a/generate/combyne/templates/class_header.h b/generate/combyne/templates/class_header.h index 41d9156c8..84f43514d 100644 --- a/generate/combyne/templates/class_header.h +++ b/generate/combyne/templates/class_header.h @@ -15,6 +15,8 @@ extern "C" { #include "{{ dependency }}" {%endeach%} +#include "../include/callbackWrapper.h" + {%if needsForwardDeclaration %} // Forward declaration. struct {{ cType }} { @@ -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 promise; + bool done; + }; + {% endif %} + {% endeach %} + {% endif %} + {% endeach %} private: {%if cType%} {{ cppClassName }}({{ cType }} *raw, bool selfFreeing); @@ -91,6 +123,13 @@ class {{ cppClassName }} : public ObjectWrap { }; {%endif%} + {%each function.args as arg %} + {%if arg.payloadFor %} + + Persistent {{ function.cppFunctionName }}_{{ arg.name }}; + {%endif%} + {%endeach%} + static NAN_METHOD({{ function.cppFunctionName }}); {%endif%} {%endeach%} diff --git a/generate/scripts/generateNativeCode.js b/generate/scripts/generateNativeCode.js index 0944ffca5..663794616 100644 --- a/generate/scripts/generateNativeCode.js +++ b/generate/scripts/generateNativeCode.js @@ -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"), diff --git a/generate/scripts/helpers.js b/generate/scripts/helpers.js index 2386461bd..d7a7e681c 100644 --- a/generate/scripts/helpers.js +++ b/generate/scripts/helpers.js @@ -87,7 +87,7 @@ var Helpers = { }, isPayloadFor: function(cbField, payloadName) { - return ~payloadName.indexOf("_payload") + return payloadName && ~payloadName.indexOf("_payload") && Helpers.isCallbackFunction(cbField.cType) && ~cbField.name.indexOf(payloadName.replace("_payload", "")); }, @@ -121,6 +121,7 @@ var Helpers = { processPayload: function(field, allFields) { if (field.name === "payload") { field.payloadFor = "*"; + field.isOptional = true; } else { var cbFieldName; @@ -134,6 +135,7 @@ var Helpers = { if (cbFieldName) { field.payloadFor = cbFieldName; + field.isOptional = true; } } }, @@ -211,7 +213,7 @@ var Helpers = { var argOverrides = fieldOverrides.args || {}; field.args = field.args || []; field.args.forEach(function (arg) { - Helpers.decorateArg(arg, null, null, argOverrides[arg.name] || {}, enums); + Helpers.decorateArg(arg, field.args, null, null, argOverrides[arg.name] || {}, enums); }); } else { @@ -226,7 +228,7 @@ var Helpers = { _.merge(field, fieldOverrides); }, - decorateArg: function(arg, typeDef, fnDef, argOverrides, enums) { + decorateArg: function(arg, allArgs, typeDef, fnDef, argOverrides, enums) { var type = arg.cType || arg.type; var normalizedType = Helpers.normalizeCtype(type); @@ -236,7 +238,22 @@ var Helpers = { Helpers.decorateLibgitType(arg, libgit2.types, enums); - if (typeDef && fnDef) { + // Some arguments can be callbacks + if (Helpers.isCallbackFunction(type)) { + Helpers.processCallback(arg); + + var argOverrides = argOverrides.args || {}; + arg.args = arg.args || []; + arg.args.forEach(function (argForCallback) { + Helpers.decorateArg(argForCallback, arg.args, null, null, argOverrides[argForCallback.name] || {}, enums); + }); + } + else if (typeDef && fnDef) { + Helpers.processPayload(arg, allArgs); + if(arg.payloadFor) { + return; + } + // Mark all of the args that are either returns or are the object // itself and determine if this function goes on the prototype // or is a constructor method. @@ -282,11 +299,11 @@ var Helpers = { var argOverrides = fnOverrides.args || {}; fnDef.args.forEach(function(arg) { - Helpers.decorateArg(arg, typeDef, fnDef, argOverrides[arg.name] || {}, enums); + Helpers.decorateArg(arg, fnDef.args, typeDef, fnDef, argOverrides[arg.name] || {}, enums); }); if (fnDef.return) { - Helpers.decorateArg(fnDef.return, typeDef, fnDef, fnOverrides.return || {}, enums); + Helpers.decorateArg(fnDef.return, fnDef.args, typeDef, fnDef, fnOverrides.return || {}, enums); } _(collisionMappings).forEach(function(newName, collidingName) { From 7601f978000948012ddd7965dcdde4cee5001fb4 Mon Sep 17 00:00:00 2001 From: John Haley Date: Mon, 2 Feb 2015 10:41:30 -0700 Subject: [PATCH 10/20] Fixed typos in combyne partials --- generate/combyne/partials/async_function.cc | 2 +- generate/combyne/partials/sync_function.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/generate/combyne/partials/async_function.cc b/generate/combyne/partials/async_function.cc index 98ea4779d..392895125 100644 --- a/generate/combyne/partials/async_function.cc +++ b/generate/combyne/partials/async_function.cc @@ -12,7 +12,7 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) { CallbackWrapper* {{ arg.name }}_cbWrapper = malloc(sizeof(CallbackWrapper)); {{ arg.name }}_cbWrapper->jsCallback = args[{{ arg.jsArg }}]; {{ arg.name }}_cbWrapper->payload = {{ args|payloadFor arg.name }}; - {%%endif%} + {%endif%} {%endeach%} {{ cppFunctionName }}Baton* baton = new {{ cppFunctionName }}Baton; diff --git a/generate/combyne/partials/sync_function.cc b/generate/combyne/partials/sync_function.cc index 9bd08497b..b504e2eab 100644 --- a/generate/combyne/partials/sync_function.cc +++ b/generate/combyne/partials/sync_function.cc @@ -29,7 +29,7 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) { CallbackWrapper* {{ arg.name }}_cbWrapper = malloc(sizeof(CallbackWrapper)); {{ arg.name }}_cbWrapper->jsCallback = args[{{ arg.jsArg }}]; {{ arg.name }}_cbWrapper->payload = {{ args|payloadFor arg.name }}; - {%%endif%} + {%endif%} {%endeach%} {%if .|hasReturns %} {{ return.cType }} result = {%endif%}{{ cFunctionName }}( @@ -55,7 +55,7 @@ from_{{ arg.name }} {%each args|argsInfo as arg %} {%if arg.isCallbackFunction %} free({{ arg.name }}_cbWrapper); - {%%endif%} + {%endif%} {%endeach%} {%if return.isErrorCode %} From 107f023fedd7d3e1b8a298c340e4cfa92e9a09b2 Mon Sep 17 00:00:00 2001 From: John Haley Date: Mon, 2 Feb 2015 10:58:29 -0700 Subject: [PATCH 11/20] Fixed error in callback_helpers.cc --- generate/combyne/partials/callback_helpers.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/generate/combyne/partials/callback_helpers.cc b/generate/combyne/partials/callback_helpers.cc index aac2b66d6..022bb131f 100644 --- a/generate/combyne/partials/callback_helpers.cc +++ b/generate/combyne/partials/callback_helpers.cc @@ -15,7 +15,7 @@ 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); + uv_queue_work(uv_default_loop(), &baton->req, {{ cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ cppFunctionName }}_{{ cbArg.name }}_asyncAfter); while(!baton->done) { this_thread::sleep_for(chrono::milliseconds(1)); @@ -28,16 +28,16 @@ return baton->result; } -void {{ cppClassName }}::{{ function.cppFunctionName }}_{{ cbArg.name }}_asyncWork(uv_work_t* req) { +void {{ cppClassName }}::{{ 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) { +void {{ cppClassName }}::{{ 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); + {{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton* baton = static_cast<{{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton*>(req->data); {{ cppClassName }}* instance = static_cast<{{ cppClassName }}*>(baton->payload); if (instance->{{ cbArg.name }}->IsEmpty()) { @@ -83,7 +83,7 @@ void {{ cppClassName }}::{{ function.cppFunctionName }}_{{ cbArg.name }}_asyncAf NanAssignPersistent(baton->promise, promise); - uv_queue_work(uv_default_loop(), &baton->req, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ function.cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling); + uv_queue_work(uv_default_loop(), &baton->req, {{ cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling); return; } } @@ -108,17 +108,17 @@ void {{ cppClassName }}::{{ function.cppFunctionName }}_{{ cbArg.name }}_asyncAf baton->done = true; } -void {{ cppClassName }}::{{ function.cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling(uv_work_t* req, int status) { +void {{ cppClassName }}::{{ 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); + {{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton* baton = static_cast<{{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton*>(req->data); Local promise = NanNew(baton->promise); NanCallback* isPendingFn = new NanCallback(promise->Get(NanNew("isPending")).As()); Local argv[1]; // MSBUILD won't assign an array of length 0 Local 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); + uv_queue_work(uv_default_loop(), &baton->req, {{ cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling); return; } From 3e357c572b16979c4905b92f351cb210d98e1953 Mon Sep 17 00:00:00 2001 From: John Haley Date: Mon, 2 Feb 2015 17:25:44 -0700 Subject: [PATCH 12/20] Callbacks in classes build now --- generate/combyne/filters/args_info.js | 13 +++- generate/combyne/filters/returns_info.js | 1 + .../combyne/manual/include/callbackWrapper.h | 2 +- generate/combyne/partials/async_function.cc | 17 +++-- generate/combyne/partials/callback_helpers.cc | 70 ++++++++----------- generate/combyne/partials/field_accessors.cc | 24 +++---- generate/combyne/partials/sync_function.cc | 11 +-- generate/combyne/templates/class_content.cc | 2 + generate/combyne/templates/class_header.h | 6 +- generate/combyne/templates/struct_header.h | 4 +- generate/input/callbacks.json | 62 +++++++++------- generate/input/descriptor.json | 10 ++- 12 files changed, 124 insertions(+), 98 deletions(-) diff --git a/generate/combyne/filters/args_info.js b/generate/combyne/filters/args_info.js index b2fb1aa5b..e2fe799d7 100644 --- a/generate/combyne/filters/args_info.js +++ b/generate/combyne/filters/args_info.js @@ -6,7 +6,7 @@ module.exports = function(args) { for(cArg = 0, jsArg = 0; cArg < args.length; cArg++) { var arg = args[cArg]; - if (!arg.isReturn && !arg.isSelf && !arg.isPayload) { + if (!arg.isReturn && !arg.isSelf) { arg.isJsArg = true; arg.jsArg = jsArg; @@ -23,6 +23,17 @@ module.exports = function(args) { arg.cArg = cArg; arg.isCppClassStringOrArray = ~["String", "Array"].indexOf(arg.cppClassName); + // if we have a callback then we also need the corresponding payload for that callback + if (arg.isCallbackFunction) { + var payload = args.filter(function(payload) { + return payload.payloadFor == arg.name || payload.payloadFor == '*'; + })[0]; + + if (payload) { + arg.payload = payload; + } + } + result.push(arg); } diff --git a/generate/combyne/filters/returns_info.js b/generate/combyne/filters/returns_info.js index 1c2856901..c1a315aee 100644 --- a/generate/combyne/filters/returns_info.js +++ b/generate/combyne/filters/returns_info.js @@ -19,6 +19,7 @@ module.exports = function(fn, argReturnsOnly, isAsync) { }); if (!result.length + && !fn.isCallbackFunction && !argReturnsOnly && fn.return && !fn.return.isErrorCode diff --git a/generate/combyne/manual/include/callbackWrapper.h b/generate/combyne/manual/include/callbackWrapper.h index e02182f09..5536b76ae 100644 --- a/generate/combyne/manual/include/callbackWrapper.h +++ b/generate/combyne/manual/include/callbackWrapper.h @@ -11,7 +11,7 @@ using namespace node; struct CallbackWrapper { NanCallback* jsCallback; - void * payload; + Persistent payload; }; #endif diff --git a/generate/combyne/partials/async_function.cc b/generate/combyne/partials/async_function.cc index 392895125..ac47ba528 100644 --- a/generate/combyne/partials/async_function.cc +++ b/generate/combyne/partials/async_function.cc @@ -9,9 +9,9 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) { {%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 }}; + CallbackWrapper* {{ arg.name }}_cbWrapper = (CallbackWrapper *)malloc(sizeof(CallbackWrapper)); + {{ arg.name }}_cbWrapper->jsCallback = new NanCallback(args[{{ arg.jsArg }}].As()); + NanAssignPersistent({{ arg.name }}_cbWrapper->payload, args[{{ arg.payload.jsArg }}]); {%endif%} {%endeach%} @@ -25,9 +25,10 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) { {%if arg.isSelf %} baton->{{ arg.name }} = ObjectWrap::Unwrap<{{ arg.cppClassName }}>(args.This())->GetValue(); {%elsif arg.isCallbackFunction %} - baton->{{ arg.name}} = {{ cppFunctionName }}_{{ arg.name }}_cppCallback + baton->{{ arg.name}} = {{ cppFunctionName }}_{{ arg.name }}_cppCallback; + baton->{{ arg.payload.name }} = {{ arg.name }}_cbWrapper; {%elsif arg.payloadFor %} - baton->{{ arg.name }} = {{ arg.payloadFor }}_cbWrapper + {%-- payloads are handled inside of the callback condition --%} {%elsif arg.name %} {%partial convertFromV8 arg%} {%if not arg.isPayload %} @@ -159,8 +160,10 @@ void {{ cppClassName }}::{{ cppFunctionName }}Worker::HandleOKCallback() { baton->{{ arg.name}}NeedsFree = false; free((void *)baton->{{ arg.name }}); } - {%elsif arg.payloadFor%} - free(baton->{{ arg.name }}); + {%elsif arg.isCallbackFunction%} + CallbackWrapper* cbWrapper = (CallbackWrapper *)baton->{{ arg.payload.name }}; + NanDisposePersistent(cbWrapper->payload); + free(baton->{{ arg.payload.name }}); {%endif%} {%endeach%} diff --git a/generate/combyne/partials/callback_helpers.cc b/generate/combyne/partials/callback_helpers.cc index 022bb131f..872458e18 100644 --- a/generate/combyne/partials/callback_helpers.cc +++ b/generate/combyne/partials/callback_helpers.cc @@ -1,58 +1,48 @@ -{%each args as cbArg %} - {%if cbArg.isCallbackFunction %} +{%each args as cbFunction %} + {%if cbFunction.isCallbackFunction %} -{{ cbArg.returnType }} {{ cppClassName }}::{{ cppFunctionName }}_{{ cbArg.name }}_cppCallback ( - {% each cbArg.args|argsInfo as arg %} +{{ cbFunction.return.type }} {{ cppClassName }}::{{ cppFunctionName }}_{{ cbFunction.name }}_cppCallback ( + {% each cbFunction.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(); + {{ cppFunctionName }}_{{ cbFunction.name|titleCase }}Baton* baton = new {{ cppFunctionName }}_{{ cbFunction.name|titleCase }}Baton(); - {% each cbArg.args|argsInfo as arg %} + {% each cbFunction.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, {{ cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ cppFunctionName }}_{{ cbArg.name }}_asyncAfter); + uv_queue_work(uv_default_loop(), &baton->req, {{ cppFunctionName }}_{{ cbFunction.name }}_asyncWork, {{ cppFunctionName }}_{{ cbFunction.name }}_asyncAfter); while(!baton->done) { this_thread::sleep_for(chrono::milliseconds(1)); } - {% each cbArg|returnsInfo true false as _return %} + {% each cbFunction|returnsInfo true false as _return %} *{{ _return.name }} = *baton->{{ _return.name }}; {% endeach %} return baton->result; } -void {{ cppClassName }}::{{ cppFunctionName }}_{{ cbArg.name }}_asyncWork(uv_work_t* req) { +void {{ cppClassName }}::{{ cppFunctionName }}_{{ cbFunction.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 }}::{{ cppFunctionName }}_{{ cbArg.name }}_asyncAfter(uv_work_t* req, int status) { +void {{ cppClassName }}::{{ cppFunctionName }}_{{ cbFunction.name }}_asyncAfter(uv_work_t* req, int status) { NanScope(); - {{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton* baton = static_cast<{{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton*>(req->data); - {{ cppClassName }}* instance = static_cast<{{ cppClassName }}*>(baton->payload); + {{ cppFunctionName }}_{{ cbFunction.name|titleCase }}Baton* baton = static_cast<{{ cppFunctionName }}_{{ cbFunction.name|titleCase }}Baton*>(req->data); - if (instance->{{ cbArg.name }}->IsEmpty()) { - {% if cbArg.returnType == "int" %} - baton->result = {{ cbArg.returnNoResults }}; // no results acquired - {% endif %} + CallbackWrapper* cbWrapper = (CallbackWrapper *)baton->payload; - baton->done = true; - return; - } - - CallbackWrapper* cbWrapper = baton->payload; - - Local argv[{{ cbArg.args|jsArgsCount }}] = { - {% each cbArg.args|argsInfo as arg %} + Local argv[{{ cbFunction.args|jsArgsCount }}] = { + {% each cbFunction.args|argsInfo as arg %} {% if arg.name == "payload" %} {%-- payload is always the last arg --%} NanNew(cbWrapper->payload) @@ -72,7 +62,7 @@ void {{ cppClassName }}::{{ cppFunctionName }}_{{ cbArg.name }}_asyncAfter(uv_wo }; TryCatch tryCatch; - Handle result = cbWrapper->jsFunction->Call({{ cbArg.args|jsArgsCount }}, argv); + Handle result = cbWrapper->jsCallback->Call({{ cbFunction.args|jsArgsCount }}, argv); if (result->IsObject() && result->ToObject()->Has(NanNew("then"))) { Handle thenProp = result->ToObject()->Get(NanNew("then")); @@ -83,42 +73,42 @@ void {{ cppClassName }}::{{ cppFunctionName }}_{{ cbArg.name }}_asyncAfter(uv_wo NanAssignPersistent(baton->promise, promise); - uv_queue_work(uv_default_loop(), &baton->req, {{ cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling); + uv_queue_work(uv_default_loop(), &baton->req, {{ cppFunctionName }}_{{ cbFunction.name }}_asyncWork, {{ cppFunctionName }}_{{ cbFunction.name }}_asyncPromisePolling); return; } } - {{ cbArg.returnType }} resultStatus; + {{ cbFunction.return.type }} resultStatus; - {% each cbArg|returnsInfo true false as _return %} + {% each cbFunction|returnsInfo true false as _return %} if (result.IsEmpty() || result->IsNativeError()) { - baton->result = {{ cbArg.returnError }}; + baton->result = {{ cbFunction.return.error }}; } 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 }}; + baton->result = {{ cbFunction.return.success }}; } else { - baton->result = {{ cbArg.returnNoResults }}; + baton->result = {{ cbFunction.return.noResults }}; } {% endeach %} baton->done = true; } -void {{ cppClassName }}::{{ cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling(uv_work_t* req, int status) { +void {{ cppClassName }}::{{ cppFunctionName }}_{{ cbFunction.name }}_asyncPromisePolling(uv_work_t* req, int status) { NanScope(); - {{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton* baton = static_cast<{{ cppFunctionName }}_{{ cbArg.name|titleCase }}Baton*>(req->data); + {{ cppFunctionName }}_{{ cbFunction.name|titleCase }}Baton* baton = static_cast<{{ cppFunctionName }}_{{ cbFunction.name|titleCase }}Baton*>(req->data); Local promise = NanNew(baton->promise); NanCallback* isPendingFn = new NanCallback(promise->Get(NanNew("isPending")).As()); Local argv[1]; // MSBUILD won't assign an array of length 0 Local isPending = isPendingFn->Call(0, argv)->ToBoolean(); if (isPending->Value()) { - uv_queue_work(uv_default_loop(), &baton->req, {{ cppFunctionName }}_{{ cbArg.name }}_asyncWork, {{ cppFunctionName }}_{{ cbArg.name }}_asyncPromisePolling); + uv_queue_work(uv_default_loop(), &baton->req, {{ cppFunctionName }}_{{ cbFunction.name }}_asyncWork, {{ cppFunctionName }}_{{ cbFunction.name }}_asyncPromisePolling); return; } @@ -128,28 +118,28 @@ void {{ cppClassName }}::{{ cppFunctionName }}_{{ cbArg.name }}_asyncPromisePoll if (isFulfilled->Value()) { NanCallback* resultFn = new NanCallback(promise->Get(NanNew("value")).As()); Handle result = resultFn->Call(0, argv); - {{ cbArg.returnType }} resultStatus; + {{ cbFunction.return.type }} resultStatus; - {% each cbArg|returnsInfo true false as _return %} + {% each cbFunction|returnsInfo true false as _return %} if (result.IsEmpty() || result->IsNativeError()) { - baton->result = {{ cbArg.returnError }}; + baton->result = {{ cbFunction.return.error }}; } 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 }}; + baton->result = {{ cbFunction.return.success }}; } else { - baton->result = {{ cbArg.returnNoResults }}; + baton->result = {{ cbFunction.return.noResults }}; } {% endeach %} baton->done = true; } else { // promise was rejected - baton->result = {{ cbArg.returnError }}; + baton->result = {{ cbFunction.return.error }}; baton->done = false; } } diff --git a/generate/combyne/partials/field_accessors.cc b/generate/combyne/partials/field_accessors.cc index a980f4b22..c166e147f 100644 --- a/generate/combyne/partials/field_accessors.cc +++ b/generate/combyne/partials/field_accessors.cc @@ -69,7 +69,7 @@ } {% if field.isCallbackFunction %} - {{ field.returnType }} {{ cppClassName }}::{{ field.name }}_cppCallback ( + {{ field.return.type }} {{ cppClassName }}::{{ field.name }}_cppCallback ( {% each field.args|argsInfo as arg %} {{ arg.cType }} {{ arg.name}}{% if not arg.lastArg %},{% endif %} {% endeach %} @@ -109,8 +109,8 @@ {{ cppClassName }}* instance = static_cast<{{ cppClassName }}*>(baton->payload); if (instance->{{ field.name }}->IsEmpty()) { - {% if field.returnType == "int" %} - baton->result = {{ field.returnNoResults }}; // no results acquired + {% if field.return.type == "int" %} + baton->result = {{ field.return.noResults }}; // no results acquired {% endif %} baton->done = true; @@ -154,21 +154,21 @@ } } - {{ field.returnType }} resultStatus; + {{ field.return.type }} resultStatus; {% each field|returnsInfo true false as _return %} if (result.IsEmpty() || result->IsNativeError()) { - baton->result = {{ field.returnError }}; + baton->result = {{ field.return.error }}; } 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 = {{ field.returnSuccess }}; + baton->result = {{ field.return.success }}; } else { - baton->result = {{ field.returnNoResults }}; + baton->result = {{ field.return.noResults }}; } {% endeach %} baton->done = true; @@ -194,28 +194,28 @@ if (isFulfilled->Value()) { NanCallback* resultFn = new NanCallback(promise->Get(NanNew("value")).As()); Handle result = resultFn->Call(0, argv); - {{ field.returnType }} resultStatus; + {{ field.return.type }} resultStatus; {% each field|returnsInfo true false as _return %} if (result.IsEmpty() || result->IsNativeError()) { - baton->result = {{ field.returnError }}; + baton->result = {{ field.return.error }}; } 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 = {{ field.returnSuccess }}; + baton->result = {{ field.return.success }}; } else { - baton->result = {{ field.returnNoResults }}; + baton->result = {{ field.return.noResults }}; } {% endeach %} baton->done = true; } else { // promise was rejected - baton->result = {{ field.returnError }}; + baton->result = {{ field.return.error }}; baton->done = false; } } diff --git a/generate/combyne/partials/sync_function.cc b/generate/combyne/partials/sync_function.cc index b504e2eab..58d0cd5fc 100644 --- a/generate/combyne/partials/sync_function.cc +++ b/generate/combyne/partials/sync_function.cc @@ -26,9 +26,9 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) { {%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 }}; +CallbackWrapper* {{ arg.name }}_cbWrapper = (CallbackWrapper *)malloc(sizeof(CallbackWrapper)); +{{ arg.name }}_cbWrapper->jsCallback = new NanCallback(args[{{ arg.jsArg }}].As()); +NanAssignPersistent({{ arg.name }}_cbWrapper->payload, args[{{ arg.payload.jsArg }}]); {%endif%} {%endeach%} {%if .|hasReturns %} @@ -42,9 +42,10 @@ ObjectWrap::Unwrap<{{ arg.cppClassName }}>(args.This())->GetValue() {%elsif arg.isReturn %} {{ arg.name }} {%elsif arg.isCallbackFunction %} -{{ cppFunctionName }}_{{ arg.name }}_cppCallback +{{ cppFunctionName }}_{{ arg.name }}_cppCallback, +{{ arg.name }}_cbWrapper {%elsif arg.payloadFor %} -{{ arg.payloadFor }}_cbWrapper +{%-- payloads are handled inside of the callback condition --%} {%else%} from_{{ arg.name }} {%endif%} diff --git a/generate/combyne/templates/class_content.cc b/generate/combyne/templates/class_content.cc index b6ef84079..7cc0e589d 100644 --- a/generate/combyne/templates/class_content.cc +++ b/generate/combyne/templates/class_content.cc @@ -1,6 +1,8 @@ // This is a generated file, modify: generate/templates/class_content.cc. #include #include +#include +#include extern "C" { #include diff --git a/generate/combyne/templates/class_header.h b/generate/combyne/templates/class_header.h index 84f43514d..090d2937d 100644 --- a/generate/combyne/templates/class_header.h +++ b/generate/combyne/templates/class_header.h @@ -49,7 +49,7 @@ class {{ cppClassName }} : public ObjectWrap { {% if not function.ignore %} {%each function.args as arg %} {%if arg.isCallbackFunction %} - static {{ arg.returnType }} {{ function.cppFunctionName }}_{{ arg.name }}_cppCallback ( + static {{ arg.return.type }} {{ function.cppFunctionName }}_{{ arg.name }}_cppCallback ( {% each arg.args|argsInfo as cbArg %} {{ cbArg.cType }} {{ cbArg.name }} {% if not cbArg.lastArg %} @@ -66,8 +66,8 @@ class {{ cppClassName }} : public ObjectWrap { {{ cbArg.cType }} {{ cbArg.name }}; {% endeach %} - uv_work_t* req; - {{ arg.returnType }} result; + uv_work_t req; + {{ arg.return.type }} result; Persistent promise; bool done; }; diff --git a/generate/combyne/templates/struct_header.h b/generate/combyne/templates/struct_header.h index 319d86136..9efe1ecd7 100644 --- a/generate/combyne/templates/struct_header.h +++ b/generate/combyne/templates/struct_header.h @@ -34,7 +34,7 @@ class {{ cppClassName }} : public ObjectWrap { {% each fields as field %} {% if not field.ignore %} {% if field.isCallbackFunction %} - static {{ field.returnType }} {{ field.name }}_cppCallback ( + static {{ field.return.type }} {{ field.name }}_cppCallback ( {% each field.args|argsInfo as arg %} {{ arg.cType }} {{ arg.name}} {% if not arg.lastArg %} @@ -52,7 +52,7 @@ class {{ cppClassName }} : public ObjectWrap { {% endeach %} uv_work_t req; - {{ field.returnType }} result; + {{ field.return.type }} result; Persistent promise; bool done; }; diff --git a/generate/input/callbacks.json b/generate/input/callbacks.json index fb7d6439e..bd619d321 100644 --- a/generate/input/callbacks.json +++ b/generate/input/callbacks.json @@ -26,10 +26,12 @@ "cType": "void *" } ], - "returnType": "int", - "returnNoResults": 1, - "returnSuccess": 0, - "returnError": -1 + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } }, "git_checkout_progress_cb": { "args": [ @@ -50,10 +52,12 @@ "cType": "void *" } ], - "returnType": "int", - "returnNoResults": 1, - "returnSuccess": 0, - "returnError": -1 + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } }, "git_cred_acquire_cb": { "args": [ @@ -79,10 +83,12 @@ "cType": "void *" } ], - "returnType": "int", - "returnNoResults": 1, - "returnSuccess": 0, - "returnError": -1 + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } }, "git_diff_notify_cb": { "args": [ @@ -103,10 +109,12 @@ "cType": "void *" } ], - "returnType": "int", - "returnNoResults": 1, - "returnSuccess": 0, - "returnError": -1 + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } }, "git_diff_file_cb": { "args": [ @@ -123,16 +131,18 @@ "cType": "void *" } ], - "returnType": "int", - "returnNoResults": 1, - "returnSuccess": 0, - "returnError": -1 + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } }, "git_status_cb": { "args": [ { "name": "path", - "cType": "const char" + "cType": "const char *" }, { "name": "status_flags", @@ -143,9 +153,11 @@ "cType": "void *" } ], - "returnType": "int", - "returnNoResults": 1, - "returnSuccess": 0, - "returnError": -1 + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } } } diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json index 1fe5d018b..842420649 100644 --- a/generate/input/descriptor.json +++ b/generate/input/descriptor.json @@ -1282,10 +1282,16 @@ "ignore": true }, "git_status_foreach": { - "isAsync": true + "isAsync": true, + "return": { + "isErrorCode": true + } }, "git_status_foreach_ext": { - "isAsync": true + "isAsync": true, + "return": { + "isErrorCode": true + } }, "git_status_init_options": { "ignore": true From 511699f51ef2a372f984927b0f3354d1e9cbd685 Mon Sep 17 00:00:00 2001 From: John Haley Date: Tue, 3 Feb 2015 09:41:04 -0700 Subject: [PATCH 13/20] Removed payload from callbacks since js can just use closure scope --- .../combyne/manual/include/callbackWrapper.h | 17 ------------- generate/combyne/partials/async_function.cc | 24 ++++++------------- generate/combyne/partials/callback_helpers.cc | 7 +++--- generate/combyne/partials/guard_arguments.cc | 8 ++++--- generate/combyne/partials/sync_function.cc | 8 +++---- generate/combyne/templates/class_content.cc | 1 - generate/combyne/templates/class_header.h | 2 -- 7 files changed, 19 insertions(+), 48 deletions(-) delete mode 100644 generate/combyne/manual/include/callbackWrapper.h diff --git a/generate/combyne/manual/include/callbackWrapper.h b/generate/combyne/manual/include/callbackWrapper.h deleted file mode 100644 index 5536b76ae..000000000 --- a/generate/combyne/manual/include/callbackWrapper.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef CALLBACK_WRAPPER_H -#define CALLBACK_WRAPPER_H - -#include -#include - -#include "nan.h" - -using namespace v8; -using namespace node; - -struct CallbackWrapper { - NanCallback* jsCallback; - Persistent payload; -}; - -#endif diff --git a/generate/combyne/partials/async_function.cc b/generate/combyne/partials/async_function.cc index ac47ba528..d1973ace0 100644 --- a/generate/combyne/partials/async_function.cc +++ b/generate/combyne/partials/async_function.cc @@ -7,14 +7,6 @@ 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 = (CallbackWrapper *)malloc(sizeof(CallbackWrapper)); - {{ arg.name }}_cbWrapper->jsCallback = new NanCallback(args[{{ arg.jsArg }}].As()); - NanAssignPersistent({{ arg.name }}_cbWrapper->payload, args[{{ arg.payload.jsArg }}]); - {%endif%} - {%endeach%} - {{ cppFunctionName }}Baton* baton = new {{ cppFunctionName }}Baton; baton->error_code = GIT_OK; @@ -26,12 +18,12 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) { baton->{{ arg.name }} = ObjectWrap::Unwrap<{{ arg.cppClassName }}>(args.This())->GetValue(); {%elsif arg.isCallbackFunction %} baton->{{ arg.name}} = {{ cppFunctionName }}_{{ arg.name }}_cppCallback; - baton->{{ arg.payload.name }} = {{ arg.name }}_cbWrapper; + baton->{{ arg.payload.name }} = new NanCallback(args[{{ arg.jsArg }}].As()); {%elsif arg.payloadFor %} - {%-- payloads are handled inside of the callback condition --%} + {%-- payloads are ignored --%} {%elsif arg.name %} {%partial convertFromV8 arg%} - {%if not arg.isPayload %} + {%if not arg.payloadFor %} baton->{{ arg.name }} = from_{{ arg.name }}; {%if arg | isOid %} baton->{{ arg.name }}NeedsFree = args[{{ arg.jsArg }}]->IsString(); @@ -135,11 +127,11 @@ void {{ cppClassName }}::{{ cppFunctionName }}Worker::HandleOKCallback() { baton->{{ arg.name}}NeedsFree = false; free((void*)baton->{{ arg.name }}); } + {%elsif arg.isCallbackFunction %} + delete baton->{{ arg.payload.name }}; {%else%} free((void*)baton->{{ arg.name }}); {%endif%} - {%elsif arg.payloadFor%} - free(baton->{{ arg.name }}); {%endif%} {%endeach%} } @@ -160,10 +152,8 @@ void {{ cppClassName }}::{{ cppFunctionName }}Worker::HandleOKCallback() { baton->{{ arg.name}}NeedsFree = false; free((void *)baton->{{ arg.name }}); } - {%elsif arg.isCallbackFunction%} - CallbackWrapper* cbWrapper = (CallbackWrapper *)baton->{{ arg.payload.name }}; - NanDisposePersistent(cbWrapper->payload); - free(baton->{{ arg.payload.name }}); + {%elsif arg.isCallbackFunction %} + delete (NanCallback *)baton->{{ arg.payload.name }}; {%endif%} {%endeach%} diff --git a/generate/combyne/partials/callback_helpers.cc b/generate/combyne/partials/callback_helpers.cc index 872458e18..e3e250f92 100644 --- a/generate/combyne/partials/callback_helpers.cc +++ b/generate/combyne/partials/callback_helpers.cc @@ -39,13 +39,14 @@ void {{ cppClassName }}::{{ cppFunctionName }}_{{ cbFunction.name }}_asyncAfter( {{ cppFunctionName }}_{{ cbFunction.name|titleCase }}Baton* baton = static_cast<{{ cppFunctionName }}_{{ cbFunction.name|titleCase }}Baton*>(req->data); - CallbackWrapper* cbWrapper = (CallbackWrapper *)baton->payload; + NanCallback* callback = (NanCallback *)baton->payload; Local argv[{{ cbFunction.args|jsArgsCount }}] = { {% each cbFunction.args|argsInfo as arg %} {% if arg.name == "payload" %} {%-- payload is always the last arg --%} - NanNew(cbWrapper->payload) + // payload is null because we can use closure scope in javascript + NanUndefined() {% elsif arg.isJsArg %} {% if arg.isEnum %} NanNew((int)baton->{{ arg.name }}), @@ -62,7 +63,7 @@ void {{ cppClassName }}::{{ cppFunctionName }}_{{ cbFunction.name }}_asyncAfter( }; TryCatch tryCatch; - Handle result = cbWrapper->jsCallback->Call({{ cbFunction.args|jsArgsCount }}, argv); + Handle result = callback->Call({{ cbFunction.args|jsArgsCount }}, argv); if (result->IsObject() && result->ToObject()->Has(NanNew("then"))) { Handle thenProp = result->ToObject()->Get(NanNew("then")); diff --git a/generate/combyne/partials/guard_arguments.cc b/generate/combyne/partials/guard_arguments.cc index 2a52b20a9..29f905a76 100644 --- a/generate/combyne/partials/guard_arguments.cc +++ b/generate/combyne/partials/guard_arguments.cc @@ -2,20 +2,22 @@ {%each args|argsInfo as arg%} {%if arg.isJsArg%} {%if not arg.isOptional%} - {%if arg | isOid %} + {%if not arg.payloadFor %} + {%if arg | isOid %} if (args.Length() == {{arg.jsArg}} || (!args[{{arg.jsArg}}]->IsObject() && !args[{{arg.jsArg}}]->IsString())) { return NanThrowError("{{arg.jsClassName}} {{arg.name}} is required."); } - {%elsif arg.isCallbackFunction %} + {%elsif arg.isCallbackFunction %} if (args.Length() == {{arg.jsArg}} || !args[{{arg.jsArg}}]->IsFunction()) { return NanThrowError("{{arg.jsClassName}} {{arg.name}} is required."); } - {%else%} + {%else%} if (args.Length() == {{arg.jsArg}} || !args[{{arg.jsArg}}]->Is{{arg.cppClassName|cppToV8}}()) { return NanThrowError("{{arg.jsClassName}} {{arg.name}} is required."); } + {%endif%} {%endif%} {%endif%} {%endif%} diff --git a/generate/combyne/partials/sync_function.cc b/generate/combyne/partials/sync_function.cc index 58d0cd5fc..d41d94eef 100644 --- a/generate/combyne/partials/sync_function.cc +++ b/generate/combyne/partials/sync_function.cc @@ -26,9 +26,7 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) { {%each args|argsInfo as arg %} {%if arg.isCallbackFunction %} -CallbackWrapper* {{ arg.name }}_cbWrapper = (CallbackWrapper *)malloc(sizeof(CallbackWrapper)); -{{ arg.name }}_cbWrapper->jsCallback = new NanCallback(args[{{ arg.jsArg }}].As()); -NanAssignPersistent({{ arg.name }}_cbWrapper->payload, args[{{ arg.payload.jsArg }}]); +NanCallback* {{ arg.name }}_callback = new NanCallback(args[{{ arg.jsArg }}].As()); {%endif%} {%endeach%} {%if .|hasReturns %} @@ -43,7 +41,7 @@ ObjectWrap::Unwrap<{{ arg.cppClassName }}>(args.This())->GetValue() {{ arg.name }} {%elsif arg.isCallbackFunction %} {{ cppFunctionName }}_{{ arg.name }}_cppCallback, -{{ arg.name }}_cbWrapper +{{ arg.name }}_callback {%elsif arg.payloadFor %} {%-- payloads are handled inside of the callback condition --%} {%else%} @@ -55,7 +53,7 @@ from_{{ arg.name }} {%each args|argsInfo as arg %} {%if arg.isCallbackFunction %} - free({{ arg.name }}_cbWrapper); +delete {{ arg.name }}_callback; {%endif%} {%endeach%} diff --git a/generate/combyne/templates/class_content.cc b/generate/combyne/templates/class_content.cc index 7cc0e589d..8df6c0399 100644 --- a/generate/combyne/templates/class_content.cc +++ b/generate/combyne/templates/class_content.cc @@ -11,7 +11,6 @@ extern "C" { {% endeach %} } -#include "../include/callbackWrapper.h" #include "../include/functions/copy.h" #include "../include/macros.h" #include "../include/{{ filename }}.h" diff --git a/generate/combyne/templates/class_header.h b/generate/combyne/templates/class_header.h index 090d2937d..1f9845562 100644 --- a/generate/combyne/templates/class_header.h +++ b/generate/combyne/templates/class_header.h @@ -15,8 +15,6 @@ extern "C" { #include "{{ dependency }}" {%endeach%} -#include "../include/callbackWrapper.h" - {%if needsForwardDeclaration %} // Forward declaration. struct {{ cType }} { From a843a9a71e1e7565401a25a15991da58df88e784 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Tue, 3 Feb 2015 16:59:56 -0500 Subject: [PATCH 14/20] Add override methods for Status --- lib/status.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/status.js b/lib/status.js index a6f930a13..286f8bbaf 100644 --- a/lib/status.js +++ b/lib/status.js @@ -1,5 +1,19 @@ var NodeGit = require("../"); +var normalizeOptions = require("./util/normalize_options"); var Status = NodeGit.Status; +// Override Status.foreach to eliminate the need to pass null payload +var foreach = Status.foreach; +Status.foreach = function(repo, callback) { + return foreach(repo, callback, null); +}; + +// Override Status.foreachExt to normalize opts +var foreachExt = Status.foreachExt; +Status.foreachExt = function(repo, opts, callback) { + opts = normalizeOptions(opts, NodeGit.StatusOptions); + return foreachExt(repo, opts, callback, null); +}; + module.exports = Status; From 88016707e51d34903ea31e5c2e10182514bfed0e Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Tue, 3 Feb 2015 17:00:04 -0500 Subject: [PATCH 15/20] Test status.js --- test/tests/status.js | 81 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 test/tests/status.js diff --git a/test/tests/status.js b/test/tests/status.js new file mode 100644 index 000000000..bb394fd89 --- /dev/null +++ b/test/tests/status.js @@ -0,0 +1,81 @@ +var assert = require("assert"); +var path = require("path"); +var promisify = require("promisify-node"); +var fse = promisify(require("fs-extra")); + +describe("Status", function() { + var reposPath = path.resolve("test/repos/workdir"); + + var Status = require("../../lib/status"); + var Repository = require("../../lib/repository"); + + before(function(done) { + var test = this; + return Repository.open(reposPath).then(function(repository) { + test.repository = repository; + done(); + }); + }); + + it("gets no statuses on clean working directory", function() { + var statuses = []; + var statusCallback = function(path, status) { + statuses.push({path: path, status: status}); + }; + return Status.foreach(this.repository, statusCallback).then(function() { + assert.equal(statuses.length, 0); + }); + }); + + it("gets a status on changing file directory", function() { + var fileName = "README.md"; + var fileContent = "Cha-cha-cha-chaaaaaangessssss"; + var repo = this.repository; + var oldContent; + var filePath = path.join(repo.workdir(), fileName); + + return fse.readFile(filePath).then(function(content) { + oldContent = content; + return fse.writeFile(filePath, fileContent) + .then(function() { + var statuses = []; + var statusCallback = function(path, status) { + statuses.push({path: path, status: status}); + }; + return Status.foreach(repo, statusCallback).then(function() { + assert.equal(statuses.length, 1); + assert.equal(statuses[0].path, fileName); + assert.equal(statuses[0].status, 256); + return fse.writeFile(filePath, oldContent); + }); + }); + }); + }); + + it("gets status with options", function() { + var fileName = "my-new-file-that-shouldnt-exist"; + var fileContent = "new file"; + var repo = this.repository; + var filePath = path.join(repo.workdir(), fileName); + + return fse.writeFile(filePath, fileContent) + .then(function() { + var statuses = []; + var statusCallback = function(path, status) { + statuses.push({path: path, status: status}); + }; + + var opts = { + flags: Status.OPT.INCLUDE_UNTRACKED + + Status.OPT.RECURSE_UNTRACKED_DIRS + }; + + return Status.foreachExt(repo, opts, statusCallback).then(function() { + assert.equal(statuses.length, 1); + assert.equal(statuses[0].path, fileName); + assert.equal(statuses[0].status, 128); + return fse.unlink(filePath); + }); + }); + }); +}); From 5b4c1eccc85b9994dab4604a7a85653713537712 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Tue, 3 Feb 2015 17:00:19 -0500 Subject: [PATCH 16/20] Add StatusFile --- lib/status_file.js | 63 +++++++++++++++++++++++++++++++++++++++ test/tests/status_file.js | 19 ++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 lib/status_file.js create mode 100644 test/tests/status_file.js diff --git a/lib/status_file.js b/lib/status_file.js new file mode 100644 index 000000000..38ec9e967 --- /dev/null +++ b/lib/status_file.js @@ -0,0 +1,63 @@ +var NodeGit = require("../"); + +var Status = NodeGit.Status; + +var StatusFile = function(path, status) { + + var codes = Status.STATUS; + + var getStatus = function() { + var fileStatuses = []; + + for(var key in Status.STATUS) { + if (status & Status.STATUS[key]) { + fileStatuses.push(key); + } + } + + return fileStatuses; + }; + + var data = { + path: path, + statusBit: status, + statuses: getStatus() + }; + + return { + status: function() { + return data.statuses; + }, + statusBit: function() { + return data.statusBit; + }, + path: function() { + return data.path; + }, + isNew: function() { + return status & codes.WT_NEW || + status & codes.INDEX_NEW; + }, + isModified: function() { + return status & codes.WT_MODIFIED || + status & codes.INDEX_MODIFIED; + }, + isDeleted: function() { + return status & codes.WT_DELETED || + status & codes.INDEX_DELETED; + }, + isTypechange: function() { + return status & codes.WT_TYPECHANGE || + status & codes.INDEX_TYPECHANGE; + }, + isRenamed: function() { + return status & codes.WT_RENAMED || + status & codes.INDEX_RENAMED; + }, + isIgnored: function() { + return data.statusBit & codes.IGNORED; + } + }; +}; + +module.exports = StatusFile; diff --git a/test/tests/status_file.js b/test/tests/status_file.js new file mode 100644 index 000000000..b72f887ed --- /dev/null +++ b/test/tests/status_file.js @@ -0,0 +1,19 @@ +var assert = require("assert"); + +describe("StatusFile", function() { + var Status = require("../../lib/status"); + var StatusFile = require("../../lib/status_file"); + var path = "README.md"; + var statusCode = Status.STATUS.WT_NEW; + + var status = new StatusFile(path, statusCode); + + it("passes the path to the working function", function() { + assert.equal(status.path(), path); + }); + + it("identifies the proper statuses", function() { + assert.ok(status.isNew()); + assert.ok(!status.isModified()); + }); +}); From 01fbc0c99e0f14a36d7cb2364c68bcfcd0875495 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Tue, 3 Feb 2015 17:00:39 -0500 Subject: [PATCH 17/20] Add getStatus convenience method to repository --- lib/repository.js | 25 +++++++++++++++++++++++++ test/tests/repository.js | 17 +++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/lib/repository.js b/lib/repository.js index 60a8d6561..23fd2488e 100644 --- a/lib/repository.js +++ b/lib/repository.js @@ -8,6 +8,8 @@ var Commit = require("./commit"); var Remote = require("./remote"); var Promise = require("nodegit-promise"); var normalizeOptions = require("./util/normalize_options"); +var Status = require("./status"); +var StatusFile = require("./status_file"); var TreeBuilder = NodeGit.Treebuilder; var Repository = NodeGit.Repository; @@ -648,5 +650,28 @@ Repository.initExt = function(repo_path, opts) { return initExt(repo_path, opts); }; +/** + * Get the status of a repo to it's working directory + * + * @param {obj} opts + * @return {Object} Promise object. + */ +Repository.prototype.getStatus = function(opts) { + var statuses = []; + var statusCallback = function(path, status) { + statuses.push(new StatusFile(path, status)); + }; + + if (!opts) { + opts = { + flags: Status.OPT.INCLUDE_UNTRACKED + + Status.OPT.RECURSE_UNTRACKED_DIRS + }; + } + + return Status.foreachExt(this, opts, statusCallback).then(function() { + return statuses; + }); +}; module.exports = Repository; diff --git a/test/tests/repository.js b/test/tests/repository.js index 1f9b87af5..0995726dd 100644 --- a/test/tests/repository.js +++ b/test/tests/repository.js @@ -75,4 +75,21 @@ describe("Repository", function() { assert(sig instanceof Signature); }); + + it("gets statuses with StatusFile", function() { + var fileName = "my-new-file-that-shouldnt-exist"; + var fileContent = "new file"; + var repo = this.repository; + var filePath = path.join(repo.workdir(), fileName); + + return fse.writeFile(filePath, fileContent) + .then(function() { + return repo.getStatus().then(function(statuses) { + assert.equal(statuses.length, 1); + assert.equal(statuses[0].path(), fileName); + assert.ok(statuses[0].isNew()); + return fse.unlink(filePath); + }); + }); + }); }); From 572ae338dd723ad378b73d8e8516c218bbf6f379 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Tue, 3 Feb 2015 17:00:44 -0500 Subject: [PATCH 18/20] Status example --- examples/status.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 examples/status.js diff --git a/examples/status.js b/examples/status.js new file mode 100644 index 000000000..9a04bae10 --- /dev/null +++ b/examples/status.js @@ -0,0 +1,24 @@ +var nodegit = require("../"), + path = require("path"); + +// This code shows working directory changes similar to git status + +nodegit.Repository.open(path.resolve(__dirname, "../.git")) + .then(function(repo) { + repo.getStatus().then(function(statuses) { + function statusToText(status) { + var words = []; + if (status.isNew()) { words.push("NEW"); } + if (status.isModified()) { words.push("MODIFIED"); } + if (status.isTypechange()) { words.push("TYPECHANGE"); } + if (status.isRenamed()) { words.push("RENAMED"); } + if (status.isIgnored()) { words.push("IGNORED"); } + + return words.join(" "); + } + + statuses.forEach(function(file) { + console.log(file.path() + " " + statusToText(file)); + }); + }); +}); From 1c970624be1491f371a0289ecb79b70b3d02cfef Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Tue, 3 Feb 2015 19:57:41 -0500 Subject: [PATCH 19/20] Add a bunch of missing callbacks --- generate/input/callbacks.json | 446 ++++++++++++++++++++++++++++++++++ 1 file changed, 446 insertions(+) diff --git a/generate/input/callbacks.json b/generate/input/callbacks.json index bd619d321..e858ad017 100644 --- a/generate/input/callbacks.json +++ b/generate/input/callbacks.json @@ -159,5 +159,451 @@ "success": 0, "error": -1 } + }, + "git_blob_chunk_cb": { + "args": [ + { + "name": "content", + "cType": "const char *" + }, + { + "name": "max_length", + "cType": "size_t" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_blob_chunk_cb": { + "args": [ + { + "name": "entry", + "cType": "const git_config_entry *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_diff_hunk_cb": { + "args": [ + { + "name": "delta", + "cType": "const git_diff_delta *" + }, + { + "name": "hunk", + "cType": "const git_diff_hunk *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_diff_hunk_cb": { + "args": [ + { + "name": "delta", + "cType": "const git_diff_delta *" + }, + { + "name": "hunk", + "cType": "const git_diff_hunk *" + }, + { + "name": "line", + "cType": "const git_diff_line *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_index_matched_path_cb": { + "args": [ + { + "name": "path", + "cType": "const char *" + }, + { + "name": "matched_pathspec", + "cType": "const char *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_note_foreach_cb": { + "args": [ + { + "name": "blob_id", + "cType": "const git_oid *" + }, + { + "name": "annotated_object_id", + "cType": "const git_oid *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_note_foreach_cb": { + "args": [ + { + "name": "id", + "cType": "const git_oid *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_transfer_progress_cb": { + "args": [ + { + "name": "stats", + "cType": "const git_transfer_progress *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_packbuilder_foreach_cb": { + "args": [ + { + "name": "buf", + "cType": "void *" + }, + { + "name": "size", + "cType": "size_t" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_reference_foreach_cb": { + "args": [ + { + "name": "refname", + "cType": "const char *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_reference_foreach_name_cb": { + "args": [ + { + "name": "name", + "cType": "const char *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_transport_message_cb": { + "args": [ + { + "name": "str", + "cType": "const char *" + }, + { + "name": "len", + "cType": "int" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_transport_message_cb": { + "args": [ + { + "name": "refname", + "cType": "const char *" + }, + { + "name": "remote_url", + "cType": "const char *" + }, + { + "name": "oid", + "cType": "const git_oid *" + }, + { + "name": "is_merge", + "cType": "unsigned int" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_repository_mergehead_foreach_cb": { + "args": [ + { + "name": "oid", + "cType": "const git_oid *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_revwalk_hide_cb": { + "args": [ + { + "name": "commit_id", + "cType": "const git_oid *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_tag_foreach_cb": { + "args": [ + { + "name": "name", + "cType": "const char *" + }, + { + "name": "oid", + "cType": "const git_oid *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_treewalk_cb": { + "args": [ + { + "name": "root", + "cType": "const char *" + }, + { + "name": "entry", + "cType": "const git_tree_entry *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_treebuilder_filter_cb": { + "args": [ + { + "name": "entry", + "cType": "const git_tree_entry *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_attr_foreach_cb": { + "args": [ + { + "name": "name", + "cType": "const char *" + }, + { + "name": "value", + "cType": "const char *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_stash_cb": { + "args": [ + { + "name": "index", + "cType": "size_t" + }, + { + "name": "message", + "cType": "const char *" + }, + { + "name": "stash_id", + "cType": "const int *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, + "git_transport_cb": { + "args": [ + { + "name": "out", + "cType": "const git_transport *" + }, + { + "name": "owner", + "cType": "const git_remote *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } } } From c396aa54896aa034a1bb15d08891fad600158b30 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Tue, 3 Feb 2015 20:19:55 -0500 Subject: [PATCH 20/20] More callbacks --- generate/input/callbacks.json | 46 ++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/generate/input/callbacks.json b/generate/input/callbacks.json index e858ad017..157bb3be8 100644 --- a/generate/input/callbacks.json +++ b/generate/input/callbacks.json @@ -138,6 +138,32 @@ "error": -1 } }, + "git_diff_line_cb": { + "args": [ + { + "name": "delta", + "cType": "const git_diff_delta *" + }, + { + "name": "hunk", + "cType": "const git_diff_hunk *" + }, + { + "name": "line", + "cType": "const git_diff_line *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } + }, "git_status_cb": { "args": [ { @@ -408,7 +434,7 @@ "error": -1 } }, - "git_transport_message_cb": { + "git_repository_fetchhead_foreach_cb": { "args": [ { "name": "refname", @@ -605,5 +631,23 @@ "success": 0, "error": -1 } + }, + "git_config_foreach_cb": { + "args": [ + { + "name": "entry", + "cType": "const git_config_entry *" + }, + { + "name": "payload", + "cType": "void *" + } + ], + "return": { + "type": "int", + "noResults": 1, + "success": 0, + "error": -1 + } } }