| /* |
| * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #include "util.h" |
| #include "stream.h" |
| #include "outStream.h" |
| #include "inStream.h" |
| #include "transport.h" |
| #include "commonRef.h" |
| #include "bag.h" |
| #include "FrameID.h" |
| |
| #define INITIAL_ID_ALLOC 50 |
| #define SMALLEST(a, b) ((a) < (b)) ? (a) : (b) |
| |
| static void |
| commonInit(PacketOutputStream *stream) |
| { |
| stream->current = &stream->initialSegment[0]; |
| stream->left = sizeof(stream->initialSegment); |
| stream->segment = &stream->firstSegment; |
| stream->segment->length = 0; |
| stream->segment->data = &stream->initialSegment[0]; |
| stream->segment->next = NULL; |
| stream->error = JDWP_ERROR(NONE); |
| stream->sent = JNI_FALSE; |
| stream->ids = bagCreateBag(sizeof(jlong), INITIAL_ID_ALLOC); |
| if (stream->ids == NULL) { |
| stream->error = JDWP_ERROR(OUT_OF_MEMORY); |
| } |
| } |
| |
| void |
| outStream_initCommand(PacketOutputStream *stream, jint id, |
| jbyte flags, jbyte commandSet, jbyte command) |
| { |
| commonInit(stream); |
| |
| /* |
| * Command-specific initialization |
| */ |
| stream->packet.type.cmd.id = id; |
| stream->packet.type.cmd.cmdSet = commandSet; |
| stream->packet.type.cmd.cmd = command; |
| |
| stream->packet.type.cmd.flags = flags; |
| } |
| |
| void |
| outStream_initReply(PacketOutputStream *stream, jint id) |
| { |
| commonInit(stream); |
| |
| /* |
| * Reply-specific initialization |
| */ |
| stream->packet.type.reply.id = id; |
| stream->packet.type.reply.errorCode = 0x0; |
| stream->packet.type.cmd.flags = (jbyte)JDWPTRANSPORT_FLAGS_REPLY; |
| } |
| |
| jint |
| outStream_id(PacketOutputStream *stream) |
| { |
| return stream->packet.type.cmd.id; |
| } |
| |
| jbyte |
| outStream_command(PacketOutputStream *stream) |
| { |
| /* Only makes sense for commands */ |
| JDI_ASSERT(!(stream->packet.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY)); |
| return stream->packet.type.cmd.cmd; |
| } |
| |
| static jdwpError |
| writeBytes(PacketOutputStream *stream, void *source, int size) |
| { |
| jbyte *bytes = (jbyte *)source; |
| |
| if (stream->error) { |
| return stream->error; |
| } |
| while (size > 0) { |
| jint count; |
| if (stream->left == 0) { |
| jint segSize = SMALLEST(2 * stream->segment->length, MAX_SEGMENT_SIZE); |
| jbyte *newSeg = jvmtiAllocate(segSize); |
| struct PacketData *newHeader = jvmtiAllocate(sizeof(*newHeader)); |
| if ((newSeg == NULL) || (newHeader == NULL)) { |
| jvmtiDeallocate(newSeg); |
| jvmtiDeallocate(newHeader); |
| stream->error = JDWP_ERROR(OUT_OF_MEMORY); |
| return stream->error; |
| } |
| newHeader->length = 0; |
| newHeader->data = newSeg; |
| newHeader->next = NULL; |
| stream->segment->next = newHeader; |
| stream->segment = newHeader; |
| stream->current = newHeader->data; |
| stream->left = segSize; |
| } |
| count = SMALLEST(size, stream->left); |
| (void)memcpy(stream->current, bytes, count); |
| stream->current += count; |
| stream->left -= count; |
| stream->segment->length += count; |
| size -= count; |
| bytes += count; |
| } |
| return JDWP_ERROR(NONE); |
| } |
| |
| jdwpError |
| outStream_writeBoolean(PacketOutputStream *stream, jboolean val) |
| { |
| jbyte byte = (val != 0) ? 1 : 0; |
| return writeBytes(stream, &byte, sizeof(byte)); |
| } |
| |
| jdwpError |
| outStream_writeByte(PacketOutputStream *stream, jbyte val) |
| { |
| return writeBytes(stream, &val, sizeof(val)); |
| } |
| |
| jdwpError |
| outStream_writeChar(PacketOutputStream *stream, jchar val) |
| { |
| val = HOST_TO_JAVA_CHAR(val); |
| return writeBytes(stream, &val, sizeof(val)); |
| } |
| |
| jdwpError |
| outStream_writeShort(PacketOutputStream *stream, jshort val) |
| { |
| val = HOST_TO_JAVA_SHORT(val); |
| return writeBytes(stream, &val, sizeof(val)); |
| } |
| |
| jdwpError |
| outStream_writeInt(PacketOutputStream *stream, jint val) |
| { |
| val = HOST_TO_JAVA_INT(val); |
| return writeBytes(stream, &val, sizeof(val)); |
| } |
| |
| jdwpError |
| outStream_writeLong(PacketOutputStream *stream, jlong val) |
| { |
| val = HOST_TO_JAVA_LONG(val); |
| return writeBytes(stream, &val, sizeof(val)); |
| } |
| |
| jdwpError |
| outStream_writeFloat(PacketOutputStream *stream, jfloat val) |
| { |
| val = HOST_TO_JAVA_FLOAT(val); |
| return writeBytes(stream, &val, sizeof(val)); |
| } |
| |
| jdwpError |
| outStream_writeDouble(PacketOutputStream *stream, jdouble val) |
| { |
| val = HOST_TO_JAVA_DOUBLE(val); |
| return writeBytes(stream, &val, sizeof(val)); |
| } |
| |
| jdwpError |
| outStream_writeObjectTag(JNIEnv *env, PacketOutputStream *stream, jobject val) |
| { |
| return outStream_writeByte(stream, specificTypeKey(env, val)); |
| } |
| |
| jdwpError |
| outStream_writeObjectRef(JNIEnv *env, PacketOutputStream *stream, jobject val) |
| { |
| jlong id; |
| jlong *idPtr; |
| |
| if (stream->error) { |
| return stream->error; |
| } |
| |
| if (val == NULL) { |
| id = NULL_OBJECT_ID; |
| } else { |
| /* Convert the object to an object id */ |
| id = commonRef_refToID(env, val); |
| if (id == NULL_OBJECT_ID) { |
| stream->error = JDWP_ERROR(OUT_OF_MEMORY); |
| return stream->error; |
| } |
| |
| /* Track the common ref in case we need to release it on a future error */ |
| idPtr = bagAdd(stream->ids); |
| if (idPtr == NULL) { |
| commonRef_release(env, id); |
| stream->error = JDWP_ERROR(OUT_OF_MEMORY); |
| return stream->error; |
| } else { |
| *idPtr = id; |
| } |
| |
| /* Add the encoded object id to the stream */ |
| id = HOST_TO_JAVA_LONG(id); |
| } |
| |
| return writeBytes(stream, &id, sizeof(id)); |
| } |
| |
| jdwpError |
| outStream_writeFrameID(PacketOutputStream *stream, FrameID val) |
| { |
| /* |
| * Not good - we're writing a pointer as a jint. Need |
| * to write as a jlong if sizeof(FrameID) == 8. |
| */ |
| if (sizeof(FrameID) == 8) { |
| /*LINTED*/ |
| return outStream_writeLong(stream, (jlong)val); |
| } else { |
| /*LINTED*/ |
| return outStream_writeInt(stream, (jint)val); |
| } |
| } |
| |
| jdwpError |
| outStream_writeMethodID(PacketOutputStream *stream, jmethodID val) |
| { |
| /* |
| * Not good - we're writing a pointer as a jint. Need |
| * to write as a jlong if sizeof(jmethodID) == 8. |
| */ |
| if (sizeof(jmethodID) == 8) { |
| /*LINTED*/ |
| return outStream_writeLong(stream, (jlong)(intptr_t)val); |
| } else { |
| /*LINTED*/ |
| return outStream_writeInt(stream, (jint)(intptr_t)val); |
| } |
| } |
| |
| jdwpError |
| outStream_writeFieldID(PacketOutputStream *stream, jfieldID val) |
| { |
| /* |
| * Not good - we're writing a pointer as a jint. Need |
| * to write as a jlong if sizeof(jfieldID) == 8. |
| */ |
| if (sizeof(jfieldID) == 8) { |
| /*LINTED*/ |
| return outStream_writeLong(stream, (jlong)(intptr_t)val); |
| } else { |
| /*LINTED*/ |
| return outStream_writeInt(stream, (jint)(intptr_t)val); |
| } |
| } |
| |
| jdwpError |
| outStream_writeLocation(PacketOutputStream *stream, jlocation val) |
| { |
| return outStream_writeLong(stream, (jlong)val); |
| } |
| |
| jdwpError |
| outStream_writeByteArray(PacketOutputStream*stream, jint length, |
| jbyte *bytes) |
| { |
| (void)outStream_writeInt(stream, length); |
| return writeBytes(stream, bytes, length); |
| } |
| |
| jdwpError |
| outStream_writeString(PacketOutputStream *stream, char *string) |
| { |
| jdwpError error; |
| jint length = string != NULL ? (int)strlen(string) : 0; |
| |
| /* Options utf8=y/n controls if we want Standard UTF-8 or Modified */ |
| if ( gdata->modifiedUtf8 ) { |
| (void)outStream_writeInt(stream, length); |
| error = writeBytes(stream, (jbyte *)string, length); |
| } else { |
| jint new_length; |
| |
| new_length = (gdata->npt->utf8mToUtf8sLength) |
| (gdata->npt->utf, (jbyte*)string, length); |
| if ( new_length == length ) { |
| (void)outStream_writeInt(stream, length); |
| error = writeBytes(stream, (jbyte *)string, length); |
| } else { |
| char *new_string; |
| |
| new_string = jvmtiAllocate(new_length+1); |
| (gdata->npt->utf8mToUtf8s) |
| (gdata->npt->utf, (jbyte*)string, length, |
| (jbyte*)new_string, new_length); |
| (void)outStream_writeInt(stream, new_length); |
| error = writeBytes(stream, (jbyte *)new_string, new_length); |
| jvmtiDeallocate(new_string); |
| } |
| } |
| return error; |
| } |
| |
| jdwpError |
| outStream_writeValue(JNIEnv *env, PacketOutputStream *out, |
| jbyte typeKey, jvalue value) |
| { |
| if (typeKey == JDWP_TAG(OBJECT)) { |
| (void)outStream_writeByte(out, specificTypeKey(env, value.l)); |
| } else { |
| (void)outStream_writeByte(out, typeKey); |
| } |
| if (isObjectTag(typeKey)) { |
| (void)outStream_writeObjectRef(env, out, value.l); |
| } else { |
| switch (typeKey) { |
| case JDWP_TAG(BYTE): |
| return outStream_writeByte(out, value.b); |
| |
| case JDWP_TAG(CHAR): |
| return outStream_writeChar(out, value.c); |
| |
| case JDWP_TAG(FLOAT): |
| return outStream_writeFloat(out, value.f); |
| |
| case JDWP_TAG(DOUBLE): |
| return outStream_writeDouble(out, value.d); |
| |
| case JDWP_TAG(INT): |
| return outStream_writeInt(out, value.i); |
| |
| case JDWP_TAG(LONG): |
| return outStream_writeLong(out, value.j); |
| |
| case JDWP_TAG(SHORT): |
| return outStream_writeShort(out, value.s); |
| |
| case JDWP_TAG(BOOLEAN): |
| return outStream_writeBoolean(out, value.z); |
| |
| case JDWP_TAG(VOID): /* happens with function return values */ |
| /* write nothing */ |
| return JDWP_ERROR(NONE); |
| |
| default: |
| EXIT_ERROR(AGENT_ERROR_INVALID_OBJECT,"Invalid type key"); |
| break; |
| } |
| } |
| return JDWP_ERROR(NONE); |
| } |
| |
| jdwpError |
| outStream_skipBytes(PacketOutputStream *stream, jint count) |
| { |
| int i; |
| for (i = 0; i < count; i++) { |
| (void)outStream_writeByte(stream, 0); |
| } |
| return stream->error; |
| } |
| |
| jdwpError |
| outStream_error(PacketOutputStream *stream) |
| { |
| return stream->error; |
| } |
| |
| void |
| outStream_setError(PacketOutputStream *stream, jdwpError error) |
| { |
| if (stream->error == JDWP_ERROR(NONE)) { |
| stream->error = error; |
| LOG_MISC(("outStream_setError error=%s(%d)", jdwpErrorText(error), error)); |
| } |
| } |
| |
| static jint |
| outStream_send(PacketOutputStream *stream) { |
| |
| jint rc; |
| jint len = 0; |
| PacketData *segment; |
| jbyte *data, *posP; |
| |
| /* |
| * If there's only 1 segment then we just send the |
| * packet. |
| */ |
| if (stream->firstSegment.next == NULL) { |
| stream->packet.type.cmd.len = 11 + stream->firstSegment.length; |
| stream->packet.type.cmd.data = stream->firstSegment.data; |
| rc = transport_sendPacket(&stream->packet); |
| return rc; |
| } |
| |
| /* |
| * Multiple segments |
| */ |
| len = 0; |
| segment = (PacketData *)&(stream->firstSegment); |
| do { |
| len += segment->length; |
| segment = segment->next; |
| } while (segment != NULL); |
| |
| data = jvmtiAllocate(len); |
| if (data == NULL) { |
| return JDWP_ERROR(OUT_OF_MEMORY); |
| } |
| |
| posP = data; |
| segment = (PacketData *)&(stream->firstSegment); |
| while (segment != NULL) { |
| (void)memcpy(posP, segment->data, segment->length); |
| posP += segment->length; |
| segment = segment->next; |
| } |
| |
| stream->packet.type.cmd.len = 11 + len; |
| stream->packet.type.cmd.data = data; |
| rc = transport_sendPacket(&stream->packet); |
| stream->packet.type.cmd.data = NULL; |
| jvmtiDeallocate(data); |
| |
| return rc; |
| } |
| |
| void |
| outStream_sendReply(PacketOutputStream *stream) |
| { |
| jint rc; |
| if (stream->error) { |
| /* |
| * Don't send any collected stream data on an error reply |
| */ |
| stream->packet.type.reply.len = 0; |
| stream->packet.type.reply.errorCode = (jshort)stream->error; |
| } |
| rc = outStream_send(stream); |
| if (rc == 0) { |
| stream->sent = JNI_TRUE; |
| } |
| } |
| |
| void |
| outStream_sendCommand(PacketOutputStream *stream) |
| { |
| jint rc; |
| if (!stream->error) { |
| rc = outStream_send(stream); |
| if (rc == 0) { |
| stream->sent = JNI_TRUE; |
| } |
| } |
| } |
| |
| |
| static jboolean |
| releaseID(void *elementPtr, void *arg) |
| { |
| jlong *idPtr = elementPtr; |
| commonRef_release(getEnv(), *idPtr); |
| return JNI_TRUE; |
| } |
| |
| void |
| outStream_destroy(PacketOutputStream *stream) |
| { |
| struct PacketData *next; |
| |
| if (stream->error || !stream->sent) { |
| (void)bagEnumerateOver(stream->ids, releaseID, NULL); |
| } |
| |
| next = stream->firstSegment.next; |
| while (next != NULL) { |
| struct PacketData *p = next; |
| next = p->next; |
| jvmtiDeallocate(p->data); |
| jvmtiDeallocate(p); |
| } |
| bagDestroyBag(stream->ids); |
| } |