/*
* Copyright (c) 1998, 2017, 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 "utf_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_writeModuleRef(JNIEnv *env, PacketOutputStream *stream, jobject val)
{
return outStream_writeObjectRef(env, stream, 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 = utf8mToUtf8sLength((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);
utf8mToUtf8s((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 = JDWP_HEADER_SIZE + 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 = JDWP_HEADER_SIZE + 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);
}