root/heap.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. check_gc
  2. alloc_object
  3. CRB_literal_to_crb_string
  4. CRB_create_crowbar_string
  5. CRB_string_substr
  6. CRB_create_array
  7. CRB_array_resize
  8. CRB_array_add
  9. CRB_array_insert
  10. CRB_array_remove
  11. CRB_create_assoc
  12. CRB_add_assoc_member
  13. CRB_search_assoc_member
  14. crb_create_scope_chain
  15. CRB_create_native_pointer
  16. CRB_get_native_pointer_type
  17. CRB_check_native_pointer_type
  18. count_stack_trace_depth
  19. create_stack_trace_line
  20. print_stack_trace
  21. CRB_create_exception
  22. gc_mark
  23. gc_reset_mark
  24. gc_mark_value
  25. gc_mark_objects
  26. gc_dispose_object
  27. gc_sweep_objects
  28. crb_garbage_collect

#include <stdio.h>
#include <string.h>
#include "MEM.h"
#include "DBG.h"
#include "crowbar.h"

static void
check_gc(CRB_Interpreter *inter)
{
    crb_garbage_collect(inter);
#if 0
    if (inter->heap.current_heap_size > inter->heap.current_threshold) {
        /* fprintf(stderr, "garbage collecting..."); */
        crb_garbage_collect(inter);
        /* fprintf(stderr, "done.\n"); */

        inter->heap.current_threshold
            = inter->heap.current_heap_size + HEAP_THRESHOLD_SIZE;
    }
#endif
}

static CRB_Object *
alloc_object(CRB_Interpreter *inter, ObjectType type)
{
    CRB_Object *ret;

    check_gc(inter);
    ret = MEM_malloc(sizeof(CRB_Object));
    inter->heap.current_heap_size += sizeof(CRB_Object);
    ret->type = type;
    ret->marked = CRB_FALSE;
    ret->prev = NULL;
    ret->next = inter->heap.header;
    inter->heap.header = ret;
    if (ret->next) {
        ret->next->prev = ret;
    }

    return ret;
}

CRB_Object *
CRB_literal_to_crb_string(CRB_Interpreter *inter, CRB_Char *str)
{
    CRB_Object *ret;

    ret = alloc_object(inter, STRING_OBJECT);
    ret->u.string.string = str;
    ret->u.string.is_literal = CRB_TRUE;

    return ret;
}

CRB_Object *
CRB_create_crowbar_string(CRB_Interpreter *inter, CRB_Char *str)
{
    CRB_Object *ret;

    ret = alloc_object(inter, STRING_OBJECT);
    ret->u.string.string = str;
    inter->heap.current_heap_size += sizeof(CRB_Char) * (CRB_wcslen(str) + 1);
    ret->u.string.is_literal = CRB_FALSE;

    return ret;
}

CRB_Object *
CRB_string_substr(CRB_Interpreter *inter, CRB_LocalEnvironment *env,
                  CRB_Object *str, int from, int len, int line_number)
{
    int org_len = CRB_wcslen(str->u.string.string);
    CRB_Char *new_str;

    if (from < 0 || from >= org_len) {
        crb_runtime_error(inter, env, line_number,
                          STRING_POS_OUT_OF_BOUNDS_ERR,
                          CRB_INT_MESSAGE_ARGUMENT, "len", org_len,
                          CRB_INT_MESSAGE_ARGUMENT, "pos", from,
                          CRB_MESSAGE_ARGUMENT_END);
    }
    if (len < 0 || from + len > org_len) {
        crb_runtime_error(inter, env, line_number, STRING_SUBSTR_LEN_ERR,
                          CRB_INT_MESSAGE_ARGUMENT, "len", len,
                          CRB_MESSAGE_ARGUMENT_END);
    }
    new_str = MEM_malloc(sizeof(CRB_Char) * (len+1));
    CRB_wcsncpy(new_str, str->u.string.string + from, len);
    new_str[len] = L'\0';
    return CRB_create_crowbar_string(inter, new_str);
}

CRB_Object *
CRB_create_array(CRB_Interpreter *inter, int size)
{
    CRB_Object *ret;

    ret = alloc_object(inter, ARRAY_OBJECT);
    ret->u.array.size = size;
    ret->u.array.alloc_size = size;
    ret->u.array.array = MEM_malloc(sizeof(CRB_Value) * size);
    inter->heap.current_heap_size += sizeof(CRB_Value) * size;

    return ret;
}

void
CRB_array_resize(CRB_Interpreter *inter, CRB_Object *obj, int new_size)
{
    int new_alloc_size;
    CRB_Boolean need_realloc;
    int i;

    check_gc(inter);
    
    if (new_size > obj->u.array.alloc_size) {
        new_alloc_size = obj->u.array.alloc_size * 2;
        if (new_alloc_size < new_size) {
            new_alloc_size = new_size + ARRAY_ALLOC_SIZE;
        } else if (new_alloc_size - obj->u.array.alloc_size
                   > ARRAY_ALLOC_SIZE) {
            new_alloc_size = obj->u.array.alloc_size + ARRAY_ALLOC_SIZE;
        }
        need_realloc = CRB_TRUE;
    } else if (obj->u.array.alloc_size - new_size > ARRAY_ALLOC_SIZE) {
        new_alloc_size = new_size;
        need_realloc = CRB_TRUE;
    } else {
        need_realloc = CRB_FALSE;
    }
    if (need_realloc) {
        check_gc(inter);
        obj->u.array.array = MEM_realloc(obj->u.array.array,
                                         new_alloc_size * sizeof(CRB_Value));
        inter->heap.current_heap_size
            += (new_alloc_size - obj->u.array.alloc_size) * sizeof(CRB_Value);
        obj->u.array.alloc_size = new_alloc_size;
    }
    for (i = obj->u.array.size; i < new_size; i++) {
        obj->u.array.array[i].type = CRB_NULL_VALUE;
    }
    obj->u.array.size = new_size;
}

void
CRB_array_add(CRB_Interpreter *inter, CRB_Object *obj, CRB_Value *v)
{
    DBG_assert(obj->type == ARRAY_OBJECT, ("bad type..%d\n", obj->type));

    CRB_array_resize(inter, obj, obj->u.array.size + 1);
    obj->u.array.array[obj->u.array.size-1] = *v;
}

void
CRB_array_insert(CRB_Interpreter *inter, CRB_LocalEnvironment *env,
                 CRB_Object *obj, int pos,
                 CRB_Value *new_value, int line_number)
{
    int i;
    DBG_assert(obj->type == ARRAY_OBJECT, ("bad type..%d\n", obj->type));

    if (pos < 0 || pos > obj->u.array.size) {
        crb_runtime_error(inter, env, line_number,
                          ARRAY_INDEX_OUT_OF_BOUNDS_ERR,
                          CRB_INT_MESSAGE_ARGUMENT, "size", obj->u.array.size,
                          CRB_INT_MESSAGE_ARGUMENT, "index", pos,
                          CRB_MESSAGE_ARGUMENT_END);
    }
    CRB_array_resize(inter, obj, obj->u.array.size + 1);
    for (i = obj->u.array.size-2; i >= pos; i--) {
        obj->u.array.array[i+1] = obj->u.array.array[i];
    }
    obj->u.array.array[pos] = *new_value;
}

void
CRB_array_remove(CRB_Interpreter *inter, CRB_LocalEnvironment *env,
                 CRB_Object *obj, int pos, int line_number)
{
    int i;

    DBG_assert(obj->type == ARRAY_OBJECT, ("bad type..%d\n", obj->type));
    if (pos < 0 || pos > obj->u.array.size-1) {
        crb_runtime_error(inter, env, line_number,
                          ARRAY_INDEX_OUT_OF_BOUNDS_ERR,
                          CRB_INT_MESSAGE_ARGUMENT, "size", obj->u.array.size,
                          CRB_INT_MESSAGE_ARGUMENT, "index", pos,
                          CRB_MESSAGE_ARGUMENT_END);
    }
    for (i = pos+1; i < obj->u.array.size; i++) {
        obj->u.array.array[i-1] = obj->u.array.array[i];
    }
    CRB_array_resize(inter, obj, obj->u.array.size - 1);
}

CRB_Object *
CRB_create_assoc(CRB_Interpreter *inter)
{
    CRB_Object *ret;

    ret = alloc_object(inter, ASSOC_OBJECT);
    ret->u.assoc.member_count = 0;
    ret->u.assoc.member = NULL;

    return ret;
}

void
CRB_add_assoc_member(CRB_Interpreter *inter, CRB_Object *assoc,
                     char *name, CRB_Value *value)
{
    AssocMember *member_p;

    check_gc(inter);
    member_p = MEM_realloc(assoc->u.assoc.member,
                           sizeof(AssocMember)
                           * (assoc->u.assoc.member_count+1));
    member_p[assoc->u.assoc.member_count].name = name;
    member_p[assoc->u.assoc.member_count].value = *value;
    assoc->u.assoc.member = member_p;
    assoc->u.assoc.member_count++;
    inter->heap.current_heap_size += sizeof(AssocMember);
}

CRB_Value *
CRB_search_assoc_member(CRB_Object *assoc, char *member_name)
{
    CRB_Value *ret = NULL;
    int i;

    if (assoc->u.assoc.member_count == 0)
        return NULL;

    for (i = 0; i < assoc->u.assoc.member_count; i++) {
        if (!strcmp(assoc->u.assoc.member[i].name, member_name)) {
            ret = &assoc->u.assoc.member[i].value;
            break;
        }
    }

    return ret;
}

CRB_Object *
crb_create_scope_chain(CRB_Interpreter *inter)
{
    CRB_Object *ret;

    ret = alloc_object(inter, SCOPE_CHAIN_OBJECT);
    ret->u.scope_chain.frame = NULL;
    ret->u.scope_chain.next = NULL;

    return ret;
}

CRB_Object *
CRB_create_native_pointer(CRB_Interpreter *inter, void *pointer,
                          CRB_NativePointerInfo *info)
{
    CRB_Object *ret;

    ret = alloc_object(inter, NATIVE_POINTER_OBJECT);
    ret->u.native_pointer.pointer = pointer;
    ret->u.native_pointer.info = info;

    return ret;
}

CRB_NativePointerInfo *
CRB_get_native_pointer_type(CRB_Object *native_pointer)
{
    return native_pointer->u.native_pointer.info;
}


CRB_Boolean
CRB_check_native_pointer_type(CRB_Object *native_pointer,
                              CRB_NativePointerInfo *info)
{
    return native_pointer->u.native_pointer.info == info;
}

static int
count_stack_trace_depth(CRB_LocalEnvironment *top)
{
    CRB_LocalEnvironment *pos;
    int count = 0;

    for (pos = top; pos; pos = pos->next) {
        count++;
    }

    return count + 1;
}

static CRB_Object *
create_stack_trace_line(CRB_Interpreter *inter, CRB_LocalEnvironment *env,
                        char *func_name, int line_number)
{
    CRB_Char    *wc_func_name;
    CRB_Object  *new_line;
    CRB_Value   new_line_value;
    CRB_Value   value;
    int         stack_count = 0;

    new_line = CRB_create_assoc(inter);
    new_line_value.type = CRB_ASSOC_VALUE;
    new_line_value.u.object = new_line;
    CRB_push_value(inter, &new_line_value);
    stack_count++;

    wc_func_name = CRB_mbstowcs_alloc(inter, env, line_number, func_name);
    DBG_assert(wc_func_name != NULL, ("wc_func_name is null.\n"));

    value.type = CRB_STRING_VALUE;
    value.u.object = CRB_create_crowbar_string(inter, wc_func_name);
    CRB_push_value(inter, &value);
    stack_count++;

    CRB_add_assoc_member(inter, new_line, EXCEPTION_MEMBER_FUNCTION_NAME,
                         &value);

    value.type = CRB_INT_VALUE;
    value.u.int_value = line_number;
    CRB_add_assoc_member(inter, new_line, EXCEPTION_MEMBER_LINE_NUMBER,
                         &value);

    CRB_shrink_stack(inter, stack_count);

    return new_line;
}

static CRB_Value
print_stack_trace(CRB_Interpreter *inter, CRB_LocalEnvironment *env,
                  int arg_count, CRB_Value *args)
{
    CRB_Value *message;
    CRB_Value *stack_trace;
    CRB_Value ret;
    int i;

    message = CRB_search_local_variable(env, EXCEPTION_MEMBER_MESSAGE);
    if (message == NULL) {
        crb_runtime_error(inter, env, __LINE__,
                          EXCEPTION_HAS_NO_MESSAGE_ERR,
                          CRB_MESSAGE_ARGUMENT_END);
    }
    if (message->type != CRB_STRING_VALUE) {
        crb_runtime_error(inter, env, __LINE__,
                          EXCEPTION_MESSAGE_IS_NOT_STRING_ERR,
                          CRB_STRING_MESSAGE_ARGUMENT, "type",
                          CRB_get_type_name(message->type),
                          CRB_MESSAGE_ARGUMENT_END);
    }
    CRB_print_wcs_ln(stderr, message->u.object->u.string.string);

    stack_trace = CRB_search_local_variable(env, EXCEPTION_MEMBER_STACK_TRACE);
    if (stack_trace == NULL) {
        crb_runtime_error(inter, env, __LINE__,
                          EXCEPTION_HAS_NO_STACK_TRACE_ERR,
                          CRB_MESSAGE_ARGUMENT_END);
    }
    if (stack_trace->type != CRB_ARRAY_VALUE) {
        crb_runtime_error(inter, env, __LINE__,
                          STACK_TRACE_IS_NOT_ARRAY_ERR,
                          CRB_STRING_MESSAGE_ARGUMENT, "type",
                          CRB_get_type_name(stack_trace->type),
                          CRB_MESSAGE_ARGUMENT_END);
    }

    for (i = 0; i < stack_trace->u.object->u.array.size; i++) {
        CRB_Object *assoc;
        CRB_Value *line_number;
        CRB_Value *function_name;
        CRB_Char *str;

        if (stack_trace->u.object->u.array.array[i].type != CRB_ASSOC_VALUE) {
            crb_runtime_error(inter, env, __LINE__,
                              STACK_TRACE_LINE_IS_NOT_ASSOC_ERR,
                              CRB_MESSAGE_ARGUMENT_END);
        }
        assoc = stack_trace->u.object->u.array.array[i].u.object;

        line_number
            = CRB_search_assoc_member(assoc,
                                      EXCEPTION_MEMBER_LINE_NUMBER);
        if (line_number == NULL
            || line_number->type != CRB_INT_VALUE) {
            crb_runtime_error(inter, env, __LINE__,
                              STACK_TRACE_LINE_HAS_NO_LINE_NUMBER_ERR,
                              CRB_MESSAGE_ARGUMENT_END);
        }
        function_name
            = CRB_search_assoc_member(assoc,
                                      EXCEPTION_MEMBER_FUNCTION_NAME);
        if (function_name == NULL
            || function_name->type != CRB_STRING_VALUE) {
            crb_runtime_error(inter, env, __LINE__,
                              STACK_TRACE_LINE_HAS_NO_FUNC_NAME_ERR,
                              CRB_MESSAGE_ARGUMENT_END);
        }
        str = CRB_value_to_string(inter, NULL,
                                  line_number->u.int_value,
                                  function_name);
        CRB_print_wcs(stderr, str);
        MEM_free(str);
            
        fprintf(stderr, " at ");

        str = CRB_value_to_string(inter, NULL,
                                  line_number->u.int_value, line_number);
        CRB_print_wcs_ln(stderr, str);
        MEM_free(str);
    }
    
    ret.type = CRB_NULL_VALUE;
    return ret;
}

CRB_Object *
CRB_create_exception(CRB_Interpreter *inter, CRB_LocalEnvironment *env,
                     CRB_Object *message, int line_number)
{
    CRB_Object  *ret;
    CRB_Value   value;
    CRB_Object  *stack_trace; /* CRB_Array */
    int         stack_count = 0;
    int         stack_trace_depth;
    CRB_LocalEnvironment *env_pos;
    int         stack_trace_idx;
    CRB_Object  *line; /* CRB_Assoc */
    char        *func_name;
    int         next_line_number;
    static      CRB_FunctionDefinition print_stack_trace_fd;
    CRB_Value   scope_chain;

    ret = CRB_create_assoc(inter);
    value.type = CRB_ASSOC_VALUE;
    value.u.object = ret;
    CRB_push_value(inter, &value);
    stack_count++;

    value.type = CRB_STRING_VALUE;
    value.u.object = message;
    CRB_push_value(inter, &value);
    stack_count++;
    CRB_add_assoc_member(inter, ret, EXCEPTION_MEMBER_MESSAGE, &value);

    stack_trace_depth = count_stack_trace_depth(env);
    stack_trace = CRB_create_array(inter, stack_trace_depth);
    value.type = CRB_ARRAY_VALUE;
    value.u.object = stack_trace;
    CRB_push_value(inter, &value);
    stack_count++;

    CRB_add_assoc_member(inter, ret, EXCEPTION_MEMBER_STACK_TRACE, &value);

    next_line_number = line_number;
    for (env_pos = env, stack_trace_idx = 0; env_pos;
         env_pos = env_pos->next, stack_trace_idx++) {
        if (env_pos->current_function_name) {
            func_name = env_pos->current_function_name;
        } else {
            func_name = "anonymous closure";
        }
        line = create_stack_trace_line(inter, env, func_name,
                                       next_line_number);
        value.type = CRB_ASSOC_VALUE;
        value.u.object = line;
        CRB_array_set(inter, env, stack_trace, stack_trace_idx,
                      &value);
        next_line_number = env_pos->caller_line_number;
    }
    
    line = create_stack_trace_line(inter, env, "top_level",
                                   next_line_number);
    value.type = CRB_ASSOC_VALUE;
    value.u.object = line;
    CRB_array_set(inter, env, stack_trace, stack_trace_idx,
                  &value);

    CRB_set_function_definition(NULL, print_stack_trace,
                                &print_stack_trace_fd);

    CRB_push_value(inter, &value);
    stack_count++;

    value.type = CRB_CLOSURE_VALUE;
    value.u.closure.function = &print_stack_trace_fd;
    value.u.closure.environment = NULL; /* to stop marking by GC */

    scope_chain.type = CRB_SCOPE_CHAIN_VALUE;
    scope_chain.u.object = crb_create_scope_chain(inter);
    scope_chain.u.object->u.scope_chain.frame = ret;
    CRB_push_value(inter, &scope_chain);
    stack_count++;

    value.u.closure.environment = scope_chain.u.object;

    CRB_add_assoc_member(inter, ret, EXCEPTION_MEMBER_PRINT_STACK_TRACE,
                         &value);

    CRB_shrink_stack(inter, stack_count);

    return ret;
}

static void gc_mark_value(CRB_Value *v);

static void
gc_mark(CRB_Object *obj)
{
    if (obj == NULL)
        return;

    if (obj->marked)
        return;

    obj->marked = CRB_TRUE;

    if (obj->type == ARRAY_OBJECT) {
        int i;
        for (i = 0; i < obj->u.array.size; i++) {
            gc_mark_value(&obj->u.array.array[i]);
        }
    } else if (obj->type == ASSOC_OBJECT) {
        int i;
        for (i = 0; i < obj->u.assoc.member_count; i++) {
            gc_mark_value(&obj->u.assoc.member[i].value);
        }
    } else if (obj->type == SCOPE_CHAIN_OBJECT) {
        gc_mark(obj->u.scope_chain.frame);
        gc_mark(obj->u.scope_chain.next);
    }
}

static void
gc_reset_mark(CRB_Object *obj)
{
    obj->marked = CRB_FALSE;
}


static void
gc_mark_value(CRB_Value *v)
{
    if (crb_is_object_value(v->type)) {
        gc_mark(v->u.object);
    } else if (v->type == CRB_CLOSURE_VALUE) {
        if (v->u.closure.environment) {
            gc_mark(v->u.closure.environment);
        }
    } else if (v->type == CRB_FAKE_METHOD_VALUE) {
        gc_mark(v->u.fake_method.object);
    }
}

static void
gc_mark_objects(CRB_Interpreter *inter)
{
    CRB_Object *obj;
    Variable *v;
    CRB_LocalEnvironment *lv;
    int i;

    for (obj = inter->heap.header; obj; obj = obj->next) {
        gc_reset_mark(obj);
    }
    
    for (v = inter->variable; v; v = v->next) {
        gc_mark_value(&v->value);
    }
    
    for (lv = inter->top_environment; lv; lv = lv->next) {
        gc_mark(lv->variable);
    }

    for (i = 0; i < inter->stack.stack_pointer; i++) {
        gc_mark_value(&inter->stack.stack[i]);
    }

    gc_mark_value(&inter->current_exception);
}

static void
gc_dispose_object(CRB_Interpreter *inter, CRB_Object *obj)
{
    switch (obj->type) {
    case ARRAY_OBJECT:
        inter->heap.current_heap_size
            -= sizeof(CRB_Value) * obj->u.array.alloc_size;
        MEM_free(obj->u.array.array);
        break;
    case STRING_OBJECT:
        if (!obj->u.string.is_literal) {
            inter->heap.current_heap_size
                -= sizeof(CRB_Char) * (CRB_wcslen(obj->u.string.string) + 1);
            MEM_free(obj->u.string.string);
        }
        break;
    case ASSOC_OBJECT:
        inter->heap.current_heap_size
            -= sizeof(AssocMember) * obj->u.assoc.member_count;
        MEM_free(obj->u.assoc.member);
        break;
    case SCOPE_CHAIN_OBJECT:
        break;
    case NATIVE_POINTER_OBJECT:
        if (obj->u.native_pointer.info->finalizer) {
            obj->u.native_pointer.info->finalizer(inter, obj);
        }
        break;
    case OBJECT_TYPE_COUNT_PLUS_1:
    default:
        DBG_assert(0, ("bad type..%d\n", obj->type));
    }
    inter->heap.current_heap_size -= sizeof(CRB_Object);
    MEM_free(obj);
}

static void
gc_sweep_objects(CRB_Interpreter *inter)
{
    CRB_Object *obj;
    CRB_Object *tmp;

    for (obj = inter->heap.header; obj; ) {
        if (!obj->marked) {
            if (obj->prev) {
                obj->prev->next = obj->next;
            } else {
                inter->heap.header = obj->next;
            }
            if (obj->next) {
                obj->next->prev = obj->prev;
            }
            tmp = obj->next;
            gc_dispose_object(inter, obj);
            obj = tmp;
        } else {
            obj = obj->next;
        }
    }
}

void
crb_garbage_collect(CRB_Interpreter *inter)
{
    gc_mark_objects(inter);
    gc_sweep_objects(inter);
}

/* [<][>][^][v][top][bottom][index][help] */