blob: 7d5f5b4d05887896a27f5d9befd9bb64ccb76f17 [file] [log] [blame]
#include "vm/object.h"
#include "jit/exception.h"
#include "vm/classloader.h"
#include "vm/preload.h"
#include "vm/errors.h"
#include "vm/stdlib.h"
#include "vm/string.h"
#include "vm/class.h"
#include "vm/types.h"
#include "vm/call.h"
#include "vm/utf8.h"
#include "vm/die.h"
#include "vm/gc.h"
#include "vm/reference.h"
#include "lib/string.h"
#include <stdbool.h>
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
static pthread_mutexattr_t obj_mutexattr;
int init_vm_objects(void)
{
int err;
err = pthread_mutexattr_init(&obj_mutexattr);
if (err)
return -err;
err = pthread_mutexattr_settype(&obj_mutexattr, PTHREAD_MUTEX_RECURSIVE);
if (err)
return -err;
return 0;
}
void vm_object_finalizer(struct vm_object *obj)
{
vm_reference_collect_for_object(obj);
if (vm_object_is_instance_of(obj, vm_java_lang_ref_Reference))
vm_reference_collect(obj);
if (obj->class == vm_java_lang_VMThread)
vm_thread_collect_vmthread(obj);
}
static void vm_object_init_common(struct vm_object *object)
{
object->monitor_record = NULL;
}
struct vm_object *vm_object_alloc(struct vm_class *class)
{
struct vm_object *res;
if (vm_class_ensure_init(class))
return rethrow_exception();
res = gc_alloc(sizeof(*res) + class->object_size);
if (!res)
return throw_oom_error();
vm_object_init_common(res);
res->class = class;
return res;
}
struct vm_object *vm_object_alloc_array_raw(struct vm_class *class, size_t elem_size, int count)
{
struct vm_array *ret;
ret = gc_alloc(sizeof(*ret) + elem_size * count);
if (!ret)
return throw_oom_error();
vm_object_init_common(&ret->object);
ret->object.class = class;
ret->array_length = count;
return &ret->object;
}
struct vm_object *vm_object_alloc_primitive_array(int type, int count)
{
struct vm_array *res;
int vm_type;
vm_type = bytecode_type_to_vmtype(type);
assert(vm_type != J_VOID);
res = gc_alloc_noscan(sizeof(*res) + vmtype_get_size(vm_type) * count);
if (!res)
return throw_oom_error();
vm_object_init_common(&res->object);
switch (type) {
case T_BOOLEAN:
res->object.class = classloader_load(NULL, "[Z");
break;
case T_CHAR:
res->object.class = classloader_load(NULL, "[C");
break;
case T_FLOAT:
res->object.class = classloader_load(NULL, "[F");
break;
case T_DOUBLE:
res->object.class = classloader_load(NULL, "[D");
break;
case T_BYTE:
res->object.class = classloader_load(NULL, "[B");
break;
case T_SHORT:
res->object.class = classloader_load(NULL, "[S");
break;
case T_INT:
res->object.class = classloader_load(NULL, "[I");
break;
case T_LONG:
res->object.class = classloader_load(NULL, "[J");
break;
default:
return throw_internal_error();
}
if (!res->object.class)
return throw_internal_error();
if (vm_class_ensure_init(res->object.class))
return throw_internal_error();
res->array_length = count;
return &res->object;
}
static struct vm_object *
do_vm_object_alloc_multi_array(struct vm_class *class, int nr_dimensions, va_list ap)
{
struct vm_class *elem_class;
struct vm_array *res;
int elem_size;
int len;
assert(nr_dimensions > 0);
if (vm_class_ensure_init(class))
return rethrow_exception();
elem_class = vm_class_get_array_element_class(class);
elem_size = vmtype_get_size(vm_class_get_storage_vmtype(elem_class));
len = va_arg(ap, int);
if (len < 0) {
signal_new_exception(vm_java_lang_NegativeArraySizeException, NULL);
return NULL;
}
res = gc_alloc(sizeof(*res) + elem_size * len);
if (!res)
return throw_oom_error();
vm_object_init_common(&res->object);
res->array_length = len;
res->object.class = class;
if (nr_dimensions == 1)
return &res->object;
struct vm_object **elems = vm_array_elems(&res->object);
for (int i = 0; i < res->array_length; ++i) {
va_list dup_ap;
va_copy(dup_ap, ap);
elems[i] = do_vm_object_alloc_multi_array(elem_class, nr_dimensions - 1, dup_ap);
}
return &res->object;
}
struct vm_object *
vm_object_alloc_multi_array(struct vm_class *class, int nr_dimensions, ...)
{
va_list ap, dup_ap;
va_start(ap, nr_dimensions);
va_copy(dup_ap, ap);
va_end(ap);
return do_vm_object_alloc_multi_array(class, nr_dimensions, ap);
}
struct vm_object *vm_object_alloc_array(struct vm_class *class, int count)
{
struct vm_array *res;
if (vm_class_ensure_init(class))
return rethrow_exception();
res = gc_alloc(sizeof(*res) + sizeof(struct vm_object *) * count);
if (!res)
return throw_oom_error();
vm_object_init_common(&res->object);
res->array_length = count;
struct vm_object **elems = vm_array_elems(&res->object);
for (int i = 0; i < count; ++i)
elems[i] = NULL;
res->object.class = class;
return &res->object;
}
struct vm_object *
vm_object_alloc_array_of(struct vm_class *elem_class, int count)
{
struct vm_class *vmc;
vmc = vm_class_get_array_class(elem_class);
if (!vmc)
return throw_internal_error();
return vm_object_alloc_array(vmc, count);
}
static struct vm_object *clone_regular(struct vm_object *obj)
{
struct vm_class *vmc = obj->class;
struct vm_object *new = vm_object_alloc(vmc);
/* XXX: What do we do about exceptions? */
if (new)
memcpy(new + 1, obj + 1, vmc->object_size);
return new;
}
static struct vm_object *clone_array(struct vm_object *obj)
{
struct vm_class *vmc = obj->class;
struct vm_class *e_vmc = vmc->array_element_class;
int count = vm_array_length(obj);
if (e_vmc->kind == VM_CLASS_KIND_PRIMITIVE) {
enum vm_type vmtype;
int type;
struct vm_object *new;
vmtype = e_vmc->primitive_vm_type;
type = vmtype_to_bytecode_type(vmtype);
/* XXX: This could be optimized by not doing the memset() in
* object_alloc_primitive_array. */
new = vm_object_alloc_primitive_array(type, count);
if (new)
memcpy(vm_array_elems(new), vm_array_elems(obj), vmtype_get_size(vmtype) * count);
return new;
} else {
struct vm_object *new;
new = vm_object_alloc_array(vmc, count);
if (new)
memcpy(vm_array_elems(new), vm_array_elems(obj), sizeof(struct vm_object *) * count);
return new;
}
}
static struct vm_object *clone_primitive(struct vm_object *obj)
{
/* XXX: Is it even possible to create instances of primitive classes?
* I suspect it will be just the same as a normal object clone,
* though. Let's do that for now... */
NOT_IMPLEMENTED;
return clone_regular(obj);
}
struct vm_object *vm_object_clone(struct vm_object *obj)
{
assert(obj);
/* (In order of likelihood:) */
switch (obj->class->kind) {
case VM_CLASS_KIND_REGULAR:
return clone_regular(obj);
case VM_CLASS_KIND_ARRAY:
return clone_array(obj);
case VM_CLASS_KIND_PRIMITIVE:
return clone_primitive(obj);
default:
/* Shouldn't happen. */
assert(0);
break;
}
return NULL;
}
struct vm_object *
vm_object_alloc_string_from_utf8(const uint8_t bytes[], unsigned int length)
{
struct vm_object *array = utf8_to_char_array(bytes, length);
if (!array)
return rethrow_exception();
struct vm_object *string = vm_object_alloc(vm_java_lang_String);
if (!string)
return rethrow_exception();
field_set_int(string, vm_java_lang_String_offset, 0);
field_set_int(string, vm_java_lang_String_count, vm_array_length(array));
field_set_object(string, vm_java_lang_String_value, array);
return vm_string_intern(string);
}
struct vm_object *
vm_object_alloc_string_from_c(const char *bytes)
{
struct vm_object *string = vm_object_alloc(vm_java_lang_String);
if (!string)
return rethrow_exception();
unsigned int n = strlen(bytes);
struct vm_object *array = vm_object_alloc_primitive_array(T_CHAR, n);
if (!array)
return rethrow_exception();
#if 0
/* XXX: Need to handle code points >= 0x80 */
NOT_IMPLEMENTED;
#endif
for (unsigned int i = 0; i < n; ++i)
array_set_field_char(array, i, bytes[i]);
field_set_int(string, vm_java_lang_String_offset, 0);
field_set_int(string, vm_java_lang_String_count, vm_array_length(array));
field_set_object(string, vm_java_lang_String_value, array);
return vm_string_intern(string);
}
bool vm_object_is_instance_of(const struct vm_object *obj, struct vm_class *class)
{
if (!obj)
return false;
return vm_class_is_assignable_from(class, obj->class);
}
void vm_object_check_null(struct vm_object *obj)
{
NOT_IMPLEMENTED;
}
void vm_object_check_array(struct vm_object *obj, jsize index)
{
jsize array_len;
struct vm_class *cb;
char index_str[32];
cb = obj->class;
if (!vm_class_is_array_class(cb)) {
signal_new_exception(vm_java_lang_RuntimeException,
"object is not an array");
return;
}
array_len = vm_array_length(obj);
/*
* We return if index >= 0 and index < array_len. This can be
* expressed by only one comparison because we are sure that
* array_len is not negative. When index is less than 0 then
* its unsigned representation is greater than any positive
* jint value. Therefore we can skip the explicit check if index >= 0.
*/
if ((uint32_t)index < (uint32_t)array_len)
return;
sprintf(index_str, "%d > %d", index, array_len - 1);
signal_new_exception(vm_java_lang_ArrayIndexOutOfBoundsException,
index_str);
}
void array_store_check(struct vm_object *arrayref, struct vm_object *obj)
{
struct vm_class *class;
struct vm_class *element_class;
struct string *str;
int err;
if (obj == NULL)
return;
class = arrayref->class;
if (!vm_class_is_array_class(class)) {
signal_new_exception(vm_java_lang_RuntimeException,
"object is not an array");
return;
}
element_class = vm_class_get_array_element_class(class);
if (vm_class_is_assignable_from(element_class, obj->class))
return;
str = string_from_cstr(slash_to_dots(obj->class->name));
if (str == NULL) {
err = -ENOMEM;
goto error;
}
signal_new_exception(vm_java_lang_ArrayStoreException, str->value);
free_str(str);
return;
error:
if (str)
free_str(str);
if (err == -ENOMEM) /* TODO: throw OutOfMemoryError */
die("out of memory");
die("error %d", err);
}
void array_store_check_vmtype(struct vm_object *arrayref, enum vm_type vm_type)
{
/* TODO: Implement assignment compatibility checking described
in chapter "2.6.7 Assignment Conversion" of The Java VM
Specification - Second Edition. */
}
void vm_object_check_cast(struct vm_object *obj, struct vm_class *class)
{
struct string *str;
int err;
if (!obj || vm_object_is_instance_of(obj, class))
return;
if (exception_occurred())
return;
str = string_from_cstr(slash_to_dots(obj->class->name));
if (str == NULL) {
err = -ENOMEM;
goto error;
}
err = str_append(str, " cannot be cast to ");
if (err)
goto error;
err = str_append(str, slash_to_dots(class->name));
if (err)
goto error;
signal_new_exception(vm_java_lang_ClassCastException, str->value);
free_str(str);
return;
error:
if (str)
free_str(str);
if (err == -ENOMEM) /* TODO: throw OutOfMemoryError */
die("out of memory");
die("error %d", err);
}
void array_size_check(int size)
{
if (size >= 0)
return;
signal_new_exception(vm_java_lang_NegativeArraySizeException, NULL);
}
char *vm_string_to_cstr(const struct vm_object *string_obj)
{
struct vm_object *array_object;
struct string *str;
int32_t offset;
int32_t count;
char *result;
offset = field_get_int(string_obj, vm_java_lang_String_offset);
count = field_get_int(string_obj, vm_java_lang_String_count);
array_object = field_get_object(string_obj, vm_java_lang_String_value);
str = alloc_str();
if (!str)
return NULL;
result = NULL;
for (int32_t i = 0; i < count; ++i) {
int16_t ch;
int err;
ch = array_get_field_char(array_object, offset + i);
if (ch < 128)
err = str_append(str, "%c", ch);
else
err = str_append(str, "<%d>", ch);
if (err)
goto exit;
}
result = strdup(str->value);
exit:
free_str(str);
return result;
}
char *vm_string_classname_to_cstr(const struct vm_object *string_obj)
{
char *result;
result = vm_string_to_cstr(string_obj);
if (!result)
return NULL;
for (unsigned int i = 0, n = strlen(result); i < n; ++i) {
if (result[i] == '.')
result[i] = '/';
}
return result;
}