1/*
2 * Copyright (c) 1998, 2016, 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 = 11 + 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 = 11 + 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