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