Skip to content

Commit b342f01

Browse files
committed
Merge pull request #983 from srajko/wrapper-base-class
Factor out base class for structs / classes with cType
2 parents f4e91e6 + e15808a commit b342f01

File tree

11 files changed

+274
-181
lines changed

11 files changed

+274
-181
lines changed

generate/input/descriptor.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1393,7 +1393,7 @@
13931393
"ignore": true
13941394
},
13951395
"oid": {
1396-
"dupFunction": "git_oid_cpy",
1396+
"cpyFunction": "git_oid_cpy",
13971397
"freeFunctionName": "free",
13981398
"shouldAlloc": true,
13991399
"functions": {

generate/scripts/generateNativeCode.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ module.exports = function generateNativeCode() {
3535
fields: utils.readFile("templates/partials/fields.cc"),
3636
guardArguments: utils.readFile("templates/partials/guard_arguments.cc"),
3737
syncFunction: utils.readFile("templates/partials/sync_function.cc"),
38-
fieldAccessors: utils.readFile("templates/partials/field_accessors.cc")
38+
fieldAccessors: utils.readFile("templates/partials/field_accessors.cc"),
39+
traits: utils.readFile("templates/partials/traits.h")
3940
};
4041

4142
var templates = {
@@ -87,7 +88,9 @@ module.exports = function generateNativeCode() {
8788

8889
// Attach all partials to select templates.
8990
Object.keys(partials).forEach(function(partial) {
91+
templates.class_header.registerPartial(partial, combyne(partials[partial]));
9092
templates.class_content.registerPartial(partial, combyne(partials[partial]));
93+
templates.struct_header.registerPartial(partial, combyne(partials[partial]));
9194
templates.struct_content.registerPartial(partial, combyne(partials[partial]));
9295
});
9396

generate/scripts/helpers.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ var Helpers = {
183183

184184
if (typeDefOverrides.freeFunctionName) {
185185
typeDef.freeFunctionName = typeDefOverrides.freeFunctionName;
186+
} else if (typeDef.type === 'struct') {
187+
typeDef.freeFunctionName = 'free';
186188
}
187189

188190
typeDef.fields = typeDef.fields || [];
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#ifndef NODEGIT_WRAPPER_H
2+
#define NODEGIT_WRAPPER_H
3+
4+
#include <nan.h>
5+
6+
// the Traits template parameter supplies:
7+
// typename cppClass - the C++ type of the NodeGit wrapper (e.g. GitRepository)
8+
// typename cType - the C type of the libgit2 object being wrapped (e.g. git_repository)
9+
//
10+
// static const bool isDuplicable
11+
// static void duplicate(cType **dest, cType *src) - duplicates src using dupFunction or cpyFunction
12+
//
13+
// static const bool isFreeable
14+
// static void free(cType *raw) - frees the object using freeFunctionName
15+
16+
template<typename Traits>
17+
class NodeGitWrapper : public Nan::ObjectWrap {
18+
public:
19+
// replicate Traits typedefs for ease of use
20+
typedef typename Traits::cType cType;
21+
typedef typename Traits::cppClass cppClass;
22+
23+
// whether raw should be freed on destruction
24+
// TODO: this should be protected but we have a few use cases that change this to
25+
// false from the outside. I suspect it gets turned to false to avoid
26+
// double-free problems in cases like when we pass cred objects to libgit2
27+
// and it frees them. We should probably be NULLing raw in that case
28+
// (and through a method) instead of changing selfFreeing, but that's
29+
// a separate issue.
30+
bool selfFreeing;
31+
protected:
32+
cType *raw;
33+
34+
// owner of the object, in the memory management sense. only populated
35+
// when using ownedByThis, and the type doesn't have a dupFunction
36+
// CopyablePersistentTraits are used to get the reset-on-destruct behavior.
37+
Nan::Persistent<v8::Object, Nan::CopyablePersistentTraits<v8::Object> > owner;
38+
39+
static Nan::Persistent<v8::Function> constructor_template;
40+
41+
// diagnostic count of self-freeing object instances
42+
static int SelfFreeingInstanceCount;
43+
// diagnostic count of constructed non-self-freeing object instances
44+
static int NonSelfFreeingConstructedCount;
45+
46+
static void InitializeTemplate(v8::Local<v8::FunctionTemplate> &tpl);
47+
48+
NodeGitWrapper(cType *raw, bool selfFreeing, v8::Local<v8::Object> owner);
49+
NodeGitWrapper(const char *error); // calls ThrowError
50+
~NodeGitWrapper();
51+
52+
static NAN_METHOD(JSNewFunction);
53+
54+
static NAN_METHOD(GetSelfFreeingInstanceCount);
55+
static NAN_METHOD(GetNonSelfFreeingConstructedCount);
56+
57+
public:
58+
static v8::Local<v8::Value> New(const cType *raw, bool selfFreeing, v8::Local<v8::Object> owner = v8::Local<v8::Object>());
59+
60+
cType *GetValue();
61+
void ClearValue();
62+
};
63+
64+
#endif
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
template<typename Traits>
2+
NodeGitWrapper<Traits>::NodeGitWrapper(typename Traits::cType *raw, bool selfFreeing, v8::Local<v8::Object> owner) {
3+
if (!owner.IsEmpty()) {
4+
// if we have an owner, there are two options - either we duplicate the raw object
5+
// (so we own the duplicate, and can self-free it)
6+
// or we keep a handle on the owner so it doesn't get garbage collected
7+
// while this wrapper is accessible
8+
if(Traits::isDuplicable) {
9+
Traits::duplicate(&this->raw, raw);
10+
selfFreeing = true;
11+
} else {
12+
this->owner.Reset(owner);
13+
this->raw = raw;
14+
}
15+
} else {
16+
this->raw = raw;
17+
}
18+
this->selfFreeing = selfFreeing;
19+
20+
if (selfFreeing) {
21+
SelfFreeingInstanceCount++;
22+
} else {
23+
NonSelfFreeingConstructedCount++;
24+
}
25+
}
26+
27+
template<typename Traits>
28+
NodeGitWrapper<Traits>::NodeGitWrapper(const char *error) {
29+
selfFreeing = false;
30+
raw = NULL;
31+
Nan::ThrowError(error);
32+
}
33+
34+
template<typename Traits>
35+
NodeGitWrapper<Traits>::~NodeGitWrapper() {
36+
if(Traits::isFreeable && selfFreeing) {
37+
Traits::free(raw);
38+
SelfFreeingInstanceCount--;
39+
raw = NULL;
40+
}
41+
}
42+
43+
template<typename Traits>
44+
NAN_METHOD(NodeGitWrapper<Traits>::JSNewFunction) {
45+
cppClass * instance;
46+
47+
if (info.Length() == 0 || !info[0]->IsExternal()) {
48+
Nan::TryCatch tryCatch;
49+
instance = new cppClass();
50+
// handle the case where the default constructor is not supported
51+
if(tryCatch.HasCaught()) {
52+
delete instance;
53+
tryCatch.ReThrow();
54+
return;
55+
}
56+
} else {
57+
instance = new cppClass(static_cast<cType *>(
58+
Local<External>::Cast(info[0])->Value()),
59+
Nan::To<bool>(info[1]).FromJust(),
60+
info.Length() >= 3 && !info[2].IsEmpty() && info[2]->IsObject() ? info[2]->ToObject() : Local<v8::Object>()
61+
);
62+
}
63+
64+
instance->Wrap(info.This());
65+
info.GetReturnValue().Set(info.This());
66+
}
67+
68+
template<typename Traits>
69+
v8::Local<v8::Value> NodeGitWrapper<Traits>::New(const typename Traits::cType *raw, bool selfFreeing, v8::Local<v8::Object> owner) {
70+
Nan::EscapableHandleScope scope;
71+
Local<v8::Value> argv[3] = { Nan::New<External>((void *)raw), Nan::New(selfFreeing), owner };
72+
return scope.Escape(
73+
Nan::NewInstance(
74+
Nan::New(constructor_template),
75+
owner.IsEmpty() ? 2 : 3, // passing an empty handle as part of the arguments causes a crash
76+
argv
77+
).ToLocalChecked());
78+
}
79+
80+
template<typename Traits>
81+
typename Traits::cType *NodeGitWrapper<Traits>::GetValue() {
82+
return raw;
83+
}
84+
85+
template<typename Traits>
86+
void NodeGitWrapper<Traits>::ClearValue() {
87+
raw = NULL;
88+
}
89+
90+
template<typename Traits>
91+
Nan::Persistent<v8::Function> NodeGitWrapper<Traits>::constructor_template;
92+
93+
template<typename Traits>
94+
int NodeGitWrapper<Traits>::SelfFreeingInstanceCount;
95+
96+
template<typename Traits>
97+
int NodeGitWrapper<Traits>::NonSelfFreeingConstructedCount;
98+
99+
template<typename Traits>
100+
NAN_METHOD(NodeGitWrapper<Traits>::GetSelfFreeingInstanceCount) {
101+
info.GetReturnValue().Set(SelfFreeingInstanceCount);
102+
}
103+
104+
template<typename Traits>
105+
NAN_METHOD(NodeGitWrapper<Traits>::GetNonSelfFreeingConstructedCount) {
106+
info.GetReturnValue().Set(NonSelfFreeingConstructedCount);
107+
}
108+
109+
template<typename Traits>
110+
void NodeGitWrapper<Traits>::InitializeTemplate(v8::Local<v8::FunctionTemplate> &tpl) {
111+
Nan::SetMethod(tpl, "getSelfFreeingInstanceCount", GetSelfFreeingInstanceCount);
112+
Nan::SetMethod(tpl, "getNonSelfFreeingConstructedCount", GetNonSelfFreeingConstructedCount);
113+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class {{ cppClassName }};
2+
3+
struct {{ cppClassName }}Traits {
4+
typedef {{ cppClassName }} cppClass;
5+
typedef {{ cType }} cType;
6+
7+
static const bool isDuplicable = {{ dupFunction|toBool |or cpyFunction|toBool}};
8+
static void duplicate({{ cType }} **dest, {{ cType }} *src) {
9+
{% if dupFunction %}
10+
{{ dupFunction }}(dest, src);
11+
{% elsif cpyFunction %}
12+
{{ cType }} *copy = ({{ cType }} *)malloc(sizeof({{ cType }}));
13+
{{ cpyFunction }}(copy, src);
14+
*dest = copy;
15+
{% else %}
16+
Nan::ThrowError("duplicate called on {{ cppClassName }} which cannot be duplicated");
17+
{% endif %}
18+
}
19+
20+
static const bool isFreeable = {{ freeFunctionName | toBool}};
21+
static void free({{ cType }} *raw) {
22+
{% if freeFunctionName %}
23+
::{{ freeFunctionName }}(raw); // :: to avoid calling this free recursively
24+
{% else %}
25+
Nan::ThrowError("free called on {{ cppClassName }} which cannot be freed");
26+
{% endif %}
27+
}
28+
};

generate/templates/templates/class_content.cc

Lines changed: 6 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ extern "C" {
1111
#include "../include/lock_master.h"
1212
#include "../include/functions/copy.h"
1313
#include "../include/{{ filename }}.h"
14+
#include "nodegit_wrapper.cc"
1415

1516
{% each dependencies as dependency %}
1617
#include "{{ dependency }}"
@@ -23,47 +24,7 @@ using namespace v8;
2324
using namespace node;
2425

2526
{% if cType %}
26-
{{ cppClassName }}::{{ cppClassName }}({{ cType }} *raw, bool selfFreeing, Local<v8::Object> owner) {
27-
if (!owner.IsEmpty()) {
28-
// if we have an owner, there are two options - either we duplicate the raw object
29-
// (so we own the duplicate, and can self-free it)
30-
// or we keep a handle on the owner so it doesn't get garbage collected
31-
// while this wrapper is accessible
32-
{% if dupFunction %}
33-
{% if shouldAlloc %}
34-
this->raw = ({{ cType }} *)malloc(sizeof({{ cType }}));
35-
{{ dupFunction }}(this->raw, raw);
36-
{% else %}
37-
{{ dupFunction }}(&this->raw, raw);
38-
{% endif %}
39-
selfFreeing = true;
40-
{% else %}
41-
this->owner.Reset(owner);
42-
this->raw = raw;
43-
{% endif %}
44-
} else {
45-
this->raw = raw;
46-
}
47-
this->selfFreeing = selfFreeing;
48-
49-
if (selfFreeing) {
50-
SelfFreeingInstanceCount++;
51-
} else {
52-
NonSelfFreeingConstructedCount++;
53-
}
54-
55-
}
56-
5727
{{ cppClassName }}::~{{ cppClassName }}() {
58-
{% if freeFunctionName %}
59-
if (this->selfFreeing) {
60-
{{ freeFunctionName }}(this->raw);
61-
SelfFreeingInstanceCount--;
62-
63-
this->raw = NULL;
64-
}
65-
{% endif %}
66-
6728
// this will cause an error if you have a non-self-freeing object that also needs
6829
// to save values. Since the object that will eventually free the object has no
6930
// way of knowing to free these values.
@@ -104,61 +65,13 @@ using namespace node;
10465
{% endif %}
10566
{% endeach %}
10667

107-
Nan::SetMethod(tpl, "getSelfFreeingInstanceCount", GetSelfFreeingInstanceCount);
108-
Nan::SetMethod(tpl, "getNonSelfFreeingConstructedCount", GetNonSelfFreeingConstructedCount);
68+
InitializeTemplate(tpl);
10969

11070
Local<Function> _constructor_template = Nan::GetFunction(tpl).ToLocalChecked();
11171
constructor_template.Reset(_constructor_template);
11272
Nan::Set(target, Nan::New("{{ jsClassName }}").ToLocalChecked(), _constructor_template);
11373
}
11474

115-
NAN_METHOD({{ cppClassName }}::JSNewFunction) {
116-
117-
if (info.Length() == 0 || !info[0]->IsExternal()) {
118-
{% if createFunctionName %}
119-
return Nan::ThrowError("A new {{ cppClassName }} cannot be instantiated. Use {{ jsCreateFunctionName }} instead.");
120-
{% else %}
121-
return Nan::ThrowError("A new {{ cppClassName }} cannot be instantiated.");
122-
{% endif %}
123-
}
124-
125-
{{ cppClassName }}* object = new {{ cppClassName }}(static_cast<{{ cType }} *>(
126-
Local<External>::Cast(info[0])->Value()),
127-
Nan::To<bool>(info[1]).FromJust(),
128-
info.Length() >= 3 && !info[2].IsEmpty() && info[2]->IsObject() ? info[2]->ToObject() : Local<v8::Object>()
129-
);
130-
object->Wrap(info.This());
131-
132-
info.GetReturnValue().Set(info.This());
133-
}
134-
135-
Local<v8::Value> {{ cppClassName }}::New(const {{ cType }} *raw, bool selfFreeing, Local<v8::Object> owner) {
136-
Nan::EscapableHandleScope scope;
137-
Local<v8::Value> argv[3] = { Nan::New<External>((void *)raw), Nan::New(selfFreeing), owner };
138-
return scope.Escape(
139-
Nan::NewInstance(
140-
Nan::New({{ cppClassName }}::constructor_template),
141-
owner.IsEmpty() ? 2 : 3, // passing an empty handle as part of the arguments causes a crash
142-
argv
143-
).ToLocalChecked());
144-
}
145-
146-
NAN_METHOD({{ cppClassName }}::GetSelfFreeingInstanceCount) {
147-
info.GetReturnValue().Set(SelfFreeingInstanceCount);
148-
}
149-
150-
NAN_METHOD({{ cppClassName }}::GetNonSelfFreeingConstructedCount) {
151-
info.GetReturnValue().Set(NonSelfFreeingConstructedCount);
152-
}
153-
154-
{{ cType }} *{{ cppClassName }}::GetValue() {
155-
return this->raw;
156-
}
157-
158-
void {{ cppClassName }}::ClearValue() {
159-
this->raw = NULL;
160-
}
161-
16275
{% else %}
16376

16477
void {{ cppClassName }}::InitializeComponent(Local<v8::Object> target) {
@@ -191,9 +104,8 @@ using namespace node;
191104

192105
{% partial fields . %}
193106

194-
{% if not cTypeIsUndefined %}
195-
Nan::Persistent<Function> {{ cppClassName }}::constructor_template;
107+
{%if cType %}
108+
// force base class template instantiation, to make sure we get all the
109+
// methods, statics, etc.
110+
template class NodeGitWrapper<{{ cppClassName }}Traits>;
196111
{% endif %}
197-
198-
int {{ cppClassName }}::SelfFreeingInstanceCount;
199-
int {{ cppClassName }}::NonSelfFreeingConstructedCount;

0 commit comments

Comments
 (0)