@agoric/xsnap 0.14.3-u14.0 → 0.14.3-u16.0

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 (137) hide show
  1. package/README.md +3 -3
  2. package/api.js +4 -2
  3. package/build.env +1 -1
  4. package/moddable/modules/data/base64/base64.js +28 -0
  5. package/moddable/modules/data/base64/manifest.json +11 -0
  6. package/moddable/modules/data/base64/modBase64.c +188 -0
  7. package/moddable/modules/data/binaryMessage/BinaryMessage.js +106 -0
  8. package/moddable/modules/data/crc/crc.c +205 -0
  9. package/moddable/modules/data/crc/crc.js +36 -0
  10. package/moddable/modules/data/crc/manifest.json +8 -0
  11. package/moddable/modules/data/hex/hex.js +28 -0
  12. package/moddable/modules/data/hex/manifest.json +11 -0
  13. package/moddable/modules/data/hex/modHex.c +139 -0
  14. package/moddable/modules/data/logical/logical.js +32 -0
  15. package/moddable/modules/data/logical/modLogical.c +98 -0
  16. package/moddable/modules/data/qrcode/manifest.json +9 -0
  17. package/moddable/modules/data/qrcode/qrcode.c +93 -0
  18. package/moddable/modules/data/qrcode/qrcode.js +23 -0
  19. package/moddable/modules/data/qrcode/qrcodegen.c +1025 -0
  20. package/moddable/modules/data/qrcode/qrcodegen.h +267 -0
  21. package/moddable/modules/data/text/decoder/manifest.json +8 -0
  22. package/moddable/modules/data/text/decoder/textdecoder.c +480 -0
  23. package/moddable/modules/data/text/decoder/textdecoder.js +27 -0
  24. package/moddable/modules/data/text/encoder/manifest.json +8 -0
  25. package/moddable/modules/data/text/encoder/textencoder.c +232 -0
  26. package/moddable/modules/data/text/encoder/textencoder.js +24 -0
  27. package/moddable/modules/data/tinyint/tinyint.c +150 -0
  28. package/moddable/modules/data/tinyint/tinyint.js +53 -0
  29. package/moddable/modules/data/url/manifest.json +17 -0
  30. package/moddable/modules/data/url/url.c +1959 -0
  31. package/moddable/modules/data/url/url.js +210 -0
  32. package/moddable/modules/data/wavreader/manifest.json +8 -0
  33. package/moddable/modules/data/wavreader/wavreader.js +128 -0
  34. package/moddable/modules/data/zlib/deflate.c +161 -0
  35. package/moddable/modules/data/zlib/deflate.js +63 -0
  36. package/moddable/modules/data/zlib/inflate.c +145 -0
  37. package/moddable/modules/data/zlib/inflate.js +66 -0
  38. package/moddable/modules/data/zlib/manifest_deflate.json +9 -0
  39. package/moddable/modules/data/zlib/manifest_inflate.json +9 -0
  40. package/moddable/modules/data/zlib/miniz.c +4924 -0
  41. package/moddable/xs/includes/xs.d.ts +73 -0
  42. package/moddable/xs/includes/xs.h +1533 -0
  43. package/moddable/xs/includes/xsmc.h +206 -0
  44. package/moddable/xs/makefiles/lin/makefile +33 -0
  45. package/moddable/xs/makefiles/lin/xsc.mk +118 -0
  46. package/moddable/xs/makefiles/lin/xsid.mk +90 -0
  47. package/moddable/xs/makefiles/lin/xsl.mk +168 -0
  48. package/moddable/xs/makefiles/lin/xst.mk +201 -0
  49. package/moddable/xs/makefiles/mac/makefile +33 -0
  50. package/moddable/xs/makefiles/mac/xsc.mk +130 -0
  51. package/moddable/xs/makefiles/mac/xsid.mk +102 -0
  52. package/moddable/xs/makefiles/mac/xsl.mk +177 -0
  53. package/moddable/xs/makefiles/mac/xst.mk +203 -0
  54. package/moddable/xs/makefiles/mac/xst_no_asan.txt +52 -0
  55. package/moddable/xs/makefiles/win/build.bat +26 -0
  56. package/moddable/xs/makefiles/win/xsc.mak +142 -0
  57. package/moddable/xs/makefiles/win/xsid.mak +113 -0
  58. package/moddable/xs/makefiles/win/xsl.mak +186 -0
  59. package/moddable/xs/makefiles/win/xst.mak +195 -0
  60. package/moddable/xs/platforms/lin_xs.h +99 -0
  61. package/moddable/xs/platforms/mac_xs.h +97 -0
  62. package/moddable/xs/platforms/wasm_xs.h +79 -0
  63. package/moddable/xs/platforms/win_xs.h +104 -0
  64. package/moddable/xs/platforms/xsHost.h +63 -0
  65. package/moddable/xs/platforms/xsPlatform.h +618 -0
  66. package/moddable/xs/sources/xsAPI.c +2555 -0
  67. package/moddable/xs/sources/xsAll.c +294 -0
  68. package/moddable/xs/sources/xsAll.h +2741 -0
  69. package/moddable/xs/sources/xsArguments.c +222 -0
  70. package/moddable/xs/sources/xsArray.c +2657 -0
  71. package/moddable/xs/sources/xsAtomics.c +844 -0
  72. package/moddable/xs/sources/xsBigInt.c +1859 -0
  73. package/moddable/xs/sources/xsBoolean.c +109 -0
  74. package/moddable/xs/sources/xsCode.c +4493 -0
  75. package/moddable/xs/sources/xsCommon.c +1710 -0
  76. package/moddable/xs/sources/xsCommon.h +1142 -0
  77. package/moddable/xs/sources/xsDataView.c +2890 -0
  78. package/moddable/xs/sources/xsDate.c +1541 -0
  79. package/moddable/xs/sources/xsDebug.c +2710 -0
  80. package/moddable/xs/sources/xsDefaults.c +134 -0
  81. package/moddable/xs/sources/xsError.c +353 -0
  82. package/moddable/xs/sources/xsFunction.c +776 -0
  83. package/moddable/xs/sources/xsGenerator.c +865 -0
  84. package/moddable/xs/sources/xsGlobal.c +839 -0
  85. package/moddable/xs/sources/xsJSON.c +1091 -0
  86. package/moddable/xs/sources/xsLexical.c +1969 -0
  87. package/moddable/xs/sources/xsLockdown.c +933 -0
  88. package/moddable/xs/sources/xsMapSet.c +1649 -0
  89. package/moddable/xs/sources/xsMarshall.c +1020 -0
  90. package/moddable/xs/sources/xsMath.c +624 -0
  91. package/moddable/xs/sources/xsMemory.c +1941 -0
  92. package/moddable/xs/sources/xsModule.c +3101 -0
  93. package/moddable/xs/sources/xsNumber.c +560 -0
  94. package/moddable/xs/sources/xsObject.c +1102 -0
  95. package/moddable/xs/sources/xsPlatforms.c +480 -0
  96. package/moddable/xs/sources/xsProfile.c +577 -0
  97. package/moddable/xs/sources/xsPromise.c +1199 -0
  98. package/moddable/xs/sources/xsProperty.c +636 -0
  99. package/moddable/xs/sources/xsProxy.c +1014 -0
  100. package/moddable/xs/sources/xsRegExp.c +1168 -0
  101. package/moddable/xs/sources/xsRun.c +4889 -0
  102. package/moddable/xs/sources/xsScope.c +1293 -0
  103. package/moddable/xs/sources/xsScript.c +288 -0
  104. package/moddable/xs/sources/xsScript.h +1186 -0
  105. package/moddable/xs/sources/xsSnapshot.c +2161 -0
  106. package/moddable/xs/sources/xsSnapshot.h +51 -0
  107. package/moddable/xs/sources/xsSourceMap.c +218 -0
  108. package/moddable/xs/sources/xsString.c +3332 -0
  109. package/moddable/xs/sources/xsSymbol.c +503 -0
  110. package/moddable/xs/sources/xsSyntaxical.c +4193 -0
  111. package/moddable/xs/sources/xsTree.c +1893 -0
  112. package/moddable/xs/sources/xsType.c +1488 -0
  113. package/moddable/xs/sources/xsdtoa.c +6672 -0
  114. package/moddable/xs/sources/xsmc.c +340 -0
  115. package/moddable/xs/sources/xsre.c +7578 -0
  116. package/package.json +37 -20
  117. package/scripts/get_xsnap_version.sh +14 -0
  118. package/scripts/test-package.sh +21 -0
  119. package/src/avaAssertXS.js +6 -2
  120. package/src/avaHandler.cjs +2 -5
  121. package/src/avaXS.js +7 -8
  122. package/src/build.js +161 -28
  123. package/src/replay.js +0 -3
  124. package/src/xsnap.js +105 -91
  125. package/src/xsrepl.js +2 -3
  126. package/xsnap-native/xsnap/makefiles/lin/makefile +10 -0
  127. package/xsnap-native/xsnap/makefiles/lin/xsnap-worker.mk +156 -0
  128. package/xsnap-native/xsnap/makefiles/lin/xsnap.mk +144 -0
  129. package/xsnap-native/xsnap/makefiles/mac/makefile +10 -0
  130. package/xsnap-native/xsnap/makefiles/mac/xsnap-worker.mk +165 -0
  131. package/xsnap-native/xsnap/makefiles/mac/xsnap.mk +153 -0
  132. package/xsnap-native/xsnap/sources/xsnap-worker.c +1008 -0
  133. package/xsnap-native/xsnap/sources/xsnap.c +717 -0
  134. package/xsnap-native/xsnap/sources/xsnap.h +142 -0
  135. package/xsnap-native/xsnap/sources/xsnapPlatform.c +1501 -0
  136. package/xsnap-native/xsnap/sources/xsnapPlatform.h +105 -0
  137. package/CHANGELOG.md +0 -654
@@ -0,0 +1,1008 @@
1
+ #include "xsnap.h"
2
+
3
+ // XS heap-snapshot contents depend upon the availability of
4
+ // __has_builtin (e.g. xsRun.c mxCase(XS_CODE_MULTIPLY) , around line
5
+ // 3419): it creates XS_INTEGER_KIND if available, XS_NUMBER_KIND if
6
+ // not. This is not visible from userspace, but breaks snapshot
7
+ // consensus between nodes compiled with e.g. gcc-9 (no __has_builtin,
8
+ // default compiler in Ubuntu-20.04-LTS) and gcc-11 (has it, default
9
+ // in Ubuntu-22.04-LTS). To prohibit variation which might break
10
+ // consensus, insist upon the newer feature, which will force an
11
+ // upgrade of either distro or compiler.
12
+
13
+ #ifndef __has_builtin
14
+ #error "xsnap requires __has_builtin (gcc>=10, e.g. Ubuntu-22.04), see https://github.com/Agoric/agoric-sdk/issues/7829"
15
+ #endif
16
+
17
+ #define SNAPSHOT_SIGNATURE "xsnap 1"
18
+ #ifndef XSNAP_VERSION
19
+ # error "You must define XSNAP_VERSION in the right Makefile"
20
+ #else
21
+ // Allows grepping the binary's strings for the version: strings xsnap-worker | grep xsnap_version
22
+ char *VERSION = "xsnap_version: " XSNAP_VERSION;
23
+ #endif
24
+
25
+ #ifndef XSNAP_TEST_RECORD
26
+ #define XSNAP_TEST_RECORD 1
27
+ #endif
28
+
29
+ #if XSNAP_TEST_RECORD
30
+ enum {
31
+ mxTestRecordJS = 1,
32
+ mxTestRecordJSON = 2,
33
+ mxTestRecordParam = 4,
34
+ mxTestRecordReply = 8,
35
+ };
36
+ int gxTestRecordParamIndex = 0;
37
+ int gxTestRecordReplyIndex = 0;
38
+ static void fxTestRecordArgs(int argc, char* argv[]);
39
+ static void fxTestRecord(int flags, void* buffer, size_t length);
40
+ #endif
41
+
42
+ static void xsBuildAgent(xsMachine* the);
43
+ static void xsPrintUsage();
44
+
45
+ // static void xs_clearTimer(xsMachine* the);
46
+ static void xs_currentMeterLimit(xsMachine* the);
47
+ static void xs_gc(xsMachine* the);
48
+ static void xs_issueCommand(xsMachine* the);
49
+ static void xs_performance_now(xsMachine* the);
50
+ static void xs_print(xsMachine* the);
51
+ static void xs_resetMeter(xsMachine* the);
52
+ static void xs_setImmediate(xsMachine* the);
53
+ // static void xs_setInterval(xsMachine* the);
54
+ // static void xs_setTimeout(xsMachine* the);
55
+
56
+ static int fxReadNetString(FILE *inStream, char** dest, size_t* len);
57
+ static char* fxReadNetStringError(int code);
58
+ static int fxWriteOkay(FILE* outStream, xsUnsignedValue meterIndex, xsMachine *the, char* buf, size_t len);
59
+ static int fxWriteNetString(FILE* outStream, char* prefix, char* buf, size_t len);
60
+ static char* fxWriteNetStringError(int code);
61
+
62
+ extern xsIntegerValue fxGetCurrentHeapCount(xsMachine* the);
63
+
64
+ extern void xs_textdecoder(xsMachine *the);
65
+ extern void xs_textdecoder_decode(xsMachine *the);
66
+ extern void xs_textdecoder_get_encoding(xsMachine *the);
67
+ extern void xs_textdecoder_get_ignoreBOM(xsMachine *the);
68
+ extern void xs_textdecoder_get_fatal(xsMachine *the);
69
+
70
+ extern void xs_textencoder(xsMachine *the);
71
+ extern void xs_textencoder_encode(xsMachine *the);
72
+ extern void xs_textencoder_encodeInto(xsMachine *the);
73
+
74
+ extern void modInstallTextDecoder(xsMachine *the);
75
+ extern void modInstallTextEncoder(xsMachine *the);
76
+
77
+ extern void xs_base64_encode(xsMachine *the);
78
+ extern void xs_base64_decode(xsMachine *the);
79
+ extern void modInstallBase64(xsMachine *the);
80
+
81
+ // The order of the callbacks materially affects how they are introduced to
82
+ // code that runs from a snapshot, so must be consistent in the face of
83
+ // upgrade.
84
+ #define mxSnapshotCallbackCount 18
85
+ xsCallback gxSnapshotCallbacks[mxSnapshotCallbackCount] = {
86
+ xs_issueCommand, // 0
87
+ xs_print, // 1
88
+ xs_setImmediate, // 2
89
+ xs_gc, // 3
90
+ xs_performance_now, // 4
91
+ xs_currentMeterLimit, // 5
92
+ xs_resetMeter, // 6
93
+
94
+ xs_textdecoder, // 7
95
+ xs_textdecoder_decode, // 8
96
+ xs_textdecoder_get_encoding, // 9
97
+ xs_textdecoder_get_ignoreBOM, // 10
98
+ xs_textdecoder_get_fatal, // 11
99
+
100
+ xs_textencoder, // 12
101
+ xs_textencoder_encode, // 13
102
+ xs_textencoder_encodeInto, // 14
103
+
104
+ xs_base64_encode, // 15
105
+ xs_base64_decode, // 16
106
+
107
+ fx_harden, // 17
108
+ // fx_setInterval,
109
+ // fx_setTimeout,
110
+ // fx_clearTimer,
111
+ };
112
+
113
+ typedef struct {
114
+ FILE *file;
115
+ int size;
116
+ } SnapshotStream;
117
+
118
+ static int fxSnapshotRead(void* stream, void* address, size_t size)
119
+ {
120
+ return (fread(address, size, 1, stream) == 1) ? 0 : errno;
121
+ }
122
+
123
+ static int fxSnapshotWrite(void* stream, void* address, size_t size)
124
+ {
125
+ SnapshotStream* snapshotStream = stream;
126
+ size_t written = fwrite(address, size, 1, snapshotStream->file);
127
+ snapshotStream->size += size * written;
128
+ return (written == 1) ? 0 : errno;
129
+ }
130
+
131
+ #if mxInstrument
132
+ #define xsnapInstrumentCount 1
133
+ static xsStringValue xsnapInstrumentNames[xsnapInstrumentCount] = {
134
+ "Metering",
135
+ };
136
+ static xsStringValue xsnapInstrumentUnits[xsnapInstrumentCount] = {
137
+ " times",
138
+ };
139
+ static xsIntegerValue xsnapInstrumentValues[xsnapInstrumentCount] = {
140
+ 0,
141
+ };
142
+ #endif
143
+
144
+ #if mxMetering
145
+ #define xsBeginCrank(_THE, _LIMIT) \
146
+ (xsSetCurrentMeter(_THE, 0), \
147
+ gxCurrentMeter = _LIMIT)
148
+ #define xsEndCrank(_THE) \
149
+ (gxCurrentMeter = 0, \
150
+ fxGetCurrentMeter(_THE))
151
+ #else
152
+ #define xsBeginCrank(_THE, _LIMIT)
153
+ #define xsEndCrank(_THE) 0
154
+ #endif
155
+
156
+ static xsUnsignedValue gxCrankMeteringLimit = 0;
157
+ static xsUnsignedValue gxCurrentMeter = 0;
158
+ xsBooleanValue fxMeteringCallback(xsMachine* the, xsUnsignedValue index)
159
+ {
160
+ if (gxCurrentMeter > 0 && index > gxCurrentMeter) {
161
+ // Just throw right out of the main loop and exit.
162
+ return 0;
163
+ }
164
+ // fprintf(stderr, "metering up to %d\n", index);
165
+ return 1;
166
+ }
167
+ static xsBooleanValue gxMeteringPrint = 0;
168
+
169
+ static FILE *fromParent;
170
+ static FILE *toParent;
171
+
172
+ typedef enum {
173
+ E_UNKNOWN_ERROR = -1,
174
+ E_SUCCESS = 0,
175
+ E_BAD_USAGE,
176
+ E_IO_ERROR,
177
+ // 10 + XS_NOT_ENOUGH_MEMORY_EXIT
178
+ E_NOT_ENOUGH_MEMORY = 11,
179
+ E_STACK_OVERFLOW = 12,
180
+ E_UNHANDLED_EXCEPTION = 15,
181
+ E_NO_MORE_KEYS = 16,
182
+ E_TOO_MUCH_COMPUTATION = 17,
183
+ } ExitCode;
184
+
185
+ // 250 syscalls
186
+ #define MAX_TIMESTAMPS 502
187
+ static struct timeval timestamps[MAX_TIMESTAMPS];
188
+ static int num_timestamps;
189
+ static unsigned int timestamps_overrun;
190
+ static void resetTimestamps() {
191
+ timestamps_overrun = 0;
192
+ num_timestamps = 0;
193
+ // on 64-bit platforms, 'struct timeval' usually needs 8+8=16 bytes
194
+ //printf("sizeof(time_t): %ld\n", sizeof(time_t));
195
+ //printf("sizeof(time_suseconts_t): %ld\n", sizeof(suseconds_t));
196
+ }
197
+ static void recordTimestamp() {
198
+ if (num_timestamps < MAX_TIMESTAMPS) {
199
+ gettimeofday(&(timestamps[num_timestamps]), NULL);
200
+ num_timestamps += 1;
201
+ } else {
202
+ timestamps_overrun = 1;
203
+ }
204
+ }
205
+
206
+ // 2^64 is 18446744073709551616 , which is 20 characters long
207
+ #define DIGITS_FOR_64 20
208
+ // [AA.AA,BB.BB,CC.CC]\0
209
+ static char timestampBuffer[1 + MAX_TIMESTAMPS * (DIGITS_FOR_64 + 1 + 6 + 1) + 1];
210
+ // over provisioning by considering all "sec" values as the max printed length.
211
+ // While the last timestamps does not have a trailing comma, the payload ends
212
+ // with both a closing square bracket and a null byte.
213
+
214
+ static char *renderTimestamps() {
215
+ // return pointer to static string buffer with '[NN.NN,NN.NN]', or NULL
216
+ int size, i, wrote;
217
+ char *p = timestampBuffer;
218
+ size = sizeof(timestampBuffer); // holds all numbers, commas, and \0
219
+ *(p++) = '['; size--;
220
+ for (i = 0; i < num_timestamps; i++) {
221
+ // snprintf() returns "the number of characters that would have
222
+ // been printed if the size were unlimited, not including the
223
+ // final \0". It writes at most size-1 characters, then writes
224
+ // the trailing \0.
225
+ // The type of tv_usec is 32 bits on Darwin and 64 bits on Linux at
226
+ // time of writing.
227
+ // We cast into the wider precision to ensure the format specifier gets
228
+ // the expected number of bits behind the variadic args reference.
229
+ // We do the same for tv_sec out of an outrageous abundance of caution.
230
+ wrote = snprintf(p, size, "%lu.%06lu",
231
+ (unsigned long)timestamps[i].tv_sec,
232
+ (unsigned long)timestamps[i].tv_usec);
233
+ if (wrote > size) {
234
+ return NULL;
235
+ }
236
+ p += wrote;
237
+ size -= wrote;
238
+ if (size < 2) { // 2 is enough for "]\0", but 1 is not
239
+ return NULL;
240
+ }
241
+ if (i+1 < num_timestamps) {
242
+ // 2 is also enough for a comma
243
+ *(p++) = ','; size--;
244
+ }
245
+ if (size < 2) { // but the comma might reduce size below 2
246
+ return NULL;
247
+ }
248
+ }
249
+ if (size < 2) { // paranoia, also in case loop never loops
250
+ return NULL;
251
+ }
252
+ *(p++) = ']'; size--;
253
+ *(p++) = '\0'; size--;
254
+ return timestampBuffer;
255
+ }
256
+
257
+ int main(int argc, char* argv[])
258
+ {
259
+ int argi;
260
+ int argr = 0;
261
+ int error = 0;
262
+ int interval = 0;
263
+ int parserBufferSize = 8192 * 1024;
264
+
265
+ xsSnapshot snapshot = {
266
+ SNAPSHOT_SIGNATURE,
267
+ sizeof(SNAPSHOT_SIGNATURE) - 1,
268
+ gxSnapshotCallbacks,
269
+ mxSnapshotCallbackCount,
270
+ fxSnapshotRead,
271
+ fxSnapshotWrite,
272
+ NULL,
273
+ 0,
274
+ NULL,
275
+ NULL,
276
+ NULL,
277
+ 0,
278
+ NULL
279
+ };
280
+
281
+ xsMachine* machine;
282
+ char *path;
283
+
284
+ #if XSNAP_TEST_RECORD
285
+ fxTestRecordArgs(argc, argv);
286
+ #endif
287
+
288
+ for (argi = 1; argi < argc; argi++) {
289
+ if (argv[argi][0] != '-')
290
+ continue;
291
+ if (!strcmp(argv[argi], "-h")) {
292
+ xsPrintUsage();
293
+ return 0;
294
+ } else if (!strcmp(argv[argi], "-i")) {
295
+ argi++;
296
+ if (argi < argc)
297
+ interval = atoi(argv[argi]);
298
+ else {
299
+ xsPrintUsage();
300
+ return E_BAD_USAGE;
301
+ }
302
+ }
303
+ else if (!strcmp(argv[argi], "-l")) {
304
+ #if mxMetering
305
+ argi++;
306
+ if (argi < argc)
307
+ gxCrankMeteringLimit = atoi(argv[argi]);
308
+ else {
309
+ xsPrintUsage();
310
+ return E_BAD_USAGE;
311
+ }
312
+ #else
313
+ fprintf(stderr, "%s flag not implemented; mxMetering is not enabled\n", argv[argi]);
314
+ return E_BAD_USAGE;
315
+ #endif
316
+ }
317
+ else if (!strcmp(argv[argi], "-p"))
318
+ gxMeteringPrint = 1;
319
+ else if (!strcmp(argv[argi], "-r")) {
320
+ argi++;
321
+ if (argi < argc)
322
+ argr = argi;
323
+ else {
324
+ xsPrintUsage();
325
+ return E_BAD_USAGE;
326
+ }
327
+ }
328
+ else if (!strcmp(argv[argi], "-s")) {
329
+ argi++;
330
+ if (argi < argc)
331
+ parserBufferSize = 1024 * atoi(argv[argi]);
332
+ else {
333
+ xsPrintUsage();
334
+ return E_BAD_USAGE;
335
+ }
336
+ }
337
+ else if (!strcmp(argv[argi], "-v")) {
338
+ char version[16];
339
+ xsVersion(version, sizeof(version));
340
+ printf("xsnap %s (XS %s)\n", XSNAP_VERSION, version);
341
+ return E_SUCCESS;
342
+ }
343
+ else if (!strcmp(argv[argi], "-n")) {
344
+ printf("deprecated\n");
345
+ return E_SUCCESS;
346
+ } else {
347
+ xsPrintUsage();
348
+ return E_BAD_USAGE;
349
+ }
350
+ }
351
+ xsCreation _creation = {
352
+ 32 * 1024 * 1024, /* initialChunkSize */
353
+ 4 * 1024 * 1024, /* incrementalChunkSize */
354
+ 256 * 1024, /* initialHeapCount */
355
+ 128 * 1024, /* incrementalHeapCount */
356
+ 4096, /* stackCount */
357
+ 32000, /* initialKeyCount */
358
+ 8000, /* incrementalKeyCount */
359
+ 1993, /* nameModulo */
360
+ 127, /* symbolModulo */
361
+ parserBufferSize, /* parserBufferSize */
362
+ 1993, /* parserTableModulo */
363
+ };
364
+ xsCreation* creation = &_creation;
365
+
366
+ if (gxCrankMeteringLimit) {
367
+ if (interval == 0)
368
+ interval = 1;
369
+ }
370
+ xsInitializeSharedCluster();
371
+ if (argr) {
372
+ char *path = argv[argr];
373
+ if (path[0] == '@') {
374
+ int fd = atoi(path + 1);
375
+ int tmpfd = dup(fd);
376
+ if (tmpfd < 0) {
377
+ snapshot.stream = NULL;
378
+ } else {
379
+ snapshot.stream = fdopen(tmpfd, "rb");
380
+ }
381
+ } else {
382
+ snapshot.stream = fopen(path, "rb");
383
+ }
384
+ if (snapshot.stream) {
385
+ machine = xsReadSnapshot(&snapshot, "xsnap", NULL);
386
+ fclose(snapshot.stream);
387
+ }
388
+ else
389
+ snapshot.error = errno;
390
+ if (snapshot.error) {
391
+ fprintf(stderr, "cannot read snapshot %s: %s\n", argv[argr], strerror(snapshot.error));
392
+ return E_IO_ERROR;
393
+ }
394
+ }
395
+ else {
396
+ machine = xsCreateMachine(creation, "xsnap", NULL);
397
+ xsBuildAgent(machine);
398
+ }
399
+ if (!(fromParent = fdopen(3, "rb"))) {
400
+ fprintf(stderr, "fdopen(3) from parent failed\n");
401
+ c_exit(E_IO_ERROR);
402
+ }
403
+ if (!(toParent = fdopen(4, "wb"))) {
404
+ fprintf(stderr, "fdopen(4) to parent failed\n");
405
+ c_exit(E_IO_ERROR);
406
+ }
407
+ #if mxInstrument
408
+ xsDescribeInstrumentation(machine, xsnapInstrumentCount, xsnapInstrumentNames, xsnapInstrumentUnits);
409
+ #endif
410
+ xsBeginMetering(machine, fxMeteringCallback, interval);
411
+ {
412
+ fd_set rfds;
413
+ char done = 0;
414
+ while (!done) {
415
+ #if mxInstrument
416
+ FD_ZERO(&rfds);
417
+ FD_SET(3, &rfds);
418
+ FD_SET(5, &rfds);
419
+ if (select(6, &rfds, NULL, NULL, NULL) >= 0) {
420
+ if (FD_ISSET(5, &rfds))
421
+ xsRunDebugger(machine);
422
+ if (!FD_ISSET(3, &rfds))
423
+ continue;
424
+ }
425
+ else {
426
+ fprintf(stderr, "select failed: %s\n", strerror(errno));
427
+ error = E_IO_ERROR;
428
+ break;
429
+ }
430
+ #endif
431
+ // By default, use the infinite meter.
432
+ gxCurrentMeter = 0;
433
+
434
+ xsUnsignedValue meterIndex = 0;
435
+ char* nsbuf;
436
+ size_t nslen;
437
+ resetTimestamps();
438
+ int readError = fxReadNetString(fromParent, &nsbuf, &nslen);
439
+ recordTimestamp(); // after delivery received from parent
440
+ int writeError = 0;
441
+
442
+ if (readError != 0) {
443
+ if (feof(fromParent)) {
444
+ break;
445
+ } else {
446
+ fprintf(stderr, "%s\n", fxReadNetStringError(readError));
447
+ c_exit(E_IO_ERROR);
448
+ }
449
+ }
450
+ char command = *nsbuf;
451
+ // fprintf(stderr, "command: len %d %c arg: %s\n", nslen, command, nsbuf + 1);
452
+ switch(command) {
453
+ case 'R': // isReady
454
+ fxWriteNetString(toParent, ".", "", 0);
455
+ break;
456
+ case '?':
457
+ case 'e':
458
+ xsBeginCrank(machine, gxCrankMeteringLimit);
459
+ char* response = NULL;
460
+ xsIntegerValue responseLength = 0;
461
+ error = 0;
462
+ xsBeginHost(machine);
463
+ {
464
+ xsVars(3);
465
+ xsTry {
466
+ if (command == '?') {
467
+ #if XSNAP_TEST_RECORD
468
+ fxTestRecord(mxTestRecordJSON | mxTestRecordParam, nsbuf + 1, nslen - 1);
469
+ #endif
470
+ // TODO: can we avoid a copy?
471
+ xsVar(0) = xsArrayBuffer(nsbuf + 1, nslen - 1);
472
+ xsVar(1) = xsCall1(xsGlobal, xsID("handleCommand"), xsVar(0));
473
+ } else {
474
+ #if XSNAP_TEST_RECORD
475
+ fxTestRecord(mxTestRecordJS | mxTestRecordParam, nsbuf + 1, nslen - 1);
476
+ #endif
477
+ xsVar(0) = xsStringBuffer(nsbuf + 1, nslen - 1);
478
+ xsVar(1) = xsCall1(xsGlobal, xsID("eval"), xsVar(0));
479
+ }
480
+ }
481
+ xsCatch {
482
+ if (xsTypeOf(xsException) != xsUndefinedType) {
483
+ // fprintf(stderr, "%c: %s\n", command, xsToString(xsException));
484
+ error = E_UNHANDLED_EXCEPTION;
485
+ xsVar(1) = xsException;
486
+ xsException = xsUndefined;
487
+ }
488
+ }
489
+ }
490
+ fxRunLoop(machine);
491
+ meterIndex = xsEndCrank(machine);
492
+ {
493
+ if (error) {
494
+ response = xsToString(xsVar(1));
495
+ responseLength = strlen(response);
496
+ } else {
497
+ // fprintf(stderr, "report: %d %s\n", xsTypeOf(report), xsToString(report));
498
+ xsTry {
499
+ if (xsTypeOf(xsVar(1)) == xsReferenceType && xsHas(xsVar(1), xsID("result"))) {
500
+ xsVar(2) = xsGet(xsVar(1), xsID("result"));
501
+ } else {
502
+ xsVar(2) = xsVar(1);
503
+ }
504
+ // fprintf(stderr, "result: %d %s\n", xsTypeOf(result), xsToString(result));
505
+ if (xsIsInstanceOf(xsVar(2), xsArrayBufferPrototype)) {
506
+ responseLength = xsGetArrayBufferLength(xsVar(2));
507
+ response = xsToArrayBuffer(xsVar(2));
508
+ }
509
+ }
510
+ xsCatch {
511
+ if (xsTypeOf(xsException) != xsUndefinedType) {
512
+ fprintf(stderr, "%c computing response %d", command, xsTypeOf(xsVar(1)));
513
+ fprintf(stderr, " %d:", xsTypeOf(xsVar(2)));
514
+ fprintf(stderr, " %s:", xsToString(xsVar(2)));
515
+ fprintf(stderr, " %s\n", xsToString(xsException));
516
+ xsException = xsUndefined;
517
+ }
518
+ }
519
+ }
520
+ }
521
+ xsEndHost(machine);
522
+ if (error) {
523
+ writeError = fxWriteNetString(toParent, "!", response, responseLength);
524
+ // fprintf(stderr, "error: %d, writeError: %d %s\n", error, writeError, response);
525
+ } else {
526
+ // fprintf(stderr, "response of %d bytes\n", responseLength);
527
+ writeError = fxWriteOkay(toParent, meterIndex, machine, response, responseLength);
528
+ }
529
+ if (writeError != 0) {
530
+ fprintf(stderr, "%s\n", fxWriteNetStringError(writeError));
531
+ c_exit(E_IO_ERROR);
532
+ }
533
+ break;
534
+ case 's':
535
+ case 'm':
536
+ xsBeginCrank(machine, gxCrankMeteringLimit);
537
+ path = nsbuf + 1;
538
+ xsBeginHost(machine);
539
+ {
540
+ xsVars(1);
541
+ xsTry {
542
+ // ISSUE: realpath necessary? realpath(x, x) doesn't seem to work.
543
+ if (command == 'm')
544
+ xsRunModuleFile(path);
545
+ else
546
+ xsRunProgramFile(path);
547
+ }
548
+ xsCatch {
549
+ if (xsTypeOf(xsException) != xsUndefinedType) {
550
+ fprintf(stderr, "%s\n", xsToString(xsException));
551
+ error = E_UNHANDLED_EXCEPTION;
552
+ xsException = xsUndefined;
553
+ }
554
+ }
555
+ }
556
+ xsEndHost(machine);
557
+ fxRunLoop(machine);
558
+ meterIndex = xsEndCrank(machine);
559
+ if (error == 0) {
560
+ int writeError = fxWriteOkay(toParent, meterIndex, machine, "", 0);
561
+ if (writeError != 0) {
562
+ fprintf(stderr, "%s\n", fxWriteNetStringError(writeError));
563
+ c_exit(E_IO_ERROR);
564
+ }
565
+ } else {
566
+ // TODO: dynamically build error message including Exception message.
567
+ int writeError = fxWriteNetString(toParent, "!", "", 0);
568
+ if (writeError != 0) {
569
+ fprintf(stderr, "%s\n", fxWriteNetStringError(writeError));
570
+ c_exit(E_IO_ERROR);
571
+ }
572
+ }
573
+ break;
574
+
575
+ case 'w':
576
+ #if XSNAP_TEST_RECORD
577
+ fxTestRecord(mxTestRecordParam, nsbuf + 1, nslen - 1);
578
+ #endif
579
+ path = nsbuf + 1;
580
+ SnapshotStream stream;
581
+ if (path[0] == '@') {
582
+ int fd = atoi(path + 1);
583
+ int tmpfd = dup(fd);
584
+ if (tmpfd < 0) {
585
+ stream.file = NULL;
586
+ } else {
587
+ stream.file = fdopen(tmpfd, "ab");
588
+ }
589
+ } else {
590
+ stream.file = fopen(path, "wb");
591
+ }
592
+ stream.size = 0;
593
+ if (stream.file) {
594
+ snapshot.stream = &stream;
595
+ fxWriteSnapshot(machine, &snapshot);
596
+ snapshot.stream = NULL;
597
+ fclose(stream.file);
598
+ }
599
+ else
600
+ snapshot.error = errno;
601
+ if (snapshot.error) {
602
+ fprintf(stderr, "cannot write snapshot %s: %s\n",
603
+ path, strerror(snapshot.error));
604
+ c_exit(E_IO_ERROR);
605
+ }
606
+ if (snapshot.error == 0) {
607
+ // Allows us to format up to 999,999,999,999 bytes (1TiB - 1)
608
+ char fsize[13];
609
+ int fsizeLength = snprintf(fsize, sizeof(fsize), "%d", stream.size);
610
+ int writeError = fxWriteOkay(toParent, meterIndex, machine, fsize, fsizeLength);
611
+ if (writeError != 0) {
612
+ fprintf(stderr, "%s\n", fxWriteNetStringError(writeError));
613
+ c_exit(E_IO_ERROR);
614
+ }
615
+ } else {
616
+ // TODO: dynamically build error message including Exception message.
617
+ int writeError = fxWriteNetString(toParent, "!", "", 0);
618
+ if (writeError != 0) {
619
+ fprintf(stderr, "%s\n", fxWriteNetStringError(writeError));
620
+ c_exit(E_IO_ERROR);
621
+ }
622
+ }
623
+ break;
624
+ case 'q':
625
+ done = 1;
626
+ break;
627
+
628
+ // We reserve some prefix characters to avoid/detect/debug confusion,
629
+ // all of which are explicitly rejected, just like unknown commands. Do not
630
+ // reuse these for new commands.
631
+ case '/': // downstream response to upstream issueCommand()
632
+ case '.': // upstream good response to downstream execute/eval
633
+ case '!': // upstream error response to downstream execute/eval
634
+ default:
635
+ // note: the nsbuf we receive from fxReadNetString is null-terminated
636
+ fprintf(stderr, "Unexpected prefix '%c' in command '%s'\n", command, nsbuf);
637
+ c_exit(E_IO_ERROR);
638
+ break;
639
+ }
640
+ free(nsbuf);
641
+ #if mxInstrument
642
+ xsnapInstrumentValues[0] = (xsIntegerValue)meterIndex;
643
+ xsSampleInstrumentation(machine, xsnapInstrumentCount, xsnapInstrumentValues);
644
+ #endif
645
+ }
646
+ xsBeginHost(machine);
647
+ {
648
+ if (xsTypeOf(xsException) != xsUndefinedType) {
649
+ fprintf(stderr, "%s\n", xsToString(xsException));
650
+ error = E_UNHANDLED_EXCEPTION;
651
+ }
652
+ }
653
+ xsEndHost(machine);
654
+ }
655
+ xsEndMetering(machine);
656
+ if (machine->abortStatus) {
657
+ switch (machine->abortStatus) {
658
+ case xsNotEnoughMemoryExit:
659
+ error = E_NOT_ENOUGH_MEMORY;
660
+ break;
661
+ case xsStackOverflowExit:
662
+ error = E_STACK_OVERFLOW;
663
+ break;
664
+ case xsNoMoreKeysExit:
665
+ error = E_NO_MORE_KEYS;
666
+ break;
667
+ case xsTooMuchComputationExit:
668
+ error = E_TOO_MUCH_COMPUTATION;
669
+ break;
670
+ default:
671
+ error = E_UNKNOWN_ERROR;
672
+ break;
673
+ }
674
+ }
675
+ if (error != E_SUCCESS) {
676
+ c_exit(error);
677
+ }
678
+ xsDeleteMachine(machine);
679
+ fxTerminateSharedCluster();
680
+ return E_SUCCESS;
681
+ }
682
+
683
+ void xsBuildAgent(xsMachine* machine)
684
+ {
685
+ xsBeginHost(machine);
686
+ xsVars(1);
687
+
688
+ // xsResult = xsNewHostFunction(xs_clearTimer, 1);
689
+ // xsDefine(xsGlobal, xsID("clearImmediate"), xsResult, xsDontEnum);
690
+ xsResult = xsNewHostFunction(xs_setImmediate, 1);
691
+ xsDefine(xsGlobal, xsID("setImmediate"), xsResult, xsDontEnum);
692
+
693
+ // xsResult = xsNewHostFunction(xs_clearTimer, 1);
694
+ // xsDefine(xsGlobal, xsID("clearInterval"), xsResult, xsDontEnum);
695
+ // xsResult = xsNewHostFunction(xs_setInterval, 1);
696
+ // xsDefine(xsGlobal, xsID("setInterval"), xsResult, xsDontEnum);
697
+
698
+ // xsResult = xsNewHostFunction(xs_clearTimer, 1);
699
+ // xsDefine(xsGlobal, xsID("clearTimeout"), xsResult, xsDontEnum);
700
+ // xsResult = xsNewHostFunction(xs_setTimeout, 1);
701
+ // xsDefine(xsGlobal, xsID("setTimeout"), xsResult, xsDontEnum);
702
+
703
+ xsResult = xsNewHostFunction(xs_gc, 1);
704
+ xsDefine(xsGlobal, xsID("gc"), xsResult, xsDontEnum);
705
+ xsResult = xsNewHostFunction(xs_print, 1);
706
+ xsDefine(xsGlobal, xsID("print"), xsResult, xsDontEnum);
707
+
708
+ xsResult = xsNewHostFunction(xs_issueCommand, 1);
709
+ xsDefine(xsGlobal, xsID("issueCommand"), xsResult, xsDontEnum);
710
+
711
+ xsResult = xsNewObject();
712
+ xsVar(0) = xsNewHostFunction(xs_performance_now, 0);
713
+ xsDefine(xsResult, xsID("now"), xsVar(0), xsDontEnum);
714
+ xsDefine(xsGlobal, xsID("performance"), xsResult, xsDontEnum);
715
+
716
+ xsResult = xsNewHostFunction(xs_currentMeterLimit, 1);
717
+ xsDefine(xsGlobal, xsID("currentMeterLimit"), xsResult, xsDontEnum);
718
+ xsResult = xsNewHostFunction(xs_resetMeter, 1);
719
+ xsDefine(xsGlobal, xsID("resetMeter"), xsResult, xsDontEnum);
720
+
721
+ modInstallTextDecoder(the);
722
+ modInstallTextEncoder(the);
723
+ modInstallBase64(the);
724
+
725
+ xsResult = xsNewHostFunction(fx_harden, 1);
726
+ xsDefine(xsGlobal, xsID("harden"), xsResult, xsDontEnum);
727
+
728
+ // xsResult = xsNewObject();
729
+ // xsVar(0) = xsNewHostFunction(fx_print, 0);
730
+ // xsDefine(xsResult, xsID("log"), xsVar(0), xsDontEnum);
731
+ // xsDefine(xsGlobal, xsID("console"), xsResult, xsDontEnum);
732
+
733
+ xsEndHost(machine);
734
+ }
735
+
736
+ void xsPrintUsage()
737
+ {
738
+ printf("xsnap [-h] [-i <interval>] [-l <limit>] [-s <size>] [-m] [-r <snapshot>] [-s] [-v]\n");
739
+ printf("\t-h: print this help message\n");
740
+ printf("\t-i <interval>: metering interval (default to 1)\n");
741
+ printf("\t-l <limit>: metering limit (default to none)\n");
742
+ printf("\t-s <size>: parser buffer size, in kB (default to 8192)\n");
743
+ printf("\t-r <snapshot>: read snapshot to create the XS machine\n");
744
+ printf("\t-v: print XS version\n");
745
+ }
746
+
747
+ void xs_clearTimer(xsMachine* the)
748
+ {
749
+ xsClearTimer();
750
+ }
751
+
752
+ void xs_currentMeterLimit(xsMachine* the)
753
+ {
754
+ #if mxMetering
755
+ xsResult = xsInteger(gxCurrentMeter);
756
+ #endif
757
+ }
758
+
759
+ void xs_gc(xsMachine* the)
760
+ {
761
+ xsCollectGarbage();
762
+ }
763
+
764
+ void xs_performance_now(xsMachine *the)
765
+ {
766
+ c_timeval tv;
767
+ c_gettimeofday(&tv, NULL);
768
+ xsResult = xsNumber((double)(tv.tv_sec * 1000.0) + ((double)(tv.tv_usec) / 1000.0));
769
+ }
770
+
771
+ void xs_print(xsMachine* the)
772
+ {
773
+ xsIntegerValue c = xsToInteger(xsArgc), i;
774
+ #if mxMetering
775
+ if (gxMeteringPrint)
776
+ fprintf(stdout, "[%u] ", xsGetCurrentMeter(the));
777
+ #endif
778
+ for (i = 0; i < c; i++) {
779
+ if (i)
780
+ fprintf(stdout, " ");
781
+ fprintf(stdout, "%s", xsToString(xsArg(i)));
782
+ }
783
+ fprintf(stdout, "\n");
784
+ fflush(stdout);
785
+ }
786
+
787
+ void xs_resetMeter(xsMachine* the)
788
+ {
789
+ #if mxMetering
790
+ xsIntegerValue argc = xsToInteger(xsArgc);
791
+ if (argc < 2) {
792
+ xsTypeError("expected newMeterLimit, newMeterIndex");
793
+ }
794
+ xsResult = xsInteger(xsGetCurrentMeter(the));
795
+ gxCurrentMeter = xsToInteger(xsArg(0));
796
+ xsSetCurrentMeter(the, xsToInteger(xsArg(1)));
797
+ #endif
798
+ }
799
+
800
+ void xs_setImmediate(xsMachine* the)
801
+ {
802
+ xsSetTimer(0, 0);
803
+ }
804
+
805
+ void xs_setInterval(xsMachine* the)
806
+ {
807
+ xsSetTimer(xsToNumber(xsArg(1)), 1);
808
+ }
809
+
810
+ void xs_setTimeout(xsMachine* the)
811
+ {
812
+ xsSetTimer(xsToNumber(xsArg(1)), 0);
813
+ }
814
+
815
+
816
+ static int fxReadNetString(FILE *inStream, char** dest, size_t* len)
817
+ {
818
+ int code = 0;
819
+ char* buf = NULL;
820
+
821
+ if (fscanf(inStream, "%9lu", len) < 1) {
822
+ /* >999999999 bytes is bad */
823
+ code = 1;
824
+ } else if (fgetc(inStream) != ':') {
825
+ code = 2;
826
+ } else {
827
+ buf = malloc(*len + 1); /* malloc(0) is not portable */
828
+ if (!buf) {
829
+ code = 3;
830
+ } else if (fread(buf, 1, *len, inStream) < *len) {
831
+ code = 4;
832
+ } else if (fgetc(inStream) != ',') {
833
+ code = 5;
834
+ } else {
835
+ *(buf + *len) = 0;
836
+ }
837
+ if (code == 0) {
838
+ *dest = buf;
839
+ } else {
840
+ *dest = 0;
841
+ free(buf);
842
+ }
843
+ }
844
+ return code;
845
+ }
846
+
847
+ static char* fxReadNetStringError(int code)
848
+ {
849
+ switch (code) {
850
+ case 0: return "OK";
851
+ case 1: return "Cannot read netstring, reading length prefix, fscanf";
852
+ case 2: return "Cannot read netstring, invalid delimiter or end of file, fgetc";
853
+ case 3: return "Cannot read netstring, cannot allocate message buffer, malloc";
854
+ case 4: return "Cannot read netstring, cannot read message body, fread";
855
+ case 5: return "Cannot read netstring, cannot read trailer, fgetc";
856
+ default: return "Cannot read netstring";
857
+ }
858
+ }
859
+
860
+ static int fxWriteOkay(FILE* outStream, xsUnsignedValue meterIndex, xsMachine *the, char* buf, size_t length)
861
+ {
862
+ recordTimestamp(); // before sending delivery-result to parent
863
+ char *tsbuf = renderTimestamps();
864
+ if (!tsbuf) {
865
+ // rendering overrun error, send empty list
866
+ tsbuf = "[]";
867
+ }
868
+ char fmt[] = ("." // OK
869
+ "{"
870
+ "\"currentHeapCount\":%u,"
871
+ "\"compute\":%u,"
872
+ "\"allocate\":%u,"
873
+ "\"timestamps\":%s}"
874
+ "\1" // separate meter info from result
875
+ );
876
+ char numeral64[] = "12345678901234567890"; // big enough for 64bit numeral
877
+ char prefix[8 + sizeof fmt + 8 * sizeof numeral64 + sizeof timestampBuffer];
878
+ // Prepend the meter usage to the reply.
879
+ snprintf(prefix, sizeof(prefix), fmt,
880
+ fxGetCurrentHeapCount(the),
881
+ meterIndex, the->allocatedSpace, tsbuf);
882
+ return fxWriteNetString(outStream, prefix, buf, length);
883
+ }
884
+
885
+ static int fxWriteNetString(FILE* outStream, char* prefix, char* buf, size_t length)
886
+ {
887
+ if (fprintf(outStream, "%lu:%s", length + strlen(prefix), prefix) < 1) {
888
+ return 1;
889
+ } else if (fwrite(buf, 1, length, outStream) < length) {
890
+ return 2;
891
+ } else if (fputc(',', outStream) == EOF) {
892
+ return 3;
893
+ } else if (fflush(outStream) < 0) {
894
+ return 4;
895
+ }
896
+
897
+ return 0;
898
+ }
899
+
900
+ static char* fxWriteNetStringError(int code)
901
+ {
902
+ switch (code) {
903
+ case 0: return "OK";
904
+ case 1: return "Cannot write netstring, error writing length prefix";
905
+ case 2: return "Cannot write netstring, error writing message body";
906
+ case 3: return "Cannot write netstring, error writing terminator";
907
+ case 4: return "Cannot write netstring, error flushing stream, fflush";
908
+ default: return "Cannot write netstring";
909
+ }
910
+ }
911
+
912
+ static void xs_issueCommand(xsMachine *the)
913
+ {
914
+ int argc = xsToInteger(xsArgc);
915
+ if (argc < 1) {
916
+ xsTypeError("expected ArrayBuffer");
917
+ }
918
+
919
+ size_t length = xsGetArrayBufferLength(xsArg(0));
920
+ char* buf = xsToArrayBuffer(xsArg(0));
921
+
922
+ recordTimestamp(); // before sending command to parent
923
+
924
+ int writeError = fxWriteNetString(toParent, "?", buf, length);
925
+
926
+ if (writeError != 0) {
927
+ xsUnknownError(fxWriteNetStringError(writeError));
928
+ }
929
+
930
+ // read netstring
931
+ size_t len;
932
+ int readError = fxReadNetString(fromParent, &buf, &len);
933
+ if (readError != 0) {
934
+ xsUnknownError(fxReadNetStringError(readError));
935
+ }
936
+ recordTimestamp(); // after command-result received from parent
937
+
938
+ #if XSNAP_TEST_RECORD
939
+ fxTestRecord(mxTestRecordJSON | mxTestRecordReply, buf, len);
940
+ #endif
941
+ char command = *buf;
942
+ if (len == 0 || command != '/') {
943
+ xsUnknownError("Received unexpected command reply.");
944
+ }
945
+
946
+ xsResult = xsArrayBuffer(buf + 1, len - 1);
947
+ free(buf);
948
+ }
949
+
950
+ #if XSNAP_TEST_RECORD
951
+
952
+ static char directory[PATH_MAX];
953
+ void fxTestRecordArgs(int argc, char* argv[])
954
+ {
955
+ struct timeval tv;
956
+ struct tm* tm_info;
957
+ gettimeofday(&tv, NULL);
958
+ char path[PATH_MAX];
959
+ FILE* file;
960
+ mkdir("xsnap-tests", 0755);
961
+ tm_info = localtime(&tv.tv_sec);
962
+ strftime(path, sizeof(path), "%Y-%m-%d-%H-%M-%S", tm_info);
963
+ sprintf(directory, "xsnap-tests/%s-%3.3d", path, tv.tv_usec / 1000);
964
+ mkdir(directory, 0755);
965
+ sprintf(path, "%s/args.sh", directory);
966
+ file = fopen(path, "w");
967
+ if (file) {
968
+ int argi;
969
+ for (argi = 0; argi < argc; argi++)
970
+ fprintf(file, " %s", argv[argi]);
971
+ fprintf(file, "\n");
972
+ fclose(file);
973
+ }
974
+ }
975
+
976
+ void fxTestRecord(int flags, void* buffer, size_t length)
977
+ {
978
+ char path[PATH_MAX];
979
+ FILE* file;
980
+ if (flags & mxTestRecordParam) {
981
+ sprintf(path, "%s/param-%d", directory, gxTestRecordReplyIndex);
982
+ gxTestRecordReplyIndex++;
983
+ }
984
+ else {
985
+ sprintf(path, "%s/reply-%d", directory, gxTestRecordParamIndex);
986
+ gxTestRecordParamIndex++;
987
+ }
988
+ if (flags & mxTestRecordJS)
989
+ strcat(path, ".js");
990
+ else if (flags & mxTestRecordJSON)
991
+ strcat(path, ".json");
992
+ else
993
+ strcat(path, ".txt");
994
+ file = fopen(path, "wb");
995
+ if (file) {
996
+ fwrite(buffer, 1, length, file);
997
+ fclose(file);
998
+ }
999
+ }
1000
+
1001
+ #endif
1002
+
1003
+ // Local Variables:
1004
+ // tab-width: 4
1005
+ // c-basic-offset: 4
1006
+ // indent-tabs-mode: t
1007
+ // End:
1008
+ // vim: noet ts=4 sw=4