blob: fe4dd48906d02a9acf9c570d8a29e2ea248e6a5e [file] [log] [blame]
#include <assert.h>
#include "vm/system.h"
#include "vm/types.h"
#include "vm/method.h"
#include "vm/field.h"
#include "vm/die.h"
#include "vm/vm.h"
#include "jit/args.h"
/* See Table 4.2 in Section 4.3.2 ("Field Descriptors") of the JVM
specification. */
enum vm_type str_to_type(const char *type)
{
switch (type[0]) {
case 'V':
return J_VOID;
case 'B':
return J_BYTE;
case 'C':
return J_CHAR;
case 'D':
return J_DOUBLE;
case 'F':
return J_FLOAT;
case 'I':
return J_INT;
case 'J':
return J_LONG;
case 'S':
return J_SHORT;
case 'Z':
return J_BOOLEAN;
default:
break;
};
return J_REFERENCE; /* L<classname>; or [ */
}
enum vm_type get_method_return_type(char *type)
{
while (*type != ')')
type++;
return str_to_type(type + 1);
}
unsigned int vm_type_size(enum vm_type type)
{
static const int size[VM_TYPE_MAX] = {
[J_VOID] = 0,
[J_REFERENCE] = sizeof(void *),
[J_BYTE] = 1,
[J_SHORT] = 2,
[J_INT] = 4,
[J_LONG] = 8,
[J_CHAR] = 2,
[J_FLOAT] = 4,
[J_DOUBLE] = 8,
[J_BOOLEAN] = 1,
[J_RETURN_ADDRESS] = sizeof(void *),
};
return size[type];
}
int count_arguments(const struct vm_method *vmm)
{
struct vm_method_arg *arg;
int count;
count = 0;
list_for_each_entry(arg, &vmm->args, list_node) {
if (arg->type_info.vm_type == J_LONG ||
arg->type_info.vm_type == J_DOUBLE)
count += 2;
else
count++;
}
return count;
}
static enum vm_type bytecode_type_to_vmtype_map[] = {
[T_BOOLEAN] = J_BOOLEAN,
[T_CHAR] = J_CHAR,
[T_FLOAT] = J_FLOAT,
[T_DOUBLE] = J_DOUBLE,
[T_BYTE] = J_BYTE,
[T_SHORT] = J_SHORT,
[T_INT] = J_INT,
[T_LONG] = J_LONG,
};
enum vm_type bytecode_type_to_vmtype(int type)
{
/* Note: The cast below is okay, because we _know_ that type is non-
* negative at that point. */
assert(type >= 0);
assert((unsigned int) type < ARRAY_SIZE(bytecode_type_to_vmtype_map));
return bytecode_type_to_vmtype_map[type];
}
static int vmtype_to_bytecode_type_map[] = {
[J_BOOLEAN] = T_BOOLEAN,
[J_CHAR] = T_CHAR,
[J_FLOAT] = T_FLOAT,
[J_DOUBLE] = T_DOUBLE,
[J_BYTE] = T_BYTE,
[J_SHORT] = T_SHORT,
[J_INT] = T_INT,
[J_LONG] = T_LONG,
};
int vmtype_to_bytecode_type(enum vm_type type)
{
assert(type < ARRAY_SIZE(vmtype_to_bytecode_type_map));
return vmtype_to_bytecode_type_map[type];
}
static const char *vm_type_names[] = {
[J_VOID] = "J_VOID",
[J_REFERENCE] = "J_REFERENCE",
[J_BYTE] = "J_BYTE",
[J_SHORT] = "J_SHORT",
[J_INT] = "J_INT",
[J_LONG] = "J_LONG",
[J_CHAR] = "J_CHAR",
[J_FLOAT] = "J_FLOAT",
[J_DOUBLE] = "J_DOUBLE",
[J_BOOLEAN] = "J_BOOLEAN",
[J_RETURN_ADDRESS] = "J_RETURN_ADDRESS"
};
const char *get_vm_type_name(enum vm_type type)
{
if (type >= ARRAY_SIZE(vm_type_names))
return NULL;
return vm_type_names[type];
}
static int parse_class_name(char **type_str)
{
do {
if (**type_str == 0)
return -1;
(*type_str)++;
} while (**type_str != ';');
(*type_str)++;
return 0;
}
static int parse_array_element_type(char **type_str)
{
switch (**type_str) {
case '[':
(*type_str)++;
return parse_array_element_type(type_str);
case 'L':
(*type_str)++;
return parse_class_name(type_str);
case 'V':
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
(*type_str)++;
return 0;
default:
return -1;
}
}
int parse_type(char **type_str, struct vm_type_info *type_info)
{
char *name_start;
switch (**type_str) {
case '[':
name_start = (*type_str)++;
if (parse_array_element_type(type_str))
return -1;
type_info->vm_type = J_REFERENCE;
type_info->class_name = strndup(name_start,
(size_t) *type_str - (size_t) name_start);
return 0;
case 'L':
name_start = ++(*type_str);
if (parse_class_name(type_str))
return -1;
type_info->vm_type = J_REFERENCE;
type_info->class_name = strndup(name_start,
(size_t) *type_str - (size_t) name_start - 1);
return 0;
case 'V':
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
type_info->class_name = strndup(*type_str, 1);
type_info->vm_type = str_to_type(type_info->class_name);
(*type_str)++;
return 0;
default:
return -1;
}
}
static struct vm_method_arg *alloc_method_arg(void)
{
struct vm_method_arg *arg;
arg = malloc(sizeof(*arg));
if (!arg)
return NULL;
INIT_LIST_HEAD(&arg->list_node);
return arg;
}
int parse_method_type(struct vm_method *vmm)
{
char *type_str;
INIT_LIST_HEAD(&vmm->args);
type_str = vmm->type;
if (*type_str++ != '(')
return -1;
while (*type_str != ')') {
struct vm_method_arg *arg = alloc_method_arg();
if (!arg)
return -ENOMEM;
if (parse_type(&type_str, &arg->type_info))
return -1;
list_add_tail(&arg->list_node, &vmm->args);
}
type_str++;
/* parse return type */
if (parse_type(&type_str, &vmm->return_type))
return -1;
if (*type_str != 0)
return -1; /* junk after return type */
return 0;
}
int parse_field_type(struct vm_field *vmf)
{
char *type_str;
type_str = vmf->type;
return parse_type(&type_str, &vmf->type_info);
}
unsigned int vm_method_arg_slots(const struct vm_method *vmm)
{
struct vm_method_arg *arg;
unsigned int count;
count = 0;
list_for_each_entry(arg, &vmm->args, list_node) {
count++;
if (vm_type_is_pair(arg->type_info.vm_type))
count++;
}
return count;
}
unsigned int count_java_arguments(const struct vm_method *vmm)
{
struct vm_method_arg *arg;
unsigned int count;
count = 0;
list_for_each_entry(arg, &vmm->args, list_node) {
count++;
}
return count;
}