1/*
2 * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include "util.h"
27#include "utf_util.h"
28#include "stream.h"
29#include "outStream.h"
30#include "inStream.h"
31#include "transport.h"
32#include "commonRef.h"
33#include "bag.h"
34#include "FrameID.h"
35
36#define INITIAL_ID_ALLOC 50
37#define SMALLEST(a, b) ((a) < (b)) ? (a) : (b)
38
39static void
40commonInit(PacketOutputStream *stream)
41{
42 stream->current = &stream->initialSegment[0];
43 stream->left = sizeof(stream->initialSegment);
44 stream->segment = &stream->firstSegment;
45 stream->segment->length = 0;
46 stream->segment->data = &stream->initialSegment[0];
47 stream->segment->next = NULL;
48 stream->error = JDWP_ERROR(NONE);
49 stream->sent = JNI_FALSE;
50 stream->ids = bagCreateBag(sizeof(jlong), INITIAL_ID_ALLOC);
51 if (stream->ids == NULL) {
52 stream->error = JDWP_ERROR(OUT_OF_MEMORY);
53 }
54}
55
56void
57outStream_initCommand(PacketOutputStream *stream, jint id,
58 jbyte flags, jbyte commandSet, jbyte command)
59{
60 commonInit(stream);
61
62 /*
63 * Command-specific initialization
64 */
65 stream->packet.type.cmd.id = id;
66 stream->packet.type.cmd.cmdSet = commandSet;
67 stream->packet.type.cmd.cmd = command;
68
69 stream->packet.type.cmd.flags = flags;
70}
71
72void
73outStream_initReply(PacketOutputStream *stream, jint id)
74{
75 commonInit(stream);
76
77 /*
78 * Reply-specific initialization
79 */
80 stream->packet.type.reply.id = id;
81 stream->packet.type.reply.errorCode = 0x0;
82 stream->packet.type.cmd.flags = (jbyte)JDWPTRANSPORT_FLAGS_REPLY;
83}
84
85jint
86outStream_id(PacketOutputStream *stream)
87{
88 return stream->packet.type.cmd.id;
89}
90
91jbyte
92outStream_command(PacketOutputStream *stream)
93{
94 /* Only makes sense for commands */
95 JDI_ASSERT(!(stream->packet.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY));
96 return stream->packet.type.cmd.cmd;
97}
98
99static jdwpError
100writeBytes(PacketOutputStream *stream, void *source, int size)
101{
102 jbyte *bytes = (jbyte *)source;
103
104 if (stream->error) {
105 return stream->error;
106 }
107 while (size > 0) {
108 jint count;
109 if (stream->left == 0) {
110 jint segSize = SMALLEST(2 * stream->segment->length, MAX_SEGMENT_SIZE);
111 jbyte *newSeg = jvmtiAllocate(segSize);
112 struct PacketData *newHeader = jvmtiAllocate(sizeof(*newHeader));
113 if ((newSeg == NULL) || (newHeader == NULL)) {
114 jvmtiDeallocate(newSeg);
115 jvmtiDeallocate(newHeader);
116 stream->error = JDWP_ERROR(OUT_OF_MEMORY);
117 return stream->error;
118 }
119 newHeader->length = 0;
120 newHeader->data = newSeg;
121 newHeader->next = NULL;
122 stream->segment->next = newHeader;
123 stream->segment = newHeader;
124 stream->current = newHeader->data;
125 stream->left = segSize;
126 }
127 count = SMALLEST(size, stream->left);
128 (void)memcpy(stream->current, bytes, count);
129 stream->current += count;
130 stream->left -= count;
131 stream->segment->length += count;
132 size -= count;
133 bytes += count;
134 }
135 return JDWP_ERROR(NONE);
136}
137
138jdwpError
139outStream_writeBoolean(PacketOutputStream *stream, jboolean val)
140{
141 jbyte byte = (val != 0) ? 1 : 0;
142 return writeBytes(stream, &byte, sizeof(byte));
143}
144
145jdwpError
146outStream_writeByte(PacketOutputStream *stream, jbyte val)
147{
148 return writeBytes(stream, &val, sizeof(val));
149}
150
151jdwpError
152outStream_writeChar(PacketOutputStream *stream, jchar val)
153{
154 val = HOST_TO_JAVA_CHAR(val);
155 return writeBytes(stream, &val, sizeof(val));
156}
157
158jdwpError
159outStream_writeShort(PacketOutputStream *stream, jshort val)
160{
161 val = HOST_TO_JAVA_SHORT(val);
162 return writeBytes(stream, &val, sizeof(val));
163}
164
165jdwpError
166outStream_writeInt(PacketOutputStream *stream, jint val)
167{
168 val = HOST_TO_JAVA_INT(val);
169 return writeBytes(stream, &val, sizeof(val));
170}
171
172jdwpError
173outStream_writeLong(PacketOutputStream *stream, jlong val)
174{
175 val = HOST_TO_JAVA_LONG(val);
176 return writeBytes(stream, &val, sizeof(val));
177}
178
179jdwpError
180outStream_writeFloat(PacketOutputStream *stream, jfloat val)
181{
182 val = HOST_TO_JAVA_FLOAT(val);
183 return writeBytes(stream, &val, sizeof(val));
184}
185
186jdwpError
187outStream_writeDouble(PacketOutputStream *stream, jdouble val)
188{
189 val = HOST_TO_JAVA_DOUBLE(val);
190 return writeBytes(stream, &val, sizeof(val));
191}
192
193jdwpError
194outStream_writeObjectTag(JNIEnv *env, PacketOutputStream *stream, jobject val)
195{
196 return outStream_writeByte(stream, specificTypeKey(env, val));
197}
198
199jdwpError
200outStream_writeModuleRef(JNIEnv *env, PacketOutputStream *stream, jobject val)
201{
202 return outStream_writeObjectRef(env, stream, val);
203}
204
205jdwpError
206outStream_writeObjectRef(JNIEnv *env, PacketOutputStream *stream, jobject val)
207{
208 jlong id;
209 jlong *idPtr;
210
211 if (stream->error) {
212 return stream->error;
213 }
214
215 if (val == NULL) {
216 id = NULL_OBJECT_ID;
217 } else {
218 /* Convert the object to an object id */
219 id = commonRef_refToID(env, val);
220 if (id == NULL_OBJECT_ID) {
221 stream->error = JDWP_ERROR(OUT_OF_MEMORY);
222 return stream->error;
223 }
224
225 /* Track the common ref in case we need to release it on a future error */
226 idPtr = bagAdd(stream->ids);
227 if (idPtr == NULL) {
228 commonRef_release(env, id);
229 stream->error = JDWP_ERROR(OUT_OF_MEMORY);
230 return stream->error;
231 } else {
232 *idPtr = id;
233 }
234
235 /* Add the encoded object id to the stream */
236 id = HOST_TO_JAVA_LONG(id);
237 }
238
239 return writeBytes(stream, &id, sizeof(id));
240}
241
242jdwpError
243outStream_writeFrameID(PacketOutputStream *stream, FrameID val)
244{
245 /*
246 * Not good - we're writing a pointer as a jint. Need
247 * to write as a jlong if sizeof(FrameID) == 8.
248 */
249 if (sizeof(FrameID) == 8) {
250 /*LINTED*/
251 return outStream_writeLong(stream, (jlong)val);
252 } else {
253 /*LINTED*/
254 return outStream_writeInt(stream, (jint)val);
255 }
256}
257
258jdwpError
259outStream_writeMethodID(PacketOutputStream *stream, jmethodID val)
260{
261 /*
262 * Not good - we're writing a pointer as a jint. Need
263 * to write as a jlong if sizeof(jmethodID) == 8.
264 */
265 if (sizeof(jmethodID) == 8) {
266 /*LINTED*/
267 return outStream_writeLong(stream, (jlong)(intptr_t)val);
268 } else {
269 /*LINTED*/
270 return outStream_writeInt(stream, (jint)(intptr_t)val);
271 }
272}
273
274jdwpError
275outStream_writeFieldID(PacketOutputStream *stream, jfieldID val)
276{
277 /*
278 * Not good - we're writing a pointer as a jint. Need
279 * to write as a jlong if sizeof(jfieldID) == 8.
280 */
281 if (sizeof(jfieldID) == 8) {
282 /*LINTED*/
283 return outStream_writeLong(stream, (jlong)(intptr_t)val);
284 } else {
285 /*LINTED*/
286 return outStream_writeInt(stream, (jint)(intptr_t)val);
287 }
288}
289
290jdwpError
291outStream_writeLocation(PacketOutputStream *stream, jlocation val)
292{
293 return outStream_writeLong(stream, (jlong)val);
294}
295
296jdwpError
297outStream_writeByteArray(PacketOutputStream*stream, jint length,
298 jbyte *bytes)
299{
300 (void)outStream_writeInt(stream, length);
301 return writeBytes(stream, bytes, length);
302}
303
304jdwpError
305outStream_writeString(PacketOutputStream *stream, char *string)
306{
307 jdwpError error;
308 jint length = string != NULL ? (int)strlen(string) : 0;
309
310 /* Options utf8=y/n controls if we want Standard UTF-8 or Modified */
311 if ( gdata->modifiedUtf8 ) {
312 (void)outStream_writeInt(stream, length);
313 error = writeBytes(stream, (jbyte *)string, length);
314 } else {
315 jint new_length;
316
317 new_length = utf8mToUtf8sLength((jbyte*)string, length);
318 if ( new_length == length ) {
319 (void)outStream_writeInt(stream, length);
320 error = writeBytes(stream, (jbyte *)string, length);
321 } else {
322 char *new_string;
323
324 new_string = jvmtiAllocate(new_length+1);
325 utf8mToUtf8s((jbyte*)string, length, (jbyte*)new_string, new_length);
326 (void)outStream_writeInt(stream, new_length);
327 error = writeBytes(stream, (jbyte *)new_string, new_length);
328 jvmtiDeallocate(new_string);
329 }
330 }
331 return error;
332}
333
334jdwpError
335outStream_writeValue(JNIEnv *env, PacketOutputStream *out,
336 jbyte typeKey, jvalue value)
337{
338 if (typeKey == JDWP_TAG(OBJECT)) {
339 (void)outStream_writeByte(out, specificTypeKey(env, value.l));
340 } else {
341 (void)outStream_writeByte(out, typeKey);
342 }
343 if (isObjectTag(typeKey)) {
344 (void)outStream_writeObjectRef(env, out, value.l);
345 } else {
346 switch (typeKey) {
347 case JDWP_TAG(BYTE):
348 return outStream_writeByte(out, value.b);
349
350 case JDWP_TAG(CHAR):
351 return outStream_writeChar(out, value.c);
352
353 case JDWP_TAG(FLOAT):
354 return outStream_writeFloat(out, value.f);
355
356 case JDWP_TAG(DOUBLE):
357 return outStream_writeDouble(out, value.d);
358
359 case JDWP_TAG(INT):
360 return outStream_writeInt(out, value.i);
361
362 case JDWP_TAG(LONG):
363 return outStream_writeLong(out, value.j);
364
365 case JDWP_TAG(SHORT):
366 return outStream_writeShort(out, value.s);
367
368 case JDWP_TAG(BOOLEAN):
369 return outStream_writeBoolean(out, value.z);
370
371 case JDWP_TAG(VOID): /* happens with function return values */
372 /* write nothing */
373 return JDWP_ERROR(NONE);
374
375 default:
376 EXIT_ERROR(AGENT_ERROR_INVALID_OBJECT,"Invalid type key");
377 break;
378 }
379 }
380 return JDWP_ERROR(NONE);
381}
382
383jdwpError
384outStream_skipBytes(PacketOutputStream *stream, jint count)
385{
386 int i;
387 for (i = 0; i < count; i++) {
388 (void)outStream_writeByte(stream, 0);
389 }
390 return stream->error;
391}
392
393jdwpError
394outStream_error(PacketOutputStream *stream)
395{
396 return stream->error;
397}
398
399void
400outStream_setError(PacketOutputStream *stream, jdwpError error)
401{
402 if (stream->error == JDWP_ERROR(NONE)) {
403 stream->error = error;
404 LOG_MISC(("outStream_setError error=%s(%d)", jdwpErrorText(error), error));
405 }
406}
407
408static jint
409outStream_send(PacketOutputStream *stream) {
410
411 jint rc;
412 jint len = 0;
413 PacketData *segment;
414 jbyte *data, *posP;
415
416 /*
417 * If there's only 1 segment then we just send the
418 * packet.
419 */
420 if (stream->firstSegment.next == NULL) {
421 stream->packet.type.cmd.len = JDWP_HEADER_SIZE + stream->firstSegment.length;
422 stream->packet.type.cmd.data = stream->firstSegment.data;
423 rc = transport_sendPacket(&stream->packet);
424 return rc;
425 }
426
427 /*
428 * Multiple segments
429 */
430 len = 0;
431 segment = (PacketData *)&(stream->firstSegment);
432 do {
433 len += segment->length;
434 segment = segment->next;
435 } while (segment != NULL);
436
437 data = jvmtiAllocate(len);
438 if (data == NULL) {
439 return JDWP_ERROR(OUT_OF_MEMORY);
440 }
441
442 posP = data;
443 segment = (PacketData *)&(stream->firstSegment);
444 while (segment != NULL) {
445 (void)memcpy(posP, segment->data, segment->length);
446 posP += segment->length;
447 segment = segment->next;
448 }
449
450 stream->packet.type.cmd.len = JDWP_HEADER_SIZE + len;
451 stream->packet.type.cmd.data = data;
452 rc = transport_sendPacket(&stream->packet);
453 stream->packet.type.cmd.data = NULL;
454 jvmtiDeallocate(data);
455
456 return rc;
457}
458
459void
460outStream_sendReply(PacketOutputStream *stream)
461{
462 jint rc;
463 if (stream->error) {
464 /*
465 * Don't send any collected stream data on an error reply
466 */
467 stream->packet.type.reply.len = 0;
468 stream->packet.type.reply.errorCode = (jshort)stream->error;
469 }
470 rc = outStream_send(stream);
471 if (rc == 0) {
472 stream->sent = JNI_TRUE;
473 }
474}
475
476void
477outStream_sendCommand(PacketOutputStream *stream)
478{
479 jint rc;
480 if (!stream->error) {
481 rc = outStream_send(stream);
482 if (rc == 0) {
483 stream->sent = JNI_TRUE;
484 }
485 }
486}
487
488
489static jboolean
490releaseID(void *elementPtr, void *arg)
491{
492 jlong *idPtr = elementPtr;
493 commonRef_release(getEnv(), *idPtr);
494 return JNI_TRUE;
495}
496
497void
498outStream_destroy(PacketOutputStream *stream)
499{
500 struct PacketData *next;
501
502 if (stream->error || !stream->sent) {
503 (void)bagEnumerateOver(stream->ids, releaseID, NULL);
504 }
505
506 next = stream->firstSegment.next;
507 while (next != NULL) {
508 struct PacketData *p = next;
509 next = p->next;
510 jvmtiDeallocate(p->data);
511 jvmtiDeallocate(p);
512 }
513 bagDestroyBag(stream->ids);
514}
515