1/*
2 * Copyright (c) 1998, 2008, 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 "stream.h"
28#include "inStream.h"
29#include "transport.h"
30#include "bag.h"
31#include "commonRef.h"
32#include "FrameID.h"
33
34#define INITIAL_REF_ALLOC 50
35#define SMALLEST(a, b) ((a) < (b)) ? (a) : (b)
36
37/*
38 * TO DO: Support processing of replies through command input streams.
39 */
40void
41inStream_init(PacketInputStream *stream, jdwpPacket packet)
42{
43    stream->packet = packet;
44    stream->error = JDWP_ERROR(NONE);
45    stream->left = packet.type.cmd.len;
46    stream->current = packet.type.cmd.data;
47    stream->refs = bagCreateBag(sizeof(jobject), INITIAL_REF_ALLOC);
48    if (stream->refs == NULL) {
49        stream->error = JDWP_ERROR(OUT_OF_MEMORY);
50    }
51}
52
53jint
54inStream_id(PacketInputStream *stream)
55{
56    return stream->packet.type.cmd.id;
57}
58
59jbyte
60inStream_command(PacketInputStream *stream)
61{
62    return stream->packet.type.cmd.cmd;
63}
64
65static jdwpError
66readBytes(PacketInputStream *stream, void *dest, int size)
67{
68    if (stream->error) {
69        return stream->error;
70    }
71
72    if (size > stream->left) {
73        stream->error = JDWP_ERROR(INTERNAL);
74        return stream->error;
75    }
76
77    if (dest) {
78        (void)memcpy(dest, stream->current, size);
79    }
80    stream->current += size;
81    stream->left -= size;
82
83    return stream->error;
84}
85
86jdwpError
87inStream_skipBytes(PacketInputStream *stream, jint size) {
88    return readBytes(stream, NULL, size);
89}
90
91jboolean
92inStream_readBoolean(PacketInputStream *stream)
93{
94    jbyte flag = 0;
95    (void)readBytes(stream, &flag, sizeof(flag));
96    if (stream->error) {
97        return 0;
98    } else {
99        return flag ? JNI_TRUE : JNI_FALSE;
100    }
101}
102
103jbyte
104inStream_readByte(PacketInputStream *stream)
105{
106    jbyte val = 0;
107    (void)readBytes(stream, &val, sizeof(val));
108    return val;
109}
110
111jbyte *
112inStream_readBytes(PacketInputStream *stream, int length, jbyte *buf)
113{
114    (void)readBytes(stream, buf, length);
115    return buf;
116}
117
118jchar
119inStream_readChar(PacketInputStream *stream)
120{
121    jchar val = 0;
122    (void)readBytes(stream, &val, sizeof(val));
123    return JAVA_TO_HOST_CHAR(val);
124}
125
126jshort
127inStream_readShort(PacketInputStream *stream)
128{
129    jshort val = 0;
130    (void)readBytes(stream, &val, sizeof(val));
131    return JAVA_TO_HOST_SHORT(val);
132}
133
134jint
135inStream_readInt(PacketInputStream *stream)
136{
137    jint val = 0;
138    (void)readBytes(stream, &val, sizeof(val));
139    return JAVA_TO_HOST_INT(val);
140}
141
142jlong
143inStream_readLong(PacketInputStream *stream)
144{
145    jlong val = 0;
146    (void)readBytes(stream, &val, sizeof(val));
147    return JAVA_TO_HOST_LONG(val);
148}
149
150jfloat
151inStream_readFloat(PacketInputStream *stream)
152{
153    jfloat val = 0;
154    (void)readBytes(stream, &val, sizeof(val));
155    return JAVA_TO_HOST_FLOAT(val);
156}
157
158jdouble
159inStream_readDouble(PacketInputStream *stream)
160{
161    jdouble val = 0;
162    (void)readBytes(stream, &val, sizeof(val));
163    return JAVA_TO_HOST_DOUBLE(val);
164}
165
166/*
167 * Read an object from the stream. The ID used in the wire protocol
168 * is converted to a reference which is returned. The reference is
169 * global and strong, but it should *not* be deleted by the caller
170 * since it is freed when this stream is destroyed.
171 */
172jobject
173inStream_readObjectRef(JNIEnv *env, PacketInputStream *stream)
174{
175    jobject ref;
176    jobject *refPtr;
177    jlong id = inStream_readLong(stream);
178    if (stream->error) {
179        return NULL;
180    }
181    if (id == NULL_OBJECT_ID) {
182        return NULL;
183    }
184
185    ref = commonRef_idToRef(env, id);
186    if (ref == NULL) {
187        stream->error = JDWP_ERROR(INVALID_OBJECT);
188        return NULL;
189    }
190
191    refPtr = bagAdd(stream->refs);
192    if (refPtr == NULL) {
193        commonRef_idToRef_delete(env, ref);
194        return NULL;
195    }
196
197    *refPtr = ref;
198    return ref;
199}
200
201/*
202 * Read a raw object id from the stream. This should be used rarely.
203 * Normally, inStream_readObjectRef is preferred since it takes care
204 * of reference conversion and tracking. Only code that needs to
205 * perform maintence of the commonRef hash table uses this function.
206 */
207jlong
208inStream_readObjectID(PacketInputStream *stream)
209{
210    return inStream_readLong(stream);
211}
212
213jclass
214inStream_readClassRef(JNIEnv *env, PacketInputStream *stream)
215{
216    jobject object = inStream_readObjectRef(env, stream);
217    if (object == NULL) {
218        /*
219         * Could be error or just the null reference. In either case,
220         * stop now.
221         */
222        return NULL;
223    }
224    if (!isClass(object)) {
225        stream->error = JDWP_ERROR(INVALID_CLASS);
226        return NULL;
227    }
228    return object;
229}
230
231jthread
232inStream_readThreadRef(JNIEnv *env, PacketInputStream *stream)
233{
234    jobject object = inStream_readObjectRef(env, stream);
235    if (object == NULL) {
236        /*
237         * Could be error or just the null reference. In either case,
238         * stop now.
239         */
240        return NULL;
241    }
242    if (!isThread(object)) {
243        stream->error = JDWP_ERROR(INVALID_THREAD);
244        return NULL;
245    }
246    return object;
247}
248
249jthreadGroup
250inStream_readThreadGroupRef(JNIEnv *env, PacketInputStream *stream)
251{
252    jobject object = inStream_readObjectRef(env, stream);
253    if (object == NULL) {
254        /*
255         * Could be error or just the null reference. In either case,
256         * stop now.
257         */
258        return NULL;
259    }
260    if (!isThreadGroup(object)) {
261        stream->error = JDWP_ERROR(INVALID_THREAD_GROUP);
262        return NULL;
263    }
264    return object;
265}
266
267jstring
268inStream_readStringRef(JNIEnv *env, PacketInputStream *stream)
269{
270    jobject object = inStream_readObjectRef(env, stream);
271    if (object == NULL) {
272        /*
273         * Could be error or just the null reference. In either case,
274         * stop now.
275         */
276        return NULL;
277    }
278    if (!isString(object)) {
279        stream->error = JDWP_ERROR(INVALID_STRING);
280        return NULL;
281    }
282    return object;
283}
284
285jclass
286inStream_readClassLoaderRef(JNIEnv *env, PacketInputStream *stream)
287{
288    jobject object = inStream_readObjectRef(env, stream);
289    if (object == NULL) {
290        /*
291         * Could be error or just the null reference. In either case,
292         * stop now.
293         */
294        return NULL;
295    }
296    if (!isClassLoader(object)) {
297        stream->error = JDWP_ERROR(INVALID_CLASS_LOADER);
298        return NULL;
299    }
300    return object;
301}
302
303jarray
304inStream_readArrayRef(JNIEnv *env, PacketInputStream *stream)
305{
306    jobject object = inStream_readObjectRef(env, stream);
307    if (object == NULL) {
308        /*
309         * Could be error or just the null reference. In either case,
310         * stop now.
311         */
312        return NULL;
313    }
314    if (!isArray(object)) {
315        stream->error = JDWP_ERROR(INVALID_ARRAY);
316        return NULL;
317    }
318    return object;
319}
320
321/*
322 * Next 3 functions read an Int and convert to a Pointer!?
323 * If sizeof(jxxxID) == 8 we must read these values as Longs.
324 */
325FrameID
326inStream_readFrameID(PacketInputStream *stream)
327{
328    if (sizeof(FrameID) == 8) {
329        /*LINTED*/
330        return (FrameID)inStream_readLong(stream);
331    } else {
332        /*LINTED*/
333        return (FrameID)inStream_readInt(stream);
334    }
335}
336
337jmethodID
338inStream_readMethodID(PacketInputStream *stream)
339{
340    if (sizeof(jmethodID) == 8) {
341        /*LINTED*/
342        return (jmethodID)(intptr_t)inStream_readLong(stream);
343    } else {
344        /*LINTED*/
345        return (jmethodID)(intptr_t)inStream_readInt(stream);
346    }
347}
348
349jfieldID
350inStream_readFieldID(PacketInputStream *stream)
351{
352    if (sizeof(jfieldID) == 8) {
353        /*LINTED*/
354        return (jfieldID)(intptr_t)inStream_readLong(stream);
355    } else {
356        /*LINTED*/
357        return (jfieldID)(intptr_t)inStream_readInt(stream);
358    }
359}
360
361jlocation
362inStream_readLocation(PacketInputStream *stream)
363{
364    return (jlocation)inStream_readLong(stream);
365}
366
367char *
368inStream_readString(PacketInputStream *stream)
369{
370    int length;
371    char *string;
372
373    length = inStream_readInt(stream);
374    string = jvmtiAllocate(length + 1);
375    if (string != NULL) {
376        int new_length;
377
378        (void)readBytes(stream, string, length);
379        string[length] = '\0';
380
381        /* This is Standard UTF-8, convert to Modified UTF-8 if necessary */
382        new_length = (gdata->npt->utf8sToUtf8mLength)
383                             (gdata->npt->utf, (jbyte*)string, length);
384        if ( new_length != length ) {
385            char *new_string;
386
387            new_string = jvmtiAllocate(new_length+1);
388            (gdata->npt->utf8sToUtf8m)
389                             (gdata->npt->utf, (jbyte*)string, length,
390                              (jbyte*)new_string, new_length);
391            jvmtiDeallocate(string);
392            return new_string;
393        }
394    }
395    return string;
396}
397
398jboolean
399inStream_endOfInput(PacketInputStream *stream)
400{
401    return (stream->left > 0);
402}
403
404jdwpError
405inStream_error(PacketInputStream *stream)
406{
407    return stream->error;
408}
409
410void
411inStream_clearError(PacketInputStream *stream) {
412    stream->error = JDWP_ERROR(NONE);
413}
414
415jvalue
416inStream_readValue(PacketInputStream *stream, jbyte *typeKeyPtr)
417{
418    jvalue value;
419    jbyte typeKey = inStream_readByte(stream);
420    if (stream->error) {
421        value.j = 0L;
422        return value;
423    }
424
425    if (isObjectTag(typeKey)) {
426        value.l = inStream_readObjectRef(getEnv(), stream);
427    } else {
428        switch (typeKey) {
429            case JDWP_TAG(BYTE):
430                value.b = inStream_readByte(stream);
431                break;
432
433            case JDWP_TAG(CHAR):
434                value.c = inStream_readChar(stream);
435                break;
436
437            case JDWP_TAG(FLOAT):
438                value.f = inStream_readFloat(stream);
439                break;
440
441            case JDWP_TAG(DOUBLE):
442                value.d = inStream_readDouble(stream);
443                break;
444
445            case JDWP_TAG(INT):
446                value.i = inStream_readInt(stream);
447                break;
448
449            case JDWP_TAG(LONG):
450                value.j = inStream_readLong(stream);
451                break;
452
453            case JDWP_TAG(SHORT):
454                value.s = inStream_readShort(stream);
455                break;
456
457            case JDWP_TAG(BOOLEAN):
458                value.z = inStream_readBoolean(stream);
459                break;
460            default:
461                stream->error = JDWP_ERROR(INVALID_TAG);
462                break;
463        }
464    }
465    if (typeKeyPtr) {
466        *typeKeyPtr = typeKey;
467    }
468    return value;
469}
470
471static jboolean
472deleteRef(void *elementPtr, void *arg)
473{
474    JNIEnv *env = arg;
475    jobject *refPtr = elementPtr;
476    commonRef_idToRef_delete(env, *refPtr);
477    return JNI_TRUE;
478}
479
480void
481inStream_destroy(PacketInputStream *stream)
482{
483    if (stream->packet.type.cmd.data != NULL) {
484    jvmtiDeallocate(stream->packet.type.cmd.data);
485    }
486
487    (void)bagEnumerateOver(stream->refs, deleteRef, (void *)getEnv());
488    bagDestroyBag(stream->refs);
489}
490