js4j 0.1.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.
@@ -0,0 +1,150 @@
1
+ 'use strict';
2
+
3
+ const { spawn } = require('child_process');
4
+ const net = require('net');
5
+ const { JavaGateway, GatewayParameters } = require('./gateway');
6
+
7
+ /**
8
+ * Launch a Java GatewayServer process and connect a JavaGateway to it.
9
+ *
10
+ * @param {object} options
11
+ * @param {string} options.classpath - Java classpath (e.g. '/path/to/py4j.jar:.')
12
+ * @param {string} options.mainClass - Fully-qualified main class (e.g. 'com.example.App')
13
+ * @param {string} [options.host='127.0.0.1'] - Gateway host
14
+ * @param {number} [options.port=25333] - Gateway port
15
+ * @param {string[]} [options.jvmArgs=[]] - Extra JVM flags (e.g. ['-Xmx512m'])
16
+ * @param {string[]} [options.args=[]] - Extra arguments passed to the main class
17
+ * @param {RegExp|string} [options.readyPattern=/GATEWAY_STARTED/] - Pattern to match in process stdout
18
+ * that signals the server is ready.
19
+ * Set to null to skip stdout check
20
+ * and rely only on port polling.
21
+ * @param {number} [options.timeout=30000] - Max ms to wait for the server to be ready
22
+ * @param {object} [options.gatewayOptions] - Extra options forwarded to GatewayParameters
23
+ *
24
+ * @returns {Promise<{ process: ChildProcess, gateway: JavaGateway, kill: Function }>}
25
+ *
26
+ * @example
27
+ * const { launchGateway } = require('js4j');
28
+ *
29
+ * const { gateway, kill } = await launchGateway({
30
+ * classpath: '/usr/share/py4j/py4j.jar:java/build',
31
+ * mainClass: 'com.example.MyApp',
32
+ * });
33
+ *
34
+ * const result = await gateway.entry_point.doSomething();
35
+ * console.log(result);
36
+ *
37
+ * await kill();
38
+ */
39
+ async function launchGateway(options = {}) {
40
+ const {
41
+ classpath,
42
+ mainClass,
43
+ host = '127.0.0.1',
44
+ port = 25333,
45
+ jvmArgs = [],
46
+ args = [],
47
+ readyPattern = /GATEWAY_STARTED/,
48
+ timeout = 30000,
49
+ gatewayOptions = {},
50
+ } = options;
51
+
52
+ if (!classpath) throw new Error('launchGateway: options.classpath is required');
53
+ if (!mainClass) throw new Error('launchGateway: options.mainClass is required');
54
+
55
+ // Build the java command: java [jvmArgs] -cp <classpath> <mainClass> [args]
56
+ const javaArgs = [...jvmArgs, '-cp', classpath, mainClass, ...args];
57
+
58
+ const child = spawn('java', javaArgs, {
59
+ stdio: ['pipe', 'pipe', 'pipe'],
60
+ });
61
+
62
+ // Forward stderr to process.stderr so errors are visible
63
+ child.stderr.on('data', (chunk) => process.stderr.write(chunk));
64
+
65
+ // Wait for the ready signal
66
+ await _waitForReady(child, host, port, readyPattern, timeout);
67
+
68
+ // Connect the gateway
69
+ const gateway = new JavaGateway(
70
+ new GatewayParameters({ host, port, ...gatewayOptions })
71
+ );
72
+ await gateway.connect();
73
+
74
+ async function kill() {
75
+ try { await gateway.shutdown(); } catch (_) {}
76
+ child.kill('SIGTERM');
77
+ }
78
+
79
+ return { process: child, gateway, kill };
80
+ }
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Internal helpers
84
+ // ---------------------------------------------------------------------------
85
+
86
+ function _waitForReady(child, host, port, readyPattern, timeout) {
87
+ return new Promise((resolve, reject) => {
88
+ const deadline = setTimeout(() => {
89
+ reject(new Error(`launchGateway: timed out after ${timeout}ms waiting for the Java gateway to start`));
90
+ }, timeout);
91
+
92
+ let stdoutBuf = '';
93
+ let patternMatched = !readyPattern; // skip if no pattern requested
94
+
95
+ child.stdout.setEncoding('utf8');
96
+ child.stdout.on('data', (chunk) => {
97
+ process.stdout.write(chunk);
98
+ if (patternMatched) return;
99
+ stdoutBuf += chunk;
100
+ const re = readyPattern instanceof RegExp ? readyPattern : new RegExp(readyPattern);
101
+ if (re.test(stdoutBuf)) {
102
+ patternMatched = true;
103
+ _pollPort(host, port, deadline).then(resolve, reject);
104
+ }
105
+ });
106
+
107
+ child.on('error', (err) => {
108
+ clearTimeout(deadline);
109
+ reject(new Error(`launchGateway: failed to spawn java process: ${err.message}`));
110
+ });
111
+
112
+ child.on('close', (code) => {
113
+ if (code !== 0) {
114
+ clearTimeout(deadline);
115
+ reject(new Error(`launchGateway: java process exited with code ${code}`));
116
+ }
117
+ });
118
+
119
+ // If no readyPattern, go straight to polling
120
+ if (patternMatched) {
121
+ _pollPort(host, port, deadline).then(resolve, reject);
122
+ }
123
+ });
124
+ }
125
+
126
+ function _pollPort(host, port, deadline) {
127
+ return new Promise((resolve, reject) => {
128
+ function attempt() {
129
+ const sock = new net.Socket();
130
+ sock.setTimeout(500);
131
+ sock.on('connect', () => {
132
+ sock.destroy();
133
+ clearTimeout(deadline);
134
+ resolve();
135
+ });
136
+ sock.on('error', () => {
137
+ sock.destroy();
138
+ setTimeout(attempt, 200);
139
+ });
140
+ sock.on('timeout', () => {
141
+ sock.destroy();
142
+ setTimeout(attempt, 200);
143
+ });
144
+ sock.connect(port, host);
145
+ }
146
+ attempt();
147
+ });
148
+ }
149
+
150
+ module.exports = { launchGateway };
@@ -0,0 +1,413 @@
1
+ 'use strict';
2
+
3
+ const { Js4JJavaError, Js4JError, Js4JNetworkError } = require('./exceptions');
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Command names (match py4j's protocol exactly)
7
+ // ---------------------------------------------------------------------------
8
+ const CALL_COMMAND_NAME = 'c\n';
9
+ const CONSTRUCTOR_COMMAND_NAME = 'i\n';
10
+ const FIELD_COMMAND_NAME = 'f\n';
11
+ const SHUTDOWN_GATEWAY_COMMAND_NAME = 's\n';
12
+ const LIST_COMMAND_NAME = 'l\n';
13
+ const ARRAY_COMMAND_NAME = 'a\n'; // array get/set/slice/len/create
14
+ const TABLE_COMMAND_NAME = 't\n';
15
+ const JVMVIEW_COMMAND_NAME = 'j\n';
16
+ const REFLECTION_COMMAND_NAME = 'r\n';
17
+ const MEMORY_COMMAND_NAME = 'm\n';
18
+ const HELP_COMMAND_NAME = 'h\n';
19
+ const DIR_COMMAND_NAME = 'd\n';
20
+ const BYTES_COMMAND_NAME = 'b\n';
21
+ const AUTH_COMMAND_NAME = 'A\n';
22
+ const STREAM_COMMAND_NAME = 'S\n';
23
+
24
+ // Sub-commands for FIELD_COMMAND
25
+ const FIELD_GET_SUB_COMMAND_NAME = 'g\n';
26
+ const FIELD_SET_SUB_COMMAND_NAME = 's\n';
27
+
28
+ // Sub-commands for JVMVIEW_COMMAND
29
+ const JVMVIEW_CLASS_SUB_COMMAND_NAME = 'c\n';
30
+ const JVMVIEW_IMPORT_SUB_COMMAND_NAME = 'i\n';
31
+ const JVMVIEW_SEARCH_SUB_COMMAND_NAME = 's\n';
32
+ const JVMVIEW_REMOVE_IMPORT_SUB_COMMAND_NAME = 'r\n';
33
+
34
+ // Sub-commands for ARRAY_COMMAND
35
+ const ARRAY_GET_SUB_COMMAND_NAME = 'g\n';
36
+ const ARRAY_SET_SUB_COMMAND_NAME = 's\n';
37
+ const ARRAY_SLICE_SUB_COMMAND_NAME = 'l\n';
38
+ const ARRAY_LEN_SUB_COMMAND_NAME = 'e\n';
39
+ const ARRAY_CREATE_SUB_COMMAND_NAME = 'c\n';
40
+
41
+ // Sub-commands for MEMORY_COMMAND
42
+ // MEMORY_DEL: Python-side GC notification — tells Java to release the object reference.
43
+ // MEMORY_ATTACH: mark an object as referenced again (used by FinalizerWorker).
44
+ const MEMORY_DEL_SUB_COMMAND_NAME = 'd\n';
45
+ const MEMORY_ATTACH_SUB_COMMAND_NAME = 'a\n';
46
+
47
+ // Sub-commands for LIST_COMMAND
48
+ const LIST_SORT_SUBCOMMAND_NAME = 's\n';
49
+ const LIST_REVERSE_SUBCOMMAND_NAME = 'r\n';
50
+ const LIST_SLICE_SUBCOMMAND_NAME = 'l\n';
51
+ const LIST_CONCAT_SUBCOMMAND_NAME = 'a\n';
52
+ const LIST_MULT_SUBCOMMAND_NAME = 'm\n';
53
+ const LIST_IMULT_SUBCOMMAND_NAME = 'i\n';
54
+ const LIST_COUNT_SUBCOMMAND_NAME = 'f\n';
55
+
56
+ // Sub-commands for REFLECTION_COMMAND
57
+ const REFL_GET_UNKNOWN_SUB_COMMAND_NAME = 'u\n';
58
+ const REFL_GET_MEMBER_SUB_COMMAND_NAME = 'm\n';
59
+ const REFL_GET_JAVA_LANG_STRING_SUB_COMMAND_NAME = 's\n';
60
+
61
+ // Sub-commands for DIR_COMMAND
62
+ const DIR_FIELDS_SUBCOMMAND_NAME = 'f\n';
63
+ const DIR_METHODS_SUBCOMMAND_NAME = 'm\n';
64
+ const DIR_STATIC_SUBCOMMAND_NAME = 's\n'; // static members of a class
65
+ const DIR_JVMVIEW_SUBCOMMAND_NAME = 'v\n'; // members visible in a JVMView
66
+
67
+ // Sub-commands for HELP_COMMAND
68
+ const HELP_OBJECT_SUBCOMMAND_NAME = 'o\n';
69
+ const HELP_CLASS_SUBCOMMAND_NAME = 'c\n';
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Type prefixes (verified against py4j Protocol.class bytecode)
73
+ // ---------------------------------------------------------------------------
74
+ const REFERENCE_TYPE = 'r'; // Java object reference
75
+ const DOUBLE_TYPE = 'd';
76
+ const LONG_TYPE = 'L'; // uppercase L (distinct from LIST_TYPE 'l')
77
+ const INTEGER_TYPE = 'i';
78
+ const BOOLEAN_TYPE = 'b';
79
+ const STRING_TYPE = 's';
80
+ const BYTES_TYPE = 'j';
81
+ const DECIMAL_TYPE = 'D';
82
+ const NULL_TYPE = 'n';
83
+ const VOID_TYPE = 'v';
84
+ const PYTHON_PROXY_TYPE = 'f'; // 'p' is PACKAGE_TYPE in the Java protocol
85
+ const LIST_TYPE = 'l'; // lowercase l
86
+ const SET_TYPE = 'h';
87
+ const ARRAY_TYPE = 't';
88
+ const MAP_TYPE = 'a';
89
+ const ITERATOR_TYPE = 'g';
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Response codes
93
+ // ---------------------------------------------------------------------------
94
+ const SUCCESS = 'y';
95
+ const ERROR = 'x';
96
+ const FATAL_ERROR = 'z';
97
+ const RETURN_MESSAGE = '!'; // Java always prefixes every response with this
98
+ const END = 'e';
99
+ const END_COMMAND_PART = '\n';
100
+
101
+ // Special object IDs
102
+ const ENTRY_POINT_OBJECT_ID = 't';
103
+ const STATIC_PREFIX = 'z:'; // prefix for static method/field calls (matches py4j's STATIC_PREFIX)
104
+ const DEFAULT_JVM_ID = 'rj'; // ID of the default JVMView on the Java side (py4j DEFAULT_JVM_ID)
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // Encoding helpers
108
+ // ---------------------------------------------------------------------------
109
+
110
+ /**
111
+ * Encode a JS value to a py4j protocol command part string (with trailing \n).
112
+ * @param {*} value
113
+ * @param {Map} [proxyPool] - JS proxy pool for callback objects
114
+ * @returns {string}
115
+ */
116
+ function encodeCommandPart(value, proxyPool) {
117
+ if (value === null || value === undefined) {
118
+ return NULL_TYPE + END_COMMAND_PART;
119
+ }
120
+
121
+ if (typeof value === 'boolean') {
122
+ return BOOLEAN_TYPE + (value ? 'true' : 'false') + END_COMMAND_PART;
123
+ }
124
+
125
+ if (typeof value === 'bigint') {
126
+ return LONG_TYPE + value.toString() + END_COMMAND_PART;
127
+ }
128
+
129
+ if (typeof value === 'number') {
130
+ if (Number.isInteger(value) && value >= -2147483648 && value <= 2147483647) {
131
+ return INTEGER_TYPE + value.toString() + END_COMMAND_PART;
132
+ }
133
+ // Check if it's a safe long (fits in 64-bit int)
134
+ if (Number.isInteger(value)) {
135
+ return LONG_TYPE + value.toString() + END_COMMAND_PART;
136
+ }
137
+ return DOUBLE_TYPE + value.toString() + END_COMMAND_PART;
138
+ }
139
+
140
+ if (typeof value === 'string') {
141
+ return STRING_TYPE + escapeNewLines(value) + END_COMMAND_PART;
142
+ }
143
+
144
+ if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
145
+ return BYTES_TYPE + Buffer.from(value).toString('base64') + END_COMMAND_PART;
146
+ }
147
+
148
+ // JavaObject / JavaClass with a _targetId (set by our wrappers)
149
+ if (value && typeof value === 'object' && value._targetId !== undefined) {
150
+ return REFERENCE_TYPE + value._targetId + END_COMMAND_PART;
151
+ }
152
+
153
+ // JS callback proxy (for Java interface implementations)
154
+ if (proxyPool && value && value._js4jProxy === true) {
155
+ const proxyId = proxyPool.register(value);
156
+ const interfaces = (value._interfaces || []).join(';');
157
+ return PYTHON_PROXY_TYPE + proxyId + ';' + interfaces + END_COMMAND_PART;
158
+ }
159
+
160
+ // Auto-convert JS arrays to ArrayList
161
+ if (Array.isArray(value)) {
162
+ // Encode as reference to a temporary list — caller must handle auto_convert
163
+ throw new Js4JError(
164
+ 'Cannot auto-convert JS Array to Java object without auto_convert=true'
165
+ );
166
+ }
167
+
168
+ throw new Js4JError(`Cannot encode value of type ${typeof value}: ${value}`);
169
+ }
170
+
171
+ /**
172
+ * Decode a py4j protocol response line to a JS value.
173
+ * @param {string} answer - Full response line (e.g. "yi42" or "ysHello")
174
+ * @param {object} gatewayClient - The GatewayClient instance for wrapping objects
175
+ * @returns {*}
176
+ */
177
+ function decodeReturnValue(answer, gatewayClient) {
178
+ if (!answer || answer.length === 0) {
179
+ throw new Js4JNetworkError('Received empty response from gateway');
180
+ }
181
+
182
+ // Java always prefixes every response with RETURN_MESSAGE ('!')
183
+ if (answer[0] === RETURN_MESSAGE) {
184
+ answer = answer.slice(1);
185
+ }
186
+
187
+ const responseCode = answer[0];
188
+
189
+ if (responseCode === FATAL_ERROR) {
190
+ throw new Js4JError('Fatal error from gateway: ' + answer.slice(1));
191
+ }
192
+
193
+ if (responseCode === ERROR) {
194
+ // Error payload is a typed value — usually REFERENCE_TYPE + objectId for
195
+ // a Java Throwable registered in the gateway (e.g. "ro0").
196
+ const errPayload = answer.slice(1);
197
+ let javaException = null;
198
+ try {
199
+ const typePrefix = errPayload[0];
200
+ const value = errPayload.slice(1);
201
+ javaException = decodeTypedValue(typePrefix, value, gatewayClient);
202
+ } catch (_) {}
203
+ throw new Js4JJavaError(
204
+ 'An error occurred while calling a Java method.',
205
+ errPayload,
206
+ javaException
207
+ );
208
+ }
209
+
210
+ if (responseCode !== SUCCESS) {
211
+ throw new Js4JNetworkError('Unexpected response code: ' + responseCode);
212
+ }
213
+
214
+ // SUCCESS path
215
+ if (answer.length < 2) {
216
+ return null; // void / empty success
217
+ }
218
+
219
+ const typePrefix = answer[1];
220
+ const value = answer.slice(2);
221
+
222
+ return decodeTypedValue(typePrefix, value, gatewayClient);
223
+ }
224
+
225
+ /**
226
+ * Decode a typed value (type prefix + raw string) to a JS value.
227
+ */
228
+ function decodeTypedValue(typePrefix, value, gatewayClient) {
229
+ switch (typePrefix) {
230
+ case VOID_TYPE:
231
+ return null;
232
+
233
+ case NULL_TYPE:
234
+ return null;
235
+
236
+ case BOOLEAN_TYPE:
237
+ return value.toLowerCase() === 'true';
238
+
239
+ case INTEGER_TYPE:
240
+ return parseInt(value, 10);
241
+
242
+ case LONG_TYPE:
243
+ // Return BigInt if it overflows safe integer range
244
+ try {
245
+ const n = parseInt(value, 10);
246
+ if (n > Number.MAX_SAFE_INTEGER || n < Number.MIN_SAFE_INTEGER) {
247
+ return BigInt(value);
248
+ }
249
+ return n;
250
+ } catch (e) {
251
+ return BigInt(value);
252
+ }
253
+
254
+ case DOUBLE_TYPE:
255
+ return parseFloat(value);
256
+
257
+ case DECIMAL_TYPE:
258
+ // Return as string to preserve precision (like Python Decimal)
259
+ return value;
260
+
261
+ case STRING_TYPE:
262
+ return unescapeNewLines(value);
263
+
264
+ case BYTES_TYPE:
265
+ return Buffer.from(value, 'base64');
266
+
267
+ case REFERENCE_TYPE:
268
+ return gatewayClient._wrapObject(value, REFERENCE_TYPE);
269
+
270
+ case LIST_TYPE:
271
+ return gatewayClient._wrapObject(value, LIST_TYPE);
272
+
273
+ case SET_TYPE:
274
+ return gatewayClient._wrapObject(value, SET_TYPE);
275
+
276
+ case MAP_TYPE:
277
+ return gatewayClient._wrapObject(value, MAP_TYPE);
278
+
279
+ case ARRAY_TYPE:
280
+ return gatewayClient._wrapObject(value, ARRAY_TYPE);
281
+
282
+ case ITERATOR_TYPE:
283
+ return gatewayClient._wrapObject(value, ITERATOR_TYPE);
284
+
285
+ case PYTHON_PROXY_TYPE:
286
+ // Java is returning a Python/JS proxy back to us — look it up
287
+ return gatewayClient._lookupProxy(value);
288
+
289
+ default:
290
+ throw new Js4JError(`Unknown type prefix '${typePrefix}' in value: ${value}`);
291
+ }
292
+ }
293
+
294
+ // ---------------------------------------------------------------------------
295
+ // String escaping (py4j uses \n as separator, so newlines in strings get escaped)
296
+ // ---------------------------------------------------------------------------
297
+
298
+ /**
299
+ * Escape newlines in a string value so it can be sent as a single command part.
300
+ * py4j uses \n as separator; literal newlines become \\n.
301
+ */
302
+ function escapeNewLines(s) {
303
+ return s.replace(/\\/g, '\\\\').replace(/\n/g, '\\n');
304
+ }
305
+
306
+ /**
307
+ * Unescape newlines in a received string value.
308
+ */
309
+ function unescapeNewLines(s) {
310
+ return s.replace(/\\n/g, '\n').replace(/\\\\/g, '\\');
311
+ }
312
+
313
+ module.exports = {
314
+ // Command names
315
+ CALL_COMMAND_NAME,
316
+ CONSTRUCTOR_COMMAND_NAME,
317
+ FIELD_COMMAND_NAME,
318
+ SHUTDOWN_GATEWAY_COMMAND_NAME,
319
+ LIST_COMMAND_NAME,
320
+ ARRAY_COMMAND_NAME,
321
+ TABLE_COMMAND_NAME,
322
+ JVMVIEW_COMMAND_NAME,
323
+ REFLECTION_COMMAND_NAME,
324
+ MEMORY_COMMAND_NAME,
325
+ HELP_COMMAND_NAME,
326
+ DIR_COMMAND_NAME,
327
+ BYTES_COMMAND_NAME,
328
+ AUTH_COMMAND_NAME,
329
+ STREAM_COMMAND_NAME,
330
+
331
+ // Field sub-commands
332
+ FIELD_GET_SUB_COMMAND_NAME,
333
+ FIELD_SET_SUB_COMMAND_NAME,
334
+
335
+ // JVMView sub-commands
336
+ JVMVIEW_CLASS_SUB_COMMAND_NAME,
337
+ JVMVIEW_IMPORT_SUB_COMMAND_NAME,
338
+ JVMVIEW_SEARCH_SUB_COMMAND_NAME,
339
+ JVMVIEW_REMOVE_IMPORT_SUB_COMMAND_NAME,
340
+
341
+ // Array sub-commands
342
+ ARRAY_GET_SUB_COMMAND_NAME,
343
+ ARRAY_SET_SUB_COMMAND_NAME,
344
+ ARRAY_SLICE_SUB_COMMAND_NAME,
345
+ ARRAY_LEN_SUB_COMMAND_NAME,
346
+ ARRAY_CREATE_SUB_COMMAND_NAME,
347
+
348
+ // Memory sub-commands
349
+ MEMORY_DEL_SUB_COMMAND_NAME,
350
+ MEMORY_ATTACH_SUB_COMMAND_NAME,
351
+
352
+ // List sub-commands
353
+ LIST_SORT_SUBCOMMAND_NAME,
354
+ LIST_REVERSE_SUBCOMMAND_NAME,
355
+ LIST_SLICE_SUBCOMMAND_NAME,
356
+ LIST_CONCAT_SUBCOMMAND_NAME,
357
+ LIST_MULT_SUBCOMMAND_NAME,
358
+ LIST_IMULT_SUBCOMMAND_NAME,
359
+ LIST_COUNT_SUBCOMMAND_NAME,
360
+
361
+ // Reflection sub-commands
362
+ REFL_GET_UNKNOWN_SUB_COMMAND_NAME,
363
+ REFL_GET_MEMBER_SUB_COMMAND_NAME,
364
+ REFL_GET_JAVA_LANG_STRING_SUB_COMMAND_NAME,
365
+
366
+ // Dir sub-commands
367
+ DIR_FIELDS_SUBCOMMAND_NAME,
368
+ DIR_METHODS_SUBCOMMAND_NAME,
369
+ DIR_STATIC_SUBCOMMAND_NAME,
370
+ DIR_JVMVIEW_SUBCOMMAND_NAME,
371
+
372
+ // Help sub-commands
373
+ HELP_OBJECT_SUBCOMMAND_NAME,
374
+ HELP_CLASS_SUBCOMMAND_NAME,
375
+
376
+ // Type prefixes
377
+ REFERENCE_TYPE,
378
+ DOUBLE_TYPE,
379
+ LONG_TYPE,
380
+ INTEGER_TYPE,
381
+ BOOLEAN_TYPE,
382
+ STRING_TYPE,
383
+ BYTES_TYPE,
384
+ DECIMAL_TYPE,
385
+ NULL_TYPE,
386
+ VOID_TYPE,
387
+ PYTHON_PROXY_TYPE,
388
+ LIST_TYPE,
389
+ SET_TYPE,
390
+ ARRAY_TYPE,
391
+ MAP_TYPE,
392
+ ITERATOR_TYPE,
393
+
394
+ // Response codes
395
+ SUCCESS,
396
+ ERROR,
397
+ FATAL_ERROR,
398
+ RETURN_MESSAGE,
399
+ END,
400
+ END_COMMAND_PART,
401
+
402
+ // Special IDs
403
+ ENTRY_POINT_OBJECT_ID,
404
+ STATIC_PREFIX,
405
+ DEFAULT_JVM_ID,
406
+
407
+ // Functions
408
+ encodeCommandPart,
409
+ decodeReturnValue,
410
+ decodeTypedValue,
411
+ escapeNewLines,
412
+ unescapeNewLines,
413
+ };