/* src/vm/array.c - Java array functions

   Copyright (C) 2007
   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO

   This file is part of CACAO.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   This program 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 for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301, USA.

*/


#include "config.h"

#include <stdint.h>

#include "native/llni.h"

#include "vm/array.h"
#include "vm/exceptions.h"
#include "vm/global.h"
#include "vm/primitive.h"
#include "vm/vm.h"


/* array_element_get ***********************************************************

   Returns a boxed element of the given Java array.

*******************************************************************************/

java_handle_t *array_element_get(java_handle_t *a, int32_t index)
{
	vftbl_t       *v;
	int            type;
	imm_union      value;
	java_handle_t *o;

	if (a == NULL) {
		exceptions_throw_nullpointerexception();
		return NULL;
	}

	v = LLNI_vftbl_direct(a);

	type = v->arraydesc->arraytype;

	value = array_element_primitive_get(a, index);

	o = primitive_box(type, value);

	return o;
}


/* array_element_set ***********************************************************

   Sets a boxed element in the given Java array.

*******************************************************************************/

void array_element_set(java_handle_t *a, int32_t index, java_handle_t *o)
{
	imm_union value;

	value = primitive_unbox(o);

	array_element_primitive_set(a, index, value);
}


/* array_element_primitive_get *************************************************

   Returns a primitive element of the given Java array.

*******************************************************************************/

imm_union array_element_primitive_get(java_handle_t *a, int32_t index)
{
	vftbl_t  *v;
	int       type;
	imm_union value;

	if (a == NULL) {
		exceptions_throw_nullpointerexception();
		value.a = NULL;
		return value;
	}

	v = LLNI_vftbl_direct(a);

	type = v->arraydesc->arraytype;

	switch (type) {
	case ARRAYTYPE_BOOLEAN:
		value.i = array_booleanarray_element_get((java_handle_booleanarray_t *) a, index);
		break;
	case ARRAYTYPE_BYTE:
		value.i = array_bytearray_element_get((java_handle_bytearray_t *) a,
											  index);
		break;
	case ARRAYTYPE_CHAR:
		value.i = array_chararray_element_get((java_handle_chararray_t *) a,
											  index);
		break;
	case ARRAYTYPE_SHORT:
		value.i = array_shortarray_element_get((java_handle_shortarray_t *) a,
											   index);
		break;
	case ARRAYTYPE_INT:
		value.i = array_intarray_element_get((java_handle_intarray_t *) a,
											 index);
		break;
	case ARRAYTYPE_LONG:
		value.l = array_longarray_element_get((java_handle_longarray_t *) a,
											  index);
		break;
	case ARRAYTYPE_FLOAT:
		value.f = array_floatarray_element_get((java_handle_floatarray_t *) a,
											   index);
		break;
	case ARRAYTYPE_DOUBLE:
		value.d = array_doublearray_element_get((java_handle_doublearray_t *) a,
												index);
		break;
	case ARRAYTYPE_OBJECT:
		value.a = array_objectarray_element_get((java_handle_objectarray_t *) a,
												index);
		break;
	default:
		vm_abort("array_element_primitive_get: invalid array element type %d",
				 type);
	}

	return value;
}


/* array_element_primitive_set *************************************************

   Sets a primitive element in the given Java array.

*******************************************************************************/

void array_element_primitive_set(java_handle_t *a, int32_t index, imm_union value)
{
	vftbl_t *v;
	int      type;

	if (a == NULL) {
		exceptions_throw_nullpointerexception();
		return;
	}

	v = LLNI_vftbl_direct(a);

	type = v->arraydesc->arraytype;

	switch (type) {
	case ARRAYTYPE_BOOLEAN:
		array_booleanarray_element_set((java_handle_booleanarray_t *) a,
									   index, value.i);
		break;
	case ARRAYTYPE_BYTE:
		array_bytearray_element_set((java_handle_bytearray_t *) a,
									index, value.i);
		break;
	case ARRAYTYPE_CHAR:
		array_chararray_element_set((java_handle_chararray_t *) a,
									index, value.i);
		break;
	case ARRAYTYPE_SHORT:
		array_shortarray_element_set((java_handle_shortarray_t *) a,
									 index, value.i);
		break;
	case ARRAYTYPE_INT:
		array_intarray_element_set((java_handle_intarray_t *) a,
								   index, value.i);
		break;
	case ARRAYTYPE_LONG:
		array_longarray_element_set((java_handle_longarray_t *) a,
									index, value.l);
		break;
	case ARRAYTYPE_FLOAT:
		array_floatarray_element_set((java_handle_floatarray_t *) a,
									 index, value.f);
		break;
	case ARRAYTYPE_DOUBLE:
		array_doublearray_element_set((java_handle_doublearray_t *) a,
									  index, value.d);
		break;
	case ARRAYTYPE_OBJECT:
		array_objectarray_element_set((java_handle_objectarray_t *) a,
									  index, value.a);
		break;
	default:
		vm_abort("array_element_primitive_set: invalid array element type %d",
				 type);
	}
}


/* array_xxxarray_element_get **************************************************

   Returns a primitive element of the given Java array.

*******************************************************************************/

#define ARRAY_TYPEARRAY_ELEMENT_GET(name, type)                                \
type array_##name##array_element_get(java_handle_##name##array_t *a, int32_t index) \
{                                                                              \
	type    value;                                                             \
	int32_t size;                                                              \
                                                                               \
	if (a == NULL) {                                                           \
		exceptions_throw_nullpointerexception();                               \
		return (type) 0;                                                       \
	}                                                                          \
                                                                               \
	size = LLNI_array_size(a);                                                 \
                                                                               \
	if ((index < 0) || (index >= size)) {                                      \
		exceptions_throw_arrayindexoutofboundsexception();                     \
		return (type) 0;                                                       \
	}                                                                          \
                                                                               \
	value = LLNI_array_direct(a, index);                                       \
                                                                               \
	return value;                                                              \
}

java_handle_t *array_objectarray_element_get(java_handle_objectarray_t *a, int32_t index)
{
	java_handle_t *value;
	int32_t size;

	if (a == NULL) {
		exceptions_throw_nullpointerexception();
		return NULL;
	}

	size = LLNI_array_size(a);

	if ((index < 0) || (index >= size)) {
		exceptions_throw_arrayindexoutofboundsexception();
		return NULL;
	}

	LLNI_CRITICAL_START;
	value = LLNI_WRAP(LLNI_array_direct(a, index));
	LLNI_CRITICAL_END;

	return value;
}

ARRAY_TYPEARRAY_ELEMENT_GET(boolean, uint8_t)
ARRAY_TYPEARRAY_ELEMENT_GET(byte,    int8_t)
ARRAY_TYPEARRAY_ELEMENT_GET(char,    uint16_t)
ARRAY_TYPEARRAY_ELEMENT_GET(short,   int16_t)
ARRAY_TYPEARRAY_ELEMENT_GET(int,     int32_t)
ARRAY_TYPEARRAY_ELEMENT_GET(long,    int64_t)
ARRAY_TYPEARRAY_ELEMENT_GET(float,   float)
ARRAY_TYPEARRAY_ELEMENT_GET(double,  double)


/* array_xxxarray_element_set **************************************************

   Sets a primitive element in the given Java array.

*******************************************************************************/

#define ARRAY_TYPEARRAY_ELEMENT_SET(name, type)                                \
void array_##name##array_element_set(java_handle_##name##array_t *a, int32_t index, type value) \
{                                                                              \
	int32_t size;                                                              \
                                                                               \
	if (a == NULL) {                                                           \
		exceptions_throw_nullpointerexception();                               \
		return;                                                                \
	}                                                                          \
                                                                               \
	size = LLNI_array_size(a);                                                 \
                                                                               \
	if ((index < 0) || (index >= size)) {                                      \
		exceptions_throw_arrayindexoutofboundsexception();                     \
		return;                                                                \
	}                                                                          \
                                                                               \
	LLNI_array_direct(a, index) = value;                                       \
}

void array_objectarray_element_set(java_handle_objectarray_t *a, int32_t index, java_handle_t *value)
{
	int32_t size;

	if (a == NULL) {
		exceptions_throw_nullpointerexception();
		return;
	}

	/* Sanity check. */

	assert(a->header.objheader.vftbl->arraydesc->arraytype == ARRAYTYPE_OBJECT);

	if (value != NULL) {
		if (builtin_canstore(a, value) == false) {
			exceptions_throw_illegalargumentexception();
			return;
		}
	}

	size = LLNI_array_size(a);

	if ((index < 0) || (index >= size)) {
		exceptions_throw_arrayindexoutofboundsexception();
		return;
	}

	LLNI_CRITICAL_START;
	LLNI_array_direct(a, index) = LLNI_UNWRAP(value);
	LLNI_CRITICAL_END;
}

ARRAY_TYPEARRAY_ELEMENT_SET(boolean, uint8_t)
ARRAY_TYPEARRAY_ELEMENT_SET(byte,    int8_t)
ARRAY_TYPEARRAY_ELEMENT_SET(char,    uint16_t)
ARRAY_TYPEARRAY_ELEMENT_SET(short,   int16_t)
ARRAY_TYPEARRAY_ELEMENT_SET(int,     int32_t)
ARRAY_TYPEARRAY_ELEMENT_SET(long,    int64_t)
ARRAY_TYPEARRAY_ELEMENT_SET(float,   float)
ARRAY_TYPEARRAY_ELEMENT_SET(double,  double)


/* array_length_get ***********************************************************

   Returns a the length of the given Java array.

   ARGUMENTS:
       a ... Java array

   RETURN VALUE:
         -1 ... exception thrown
	   >= 0 ... length of the Java array

*******************************************************************************/

int32_t array_length_get(java_handle_t *a)
{
	classinfo *c;
	int32_t    size;

	if (a == NULL) {
		exceptions_throw_nullpointerexception();
		return -1;
	}

	LLNI_class_get(a, c);

	if (!class_is_array(c)) {
/* 		exceptions_throw_illegalargumentexception("Argument is not an array"); */
		exceptions_throw_illegalargumentexception();
		return -1;
	}

	size = LLNI_array_size(a);

	return size;
}


/*
 * These are local overrides for various environment variables in Emacs.
 * Please do not remove this and leave it at the end of the file, where
 * Emacs will automagically detect them.
 * ---------------------------------------------------------------------
 * Local variables:
 * mode: c
 * indent-tabs-mode: t
 * c-basic-offset: 4
 * tab-width: 4
 * End:
 * vim:noexpandtab:sw=4:ts=4:
 */
