Skip to content

Commit 2d832bc

Browse files
authored
Merge pull request hyperledger-indy#616 from Picolab/master
Node.js wrapper
2 parents f7a483b + bc2ba78 commit 2d832bc

28 files changed

+9279
-0
lines changed

wrappers/nodejs/.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/libindy.so
2+
/libindy.dylib
3+
/indy.dll
4+
5+
# this is the libindy header files needed for the build, must be published to npm
6+
/include/
7+
8+
node_modules/
9+
build/
10+
build-pre-gyp/
11+
Release/
12+
prebuilds/
13+
*.sln
14+
*.vcxproj
15+
*.vcxproj.filters
16+
*.tlog
17+
*.obj
18+
*.1sdk.pdb
19+
*.lastbuildstate
20+
npm-debug.log
21+
package-lock.json
22+
yarn.lock

wrappers/nodejs/README.md

Lines changed: 1689 additions & 0 deletions
Large diffs are not rendered by default.

wrappers/nodejs/binding.gyp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"targets": [
3+
{
4+
"target_name": "indynodejs",
5+
"include_dirs": [
6+
"<!(node -e \"require('nan')\")",
7+
"<(module_root_dir)/include",
8+
],
9+
"sources": [
10+
"src/indy.cc"
11+
],
12+
"link_settings": {
13+
"libraries": [
14+
"-L<(module_root_dir)",
15+
"<!(node -e \"console.log((process.env.LD_LIBRARY_PATH || '').split(':').map(a => '-L' + a.trim()).filter(a => a != '-L').join(' '))\")",
16+
"-lindy"
17+
]
18+
}
19+
}
20+
]
21+
}

wrappers/nodejs/codegen/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# ABANDON ALL HOPE, ALL YE WHO ENTER HERE
2+
3+
This is just quick-n-dirty code to bootstrap the project.
4+
5+
This code will likely be thrown away, so don't love it.

wrappers/nodejs/codegen/api.json

Lines changed: 1645 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
var api = require('./api.json')
2+
3+
var fixBufferParams = function (params) {
4+
var out = []
5+
var i = 0
6+
while (i < params.length) {
7+
if (params[i].type.replace(/[^a-z0-9_*]/ig, '') === 'constindy_u8_t*') {
8+
if (params[i + 1].type !== 'indy_u32_t' && /_len$/.test(params[i + 1].name)) {
9+
throw new Error('Expected buffer _len next')
10+
}
11+
out.push({
12+
name: params[i].name,
13+
type: 'Buffer'
14+
})
15+
i++
16+
} else {
17+
out.push(params[i])
18+
}
19+
i++
20+
}
21+
return out
22+
}
23+
24+
var toJsName = function (name) {
25+
return name
26+
.replace(/_json$/, '')
27+
.replace(/_(\w)/g, function (matches, letter) {
28+
return letter.toUpperCase()
29+
})
30+
.replace(/^type_$/, 'type')
31+
.replace(/^walletHandle$/, 'wh')
32+
}
33+
34+
var toJsParams = function (params) {
35+
return fixBufferParams(params)
36+
.map(function (param) {
37+
return Object.assign({}, param, {
38+
jsName: toJsName(param.name)
39+
})
40+
})
41+
}
42+
43+
var functions = []
44+
45+
Object.keys(api.functions).forEach(function (name) {
46+
if (name === 'indy_register_wallet_type') {
47+
return
48+
}
49+
50+
var fn = Object.assign({}, api.functions[name], {
51+
name: name,
52+
jsName: name
53+
.replace(/^indy_/, '')
54+
.replace(/_(\w)/g, function (matches, letter) {
55+
return letter.toUpperCase()
56+
}),
57+
jsParams: [],
58+
jsCbParams: []
59+
})
60+
61+
if (fn.ret !== 'indy_error_t') {
62+
throw new Error('Does not return an IndyError: ' + fn.name)
63+
}
64+
65+
fn.params.forEach(function (param, i) {
66+
if (i === 0) {
67+
if (param.type !== 'indy_handle_t' || !/command_han.le$/.test(param.name)) {
68+
throw new Error('Expected a command_handle as the first argument: ' + fn.name)
69+
}
70+
return
71+
}
72+
if (i === fn.params.length - 1) {
73+
if (!param.hasOwnProperty('params')) {
74+
throw new Error('Expected a callback as the as the last argument: ' + fn.name)
75+
}
76+
if (param.params[0].type !== 'indy_handle_t' || !/command_handle$/.test(param.params[0].name) || param.params[1].type !== 'indy_error_t') {
77+
throw new Error('Callback doesn\'t have the standard handle + err: ' + fn.name)
78+
}
79+
param.params.forEach(function (param, i) {
80+
if (i > 1) {
81+
fn.jsCbParams.push(param)
82+
}
83+
})
84+
return
85+
}
86+
fn.jsParams.push(param)
87+
})
88+
fn.jsParams = toJsParams(fn.jsParams)
89+
fn.jsCbParams = toJsParams(fn.jsCbParams)
90+
91+
fn.humanReturnValue = 'void'
92+
if (fn.jsCbParams.length === 1) {
93+
fn.humanReturnValue = toJsName(fn.jsCbParams[0].name)
94+
} else if (fn.jsCbParams.length > 1) {
95+
fn.humanReturnValue = '[ ' + fn.jsCbParams.map(arg => toJsName(arg.name)).join(', ') + ' ]'
96+
}
97+
98+
var humanArgs = fn.jsParams.map(arg => arg.name)
99+
var humanCb = 'cb(err'
100+
if (fn.humanReturnValue !== 'void') {
101+
humanCb += ', ' + fn.humanReturnValue
102+
}
103+
humanCb += ')'
104+
humanArgs.push(humanCb)
105+
fn.humanSignature = fn.jsName + '(' + humanArgs.join(', ') + ')'
106+
107+
functions.push(fn)
108+
})
109+
110+
module.exports = functions

wrappers/nodejs/codegen/cpp.js

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
var fs = require('fs')
2+
var path = require('path')
3+
var apiFunctions = require('./apiFunctions')
4+
5+
var OUT_FILE = path.resolve(__dirname, '../src/indy_codegen.h')
6+
7+
var normalizeType = function (param) {
8+
if (param.timestamp) {
9+
return 'Timestamp'
10+
}
11+
switch (param.type.replace(/[^a-z0-9_*]/ig, '')) {
12+
case 'constchar*':
13+
case 'constchar*const':
14+
return 'String'
15+
16+
case 'indy_bool_t':
17+
return 'Boolean'
18+
19+
case 'indy_handle_t':
20+
return 'IndyHandle'
21+
22+
case 'indy_error_t':
23+
return 'IndyError'
24+
25+
case 'void':
26+
case 'indy_u32_t':
27+
case 'indy_i32_t':
28+
return param.type
29+
30+
case 'Buffer':
31+
return 'Buffer'
32+
}
33+
throw new Error('normalizeType doesn\'t handle: ' + param.type + ' ' + JSON.stringify(param))
34+
}
35+
36+
var cpp = ''
37+
38+
apiFunctions.forEach(function (fn) {
39+
var cppReturnThrow = function (msg) {
40+
var errmsg = JSON.stringify(msg + ': ' + fn.humanSignature)
41+
return ' return Nan::ThrowError(Nan::New(' + errmsg + ').ToLocalChecked());\n'
42+
}
43+
44+
cpp += 'void ' + fn.jsName + '_cb(indy_handle_t handle, indy_error_t xerr'
45+
cpp += fn.jsCbParams.map(function (arg, i) {
46+
if (arg.type === 'Buffer') {
47+
return ', const indy_u8_t* arg' + i + 'data, indy_u32_t arg' + i + 'len'
48+
}
49+
return ', ' + arg.type + ' arg' + i
50+
}).join('')
51+
cpp += ') {\n'
52+
cpp += ' IndyCallback* icb = IndyCallback::getCallback(handle);\n'
53+
cpp += ' if(icb != nullptr){\n'
54+
var cbArgTypes = fn.jsCbParams.map(arg => normalizeType(arg)).join('+')
55+
switch (cbArgTypes) {
56+
case '':
57+
cpp += ' icb->cbNone(xerr);\n'
58+
break
59+
case 'String':
60+
cpp += ' icb->cbString(xerr, arg0);\n'
61+
break
62+
case 'Boolean':
63+
cpp += ' icb->cbBoolean(xerr, arg0);\n'
64+
break
65+
case 'IndyHandle':
66+
cpp += ' icb->cbHandle(xerr, arg0);\n'
67+
break
68+
case 'indy_i32_t':
69+
cpp += ' icb->cbI32(xerr, arg0);\n'
70+
break
71+
case 'String+String':
72+
cpp += ' icb->cbStringString(xerr, arg0, arg1);\n'
73+
break
74+
case 'String+String+String':
75+
cpp += ' icb->cbStringStringString(xerr, arg0, arg1, arg2);\n'
76+
break
77+
case 'String+String+Timestamp':
78+
cpp += ' icb->cbStringStringTimestamp(xerr, arg0, arg1, arg2);\n'
79+
break
80+
case 'Buffer':
81+
cpp += ' icb->cbBuffer(xerr, arg0data, arg0len);\n'
82+
break
83+
case 'String+Buffer':
84+
cpp += ' icb->cbStringBuffer(xerr, arg0, arg1data, arg1len);\n'
85+
break
86+
default:
87+
throw new Error('Unhandled callback args type: ' + cbArgTypes + ' for ' + fn.name)
88+
}
89+
cpp += ' }\n'
90+
cpp += '}\n'
91+
cpp += 'NAN_METHOD(' + fn.jsName + ') {\n'
92+
cpp += ' if(info.Length() != ' + (fn.jsParams.length + 1) + '){\n'
93+
cpp += cppReturnThrow('Expected ' + (fn.jsParams.length + 1) + ' arguments')
94+
cpp += ' }\n'
95+
fn.jsParams.forEach(function (arg, i) {
96+
var type = normalizeType(arg)
97+
98+
var chkType = function (isfn) {
99+
cpp += ' if(!info[' + i + ']->' + isfn + '()){\n'
100+
cpp += cppReturnThrow('Expected ' + type + ' for ' + arg.name)
101+
cpp += ' }\n'
102+
}
103+
104+
switch (type) {
105+
case 'String':
106+
cpp += ' Nan::Utf8String* arg' + i + 'UTF = nullptr;\n'
107+
cpp += ' const char* arg' + i + ' = nullptr;\n'
108+
cpp += ' if(info[' + i + ']->IsString()){\n'
109+
cpp += ' arg' + i + 'UTF = new Nan::Utf8String(info[' + i + ']);\n'
110+
cpp += ' arg' + i + ' = (const char*)(**arg' + i + 'UTF);\n'
111+
cpp += ' } else if(!info[' + i + ']->IsNull() && !info[' + i + ']->IsUndefined()){\n'
112+
cpp += cppReturnThrow('Expected String or null for ' + arg.name)
113+
cpp += ' }\n'
114+
break
115+
case 'IndyHandle':
116+
chkType('IsNumber')
117+
cpp += ' indy_handle_t arg' + i + ' = info[' + i + ']->Int32Value();\n'
118+
break
119+
case 'indy_u32_t':
120+
chkType('IsUint32')
121+
cpp += ' indy_u32_t arg' + i + ' = info[' + i + ']->Uint32Value();\n'
122+
break
123+
case 'indy_i32_t':
124+
chkType('IsInt32')
125+
cpp += ' indy_i32_t arg' + i + ' = info[' + i + ']->Int32Value();\n'
126+
break
127+
case 'Timestamp':
128+
chkType('IsUint32')
129+
cpp += ' long long arg' + i + ' = info[' + i + ']->Uint32Value();\n'
130+
break
131+
case 'Boolean':
132+
chkType('IsBoolean')
133+
cpp += ' indy_bool_t arg' + i + ' = info[' + i + ']->IsTrue();\n'
134+
break
135+
case 'Buffer':
136+
chkType('IsUint8Array')
137+
cpp += ' const indy_u8_t* arg' + i + 'data = (indy_u8_t*)node::Buffer::Data(info[' + i + ']->ToObject());\n'
138+
cpp += ' indy_u32_t arg' + i + 'len = node::Buffer::Length(info[' + i + ']);\n'
139+
break
140+
default:
141+
throw new Error('Unhandled argument reading type: ' + type)
142+
}
143+
})
144+
cpp += ' if(!info[' + fn.jsParams.length + ']->IsFunction()) {\n'
145+
cpp += ' return Nan::ThrowError(Nan::New("' + fn.jsName + ' arg ' + fn.jsParams.length + ' expected callback Function").ToLocalChecked());\n'
146+
cpp += ' }\n'
147+
cpp += ' IndyCallback* icb = new IndyCallback(Nan::To<v8::Function>(info[' + fn.jsParams.length + ']).ToLocalChecked());\n'
148+
cpp += ' indyCalled(icb, ' + fn.name + '(icb->handle'
149+
cpp += fn.jsParams.map(function (arg, i) {
150+
if (arg.type === 'Buffer') {
151+
return ', arg' + i + 'data, arg' + i + 'len'
152+
}
153+
return ', arg' + i
154+
}).join('')
155+
cpp += ', ' + fn.jsName + '_cb));\n'
156+
157+
fn.jsParams.forEach(function (arg, i) {
158+
var type = normalizeType(arg)
159+
switch (type) {
160+
case 'String':
161+
cpp += ' delete arg' + i + 'UTF;\n'
162+
break
163+
case 'Buffer':
164+
case 'IndyHandle':
165+
case 'indy_u32_t':
166+
case 'indy_i32_t':
167+
case 'Timestamp':
168+
case 'Boolean':
169+
break
170+
default:
171+
throw new Error('Unhandled argument cleanup for type: ' + type)
172+
}
173+
})
174+
cpp += '}\n\n'
175+
})
176+
177+
cpp += 'NAN_MODULE_INIT(InitAll) {\n'
178+
apiFunctions.forEach(function (fn) {
179+
cpp += ' Nan::Export(target, "' + fn.jsName + '", ' + fn.jsName + ');\n'
180+
})
181+
cpp += '}\n'
182+
cpp += 'NODE_MODULE(indynodejs, InitAll)\n'
183+
184+
fs.writeFileSync(OUT_FILE, cpp, 'utf8')

0 commit comments

Comments
 (0)