koffi 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CMakeLists.txt +12 -11
  2. package/README.md +19 -17
  3. package/build/qemu/1.0.3/koffi_darwin_x64.tar.gz +0 -0
  4. package/build/qemu/1.0.3/koffi_freebsd_arm64.tar.gz +0 -0
  5. package/build/qemu/1.0.3/koffi_freebsd_ia32.tar.gz +0 -0
  6. package/build/qemu/1.0.3/koffi_freebsd_x64.tar.gz +0 -0
  7. package/build/qemu/1.0.3/koffi_linux_arm.tar.gz +0 -0
  8. package/build/qemu/1.0.3/koffi_linux_arm64.tar.gz +0 -0
  9. package/build/qemu/1.0.3/koffi_linux_ia32.tar.gz +0 -0
  10. package/build/qemu/1.0.3/koffi_linux_x64.tar.gz +0 -0
  11. package/build/qemu/1.0.3/koffi_win32_ia32.tar.gz +0 -0
  12. package/build/qemu/1.0.3/koffi_win32_x64.tar.gz +0 -0
  13. package/package.json +8 -4
  14. package/qemu/qemu.js +794 -0
  15. package/qemu/registry/machines.json +415 -0
  16. package/qemu/registry/sha256sum.txt +45 -0
  17. package/src/{call_arm32.cc → abi_arm32.cc} +76 -50
  18. package/src/{call_arm32_fwd.S → abi_arm32_fwd.S} +0 -0
  19. package/src/{call_arm64.cc → abi_arm64.cc} +79 -51
  20. package/src/{call_arm64_fwd.S → abi_arm64_fwd.S} +0 -0
  21. package/src/{call_x64_sysv.cc → abi_x64_sysv.cc} +77 -49
  22. package/src/{call_x64_sysv_fwd.S → abi_x64_sysv_fwd.S} +0 -0
  23. package/src/{call_x64_win.cc → abi_x64_win.cc} +71 -47
  24. package/src/{call_x64_win_fwd.asm → abi_x64_win_fwd.asm} +0 -0
  25. package/src/{call_x86.cc → abi_x86.cc} +74 -56
  26. package/src/{call_x86_fwd.S → abi_x86_fwd.S} +0 -0
  27. package/src/{call_x86_fwd.asm → abi_x86_fwd.asm} +0 -0
  28. package/src/call.cc +228 -0
  29. package/src/call.hh +96 -4
  30. package/src/ffi.cc +8 -3
  31. package/src/ffi.hh +2 -0
  32. package/src/util.cc +11 -157
  33. package/src/util.hh +0 -91
  34. package/test/CMakeLists.txt +1 -0
  35. package/test/misc.c +289 -0
  36. package/test/misc.def +3 -0
  37. package/test/misc.js +180 -0
  38. package/test/raylib.js +165 -0
  39. package/test/sqlite.js +104 -0
  40. package/build/qemu/1.0.0/koffi_darwin_x64.tar.gz +0 -0
  41. package/build/qemu/1.0.0/koffi_freebsd_arm64.tar.gz +0 -0
  42. package/build/qemu/1.0.0/koffi_freebsd_ia32.tar.gz +0 -0
  43. package/build/qemu/1.0.0/koffi_freebsd_x64.tar.gz +0 -0
  44. package/build/qemu/1.0.0/koffi_linux_arm.tar.gz +0 -0
  45. package/build/qemu/1.0.0/koffi_linux_arm64.tar.gz +0 -0
  46. package/build/qemu/1.0.0/koffi_linux_ia32.tar.gz +0 -0
  47. package/build/qemu/1.0.0/koffi_linux_x64.tar.gz +0 -0
  48. package/build/qemu/1.0.0/koffi_win32_ia32.tar.gz +0 -0
  49. package/build/qemu/1.0.0/koffi_win32_x64.tar.gz +0 -0
package/test/misc.js ADDED
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+
3
+ // This program is free software: you can redistribute it and/or modify
4
+ // it under the terms of the GNU Affero General Public License as published by
5
+ // the Free Software Foundation, either version 3 of the License, or
6
+ // (at your option) any later version.
7
+ //
8
+ // This program is distributed in the hope that it will be useful,
9
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ // GNU Affero General Public License for more details.
12
+ //
13
+ // You should have received a copy of the GNU Affero General Public License
14
+ // along with this program. If not, see https://www.gnu.org/licenses/.
15
+
16
+ const koffi = require('./build/koffi.node');
17
+ const assert = require('assert');
18
+ const path = require('path');
19
+
20
+ const Pack1 = koffi.struct('Pack1', {
21
+ a: 'int'
22
+ });
23
+ const Pack2 = koffi.struct('Pack2', {
24
+ a: 'int',
25
+ b: 'int'
26
+ });
27
+ const Pack3 = koffi.struct('Pack3', {
28
+ a: 'int',
29
+ b: 'int',
30
+ c: 'int'
31
+ });
32
+
33
+ const BFG = koffi.struct('BFG', {
34
+ a: 'int8_t',
35
+ b: 'int64_t',
36
+ c: 'char',
37
+ d: 'string',
38
+ e: 'short',
39
+ inner: koffi.struct({
40
+ f: 'float',
41
+ g: 'double'
42
+ })
43
+ });
44
+ const PackedBFG = koffi.pack('PackedBFG', {
45
+ a: 'int8_t',
46
+ b: 'int64_t',
47
+ c: 'char',
48
+ d: 'string',
49
+ e: 'short',
50
+ inner: koffi.pack({
51
+ f: 'float',
52
+ g: 'double'
53
+ })
54
+ });
55
+
56
+ main();
57
+
58
+ async function main() {
59
+ try {
60
+ await test();
61
+ console.log('Success!');
62
+ } catch (err) {
63
+ console.error(err);
64
+ process.exit(1);
65
+ }
66
+ }
67
+
68
+ async function test() {
69
+ let lib_filename = path.dirname(__filename) + '/build/misc' + koffi.extension;
70
+ let lib = koffi.load(lib_filename);
71
+
72
+ const FillPack1 = lib.func('FillPack1', 'void', ['int', koffi.out(koffi.pointer(Pack1))]);
73
+ const RetPack1 = lib.func('RetPack1', Pack1, ['int']);
74
+ const AddPack1 = lib.fastcall('AddPack1', 'void', ['int', koffi.inout(koffi.pointer(Pack1))]);
75
+ const FillPack2 = lib.func('FillPack2', 'void', ['int', 'int', koffi.out(koffi.pointer(Pack2))]);
76
+ const RetPack2 = lib.func('RetPack2', Pack2, ['int', 'int']);
77
+ const AddPack2 = lib.fastcall('AddPack2', 'void', ['int', 'int', koffi.inout(koffi.pointer(Pack2))]);
78
+ const FillPack3 = lib.func('FillPack3', 'void', ['int', 'int', 'int', koffi.out(koffi.pointer(Pack3))]);
79
+ const RetPack3 = lib.func('RetPack3', Pack3, ['int', 'int', 'int']);
80
+ const AddPack3 = lib.fastcall('AddPack3', 'void', ['int', 'int', 'int', koffi.inout(koffi.pointer(Pack3))]);
81
+ const ConcatenateToInt1 = lib.func('ConcatenateToInt1', 'int64_t', Array(12).fill('int8_t'));
82
+ const ConcatenateToInt4 = lib.func('ConcatenateToInt4', 'int64_t', Array(12).fill('int32_t'));
83
+ const ConcatenateToInt8 = lib.func('ConcatenateToInt8', 'int64_t', Array(12).fill('int64_t'));
84
+ const ConcatenateToStr1 = lib.func('ConcatenateToStr1', 'string', [...Array(8).fill('int8_t'), koffi.struct('IJK1', {i: 'int8_t', j: 'int8_t', k: 'int8_t'}), 'int8_t']);
85
+ const ConcatenateToStr4 = lib.func('ConcatenateToStr4', 'string', [...Array(8).fill('int32_t'), koffi.pointer(koffi.struct('IJK4', {i: 'int32_t', j: 'int32_t', k: 'int32_t'})), 'int32_t']);
86
+ const ConcatenateToStr8 = lib.func('ConcatenateToStr8', 'string', [...Array(8).fill('int64_t'), koffi.struct('IJK8', {i: 'int64_t', j: 'int64_t', k: 'int64_t'}), 'int64_t']);
87
+ const MakeBFG = lib.func('BFG __stdcall MakeBFG(_Out_ BFG *p, int x, double y, const char *str)');
88
+ const MakePackedBFG = lib.func('PackedBFG __fastcall MakePackedBFG(int x, double y, _Out_ PackedBFG *p, const char *str)');
89
+ const ReturnBigString = process.platform == 'win32' ?
90
+ lib.stdcall(1, 'string', ['string']) :
91
+ lib.func('const char * __stdcall ReturnBigString(const char *str)');
92
+ const PrintFmt = lib.func('const char *PrintFmt(const char *fmt, ...)');
93
+ const Concat16 = lib.func('const char16_t *Concat16(const char16_t *str1, const char16_t *str2)')
94
+
95
+ // Simple tests with Pack1
96
+ {
97
+ let p = {};
98
+
99
+ FillPack1(777, p);
100
+ assert.deepEqual(p, { a: 777 });
101
+
102
+ let q = RetPack1(6);
103
+ assert.deepEqual(q, { a: 6 });
104
+
105
+ AddPack1(6, p);
106
+ assert.deepEqual(p, { a: 783 });
107
+ }
108
+
109
+ // Simple tests with Pack2
110
+ {
111
+ let p = {};
112
+
113
+ FillPack2(123, 456, p);
114
+ assert.deepEqual(p, { a: 123, b: 456 });
115
+
116
+ let q = RetPack2(6, 9);
117
+ assert.deepEqual(q, { a: 6, b: 9 });
118
+
119
+ AddPack2(6, 9, p);
120
+ assert.deepEqual(p, { a: 129, b: 465 });
121
+ }
122
+
123
+ // Simple tests with Pack3
124
+ {
125
+ let p = {};
126
+
127
+ FillPack3(1, 2, 3, p);
128
+ assert.deepEqual(p, { a: 1, b: 2, c: 3 });
129
+
130
+ let q = RetPack3(6, 9, -12);
131
+ assert.deepEqual(q, { a: 6, b: 9, c: -12 });
132
+
133
+ AddPack3(6, 9, -12, p);
134
+ assert.deepEqual(p, { a: 7, b: 11, c: -9 });
135
+ }
136
+
137
+ // Many parameters
138
+ {
139
+ assert.equal(ConcatenateToInt1(5, 6, 1, 2, 3, 9, 4, 4, 0, 6, 8, 7), 561239440687n);
140
+ assert.equal(ConcatenateToInt4(5, 6, 1, 2, 3, 9, 4, 4, 0, 6, 8, 7), 561239440687n);
141
+ assert.equal(ConcatenateToInt8(5, 6, 1, 2, 3, 9, 4, 4, 0, 6, 8, 7), 561239440687n);
142
+ assert.equal(ConcatenateToStr1(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
143
+ assert.equal(ConcatenateToStr4(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
144
+ assert.equal(ConcatenateToStr8(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
145
+ }
146
+
147
+ // Big struct
148
+ {
149
+ let out = {};
150
+ let bfg = MakeBFG(out, 2, 7, '__Hello123456789++++foobarFOOBAR!__');
151
+ assert.deepEqual(bfg, { a: 2, b: 4, c: -25, d: 'X/__Hello123456789++++foobarFOOBAR!__/X', e: 54, inner: { f: 14, g: 5 } });
152
+ assert.deepEqual(out, bfg);
153
+ }
154
+
155
+ // Packed struct
156
+ {
157
+ let out = {};
158
+ let bfg = MakePackedBFG(2, 7, out, '__Hello123456789++++foobarFOOBAR!__');
159
+ assert.deepEqual(bfg, { a: 2, b: 4, c: -25, d: 'X/__Hello123456789++++foobarFOOBAR!__/X', e: 54, inner: { f: 14, g: 5 } });
160
+ assert.deepEqual(out, bfg);
161
+ }
162
+
163
+ // Big string
164
+ {
165
+ let str = 'fooBAR!'.repeat(1024 * 1024);
166
+ assert.equal(ReturnBigString(str), str);
167
+ }
168
+
169
+ // Variadic
170
+ {
171
+ let str = PrintFmt('foo %d %g %s', 'int', 200, 'double', 1.5, 'string', 'BAR');
172
+ assert.equal(str, 'foo 200 1.5 BAR');
173
+ }
174
+
175
+ // UTF-16LE strings
176
+ {
177
+ let str = Concat16('Hello ', 'World!');
178
+ assert.equal(str, 'Hello World!');
179
+ }
180
+ }
package/test/raylib.js ADDED
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+
3
+ // This program is free software: you can redistribute it and/or modify
4
+ // it under the terms of the GNU Affero General Public License as published by
5
+ // the Free Software Foundation, either version 3 of the License, or
6
+ // (at your option) any later version.
7
+ //
8
+ // This program is distributed in the hope that it will be useful,
9
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ // GNU Affero General Public License for more details.
12
+ //
13
+ // You should have received a copy of the GNU Affero General Public License
14
+ // along with this program. If not, see https://www.gnu.org/licenses/.
15
+
16
+ const koffi = require('./build/koffi.node');
17
+ const crypto = require('crypto');
18
+ const fs = require('fs');
19
+ const os = require('os');
20
+ const path = require('path');
21
+
22
+ const Color = koffi.struct('Color', {
23
+ r: 'uchar',
24
+ g: 'uchar',
25
+ b: 'uchar',
26
+ a: 'uchar'
27
+ });
28
+
29
+ const Image = koffi.struct('Image', {
30
+ data: koffi.pointer('void'),
31
+ width: 'int',
32
+ height: 'int',
33
+ mipmaps: 'int',
34
+ format: 'int'
35
+ });
36
+
37
+ const GlyphInfo = koffi.struct('GlyphInfo', {
38
+ value: 'int',
39
+ offsetX: 'int',
40
+ offsetY: 'int',
41
+ advanceX: 'int',
42
+ image: Image
43
+ });
44
+
45
+ const Vector2 = koffi.struct('Vector2', {
46
+ x: 'float',
47
+ y: 'float'
48
+ });
49
+
50
+ const Rectangle = koffi.struct('Rectangle', {
51
+ x: 'float',
52
+ y: 'float',
53
+ width: 'float',
54
+ height: 'float'
55
+ });
56
+
57
+ const Texture = koffi.struct('Texture', {
58
+ id: 'uint',
59
+ width: 'int',
60
+ height: 'int',
61
+ mipmaps: 'int',
62
+ format: 'int'
63
+ });
64
+
65
+ const Font = koffi.struct('Font', {
66
+ baseSize: 'int',
67
+ glyphCount: 'int',
68
+ glyphPadding: 'int',
69
+ texture: Texture,
70
+ recs: koffi.pointer(Rectangle),
71
+ glyphs: koffi.pointer(GlyphInfo)
72
+ });
73
+
74
+ main();
75
+
76
+ async function main() {
77
+ try {
78
+ await test();
79
+ } catch (err) {
80
+ console.error(err);
81
+ process.exit(1);
82
+ }
83
+ }
84
+
85
+ async function test() {
86
+ let lib_filename = path.dirname(__filename) + '/build/raylib' + koffi.extension;
87
+ let lib = koffi.load(lib_filename);
88
+
89
+ const InitWindow = lib.func('InitWindow', 'void', ['int', 'int', 'string']);
90
+ const SetTraceLogLevel = lib.func('SetTraceLogLevel', 'void', ['int']);
91
+ const SetWindowState = lib.func('SetWindowState', 'void', ['uint']);
92
+ const GenImageColor = lib.func('GenImageColor', Image, ['int', 'int', Color]);
93
+ const GetFontDefault = lib.func('GetFontDefault', Font, []);
94
+ const MeasureTextEx = lib.func('MeasureTextEx', Vector2, [Font, 'string', 'float', 'float']);
95
+ const ImageDrawTextEx = lib.func('ImageDrawTextEx', 'void', [koffi.pointer(Image), Font, 'string', Vector2, 'float', 'float', Color]);
96
+ const ExportImage = lib.func('ExportImage', 'bool', [Image, 'string']);
97
+
98
+ // We need to call InitWindow before using anything else (such as fonts)
99
+ SetTraceLogLevel(4); // Warnings
100
+ SetWindowState(0x80); // Hidden
101
+ InitWindow(640, 480, "Raylib Test");
102
+
103
+ let img = GenImageColor(800, 600, { r: 0, g: 0, b: 0, a: 255 });
104
+ let font = GetFontDefault();
105
+
106
+ for (let i = 0; i < 3600; i++) {
107
+ let text = 'Hello World!';
108
+ let text_width = MeasureTextEx(font, text, 10, 1).x;
109
+
110
+ let angle = (i * 7) * Math.PI / 180;
111
+ let color = {
112
+ r: 127.5 + 127.5 * Math.sin(angle),
113
+ g: 127.5 + 127.5 * Math.sin(angle + Math.PI / 2),
114
+ b: 127.5 + 127.5 * Math.sin(angle + Math.PI),
115
+ a: 255
116
+ };
117
+ let pos = {
118
+ x: (img.width / 2 - text_width / 2) + i * 0.1 * Math.cos(angle - Math.PI / 2),
119
+ y: (img.height / 2 - 16) + i * 0.1 * Math.sin(angle - Math.PI / 2)
120
+ };
121
+
122
+ ImageDrawTextEx(img, font, text, pos, 10, 1, color);
123
+ }
124
+
125
+ // In theory we could directly checksum the image data, but we can't do it easily right now
126
+ // until koffi gives us something to transform a pointer to a buffer.
127
+ let tmp_dir = fs.mkdtempSync(path.join(os.tmpdir(), 'raylib'));
128
+
129
+ try {
130
+ ExportImage(img, tmp_dir + '/hello.png');
131
+
132
+ let sha256 = await new Promise((resolve, reject) => {
133
+ try {
134
+ let reader = fs.createReadStream(tmp_dir + '/hello.png');
135
+ let sha = crypto.createHash('sha256');
136
+
137
+ reader.on('error', reject);
138
+ reader.on('data', buf => sha.update(buf));
139
+ reader.on('end', () => {
140
+ let hash = sha.digest('hex');
141
+ resolve(hash);
142
+ });
143
+ } catch (err) {
144
+ reject(err);
145
+ }
146
+ });
147
+ let expected = '5ec23005377082582a14d261f5d07f51a6615b3bcdf5b91ada29aba65da22c5f';
148
+
149
+ console.log('Computed checksum: ' + sha256);
150
+ console.log('Expected: ' + expected);
151
+ console.log('');
152
+
153
+ if (sha256 == expected) {
154
+ console.log('Success!');
155
+ } else {
156
+ throw new Error('Image mismatch');
157
+ }
158
+ } finally {
159
+ if (fs.rmSync != null) {
160
+ fs.rmSync(tmp_dir, { recursive: true });
161
+ } else {
162
+ fs.rmdirSync(tmp_dir, { recursive: true });
163
+ }
164
+ }
165
+ }
package/test/sqlite.js ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+
3
+ // This program is free software: you can redistribute it and/or modify
4
+ // it under the terms of the GNU Affero General Public License as published by
5
+ // the Free Software Foundation, either version 3 of the License, or
6
+ // (at your option) any later version.
7
+ //
8
+ // This program is distributed in the hope that it will be useful,
9
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ // GNU Affero General Public License for more details.
12
+ //
13
+ // You should have received a copy of the GNU Affero General Public License
14
+ // along with this program. If not, see https://www.gnu.org/licenses/.
15
+
16
+ const crypto = require('crypto');
17
+ const koffi = require('./build/koffi.node');
18
+ const assert = require('assert');
19
+ const fs = require('fs');
20
+ const os = require('os');
21
+ const path = require('path');
22
+
23
+ const sqlite3_db = koffi.handle('sqlite3_db');
24
+ const sqlite3_stmt = koffi.handle('sqlite3_stmt');
25
+
26
+ main();
27
+
28
+ async function main() {
29
+ try {
30
+ await test();
31
+ console.log('Success!');
32
+ } catch (err) {
33
+ console.error(err);
34
+ process.exit(1);
35
+ }
36
+ }
37
+
38
+ async function test() {
39
+ let lib_filename = path.dirname(__filename) + '/build/sqlite3' + koffi.extension;
40
+ let lib = koffi.load(lib_filename);
41
+
42
+ const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['string', koffi.out(koffi.pointer(sqlite3_db)), 'int', 'string']);
43
+ const sqlite3_exec = lib.func('sqlite3_exec', 'int', [sqlite3_db, 'string', 'void *', 'void *', 'void *']);
44
+ const sqlite3_prepare_v2 = lib.func('sqlite3_prepare_v2', 'int', [sqlite3_db, 'string', 'int', koffi.out(koffi.pointer(sqlite3_stmt)), 'string']);
45
+ const sqlite3_reset = lib.func('sqlite3_reset', 'int', [sqlite3_stmt]);
46
+ const sqlite3_bind_text = lib.func('sqlite3_bind_text', 'int', [sqlite3_stmt, 'int', 'string', 'int', 'void *']);
47
+ const sqlite3_bind_int = lib.func('sqlite3_bind_int', 'int', [sqlite3_stmt, 'int', 'int']);
48
+ const sqlite3_step = lib.func('sqlite3_step', 'int', [sqlite3_stmt]);
49
+ const sqlite3_finalize = lib.func('sqlite3_finalize', 'int', [sqlite3_stmt]);
50
+ const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [sqlite3_db]);
51
+
52
+ let filename = await create_temporary_file(path.join(os.tmpdir(), 'test_sqlite'));
53
+ let db = {};
54
+
55
+ try {
56
+ if (sqlite3_open_v2(filename, db, 0x2 | 0x4, null) != 0)
57
+ throw new Error('Failed to open database');
58
+ if (sqlite3_exec(db, 'CREATE TABLE foo (id INTEGER PRIMARY KEY, bar TEXT, value INT);', null, null, null) != 0)
59
+ throw new Error('Failed to create table');
60
+
61
+ let stmt = {};
62
+ if (sqlite3_prepare_v2(db, "INSERT INTO foo (bar, value) VALUES (?1, ?2)", -1, stmt, null) != 0)
63
+ throw new Error('Failed to prepare insert statement for table foo');
64
+ for (let i = 0; i < 200; i++) {
65
+ sqlite3_reset(stmt);
66
+
67
+ sqlite3_bind_text(stmt, 1, `TXT ${i}`, -1, null);
68
+ sqlite3_bind_int(stmt, 2, i * 2);
69
+
70
+ if (sqlite3_step(stmt) != 101)
71
+ throw new Erorr('Failed to insert new test row');
72
+ }
73
+ sqlite3_finalize(stmt);
74
+ } finally {
75
+ sqlite3_close_v2(db);
76
+ fs.unlinkSync(filename);
77
+ }
78
+ }
79
+
80
+ async function create_temporary_file(prefix) {
81
+ let buf = Buffer.allocUnsafe(4);
82
+
83
+ for (;;) {
84
+ try {
85
+ crypto.randomFillSync(buf);
86
+
87
+ let suffix = buf.toString('hex').padStart(8, '0');
88
+ let filename = `${prefix}.${suffix}`;
89
+
90
+ let file = await new Promise((resolve, reject) => {
91
+ let file = fs.createWriteStream(filename, { flags: 'wx', mode: 0o644 });
92
+
93
+ file.on('open', () => resolve(file));
94
+ file.on('error', reject);
95
+ });
96
+ file.close();
97
+
98
+ return filename;
99
+ } catch (err) {
100
+ if (err.code != 'EEXIST')
101
+ throw err;
102
+ }
103
+ }
104
+ }