// Copyright (c) 2023 Yuki Kimoto
// MIT License

#include <inttypes.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>

#include "spvm_native.h"
#include "spvm_api.h"
#include "spvm_vm.h"
#include "spvm_api_runtime.h"
#include "spvm_api_allocator.h"
#include "spvm_api_string_buffer.h"
#include "spvm_opcode.h"
#include "spvm_strerror.h"
#include "spvm_object.h"
#include "spvm_allocator.h"
#include "spvm_weaken_backref.h"
#include "spvm_hash.h"
#include "spvm_string_buffer.h"
#include "spvm_runtime.h"
#include "spvm_runtime_string.h"
#include "spvm_runtime_basic_type.h"
#include "spvm_runtime_class_var.h"
#include "spvm_runtime_field.h"
#include "spvm_runtime_method.h"
#include "spvm_runtime_arg.h"
#include "spvm_implement.h"
#include "spvm_api_string_buffer.h"
#include "spvm_api_compiler.h"
#include "spvm_api_runtime.h"
#include "spvm_api_class_file.h"
#include "spvm_api_basic_type.h"
#include "spvm_api_class_var.h"
#include "spvm_api_field.h"
#include "spvm_api_method.h"
#include "spvm_api_arg.h"
#include "spvm_api_type.h"
#include "spvm_api_internal.h"
#include "spvm_api_mutex.h"
#include "spvm_utf8.h"
#include "spvm_type.h"
#include "spvm_runtime_call_stack_frame_info.h"
#include "spvm_toke.h"

// OS specific
#include "spvm_builtin_expect.h"
#include "spvm_atomic.h"
#include "spvm_mutex.h"

static const char* FILE_NAME = "spvm_api.c";

SPVM_ENV* SPVM_NATIVE_new_env(void) {
  return SPVM_API_new_env();
}

SPVM_ENV* SPVM_API_new_env(void) {

  SPVM_API_ALLOCATOR* api_allocator = SPVM_API_ALLOCATOR_new_api();
  SPVM_API_STRING_BUFFER* api_string_buffer = SPVM_API_STRING_BUFFER_new_api();
  SPVM_API_COMPILER* api_compiler = SPVM_API_COMPILER_new_api();
  SPVM_API_RUNTIME* api_runtime = SPVM_API_RUNTIME_new_api();
  SPVM_API_CLASS_FILE* api_class_file = SPVM_API_CLASS_FILE_new_api();
  SPVM_API_BASIC_TYPE* api_basic_type = SPVM_API_BASIC_TYPE_new_api();
  SPVM_API_CLASS_VAR* api_class_var = SPVM_API_CLASS_VAR_new_api();
  SPVM_API_FIELD* api_field = SPVM_API_FIELD_new_api();
  SPVM_API_TYPE* api_type = SPVM_API_TYPE_new_api();
  SPVM_API_METHOD* api_method = SPVM_API_METHOD_new_api();
  SPVM_API_ARG* api_arg = SPVM_API_ARG_new_api();
  SPVM_API_INTERNAL* api_internal = SPVM_API_INTERNAL_new_api();
  SPVM_API_MUTEX* api_mutex = SPVM_API_MUTEX_new_api();
  
  void* env_api_init[]  = {
    api_allocator,
    api_string_buffer,
    api_compiler,
    api_class_file,
    api_runtime,
    api_basic_type,
    api_class_var,
    api_field,
    api_method,
    api_arg,
    api_type,
    api_internal,
    api_mutex,
  };
  SPVM_ENV_API* env_api = calloc(1, sizeof(env_api_init));
  memcpy(env_api, env_api_init, sizeof(env_api_init));
  
  
  
  
  
  
  
  
  
  
  // Native APIs
  void* env_init[]  = {
    NULL, // runtime
    env_api, // api
    SPVM_API_new_env,
    SPVM_API_free_env,
    SPVM_API_call_init_methods,
    SPVM_API_set_command_info_program_name,
    SPVM_API_set_command_info_argv,
    SPVM_API_set_command_info_basetime,
    SPVM_API_destroy_class_vars,
    SPVM_API_args_width,
    SPVM_API_get_object_basic_type,
    SPVM_API_get_object_basic_type_id,
    SPVM_API_get_object_basic_type_name,
    SPVM_API_get_object_type_dimension,
    SPVM_API_get_basic_type,
    SPVM_API_get_basic_type_by_name,
    SPVM_API_get_basic_type_by_id,
    SPVM_API_get_basic_type_id,
    SPVM_API_get_basic_type_id_by_name,
    SPVM_API_get_class_var,
    SPVM_API_get_class_var_byte,
    SPVM_API_get_class_var_short,
    SPVM_API_get_class_var_int,
    SPVM_API_get_class_var_long,
    SPVM_API_get_class_var_float,
    SPVM_API_get_class_var_double,
    SPVM_API_get_class_var_object,
    SPVM_API_get_class_var_string,
    SPVM_API_set_class_var_byte,
    SPVM_API_set_class_var_short,
    SPVM_API_set_class_var_int,
    SPVM_API_set_class_var_long,
    SPVM_API_set_class_var_float,
    SPVM_API_set_class_var_double,
    SPVM_API_set_class_var_object,
    SPVM_API_set_class_var_string,
    SPVM_API_get_class_var_object_ref,
    SPVM_API_get_class_var_byte_by_name,
    SPVM_API_get_class_var_short_by_name,
    SPVM_API_get_class_var_int_by_name,
    SPVM_API_get_class_var_long_by_name,
    SPVM_API_get_class_var_float_by_name,
    SPVM_API_get_class_var_double_by_name,
    SPVM_API_get_class_var_object_by_name,
    SPVM_API_get_class_var_string_by_name,
    SPVM_API_set_class_var_byte_by_name,
    SPVM_API_set_class_var_short_by_name,
    SPVM_API_set_class_var_int_by_name,
    SPVM_API_set_class_var_long_by_name,
    SPVM_API_set_class_var_float_by_name,
    SPVM_API_set_class_var_double_by_name,
    SPVM_API_set_class_var_object_by_name,
    SPVM_API_set_class_var_string_by_name,
    SPVM_API_get_field,
    SPVM_API_get_field_static,
    SPVM_API_get_field_byte,
    SPVM_API_get_field_short,
    SPVM_API_get_field_int,
    SPVM_API_get_field_long,
    SPVM_API_get_field_float,
    SPVM_API_get_field_double,
    SPVM_API_get_field_object,
    SPVM_API_get_field_string,
    SPVM_API_set_field_byte,
    SPVM_API_set_field_short,
    SPVM_API_set_field_int,
    SPVM_API_set_field_long,
    SPVM_API_set_field_float,
    SPVM_API_set_field_double,
    SPVM_API_set_field_object,
    SPVM_API_set_field_string,
    SPVM_API_get_field_byte_by_name,
    SPVM_API_get_field_short_by_name,
    SPVM_API_get_field_int_by_name,
    SPVM_API_get_field_long_by_name,
    SPVM_API_get_field_float_by_name,
    SPVM_API_get_field_double_by_name,
    SPVM_API_get_field_object_by_name,
    SPVM_API_get_field_string_by_name,
    SPVM_API_set_field_byte_by_name,
    SPVM_API_set_field_short_by_name,
    SPVM_API_set_field_int_by_name,
    SPVM_API_set_field_long_by_name,
    SPVM_API_set_field_float_by_name,
    SPVM_API_set_field_double_by_name,
    SPVM_API_set_field_object_by_name,
    SPVM_API_set_field_string_by_name,
    SPVM_API_get_field_string_chars_by_name,
    SPVM_API_get_method,
    SPVM_API_get_class_method,
    SPVM_API_get_instance_method_static,
    SPVM_API_get_instance_method,
    SPVM_API_call_method_no_mortal,
    SPVM_API_call_method,
    SPVM_API_call_class_method_by_name,
    SPVM_API_call_instance_method_static_by_name,
    SPVM_API_call_instance_method_by_name,
    SPVM_API_new_object_no_mortal,
    SPVM_API_new_object,
    SPVM_API_new_object_by_name,
    SPVM_API_new_pointer_object_no_mortal,
    SPVM_API_new_pointer_object,
    SPVM_API_new_pointer_object_by_name,
    SPVM_API_get_pointer,
    SPVM_API_set_pointer,
    SPVM_API_new_string_nolen_no_mortal,
    SPVM_API_new_string_nolen,
    SPVM_API_new_string_no_mortal,
    SPVM_API_new_string,
    SPVM_API_new_byte_array_no_mortal,
    SPVM_API_new_byte_array,
    SPVM_API_new_short_array_no_mortal,
    SPVM_API_new_short_array,
    SPVM_API_new_int_array_no_mortal,
    SPVM_API_new_int_array,
    SPVM_API_new_long_array_no_mortal,
    SPVM_API_new_long_array,
    SPVM_API_new_float_array_no_mortal,
    SPVM_API_new_float_array,
    SPVM_API_new_double_array_no_mortal,
    SPVM_API_new_double_array,
    SPVM_API_new_object_array_no_mortal,
    SPVM_API_new_object_array,
    SPVM_API_new_object_array_by_name,
    SPVM_API_new_string_array,
    SPVM_API_new_muldim_array_no_mortal,
    SPVM_API_new_muldim_array,
    SPVM_API_new_muldim_array_by_name,
    SPVM_API_new_mulnum_array_no_mortal,
    SPVM_API_new_mulnum_array,
    SPVM_API_new_mulnum_array_by_name,
    SPVM_API_new_array_proto_no_mortal,
    SPVM_API_new_array_proto,
    SPVM_API_length,
    SPVM_API_get_elems_byte,
    SPVM_API_get_elems_short,
    SPVM_API_get_elems_int,
    SPVM_API_get_elems_long,
    SPVM_API_get_elems_float,
    SPVM_API_get_elems_double,
    SPVM_API_get_elem_object,
    SPVM_API_get_elem_string,
    SPVM_API_set_elem_object,
    SPVM_API_set_elem_string,
    SPVM_API_get_chars,
    SPVM_API_get_bool_object_value,
    SPVM_API_concat_no_mortal,
    SPVM_API_concat,
    SPVM_API_shorten,
    SPVM_API_make_read_only,
    SPVM_API_is_read_only,
    SPVM_API_print,
    SPVM_API_print_stderr,
    SPVM_API_dump_no_mortal,
    SPVM_API_dump,
    SPVM_API_dumpc,
    SPVM_API_copy_no_mortal,
    SPVM_API_copy,
    SPVM_API_get_spvm_version_string,
    SPVM_API_get_spvm_version_number,
    SPVM_API_get_version_string,
    SPVM_API_get_version_number,
    SPVM_API_die,
    SPVM_API_get_exception,
    SPVM_API_set_exception,
    SPVM_API_new_stack_trace_no_mortal,
    SPVM_API_new_stack_trace,
    SPVM_API_is_string,
    SPVM_API_is_class,
    SPVM_API_is_pointer_class,
    SPVM_API_is_array,
    SPVM_API_is_object_array,
    SPVM_API_is_numeric_array,
    SPVM_API_is_mulnum_array,
    SPVM_API_isa,
    SPVM_API_isa_by_name,
    SPVM_API_is_type,
    SPVM_API_is_type_by_name,
    SPVM_API_elem_isa,
    SPVM_API_get_elem_size,
    SPVM_API_get_type_name_no_mortal,
    SPVM_API_get_type_name,
    SPVM_API_get_compile_type_name_no_mortal,
    SPVM_API_get_compile_type_name,
    SPVM_API_enter_scope,
    SPVM_API_leave_scope,
    SPVM_API_push_mortal,
    SPVM_API_weaken,
    SPVM_API_isweak,
    SPVM_API_unweaken,
    SPVM_API_strerror_string,
    SPVM_API_strerror_string_nolen,
    SPVM_API_strerror,
    SPVM_API_strerror_nolen,
    SPVM_API_is_binary_compatible_object,
    SPVM_API_is_binary_compatible_stack,
    SPVM_API_new_stack,
    SPVM_API_free_stack,
    SPVM_API_get_field_object_defined_and_has_pointer_by_name,
    SPVM_API_get_field_object_ref,
    SPVM_API_get_field_object_ref_by_name,
    SPVM_API_check_bootstrap_method,
    SPVM_API_assign_object,
    SPVM_API_new_string_array_no_mortal,
    SPVM_API_new_memory_block,
    SPVM_API_free_memory_block,
    SPVM_API_get_memory_blocks_count,
    SPVM_API_say,
    SPVM_API_warn,
    SPVM_API_spvm_stdin,
    SPVM_API_spvm_stdout,
    SPVM_API_spvm_stderr,
    SPVM_API_new_array_proto_element_no_mortal,
    SPVM_API_new_array_proto_element,
    SPVM_API_get_byte_object_value,
    SPVM_API_get_short_object_value,
    SPVM_API_get_int_object_value,
    SPVM_API_get_long_object_value,
    SPVM_API_get_float_object_value,
    SPVM_API_get_double_object_value,
    SPVM_API_no_free,
    SPVM_API_set_no_free,
    SPVM_API_get_stack_tmp_buffer,
    SPVM_API_print_exception_to_stderr,
    SPVM_API_dump_object_internal,
    SPVM_API_get_seed,
    SPVM_API_set_seed,
    SPVM_API_seed_initialized,
    SPVM_API_get_basic_type_name_in_version_from,
    SPVM_API_set_command_info_warning,
    SPVM_API_destroy_cache_class_vars,
    SPVM_API_new_stack_with_all_method_call_permitted,
    SPVM_API_call_instance_method_no_mortal,
    SPVM_API_call_instance_method,
    SPVM_API_call_method_no_mortal_no_check_args,
    SPVM_API_call_instance_method_no_mortal_less_check_args,
    SPVM_API_enable_options,
    SPVM_API_disable_options,
    SPVM_API_is_options,
    SPVM_API_is_any_object_array,
    SPVM_API_exists_field,
    SPVM_API_exists_field_by_name,
    SPVM_API_delete_field,
    SPVM_API_delete_field_by_name,
    SPVM_API_make_fixed_length,
    SPVM_API_is_fixed_length,
    SPVM_API_set_length,
    SPVM_API_capacity,
    SPVM_API_set_capacity,
    SPVM_API_numeric_object_to_byte,
    SPVM_API_numeric_object_to_short,
    SPVM_API_numeric_object_to_int,
    SPVM_API_numeric_object_to_long,
    SPVM_API_numeric_object_to_float,
    SPVM_API_numeric_object_to_double,
    SPVM_API_numeric_object_to_string_no_mortal,
    SPVM_API_numeric_object_to_string,
    SPVM_API_is_numeric_object,
    SPVM_API_say_stderr,
    SPVM_API_get_call_depth,
    SPVM_API_get_caller_info_stack,
    SPVM_API_get_caller_info_stack_record_size,
    SPVM_API_get_current_method,
    SPVM_API_caller_no_mortal,
    SPVM_API_caller,
    SPVM_API_die_with_string,
    SPVM_API_build_exception_message_no_mortal,
    SPVM_API_build_exception_message,
    SPVM_API_call_end_methods,
    SPVM_API_is_utf8,
    SPVM_API_set_byte_object_value,
    SPVM_API_set_short_object_value,
    SPVM_API_set_int_object_value,
    SPVM_API_set_long_object_value,
    SPVM_API_set_float_object_value,
    SPVM_API_set_double_object_value,
  };
  
  SPVM_ENV* env = calloc(1, sizeof(env_init));
  if (env == NULL) {
    return NULL;
  }
  memcpy(env, env_init, sizeof(env_init));
  
  return env;
}

void SPVM_API_free_env(SPVM_ENV* env) {

  // Free env api
  SPVM_API_ALLOCATOR_free_api(env->api->allocator);
  SPVM_API_STRING_BUFFER_free_api(env->api->string_buffer);
  SPVM_API_COMPILER_free_api(env->api->compiler);
  SPVM_API_RUNTIME_free_api(env->api->runtime);
  SPVM_API_CLASS_FILE_free_api(env->api->class_file);
  SPVM_API_BASIC_TYPE_free_api(env->api->basic_type);
  SPVM_API_CLASS_VAR_free_api(env->api->class_var);
  SPVM_API_FIELD_free_api(env->api->field);
  SPVM_API_TYPE_free_api(env->api->type);
  SPVM_API_METHOD_free_api(env->api->method);
  SPVM_API_ARG_free_api(env->api->arg);
  SPVM_API_INTERNAL_free_api(env->api->internal);
  
  free(env->api);
  
  // Free env
  free(env);
  env = NULL;
}

int32_t SPVM_API_call_method_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_METHOD* method, int32_t args_width, const char* func_name, const char* file, int32_t line) {
  
  int32_t mortal = 0;
  int32_t check_args_level = SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_FULL_CHECK;
  int32_t error_id = SPVM_API_call_method_common(env, stack, method, args_width, mortal, check_args_level, func_name, file, line);
  
  return error_id;
}

int32_t SPVM_API_call_method(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_METHOD* method, int32_t args_width, const char* func_name, const char* file, int32_t line) {
  
  int32_t mortal = 1;
  int32_t check_args_level = SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_FULL_CHECK;
  int32_t error_id = SPVM_API_call_method_common(env, stack, method, args_width, mortal, check_args_level, func_name, file, line);
  
  return error_id;
}

int32_t SPVM_API_call_method_no_mortal_no_check_args(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_METHOD* method, int32_t args_width, const char* func_name, const char* file, int32_t line) {
  
  int32_t mortal = 0;
  int32_t less_check_level = SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_NO_CHECK;
  int32_t error_id = SPVM_API_call_method_common(env, stack, method, args_width, mortal, less_check_level, func_name, file, line);
  
  return error_id;
}

int32_t SPVM_API_call_instance_method_common(SPVM_ENV* env, SPVM_VALUE* stack, const char* method_name, int32_t args_width, int32_t mortal, int32_t check_args_level, const char* decl_args_signature, const char* func_name, const char* file, int32_t line) {
  
  int32_t error_id = 0;
  
  SPVM_OBJECT* object = (SPVM_OBJECT*)stack[0].oval;
  
  SPVM_RUNTIME_METHOD* method = NULL;
  if (__builtin_expect(!!object, 1)) {
    method = SPVM_API_get_instance_method(env, stack, object, method_name);
    
    if (__builtin_expect(!!method, 1)) {
      if (check_args_level == SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_AUTO_CHECK) {
        if (strcmp(method->args_signature, decl_args_signature) == 0) {
          check_args_level = SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_NO_CHECK;
        }
      }
      
      error_id = SPVM_API_call_method_common(env, stack, method, args_width, mortal, check_args_level, func_name, file, line);
    }
    else {
      int32_t scope_id = SPVM_API_enter_scope(env, stack);
      SPVM_OBJECT* obj_invocant_type_name = SPVM_API_get_type_name(env, stack, object);
      const char* invocant_type_name = SPVM_API_get_chars(env, stack, obj_invocant_type_name);
      
      char* tmp_buffer = SPVM_API_get_stack_tmp_buffer(env, stack);
      snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, SPVM_IMPLEMENT_STRING_LITERALS[SPVM_IMPLEMENT_C_EXCEPTION_CALL_INSTANCE_METHOD_IMPLEMENT_NOT_FOUND], invocant_type_name, method_name);
      SPVM_API_leave_scope(env, stack, scope_id);
      SPVM_OBJECT* exception = SPVM_API_new_string_nolen_no_mortal(env, stack, tmp_buffer);
      SPVM_API_set_exception(env, stack, exception);
      error_id = SPVM_NATIVE_C_BASIC_TYPE_ID_ERROR_CLASS;
    }
  }
  else {
    char* tmp_buffer = SPVM_API_get_stack_tmp_buffer(env, stack);
    snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, SPVM_IMPLEMENT_STRING_LITERALS[SPVM_IMPLEMENT_C_EXCEPTION_CALL_INSTANCE_METHOD_INVOCANT_UNDEF], method_name);
    SPVM_OBJECT* exception = SPVM_API_new_string_nolen_no_mortal(env, stack, tmp_buffer);
    SPVM_API_set_exception(env, stack, exception);
    error_id = SPVM_NATIVE_C_BASIC_TYPE_ID_ERROR_CLASS;
  }
  
  return error_id;
}

int32_t SPVM_API_call_instance_method_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, const char* method_name, int32_t args_width, const char* func_name, const char* file, int32_t line) {
  
  int32_t mortal = 0;
  int32_t check_args_level = SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_FULL_CHECK;
  int32_t error_id = SPVM_API_call_instance_method_common(env, stack, method_name, args_width, mortal, check_args_level, NULL, func_name, file, line);
  
  return error_id;
}

int32_t SPVM_API_call_instance_method_no_mortal_less_check_args(SPVM_ENV* env, SPVM_VALUE* stack, const char* method_name, int32_t args_width, const char* decl_args_signature, const char* func_name, const char* file, int32_t line) {
  
  int32_t mortal = 0;
  int32_t check_args_level = SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_AUTO_CHECK;
  int32_t error_id = SPVM_API_call_instance_method_common(env, stack, method_name, args_width, mortal, check_args_level, decl_args_signature, func_name, file, line);
  
  return error_id;
}

int32_t SPVM_API_call_instance_method(SPVM_ENV* env, SPVM_VALUE* stack, const char* method_name, int32_t args_width, const char* func_name, const char* file, int32_t line) {
  
  int32_t mortal = 1;
  int32_t check_args_level = SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_FULL_CHECK;
  int32_t error_id = SPVM_API_call_instance_method_common(env, stack, method_name, args_width, mortal, check_args_level, NULL, func_name, file, line);
  
  return error_id;
}

int32_t SPVM_API_call_method_common(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_METHOD* method, int32_t args_width, int32_t mortal, int32_t check_args_level, const char* func_name, const char* file, int32_t line) {
  
  int32_t error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  stack[SPVM_API_C_STACK_INDEX_ARGS_WIDTH].ival = args_width;
  stack[SPVM_API_C_STACK_INDEX_CALL_DEPTH].ival++;

  /* Push caller information onto the caller stack */
  // Note: Assuming SPVM_API_push_caller_info exists or similar logic
  SPVM_API_push_caller_info(env, stack, method, func_name, file, line);
  
  SPVM_RUNTIME_BASIC_TYPE* current_basic_type = method->current_basic_type;
  
  int32_t max_call_depth = 512;
  if (__builtin_expect(stack[SPVM_API_C_STACK_INDEX_CALL_DEPTH].ival > max_call_depth, 0)) {
    error_id = SPVM_API_die(env, stack, "Deep recursion occurs. The depth of a method call must be less than %d.", __func__, FILE_NAME, __LINE__, max_call_depth);
    goto END_OF_FUNC;
  }
  
  if (__builtin_expect(method->is_init_method, 0)) {
    if (current_basic_type->initialized) {
      goto END_OF_FUNC;
    }
    else {
      current_basic_type->initialized = 1;
    }
  }
  
  /* Call method begin callback */
  if (__builtin_expect(env->api->runtime->method_begin_cb != NULL, 0)) {
    SPVM_RUNTIME_method_cb_t* method_begin_cb = (SPVM_RUNTIME_method_cb_t*)env->api->runtime->method_begin_cb;
    method_begin_cb(env, stack);
  }
  
  // Set default values for optional arguments
  int32_t args_length = method->args_length;
  for (int32_t arg_index = 0; arg_index < method->args_length; arg_index++) {
    SPVM_RUNTIME_ARG* arg = &method->args[arg_index];
    
    int32_t arg_stack_index = arg->stack_index;
    if (arg_stack_index >= args_width) {
      int32_t arg_type_width = SPVM_API_get_type_width(runtime, arg->basic_type, arg->type_dimension, arg->type_flag);
      
      // Optional argument without multi-numeric type
      if (arg->is_optional && arg_type_width == 1) {
        stack[arg_stack_index] = arg->default_value;
      }
      else {
        memset(&stack[arg_stack_index], 0, sizeof(SPVM_VALUE) * arg_type_width);
      }
    }
  }
  
  if (!(check_args_level == SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_NO_CHECK)) {
    if (check_args_level == SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_FULL_CHECK) {
      // Do nothing
    }
    else if (check_args_level == SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_AUTO_CHECK) {
      if (!method->has_object_args) {
        check_args_level = SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_NO_CHECK;
      }
    }
    else {
      assert(0);
    }
    
    if (!(check_args_level == SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_NO_CHECK)) {
      
      int32_t arg_index = 0;
      if (check_args_level == SPVM_API_C_CALL_METHOD_CHECK_ARGS_LEVEL_AUTO_CHECK && !method->is_class_method) {
        arg_index = 1;
      }
      for (; arg_index < method->args_length; arg_index++) {
        SPVM_RUNTIME_ARG* arg = &method->args[arg_index];
        
        int32_t arg_stack_index = arg->stack_index;
        if (arg_stack_index < args_width) {
          int32_t arg_is_object_type = SPVM_API_is_object_type((SPVM_RUNTIME*)env->runtime, arg->basic_type, arg->type_dimension, arg->type_flag);
          if (arg_is_object_type) {
            SPVM_OBJECT* obj_arg = (SPVM_OBJECT*)stack[arg_stack_index].oval;
            
            if (obj_arg) {
              int32_t can_assign = SPVM_API_isa(env, stack, obj_arg, arg->basic_type, arg->type_dimension);
              if (!can_assign) {
                int32_t arg_index_nth = method->is_class_method ? arg_index + 1 : arg_index;
                
                error_id = SPVM_API_die(env, stack, "The %ith argument must be assigned to the type of %ith argument of %s#%s method.", __func__, FILE_NAME, __LINE__, arg_index_nth, arg_index_nth, current_basic_type->name, method->name);
                goto END_OF_FUNC;
              }
            }
          }
        }
      }
    }
  }
  
  if (method->is_native) {
    error_id = SPVM_API_call_method_native(env, stack, method, args_width);
    if (error_id) {
      goto END_OF_FUNC;
    }
  }
  else if (method->is_precompile) {
    void* method_precompile_address = method->precompile_address;
    if (method_precompile_address) {
      int32_t (*precompile_address)(SPVM_ENV*, SPVM_VALUE*) = method_precompile_address;
      error_id = (*precompile_address)(env, stack);
      if (error_id) {
        goto END_OF_FUNC;
      }
    }
    else if (method->is_precompile_fallback) {
      error_id = SPVM_VM_call_method(env, stack, method, args_width);
      if (error_id) {
        goto END_OF_FUNC;
      }
    }
    else {
      error_id = SPVM_API_die(env, stack, "The execution address of %s#%s precompile method must not be NULL. Loading the dynamic link library maybe failed.", __func__, FILE_NAME, __LINE__, method->current_basic_type->name, method->name);
      goto END_OF_FUNC;
    }
  }
  else {
    error_id = SPVM_VM_call_method(env, stack, method, args_width);
    if (__builtin_expect(error_id, 0)) {
      goto END_OF_FUNC;
    }
  }
  
  if (__builtin_expect(mortal, 0)) {
    SPVM_RUNTIME_BASIC_TYPE* method_return_basic_type = method->return_basic_type;
    int32_t method_return_type_dimension = method->return_type_dimension;
    int32_t method_return_type_flag = method->return_type_flag;
    int32_t method_return_type_is_object = SPVM_API_is_object_type(runtime, method_return_basic_type, method_return_type_dimension, method_return_type_flag);
    
    if (method_return_type_is_object) {
      SPVM_API_push_mortal(env, stack, (SPVM_OBJECT*)stack[0].oval);
    }
  }
  
  END_OF_FUNC:
  
  if (method->return_type_is_void) {
    stack[0].oval = NULL;
  }
  
  /* Call method end callback */
  if (__builtin_expect(env->api->runtime->method_end_cb != NULL, 0)) {
    SPVM_RUNTIME_method_cb_t* method_end_cb = (SPVM_RUNTIME_method_cb_t*)env->api->runtime->method_end_cb;
    method_end_cb(env, stack);
  }
  
  stack[SPVM_API_C_STACK_INDEX_CALL_DEPTH].ival--;
  
  return error_id;
}

int32_t SPVM_API_call_method_native(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_METHOD* method, int32_t args_width) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  int32_t error_id = 0;
  
  // Call native method
  int32_t (*native_address)(SPVM_ENV*, SPVM_VALUE*) = method->native_address;
  if (__builtin_expect(!native_address, 0)) {
    error_id = SPVM_API_die(env, stack, "The execution address of %s#%s native method must not be NULL. Loading the dynamic link library maybe failed.", __func__, FILE_NAME, __LINE__, method->current_basic_type->name, method->name);
    goto END_OF_FUNC;
  }
  
  int32_t native_mortal_stack_top = SPVM_API_enter_scope(env, stack);
  
  error_id = (*native_address)(env, stack);
  
  int32_t method_return_type_is_object = method->return_type_is_object;
  
  // Increment ref count of return value
  if (__builtin_expect(!error_id, 1)) {
    if (method_return_type_is_object) {
      SPVM_OBJECT* return_object = *(SPVM_OBJECT**)&stack[0];
      if (return_object != NULL) {
        SPVM_API_inc_ref_count(env, stack, return_object);
      }
    }
  }
  
  if (*(int32_t*)&stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK_LENGTH] > native_mortal_stack_top) {
    SPVM_API_leave_scope(env, stack, native_mortal_stack_top);
  }
  
  // Decrement ref count of return value
  if (__builtin_expect(!error_id, 1)) {
    if (method_return_type_is_object) {
      SPVM_OBJECT* return_object = *(SPVM_OBJECT**)&stack[0];
      if (return_object != NULL) {
        SPVM_API_dec_ref_count(env, stack, return_object);
      }
    }
  }
  
  END_OF_FUNC:
  
  // Set an default exception message
  if (__builtin_expect(error_id, 0)) {
    if (SPVM_API_get_exception(env, stack) == NULL) {
      SPVM_OBJECT* exception = SPVM_API_new_string_nolen_no_mortal(env, stack, "Error");
      SPVM_API_set_exception(env, stack, exception);
    }
  }
  
  return error_id;
}

void SPVM_API_call_class_method_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* method_name, int32_t args_width, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return;
  }
  
  SPVM_RUNTIME_METHOD* method = SPVM_API_BASIC_TYPE_get_method_by_name((SPVM_RUNTIME*)env->runtime, basic_type, method_name);
  
  if (!method) {
    *error_id = SPVM_API_die(env, stack, "%s#%s method is not found.", func_name, file, line, basic_type_name, method_name);
    return;
  }
  
  if (!method->is_class_method) {
    *error_id = SPVM_API_die(env, stack, "%s#%s method must be a class method.", func_name, file, line, basic_type_name, method_name);
    return;
  }
  
  *error_id = SPVM_API_call_method(env, stack, method, args_width, func_name, file, line);
}

void SPVM_API_call_instance_method_static_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* method_name, int32_t args_width, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return;
  }
  
  SPVM_RUNTIME_METHOD* method = SPVM_API_BASIC_TYPE_get_method_by_name((SPVM_RUNTIME*)env->runtime, basic_type, method_name);
  
  if (!method) {
    *error_id = SPVM_API_die(env, stack, "%s#%s method is not found.", func_name, file, line, basic_type_name, method_name);
    return;
  }
  
  if (method->is_class_method) {
    *error_id = SPVM_API_die(env, stack, "%s#%s method must be an instance method.", func_name, file, line, basic_type_name, method_name);
    return;
  }
  
  SPVM_OBJECT* object = (SPVM_OBJECT*)stack[0].oval;
  
  if (!object) {
    *error_id = SPVM_API_die(env, stack, "The invocant must be defined.", func_name, file, line);
    return;
  };
  
  if (!SPVM_API_isa(env, stack, object, basic_type, 0)) {
    *error_id = SPVM_API_die(env, stack, "The invocant must be assigned to %s class.", func_name, file, line, basic_type_name);
    return;
  };
  
  *error_id = SPVM_API_call_method(env, stack, method, args_width, func_name, file, line);
}

void SPVM_API_call_instance_method_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* method_name, int32_t args_width, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = SPVM_API_call_instance_method(env, stack, method_name, args_width, func_name, file, line);
}

SPVM_RUNTIME_METHOD* SPVM_API_get_method(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* method_name) {
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  
  if (!basic_type) {
    return NULL;
  }
  
  SPVM_RUNTIME_METHOD* method = SPVM_API_BASIC_TYPE_get_method_by_name((SPVM_RUNTIME*)env->runtime, basic_type, method_name);
  
  return method;
}

SPVM_RUNTIME_METHOD* SPVM_API_get_class_method(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* method_name) {
  
  SPVM_RUNTIME_METHOD* method = SPVM_API_get_method(env, stack, basic_type_name, method_name);
  
  if (method) {
    if (!method->is_class_method) {
      return NULL;
    }
  }
  
  return method;
}

SPVM_RUNTIME_METHOD* SPVM_API_get_instance_method_static(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* method_name) {
  
  SPVM_RUNTIME_METHOD* method = SPVM_API_get_method(env, stack, basic_type_name, method_name);
  
  if (method) {
    if (method->is_class_method) {
      return NULL;
    }
  }
  
  return method;
}

SPVM_RUNTIME_METHOD* SPVM_API_get_instance_method(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* method_name) {
  
  // Method
  SPVM_RUNTIME_METHOD* method = NULL;
  
  // Compiler
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (!object) {
    return NULL;
  }
  
  // Basic type
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
  SPVM_RUNTIME_BASIC_TYPE* parent_basic_type = object_basic_type;
  
  while (1) {
    if (!parent_basic_type) {
      break;
    }
    
    // Method
    method = SPVM_API_BASIC_TYPE_get_method_by_name(runtime, parent_basic_type, method_name);
    if (method) {
      // Instance method
      if (method->is_class_method) {
        method = NULL;
      }
      break;
    }
    
    parent_basic_type = parent_basic_type->parent;
  }
  
  return method;
}

int32_t SPVM_API_call_init_methods(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  int32_t error_id = 0;
  
  // Runtime
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  // Call INIT blocks
  int32_t basic_types_length = runtime->basic_types_length;
  for (int32_t basic_type_id = 0; basic_type_id < basic_types_length; basic_type_id++) {
    SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_RUNTIME_get_basic_type_by_id((SPVM_RUNTIME*)env->runtime, basic_type_id);
    if (basic_type->init_method) {
      SPVM_RUNTIME_METHOD* init_method = SPVM_API_BASIC_TYPE_get_method_by_index((SPVM_RUNTIME*)env->runtime, basic_type, basic_type->init_method->index);      
      int32_t args_width = 0;
      error_id = SPVM_API_call_method(env, stack, init_method, args_width, __func__, FILE_NAME, __LINE__);
      if (error_id) { break; }
    }
  }
  
  return error_id;
}

int32_t SPVM_API_call_end_methods(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  // Runtime
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  // Call the consolidated end_method of each basic type.
  // The execution order between classes is not guaranteed.
  int32_t basic_types_length = runtime->basic_types_length;
  for (int32_t basic_type_id = 0; basic_type_id < basic_types_length; basic_type_id++) {
    SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_RUNTIME_get_basic_type_by_id((SPVM_RUNTIME*)env->runtime, basic_type_id);
    
    if (basic_type->end_method) {
      int32_t args_width = 0;
      
      // Execute the consolidated END method for this package
      int32_t error_id = SPVM_API_call_method(env, stack, basic_type->end_method, args_width, __func__, FILE_NAME, __LINE__);
      
      // An exception thrown in an END block is converted to a warning message
      if (error_id) {
        SPVM_OBJECT* exception = SPVM_API_get_exception(env, stack);
        
        if (exception) {
          const char* exception_chars = SPVM_API_get_chars(env, stack, exception);
          
          fprintf(runtime->spvm_stderr, "[An exception thrown in END block is converted to a warning message]\n%s\n", exception_chars);
        }
      }
    }
  }
  
  return 0;
}

int32_t SPVM_API_set_command_info_program_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* obj_program_name) {
  
  int32_t error_id = 0;
  
  if (!obj_program_name) {
    return SPVM_API_die(env, stack, "The obj_program_name must be defined.", __func__, FILE_NAME, __LINE__);
  }
  
  SPVM_RUNTIME_BASIC_TYPE* obj_program_name_basic_type = SPVM_API_get_object_basic_type(env, stack, obj_program_name);
  if (!(obj_program_name_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_STRING && obj_program_name->type_dimension == 0)) {
    return SPVM_API_die(env, stack, "The obj_program_name must be a string.", __func__, FILE_NAME, __LINE__);
  }
  
  SPVM_API_set_class_var_object_by_name(env, stack, "CommandInfo", "$PROGRAM_NAME", obj_program_name, &error_id, __func__, FILE_NAME, __LINE__);
  if (error_id) { return error_id; }
  
  return 0;
}

int32_t SPVM_API_set_command_info_argv(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* obj_argv) {
  
  int32_t error_id = 0;
  
  if (!obj_argv) {
    return SPVM_API_die(env, stack, "The obj_argv must be defined.", __func__, FILE_NAME, __LINE__);
  }
  
  SPVM_RUNTIME_BASIC_TYPE* obj_argv_basic_type = SPVM_API_get_object_basic_type(env, stack, obj_argv);
  if (!(obj_argv_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_STRING && obj_argv->type_dimension == 1)) {
    return SPVM_API_die(env, stack, "The obj_argv must be a string array.", __func__, FILE_NAME, __LINE__);
  }
  
  SPVM_API_set_class_var_object_by_name(env, stack, "CommandInfo", "$ARGV", obj_argv, &error_id, __func__, FILE_NAME, __LINE__);
  if (error_id) { return error_id; }
  
  return 0;
}

int32_t SPVM_API_set_command_info_basetime(SPVM_ENV* env, SPVM_VALUE* stack, int64_t basetime) {
  
  int32_t error_id = 0;
  
  SPVM_API_set_class_var_long_by_name(env, stack, "CommandInfo", "$BASETIME", basetime, &error_id, __func__, FILE_NAME, __LINE__);
  if (error_id) { return error_id; }
  
  return 0;
}

static void SPVM_API_destroy_class_vars_common(SPVM_ENV* env, SPVM_VALUE* stack, int32_t only_cache){

  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  for (int32_t basic_type_id = 0; basic_type_id < runtime->basic_types_length; basic_type_id++) {
    SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_RUNTIME_get_basic_type_by_id((SPVM_RUNTIME*)env->runtime, basic_type_id);
    
    for (int32_t class_var_index = 0; class_var_index < basic_type->class_vars_length; class_var_index++) {
      
      SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_index(runtime, basic_type, class_var_index);
      
      SPVM_RUNTIME_BASIC_TYPE* class_var_basic_type = class_var->basic_type;
      int32_t class_var_type_dimension = class_var->type_dimension;
      int32_t class_var_type_flag = class_var->type_flag;
      int32_t class_var_is_cache = class_var->is_cache;
      int32_t class_var_type_is_object = SPVM_API_is_object_type(runtime, class_var_basic_type, class_var_type_dimension, class_var_type_flag);
      
      int32_t do_free = 0;
      if (only_cache) {
        if (class_var_is_cache) {
          do_free = 1;
        }
      }
      else {
        do_free = 1;
      }
      
      if (do_free) {
        if (class_var_type_is_object) {
          SPVM_OBJECT** ref = (SPVM_OBJECT**)&class_var->data;
          SPVM_API_assign_object(env, stack, ref, NULL);
        }
      }
    }
  }
}

void SPVM_API_destroy_class_vars(SPVM_ENV* env, SPVM_VALUE* stack){
  
  int32_t only_cache = 0;
  SPVM_API_destroy_class_vars_common(env, stack, only_cache);
}

void SPVM_API_destroy_cache_class_vars(SPVM_ENV* env, SPVM_VALUE* stack){

  int32_t only_cache = 1;
  SPVM_API_destroy_class_vars_common(env, stack, only_cache);
}

int32_t SPVM_API_args_width(SPVM_ENV* env, SPVM_VALUE* stack) {
  int32_t args_length = stack[SPVM_API_C_STACK_INDEX_ARGS_WIDTH].ival;
  
  return args_length;
}

int32_t SPVM_API_get_basic_type_id(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (basic_type_name == NULL) {
    return -1;
  }
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_RUNTIME_get_basic_type_by_name(runtime, basic_type_name);
  if (basic_type) {
    int32_t basic_type_id = basic_type->id;
    return basic_type_id;
  }
  else {
    return -1;
  }
}

SPVM_RUNTIME_BASIC_TYPE* SPVM_API_get_basic_type(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (!basic_type_name) {
    return NULL;
  }
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_RUNTIME_get_basic_type_by_name(runtime, basic_type_name);
  
  return basic_type;
}

SPVM_OBJECT* SPVM_API_new_object_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  *error_id = 0;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return NULL;
  };
  
  SPVM_OBJECT* object = SPVM_API_new_object(env, stack, basic_type);
  
  if (!object) {
    *error_id = SPVM_API_die(env, stack, "The creation of the object of %s class failed.", func_name, file, line, basic_type_name);
    return NULL;
  }
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_pointer_object_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, void* pointer, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  SPVM_OBJECT* object = SPVM_API_new_object_by_name(env, stack, basic_type_name, error_id, func_name, file, line);
  
  if (object) {
    SPVM_API_set_pointer(env, stack, object, pointer);
  }
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_object_array_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, int32_t length, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  *error_id = 0;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return NULL;
  };
  
  SPVM_OBJECT* array = SPVM_API_new_object_array(env, stack, basic_type, length);
  
  if (!array) {
    *error_id = SPVM_API_die(env, stack, "The creation of the array of %s class failed.", func_name, file, line, basic_type_name);
    return NULL;
  }
  
  return array;
}

SPVM_OBJECT* SPVM_API_new_muldim_array_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, int32_t type_dimension, int32_t length, int32_t* error_id, const char* func_name, const char* file, int32_t line) {  *error_id = 0;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return NULL;
  };
  
  SPVM_OBJECT* object = SPVM_API_new_muldim_array(env, stack, basic_type, type_dimension, length);
  
  if (!object) {
    *error_id = SPVM_API_die(env, stack, "The creation of the multi-dimensional array of %s class with the dimension %d failed.", func_name, file, line, basic_type_name, type_dimension);
    return NULL;
  }
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_mulnum_array_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, int32_t length, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  *error_id = 0;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return NULL;
  };
  
  SPVM_OBJECT* array = SPVM_API_new_mulnum_array(env, stack, basic_type, length);
  
  if (!array) {
    *error_id = SPVM_API_die(env, stack, "The creation of the multi-numeric array of %s class failed.", func_name, file, line, basic_type_name);
    return NULL;
  }
  
  return array;
}

int8_t SPVM_API_get_class_var_byte(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var) {
  
  assert(class_var);
  
  int8_t value = class_var->data.bval;
  
  return value;
}

int16_t SPVM_API_get_class_var_short(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var) {
  
  assert(class_var);
  
  int16_t value = class_var->data.sval;
  
  return value;
}

int32_t SPVM_API_get_class_var_int(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var) {
  
  assert(class_var);
  
  int32_t value = class_var->data.ival;
  
  return value;
}

int64_t SPVM_API_get_class_var_long(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var) {
  
  assert(class_var);
  
  int64_t value = class_var->data.lval;
  
  return value;
}

float SPVM_API_get_class_var_float(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var) {
  
  assert(class_var);
  
  float value = class_var->data.fval;
 
  return value;
}

double SPVM_API_get_class_var_double(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var) {
  
  assert(class_var);
  
  double value = class_var->data.dval;
  
  return value;
}

SPVM_OBJECT* SPVM_API_get_class_var_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var) {
  
  assert(class_var);
  
  SPVM_OBJECT* value_maybe_weaken = (SPVM_OBJECT*)class_var->data.oval;
  SPVM_OBJECT* value = SPVM_API_drop_tag_pointers(env, stack, value_maybe_weaken);
  
  return value;
}

SPVM_OBJECT** SPVM_API_get_class_var_object_ref(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var) {
  
  assert(class_var);
  
  SPVM_OBJECT** ref = (SPVM_OBJECT**)&class_var->data.oval;
  
  return ref;
}

SPVM_OBJECT* SPVM_API_get_class_var_string(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var) {
  
  return SPVM_API_get_class_var_object(env, stack, class_var);
}

void SPVM_API_set_class_var_byte(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var, int8_t value) {
  
  assert(class_var);
  
  class_var->data.bval = value;
}

void SPVM_API_set_class_var_short(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var, int16_t value) {
  
  assert(class_var);
  
  class_var->data.sval = value;
}

void SPVM_API_set_class_var_int(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var, int32_t value) {
  
  assert(class_var);
  
  class_var->data.ival = value;
}

void SPVM_API_set_class_var_long(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var, int64_t value) {
  
  assert(class_var);
  
  class_var->data.lval = value;
}

void SPVM_API_set_class_var_float(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var, float value) {
  
  assert(class_var);
  
  class_var->data.fval = value;
}

void SPVM_API_set_class_var_double(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var, double value) {
  
  assert(class_var);
  
  class_var->data.dval = value;
}

void SPVM_API_set_class_var_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var, SPVM_OBJECT* value) {
  
  assert(class_var);
  
  SPVM_OBJECT** ref = (SPVM_OBJECT**)&class_var->data.oval;
  SPVM_API_assign_object(env, stack, ref, value);
}

void SPVM_API_set_class_var_string(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CLASS_VAR* class_var, SPVM_OBJECT* value) {
  
  assert(class_var);
  
  SPVM_API_set_class_var_object(env, stack, class_var, value);
}

int8_t SPVM_API_get_class_var_byte_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return 0;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  int8_t value = 0;
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_class_var_byte(env, stack, class_var);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be byte type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

int16_t SPVM_API_get_class_var_short_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return 0;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  int16_t value = 0;
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_class_var_byte(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_class_var_short(env, stack, class_var);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be short type or smaller numeric type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

int32_t SPVM_API_get_class_var_int_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return 0;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  int32_t value = 0;
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_class_var_byte(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_class_var_short(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        value = SPVM_API_get_class_var_int(env, stack, class_var);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be int type or smaller numeric type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

int64_t SPVM_API_get_class_var_long_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return 0;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  int64_t value = 0;
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_class_var_byte(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_class_var_short(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        value = SPVM_API_get_class_var_int(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        value = SPVM_API_get_class_var_long(env, stack, class_var);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be long type or smaller numeric type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

float SPVM_API_get_class_var_float_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return 0;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  float value = 0;
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_class_var_byte(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_class_var_short(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        value = SPVM_API_get_class_var_int(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        value = SPVM_API_get_class_var_long(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        value = SPVM_API_get_class_var_float(env, stack, class_var);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be float type or smaller numeric type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

double SPVM_API_get_class_var_double_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return 0;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  double value = 0;
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_class_var_byte(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_class_var_short(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        value = SPVM_API_get_class_var_int(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        value = SPVM_API_get_class_var_long(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        value = SPVM_API_get_class_var_float(env, stack, class_var);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        value = SPVM_API_get_class_var_double(env, stack, class_var);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be double type or smaller numeric type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

SPVM_OBJECT* SPVM_API_get_class_var_object_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return 0;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return 0;
  };
  
  int32_t is_object_type = SPVM_API_is_object_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  SPVM_OBJECT* value = NULL;
  if (is_object_type) {
    value = SPVM_API_get_class_var_object(env, stack, class_var);
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be an object type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

SPVM_OBJECT* SPVM_API_get_class_var_string_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  return SPVM_API_get_class_var_object_by_name(env, stack, basic_type_name, class_var_name, error_id, func_name, file, line);
}

void SPVM_API_set_class_var_byte_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int8_t value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        SPVM_API_set_class_var_byte(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        SPVM_API_set_class_var_short(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        SPVM_API_set_class_var_int(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_class_var_long(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_class_var_float(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_class_var_double(env, stack, class_var, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be byte or larger numeric type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_class_var_short_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int16_t value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        SPVM_API_set_class_var_short(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        SPVM_API_set_class_var_int(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_class_var_long(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_class_var_float(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_class_var_double(env, stack, class_var, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be short or larger numeric type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_class_var_int_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int32_t value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        SPVM_API_set_class_var_int(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_class_var_long(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_class_var_float(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_class_var_double(env, stack, class_var, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be int or larger numeric type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_class_var_long_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, int64_t value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_class_var_long(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_class_var_float(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_class_var_double(env, stack, class_var, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be long or larger numeric type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_class_var_float_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, float value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_class_var_float(env, stack, class_var, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_class_var_double(env, stack, class_var, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be float or larger numeric type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_class_var_double_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, double value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (class_var->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_class_var_double(env, stack, class_var, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be double type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_class_var_object_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, SPVM_OBJECT* value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "%s class is not found.", func_name, file, line, basic_type_name);
    return;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  if (!class_var) {
    *error_id = SPVM_API_die(env, stack, "$%s#%s class variable is not found.", func_name, file, line, basic_type_name, class_var_name + 1);
    return;
  };
  
  int32_t is_object_type = SPVM_API_is_object_type(runtime, class_var->basic_type, class_var->type_dimension, class_var->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_object_type) {
    SPVM_API_set_class_var_object(env, stack, class_var, value);
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the class variable must be an object type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_class_var_string_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name, SPVM_OBJECT* value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  SPVM_API_set_class_var_object_by_name(env, stack, basic_type_name, class_var_name, value, error_id, func_name, file, line);
}

int8_t SPVM_API_get_field_byte(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  int8_t value = *(int8_t*)(GET_DATA_ADDRESS(env, stack, object) + field->offset);
  
  return value;
}

int16_t SPVM_API_get_field_short(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  int16_t value = *(int16_t*)(GET_DATA_ADDRESS(env, stack, object) + field->offset);
  
  return value;
}

int32_t SPVM_API_get_field_int(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  int32_t value = *(int32_t*)(GET_DATA_ADDRESS(env, stack, object) + field->offset);
  
  return value;
}

int64_t SPVM_API_get_field_long(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  int64_t value = *(int64_t*)(GET_DATA_ADDRESS(env, stack, object) + field->offset);
  
  return value;
}

float SPVM_API_get_field_float(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  float value = *(float*)(GET_DATA_ADDRESS(env, stack, object) + field->offset);
  
  return value;
}

double SPVM_API_get_field_double(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  double value = *(double*)(GET_DATA_ADDRESS(env, stack, object) + field->offset);
  
  return value;
}

SPVM_OBJECT* SPVM_API_get_field_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  SPVM_OBJECT* value_maybe_weaken = *(SPVM_OBJECT**)(GET_DATA_ADDRESS(env, stack, object) + field->offset);
  SPVM_OBJECT* value = SPVM_API_drop_tag_pointers(env, stack, value_maybe_weaken);
  
  return value;
}

SPVM_OBJECT** SPVM_API_get_field_object_ref(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  SPVM_OBJECT** ref = (SPVM_OBJECT**)(GET_DATA_ADDRESS(env, stack, object) + field->offset);
  
  return ref;
}

SPVM_OBJECT* SPVM_API_get_field_string(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  SPVM_OBJECT* value = SPVM_API_get_field_object(env, stack, object, field);
  
  return value;
}

void SPVM_API_set_field_byte(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field, int8_t value) {
  
  *(int8_t*)(GET_DATA_ADDRESS(env, stack, object) + field->offset) = value;
  SPVM_IMPLEMENT_ENABLE_EXISTS_FLAG(env, stack, (SPVM_OBJ*)object, field->exists_offset, field->exists_bit);
}

void SPVM_API_set_field_short(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field, int16_t value) {
  
  *(int16_t*)(GET_DATA_ADDRESS(env, stack, object) + field->offset) = value;
  SPVM_IMPLEMENT_ENABLE_EXISTS_FLAG(env, stack, (SPVM_OBJ*)object, field->exists_offset, field->exists_bit);
}

void SPVM_API_set_field_int(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field, int32_t value) {
  
  *(int32_t*)(GET_DATA_ADDRESS(env, stack, object) + field->offset) = value;
  SPVM_IMPLEMENT_ENABLE_EXISTS_FLAG(env, stack, (SPVM_OBJ*)object, field->exists_offset, field->exists_bit);
}

void SPVM_API_set_field_long(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field, int64_t value) {
  
  *(int64_t*)(GET_DATA_ADDRESS(env, stack, object) + field->offset) = value;
  SPVM_IMPLEMENT_ENABLE_EXISTS_FLAG(env, stack, (SPVM_OBJ*)object, field->exists_offset, field->exists_bit);
}

void SPVM_API_set_field_float(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field, float value) {
  
  *(float*)(GET_DATA_ADDRESS(env, stack, object) + field->offset) = value;
  SPVM_IMPLEMENT_ENABLE_EXISTS_FLAG(env, stack, (SPVM_OBJ*)object, field->exists_offset, field->exists_bit);
}

void SPVM_API_set_field_double(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field, double value) {
  
  *(double*)(GET_DATA_ADDRESS(env, stack, object) + field->offset) = value;
  SPVM_IMPLEMENT_ENABLE_EXISTS_FLAG(env, stack, (SPVM_OBJ*)object, field->exists_offset, field->exists_bit);
}

void SPVM_API_set_field_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field, SPVM_OBJECT* value) {
  
  SPVM_OBJECT** ref = (SPVM_OBJECT**)(GET_DATA_ADDRESS(env, stack, object) + field->offset);
  
  SPVM_API_assign_object(env, stack, ref, value);
  SPVM_IMPLEMENT_ENABLE_EXISTS_FLAG(env, stack,(SPVM_OBJ*) object, field->exists_offset, field->exists_bit);
}

void SPVM_API_set_field_string(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field, SPVM_OBJECT* value) {
  
  SPVM_API_set_field_object(env, stack, object, field, value);
}

int8_t SPVM_API_get_field_byte_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  int8_t value = 0;
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_field_byte(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_field_short(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        value = SPVM_API_get_field_int(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        value = SPVM_API_get_field_long(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        value = SPVM_API_get_field_float(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        value = SPVM_API_get_field_double(env, stack, object, field);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be byte type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

int16_t SPVM_API_get_field_short_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  int16_t value = 0;
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_field_byte(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_field_short(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        value = SPVM_API_get_field_int(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        value = SPVM_API_get_field_long(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        value = SPVM_API_get_field_float(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        value = SPVM_API_get_field_double(env, stack, object, field);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be short type or smaller numeric type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

int32_t SPVM_API_get_field_int_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  int32_t value = 0;
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_field_byte(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_field_short(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        value = SPVM_API_get_field_int(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        value = SPVM_API_get_field_long(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        value = SPVM_API_get_field_float(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        value = SPVM_API_get_field_double(env, stack, object, field);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be int type or smaller numeric type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

int64_t SPVM_API_get_field_long_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  int64_t value = 0;
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_field_byte(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_field_short(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        value = SPVM_API_get_field_int(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        value = SPVM_API_get_field_long(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        value = SPVM_API_get_field_float(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        value = SPVM_API_get_field_double(env, stack, object, field);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be long type or smaller numeric type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

float SPVM_API_get_field_float_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  float value = 0;
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_field_byte(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_field_short(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        value = SPVM_API_get_field_int(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        value = SPVM_API_get_field_long(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        value = SPVM_API_get_field_float(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        value = SPVM_API_get_field_double(env, stack, object, field);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be float type or smaller numeric type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

double SPVM_API_get_field_double_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return 0;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  double value = 0;
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        value = SPVM_API_get_field_byte(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        value = SPVM_API_get_field_short(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        value = SPVM_API_get_field_int(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        value = SPVM_API_get_field_long(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        value = SPVM_API_get_field_float(env, stack, object, field);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        value = SPVM_API_get_field_double(env, stack, object, field);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be double type or smaller numeric type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

SPVM_OBJECT* SPVM_API_get_field_object_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return NULL;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return NULL;
  };
  
  int32_t is_object_type = SPVM_API_is_object_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  SPVM_OBJECT* value = NULL;
  if (is_object_type) {
    value = SPVM_API_get_field_object(env, stack, object, field);
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be an object type.", func_name, file, line);
    return 0;
  }
  
  return value;
}

SPVM_OBJECT** SPVM_API_get_field_object_ref_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return NULL;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return NULL;
  };
  
  int32_t is_object_type = SPVM_API_is_object_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  SPVM_OBJECT** ref = NULL;
  if (is_object_type) {
    ref = SPVM_API_get_field_object_ref(env, stack, object, field);
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be an object type.", func_name, file, line);
    return 0;
  }
  
  return ref;
}

SPVM_OBJECT* SPVM_API_get_field_object_defined_and_has_pointer_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file_name, int32_t line) {
  
  *error_id = 0;
  
  SPVM_OBJECT* obj_field = SPVM_API_get_field_object_by_name(env, stack, object, field_name, error_id, func_name, file_name, line);
  
  if (*error_id) { return NULL; }
  
  if (!obj_field) {
    *error_id = SPVM_API_die(env, stack, "%s field must be defined.", func_name, file_name, line, field_name);
    return obj_field;
  }
  
  void* pointer = SPVM_API_get_pointer(env, stack, obj_field);
  
  if (!pointer) {
    *error_id = SPVM_API_die(env, stack, "The pointer of %s field must be defined.", func_name, file_name, line, field_name);
  }
  
  return obj_field;
}

SPVM_OBJECT* SPVM_API_get_field_string_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  return SPVM_API_get_field_object_by_name(env, stack, object, field_name, error_id, func_name, file, line);
}

const char* SPVM_API_get_field_string_chars_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    *error_id = SPVM_API_die(env, stack, "%s field is not found.", func_name, file, line, field_name);
    return NULL;
  };
  SPVM_OBJECT* value = SPVM_API_get_field_string(env, stack, object, field);
  if (value == NULL) {
    return NULL;
  }
  else {
    const char* chars = SPVM_API_get_chars(env, stack, value);
    return chars;
  }
}

void SPVM_API_set_field_byte_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int8_t value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return;
  }
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        SPVM_API_set_field_byte(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        SPVM_API_set_field_short(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        SPVM_API_set_field_int(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_field_long(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_field_float(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_field_double(env, stack, object, field, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be byte or larger numeric type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_field_short_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int16_t value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        SPVM_API_set_field_byte(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        SPVM_API_set_field_short(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        SPVM_API_set_field_int(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_field_long(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_field_float(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_field_double(env, stack, object, field, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be short or larger numeric type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_field_int_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        SPVM_API_set_field_byte(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        SPVM_API_set_field_short(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        SPVM_API_set_field_int(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_field_long(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_field_float(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_field_double(env, stack, object, field, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be int or larger numeric type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_field_long_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int64_t value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        SPVM_API_set_field_byte(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        SPVM_API_set_field_short(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        SPVM_API_set_field_int(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_field_long(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_field_float(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_field_double(env, stack, object, field, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be long or larger numeric type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_field_float_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, float value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        SPVM_API_set_field_byte(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        SPVM_API_set_field_short(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        SPVM_API_set_field_int(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_field_long(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_field_float(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_field_double(env, stack, object, field, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be float or larger numeric type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_field_double_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, double value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return;
  };
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        SPVM_API_set_field_byte(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        SPVM_API_set_field_short(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        SPVM_API_set_field_int(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_field_long(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_field_float(env, stack, object, field, value);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_field_double(env, stack, object, field, value);
        break;
      }
      default : {
        is_invalid_type = 1;
      }
    }
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be double type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_field_object_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, SPVM_OBJECT* value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return;
  };
  
  
  int32_t is_object_type = SPVM_API_is_object_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  int32_t is_invalid_type = 0;
  
  if (is_object_type) {
    SPVM_API_set_field_object(env, stack, object, field, value);
  }
  else {
    is_invalid_type = 1;
  }
  
  if (is_invalid_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the field must be an object type.", func_name, file, line);
    return;
  }
}

void SPVM_API_set_field_string_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, SPVM_OBJECT* value, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  SPVM_API_set_field_object_by_name(env, stack, object, field_name, value, error_id, func_name, file, line);
}

SPVM_VALUE* SPVM_API_new_stack(SPVM_ENV* env) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_VALUE* stack = SPVM_API_new_memory_block(env, NULL, sizeof(SPVM_VALUE) * SPVM_API_C_STACK_LENGTH);
  
  stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK_CAPACITY].ival = 1;
  SPVM_OBJECT* native_mortal_stack = SPVM_API_new_memory_block(env, stack, sizeof(SPVM_OBJECT*) * stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK_CAPACITY].ival);
  if (native_mortal_stack == NULL) {
    return NULL;
  }
  stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK].address = native_mortal_stack;
  stack[SPVM_API_C_STACK_INDEX_ENV].address = env;
  
  stack[SPVM_API_C_STACK_INDEX_CALL_DEPTH].ival = -1;
  
  stack[SPVM_API_C_STACK_INDEX_CALL_STACK_FRAME_INFOS_CAPACITY].ival = 1;
  stack[SPVM_API_C_STACK_INDEX_CALL_STACK_FRAME_INFOS].address = SPVM_API_new_memory_block_for_call_stack(env, stack, sizeof(SPVM_RUNTIME_CALL_STACK_FRAME_INFO) * stack[SPVM_API_C_STACK_INDEX_CALL_STACK_FRAME_INFOS_CAPACITY].ival);
  
  stack[SPVM_API_C_STACK_INDEX_CALL_STACK_MEMORY_BLOCKS_CAPACITY].ival = 1;
  char** call_stack_memory_blocks = SPVM_API_new_memory_block_for_call_stack(env, stack, sizeof(char*) * stack[SPVM_API_C_STACK_INDEX_CALL_STACK_FRAME_INFOS_CAPACITY].ival);
  for (int32_t i = 0; i < stack[SPVM_API_C_STACK_INDEX_CALL_STACK_MEMORY_BLOCKS_CAPACITY].ival; i++) {
    call_stack_memory_blocks[i] = SPVM_API_new_memory_block_for_call_stack(env, stack, SPVM_API_C_CALL_STACK_MEMORY_BLOCK_SIZE);
  }
  stack[SPVM_API_C_STACK_INDEX_CALL_STACK_MEMORY_BLOCKS].address = call_stack_memory_blocks;
  
  // Initialize the record size of the caller information stack (e.g., name, file, line)
  int32_t caller_info_stack_record_size = 4;
  stack[SPVM_API_C_STACK_INDEX_CALLER_INFO_STACK_RECORD_SIZE].ival = caller_info_stack_record_size;

  // Initialize the initial capacity of the caller information stack
  int32_t caller_info_stack_capacity = 1;
  stack[SPVM_API_C_STACK_INDEX_CALLER_INFO_STACK_CAPACITY].ival = caller_info_stack_capacity;

  // Allocate the caller information stack with the initial capacity
  SPVM_VALUE* caller_info_stack = (SPVM_VALUE*)SPVM_API_new_memory_block(env, stack, sizeof(SPVM_VALUE) * caller_info_stack_record_size * caller_info_stack_capacity);
  
  if (caller_info_stack == NULL) {
    return NULL;
  }
  
  stack[SPVM_API_C_STACK_INDEX_CALLER_INFO_STACK].address = caller_info_stack;
  
  return stack;
}

SPVM_VALUE* SPVM_API_new_stack_with_all_method_call_permitted(SPVM_ENV* env) {
  
  SPVM_VALUE* stack = SPVM_API_new_stack(env);
  
  stack[SPVM_API_C_STACK_INDEX_ALL_METHOD_CALL_PERMITTED].ival = 1;
  
  return stack;
}

void SPVM_API_free_stack(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_API_set_exception(env, stack, NULL);
  
  SPVM_OBJECT** mortal_stack = (SPVM_OBJECT**)stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK].oval;
  
  if (mortal_stack != NULL) {
    SPVM_API_free_memory_block(env, stack, mortal_stack);
    mortal_stack = NULL;
  }
  
  // Free caller information stack
  SPVM_VALUE* caller_info_stack = stack[SPVM_API_C_STACK_INDEX_CALLER_INFO_STACK].address;
  if (caller_info_stack != NULL) {
    SPVM_API_free_memory_block(env, stack, caller_info_stack);
    caller_info_stack = NULL;
  }
  
  char** call_stack_memory_blocks = stack[SPVM_API_C_STACK_INDEX_CALL_STACK_MEMORY_BLOCKS].address;
  int32_t call_stack_memory_blocks_capacity = stack[SPVM_API_C_STACK_INDEX_CALL_STACK_MEMORY_BLOCKS_CAPACITY].ival;
  for (int32_t i = 0; i < call_stack_memory_blocks_capacity; i++) {
    char* call_stack_memory_block = call_stack_memory_blocks[i];
    SPVM_API_free_memory_block_for_call_stack(env, stack, call_stack_memory_block);
    call_stack_memory_block = NULL;
  }
  
  SPVM_API_free_memory_block_for_call_stack(env, stack, call_stack_memory_blocks);
  
  call_stack_memory_blocks = NULL;
  
  SPVM_RUNTIME_CALL_STACK_FRAME_INFO** call_stack_frame_infos = stack[SPVM_API_C_STACK_INDEX_CALL_STACK_FRAME_INFOS].address;
  SPVM_API_free_memory_block_for_call_stack(env, stack, call_stack_frame_infos);
  call_stack_frame_infos = NULL;
  
  assert(stack[SPVM_API_C_STACK_INDEX_MEMORY_BLOCKS_FOR_CALL_STACK].ival == 0);
  
  env->free_memory_block(env, stack, stack);
  stack = NULL;
}

int32_t SPVM_API_is_array(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t is_array;
  if (object) {
    is_array = object->type_dimension > 0;
  }
  else {
    is_array = 0;
  }
  
  return is_array;
}

int32_t SPVM_API_is_string(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t is_string;
  if (object) {
    SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
    is_string = (object_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_STRING && object->type_dimension == 0);
  }
  else {
    is_string = 0;
  }
  
  return is_string;
}

int32_t SPVM_API_is_numeric_array(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {

  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  int32_t is_numeric_array;
  if (object) {
    int32_t object_type_dimension = object->type_dimension;
    if (object_type_dimension == 0) {
      is_numeric_array = 0;
    }
    else if (object_type_dimension == 1) {
      SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
      int32_t object_basic_type_category = object_basic_type->category;
      switch (object_basic_type_category) {
        case SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_NUMERIC:
        {
          is_numeric_array = 1;
          break;
        }
        default: {
          is_numeric_array = 0;
        }
      }
    }
    else if (object_type_dimension > 1) {
      is_numeric_array = 0;
    }
    else {
      assert(0);
    }
  }
  else {
    is_numeric_array = 0;
  }
  
  return is_numeric_array;
}

int32_t SPVM_API_is_numeric_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t is_numeric_object = 0;
  if (object) {
    is_numeric_object = SPVM_API_is_numeric_object_type((SPVM_RUNTIME*)env->runtime, object->basic_type, object->type_dimension, 0);
  }
  
  return is_numeric_object;
}

int32_t SPVM_API_is_object_array(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;

  int32_t is_object_array;
  if (object) {
    int32_t object_type_dimension = object->type_dimension;
    if (object_type_dimension == 0) {
      is_object_array = 0;
    }
    else if (object_type_dimension == 1) {
      SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
      int32_t element_type_dimension = 0;
      int32_t type_flag = 0;
      is_object_array = SPVM_API_is_object_type((SPVM_RUNTIME*)env->runtime, object_basic_type, element_type_dimension, type_flag);
    }
    else if (object_type_dimension > 1) {
      is_object_array = 1;
    }
    else {
      assert(0);
    }
  }
  else {
    is_object_array = 0;
  }
  
  return is_object_array;
}

int32_t SPVM_API_is_mulnum_array(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {

  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;

  int32_t is_mulnum_array;
  if (object) {
    int32_t object_type_dimension = object->type_dimension;
    if (object_type_dimension == 0) {
      is_mulnum_array = 0;
    }
    else if (object_type_dimension == 1) {
      SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
      int32_t object_basic_type_category = object_basic_type->category;
      switch (object_basic_type_category) {
        case SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_MULNUM:
        {
          is_mulnum_array = 1;
          break;
        }
        default: {
          is_mulnum_array = 0;
        }
      }
    }
    else if (object_type_dimension > 1) {
      is_mulnum_array = 0;
    }
    else {
      assert(0);
    }
  }
  else {
    is_mulnum_array = 0;
  }

  return is_mulnum_array;
}

int32_t SPVM_API_is_class(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;

  int32_t is_class;
  if (object) {
    int32_t object_type_dimension = object->type_dimension;
    if (object_type_dimension == 0) {
      SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
      int32_t object_basic_type_category = object_basic_type->category;
      
      switch (object_basic_type_category) {
        case SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_CLASS: {
          is_class = 1;
          break;
        }
        default: {
          is_class = 0;
        }
      }
    }
    else {
      is_class = 0;
    }
  }
  else {
    is_class = 0;
  }
  
  return is_class;
}

int32_t SPVM_API_is_any_object_array(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t is_any_object_array;
  if (object) {
    SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
    is_any_object_array = (object_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_ANY_OBJECT && object->type_dimension == 1);
  }
  else {
    is_any_object_array = 0;
  }
  
  return is_any_object_array;
}

int32_t SPVM_API_is_pointer_class(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  int32_t is_pointer_class;
  if (object) {
    int32_t object_type_dimension = object->type_dimension;
    if (object_type_dimension == 0) {
      SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
      int32_t object_basic_type_category = object_basic_type->category;
      
      switch (object_basic_type_category) {
        case SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_CLASS: {
          int32_t basic_type_is_pointer = object_basic_type->is_pointer;
          
          if (basic_type_is_pointer) {
            is_pointer_class = 1;
          }
          else {
            is_pointer_class = 0;
          }
          break;
        }
        default: {
          is_pointer_class = 0;
        }
      }
    }
    else {
      is_pointer_class = 0;
    }
  }
  else {
    is_pointer_class = 0;
  }
  
  return is_pointer_class;
}

int32_t SPVM_API_get_elem_size(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* array) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  int32_t elem_size;
  if (array) {
    if (SPVM_API_is_string(env, stack, array)) {
      elem_size = 1;
    }
    else if (SPVM_API_is_object_array(env, stack, array)) {
      elem_size = sizeof(SPVM_OBJECT*);
    }
    else if (SPVM_API_is_numeric_array(env, stack, array)) {
      
      SPVM_RUNTIME_BASIC_TYPE* array_basic_type = SPVM_API_get_object_basic_type(env, stack, array);
      int32_t type_dimension = array->type_dimension;
      assert(type_dimension == 1);
      
      if (array_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE) {
        elem_size = 1;
      }
      else if (array_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT) {
        elem_size = 2;
      }
      else if (array_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_INT || array_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT) {
        elem_size = 4;
      }
      else if (array_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_LONG || array_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE) {
        elem_size = 8;
      }
      else {
        assert(0);
      }
    }
    else if (SPVM_API_is_mulnum_array(env, stack, array)) {
      SPVM_RUNTIME_BASIC_TYPE* array_basic_type = SPVM_API_get_object_basic_type(env, stack, array);
      int32_t type_dimension = array->type_dimension;
      assert(type_dimension == 1);
      
      int32_t fields_length = array_basic_type->fields_length;
      
      SPVM_RUNTIME_FIELD* first_field = SPVM_API_BASIC_TYPE_get_field_by_index(runtime, array_basic_type, 0);
      
      int32_t field_basic_type_id = first_field->basic_type->id;
      
      if (field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE) {
        elem_size = 1 * fields_length;
      }
      else if (field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT) {
        elem_size = 2 * fields_length;
      }
      else if (field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_INT || field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT) {
        elem_size = 4 * fields_length;
      }
      else if (field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_LONG || field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE) {
        elem_size = 8 * fields_length;
      }
      else {
        assert(0);
      }
    }
  }
  else {
    elem_size = 0;
  }
  
  return elem_size;
}

SPVM_OBJECT* SPVM_API_get_type_name_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  assert(object);
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
  int32_t type_dimension = object->type_dimension;
  
  int32_t length = 0;
  
  // Basic type
  length += strlen(basic_type_name);
  
  //[]
  length += type_dimension * 2;
  
  SPVM_OBJECT* obj_type_name = SPVM_API_new_string_no_mortal(env, stack, NULL, length);
  
  if (obj_type_name) {
    char* type_name = (char*)SPVM_API_get_chars(env, stack, obj_type_name);
    
    int32_t type_name_index = 0;
    sprintf((char*)type_name, "%s", basic_type_name);
    type_name_index += strlen(basic_type_name);
    
    int32_t dim_index;
    for (dim_index = 0; dim_index < type_dimension; dim_index++) {
      sprintf((char*)(type_name + type_name_index), "[]");
      type_name_index += 2;
    }
  }
  
  return obj_type_name;
}

SPVM_OBJECT* SPVM_API_get_type_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  
  SPVM_OBJECT* obj_type_name = SPVM_API_get_type_name_no_mortal(env, stack, object);
  
  SPVM_API_push_mortal(env, stack, obj_type_name);
  
  return obj_type_name;
}

int32_t SPVM_API_get_compile_type_name_length(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, int32_t type_dimension, int32_t type_flag) {
  
  int32_t compile_type_name_length = 0;
  
  // mutable
  if (type_flag & SPVM_NATIVE_C_TYPE_FLAG_MUTABLE) {
    compile_type_name_length += strlen("mutable ");
  }
  
  // Basic type
  compile_type_name_length += strlen(basic_type_name);
  
  // []
  compile_type_name_length += type_dimension * 2;
  
  // *
  if (type_flag & SPVM_NATIVE_C_TYPE_FLAG_REF) {
    compile_type_name_length += 1;
  }
  
  return compile_type_name_length;
}

SPVM_OBJECT* SPVM_API_get_compile_type_name_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, int32_t type_dimension, int32_t type_flag) {
  
  int32_t compile_type_name_length = SPVM_API_get_compile_type_name_length(env, stack, basic_type_name, type_dimension, type_flag);
  
  SPVM_OBJECT* obj_compile_type_name = SPVM_API_new_string_no_mortal(env, stack, NULL, compile_type_name_length);
  
  if (obj_compile_type_name) {
    char* compile_type_name = (char*)SPVM_API_get_chars(env, stack, obj_compile_type_name);
    
    if (type_flag & SPVM_NATIVE_C_TYPE_FLAG_MUTABLE) {
      sprintf(compile_type_name, "mutable ");
      compile_type_name += strlen("mutable ");
    }
    
    sprintf(compile_type_name, "%s", basic_type_name);
    compile_type_name += strlen(basic_type_name);
    
    for (int32_t type_dimension_index = 0; type_dimension_index < type_dimension; type_dimension_index++) {
      sprintf(compile_type_name, "[]");
      compile_type_name += 2;
    }
    
    // Reference
    if (type_flag & SPVM_NATIVE_C_TYPE_FLAG_REF) {
      sprintf(compile_type_name, "*");
      compile_type_name += 1;
    }
    
    *compile_type_name = '\0';
    compile_type_name++;
  }
  
  return obj_compile_type_name;
}

SPVM_OBJECT* SPVM_API_get_compile_type_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, int32_t type_dimension, int32_t type_flag) {
  
  SPVM_OBJECT* obj_compile_type_name = SPVM_API_get_compile_type_name_no_mortal(env, stack, basic_type_name, type_dimension, type_flag);
  
  SPVM_API_push_mortal(env, stack, obj_compile_type_name);
  
  return obj_compile_type_name;
}

void SPVM_API_fprint(SPVM_ENV* env, SPVM_VALUE* stack, FILE* fh, SPVM_OBJECT* string) {
  
  if (string) {
    const char* bytes = SPVM_API_get_chars(env, stack, string);
    int32_t string_length = SPVM_API_length(env, stack, string);
    
    if (string_length > 0) {
      fwrite(bytes, 1, string_length, fh);
    }
  }
}

void SPVM_API_print(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_API_fprint(env, stack, runtime->spvm_stdout, string);
}

void SPVM_API_say(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_API_fprint(env, stack, runtime->spvm_stdout, string);
  
  fputc('\n', runtime->spvm_stdout);
}

void SPVM_API_say_stderr(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_API_fprint(env, stack, runtime->spvm_stderr, string);
  
  fputc('\n', runtime->spvm_stderr);
}

void SPVM_API_warn(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string, const char* func_name, const char* file, int32_t line) {
  
  FILE* spvm_stderr = SPVM_API_RUNTIME_get_spvm_stderr((SPVM_RUNTIME*)env->runtime);
  
  if (string) {
    if (SPVM_API_is_type_by_name(env, stack, string, "string", 0)) {
      const char* chars =SPVM_API_get_chars(env, stack, string);
      
      int32_t string_length = SPVM_API_length(env, stack, string);
      
      if (string_length > 0) {
        size_t ret = fwrite(chars, 1, string_length, spvm_stderr);
      }
      
      fprintf(spvm_stderr, "\n  %s at %s line %d\n", func_name, file, line);
    }
    else {
      int32_t scope_id = SPVM_API_enter_scope(env, stack);
      SPVM_OBJECT* obj_type_name = SPVM_API_get_type_name(env, stack, string);
      const char* type_name = SPVM_API_get_chars(env, stack, obj_type_name);
      
      fprintf(spvm_stderr, "%s", type_name);
      fprintf(spvm_stderr, "(0x%" PRIxPTR ")\n  %s at %s line %d\n", (uintptr_t)string, func_name, file, line);
      SPVM_API_leave_scope(env, stack, scope_id);
    }
  }
  else {
    fprintf(spvm_stderr, "undef\n  %s at %s line %d\n", func_name, file, line);
  }
}

void SPVM_API_print_stderr(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_API_fprint(env, stack, runtime->spvm_stderr, string);
}

SPVM_OBJECT* SPVM_API_concat_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string1, SPVM_OBJECT* string2) {
  
  int32_t string1_length = SPVM_API_length(env, stack, string1);
  int32_t string2_length = SPVM_API_length(env, stack, string2);
  
  int32_t string3_length = string1_length + string2_length;
  SPVM_OBJECT* string3 = SPVM_API_new_string_no_mortal(env, stack, NULL, string3_length);
  
  if (string3) {
    const char* string1_bytes = SPVM_API_get_chars(env, stack, string1);
    const char* string2_bytes = SPVM_API_get_chars(env, stack, string2);
    char* string3_bytes = (char*)SPVM_API_get_chars(env, stack, string3);
    
    if (string1_length > 0) {
      memcpy(string3_bytes, string1_bytes, string1_length);
    }
    if (string2_length) {
      memcpy(string3_bytes + string1_length, string2_bytes, string2_length);
    }
  }
  
  return string3;
}

SPVM_OBJECT* SPVM_API_concat(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string1, SPVM_OBJECT* string2) {
  
  
  SPVM_OBJECT* str = SPVM_API_concat_no_mortal(env, stack, string1, string2);
  
  SPVM_API_push_mortal(env, stack, str);
  
  return str;
}

SPVM_OBJECT* SPVM_API_dump(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  
  SPVM_OBJECT* str = SPVM_API_dump_no_mortal(env, stack, object);
  
  SPVM_API_push_mortal(env, stack, str);
  
  return str;
}

SPVM_OBJECT* SPVM_API_dump_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  int32_t depth = 0;
  SPVM_STRING_BUFFER* string_buffer = SPVM_STRING_BUFFER_new(runtime->allocator, 255, SPVM_ALLOCATOR_C_ALLOC_TYPE_TMP);
  SPVM_HASH* address_symtable = SPVM_HASH_new(runtime->allocator, 255, SPVM_ALLOCATOR_C_ALLOC_TYPE_TMP);
  
  SPVM_API_dump_recursive(env, stack, object, &depth, string_buffer, address_symtable);
  
  int32_t string_buffer_length = string_buffer->length;
  
  SPVM_OBJECT* dump = SPVM_API_new_string_no_mortal(env, stack, string_buffer->string, string_buffer->length);
  
  SPVM_HASH_free(address_symtable);
  address_symtable = NULL;
  
  SPVM_STRING_BUFFER_free(string_buffer);
  string_buffer = NULL;
  
  return dump;
}

const char* SPVM_API_dumpc(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  
  SPVM_OBJECT* obj_dump = SPVM_API_dump(env, stack, object);
  
  const char* dump_chars = SPVM_API_get_chars(env, stack, obj_dump);
  
  return dump_chars;
}

// This code is copied from SPVM_TOKE_isprint_ascii
static int32_t isprint_ascii(int32_t ch) {
  
  int32_t isprint_ascii = 0;
  
  // Avoid isprint because it depends locale dependent.
  if (ch >= 0x20 && ch <= 0x7F) {
    isprint_ascii = 1;
  }
  
  return isprint_ascii;
}

void SPVM_API_dump_recursive(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t* depth, SPVM_STRING_BUFFER* string_buffer, SPVM_HASH* address_symtable) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  char tmp_buffer[SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE] = {0};
  
  SPVM_OBJECT* dump;
  if (object == NULL) {
    SPVM_STRING_BUFFER_add(string_buffer, "undef");
  }
  else {
    int32_t type_dimension = object->type_dimension;
    
    if (SPVM_API_is_string(env, stack, object)) {
      const char* chars = SPVM_API_get_chars(env, stack, object);
      int32_t chars_length  = SPVM_API_length(env, stack, object);
      
      SPVM_STRING_BUFFER_add(string_buffer, "\"");
      
      int32_t offset = 0;
      while (1) {
        
        if (offset >= chars_length) {
          break;
        }
        
        int32_t dst;
        int32_t utf8_char_len = (int32_t)SPVM_UTF8_iterate((const uint8_t*)(chars + offset), chars_length, &dst);
        
        int32_t uchar;
        if (utf8_char_len > 0) {
            
          if (utf8_char_len == 1 && !isprint_ascii(*(chars + offset))) {
            snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "\\x{%02X}", *(chars + offset) & 0xFF);
            
            SPVM_STRING_BUFFER_add_len(string_buffer, tmp_buffer, strlen(tmp_buffer));
          }
          else {
            SPVM_STRING_BUFFER_add_len(string_buffer, (char*)(chars + offset), utf8_char_len);
          }
          
          offset += utf8_char_len;
        }
        else {
          
          snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "\\x{%02X}", *(chars + offset) & 0xFF);
          
          SPVM_STRING_BUFFER_add_len(string_buffer, tmp_buffer, strlen(tmp_buffer));
          
          offset += 1;
        }
      }
      
      SPVM_STRING_BUFFER_add(string_buffer, "\"");
    }
    else if (type_dimension > 0) {
      int32_t array_length = SPVM_API_length(env, stack, object);
      int32_t element_type_dimension = type_dimension - 1;
      
      SPVM_STRING_BUFFER_add(string_buffer, "[\n");
            
      SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
      const char* object_basic_type_name =  object_basic_type->name;
          
      for (int32_t array_index = 0; array_index < array_length; array_index++) {
        
        for (int32_t depth_index = 0; depth_index < *depth + 1; depth_index++) {
          SPVM_STRING_BUFFER_add(string_buffer, "  ");
        }
        
        if (SPVM_API_is_mulnum_array(env, stack, object)) {
          
          SPVM_STRING_BUFFER_add(string_buffer, "{\n");
          
          int32_t fields_length = object_basic_type->fields_length;
          
          for (int32_t field_index = 0; field_index < fields_length; field_index++) {
            for (int32_t depth_index = 0; depth_index < *depth + 2; depth_index++) {
              SPVM_STRING_BUFFER_add(string_buffer, "  ");
            }
            
            SPVM_RUNTIME_FIELD* field = SPVM_API_BASIC_TYPE_get_field_by_index(runtime, object_basic_type, field_index);
            
            int32_t field_basic_type_id = field->basic_type->id;
            
            const char* field_name = field->name;
            SPVM_STRING_BUFFER_add(string_buffer, field_name);
            SPVM_STRING_BUFFER_add(string_buffer, " => ");
            
            switch (field_basic_type_id) {
              case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE: {
                int8_t* element = &((int8_t*)(GET_DATA_ADDRESS(env, stack, object)))[array_index * fields_length];
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%d", element[field_index]);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT: {
                int16_t* element = &((int16_t*)(GET_DATA_ADDRESS(env, stack, object)))[array_index * fields_length];
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%d", element[field_index]);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              case SPVM_NATIVE_C_BASIC_TYPE_ID_INT: {
                int32_t* element = &((int32_t*)(GET_DATA_ADDRESS(env, stack, object)))[array_index * fields_length];
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%d", element[field_index]);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG: {
                int64_t* element = &((int64_t*)(GET_DATA_ADDRESS(env, stack, object)))[array_index * fields_length];
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%lld", (long long int)element[field_index]);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT: {
                float* element = &((float*)(GET_DATA_ADDRESS(env, stack, object)))[array_index * fields_length];
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%g", element[field_index]);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE: {
                double* element = &((double*)(GET_DATA_ADDRESS(env, stack, object)))[array_index * fields_length];
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%g", element[field_index]);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
            }
            
            SPVM_STRING_BUFFER_add(string_buffer, ",\n");
          }
          
          SPVM_STRING_BUFFER_add(string_buffer, "  }");
        }
        else if (SPVM_API_is_numeric_array(env, stack, object)) {
          switch (object_basic_type->id) {
            case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE: {
              int8_t element = ((int8_t*)(GET_DATA_ADDRESS(env, stack, object)))[array_index];
              snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%d", element);
              SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
              break;
            }
            case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT: {
              int16_t element = ((int16_t*)(GET_DATA_ADDRESS(env, stack, object)))[array_index];
              snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%d", element);
              SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
              break;
            }
            case SPVM_NATIVE_C_BASIC_TYPE_ID_INT: {
              int32_t element = ((int32_t*)(GET_DATA_ADDRESS(env, stack, object)))[array_index];
              snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%d", element);
              SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
              break;
            }
            case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG: {
              int64_t element = ((int64_t*)(GET_DATA_ADDRESS(env, stack, object)))[array_index];
              snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%lld", (long long int)element);
              SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
              break;
            }
            case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT: {
              float element = ((float*)(GET_DATA_ADDRESS(env, stack, object)))[array_index];
              snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%g", element);
              SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
              break;
            }
            case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE: {
              double element = ((double*)(GET_DATA_ADDRESS(env, stack, object)))[array_index];
              snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%g", element);
              SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
              break;
            }
          }
        }
        else if (SPVM_API_is_object_array(env, stack, object)) {
          SPVM_OBJECT* element = (((SPVM_OBJECT**)(GET_DATA_ADDRESS(env, stack, object)))[array_index]);
          element = SPVM_API_drop_tag_pointers(env, stack, element);
          (*depth)++;
          SPVM_API_dump_recursive(env, stack, element, depth, string_buffer, address_symtable);
          (*depth)--;
        }
        else {
          assert(0);
        }
        
        SPVM_STRING_BUFFER_add(string_buffer, ",\n");
      }
      
      for (int32_t depth_index = 0; depth_index < *depth; depth_index++) {
        SPVM_STRING_BUFFER_add(string_buffer, "  ");
      }
      SPVM_STRING_BUFFER_add(string_buffer, "]");
      
      SPVM_STRING_BUFFER_add(string_buffer, " : ");
      SPVM_STRING_BUFFER_add(string_buffer, object_basic_type_name);
      for (int32_t type_dimension_index = 0; type_dimension_index < type_dimension; type_dimension_index++) {
        SPVM_STRING_BUFFER_add(string_buffer, "[]");
      }
      
      // If the object is weaken, this get the real address
      snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "(%p)", SPVM_API_drop_tag_pointers(env, stack, object));
      SPVM_STRING_BUFFER_add(string_buffer, tmp_buffer);
    }
    else {
      // If the object is weaken, this get the real address
      snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%p", SPVM_API_drop_tag_pointers(env, stack, object));
      int32_t exists = (int32_t)(intptr_t)SPVM_HASH_get(address_symtable, tmp_buffer, strlen(tmp_buffer));
      if (exists) {
        // If the object is weaken, this get the real address
        snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "REUSE_OBJECT(%p)", SPVM_API_drop_tag_pointers(env, stack, object));
        SPVM_STRING_BUFFER_add(string_buffer, tmp_buffer);
      }
      else {
        SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
        const char* basic_type_name = object_basic_type->name;
        
        SPVM_HASH_set(address_symtable, tmp_buffer, strlen(tmp_buffer), (void*)(intptr_t)1);
        
        SPVM_STRING_BUFFER_add(string_buffer, basic_type_name);
        snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, " (%p) ", object);
        SPVM_STRING_BUFFER_add(string_buffer, tmp_buffer);
        
        SPVM_STRING_BUFFER_add(string_buffer, "{\n");
        
        // Free object fields
        int32_t fields_length = object_basic_type->fields_length;
        for (int32_t field_index = 0; field_index < fields_length; field_index++) {
          SPVM_RUNTIME_FIELD* field = SPVM_API_BASIC_TYPE_get_field_by_index(runtime, object_basic_type, field_index);
          
          int32_t field_basic_type_id = field->basic_type->id;
          int32_t field_type_dimension = field->type_dimension;
          int32_t field_offset = field->offset;
          const char* field_name = field->name;
          
          for (int32_t depth_index = 0; depth_index < *depth + 1; depth_index++) {
            SPVM_STRING_BUFFER_add(string_buffer, "  ");
          }
          
          SPVM_STRING_BUFFER_add(string_buffer, field_name);
          SPVM_STRING_BUFFER_add(string_buffer, " => ");
          if (field_type_dimension == 0 && field_basic_type_id >= SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE && field_basic_type_id <= SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE) {
            switch (field_basic_type_id) {
              case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE: {
                int8_t field_value = *(int8_t*)(GET_DATA_ADDRESS(env, stack, object) + field_offset);
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%d", field_value);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT: {
                int16_t field_value = *(int16_t*)(GET_DATA_ADDRESS(env, stack, object) + field_offset);
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%d", field_value);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              case SPVM_NATIVE_C_BASIC_TYPE_ID_INT: {
                int32_t field_value = *(int32_t*)(GET_DATA_ADDRESS(env, stack, object) + field_offset);
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%d", field_value);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG: {
                int64_t field_value = *(int64_t*)(GET_DATA_ADDRESS(env, stack, object) + field_offset);
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%lld", (long long int)field_value);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT: {
                float field_value = *(float*)(GET_DATA_ADDRESS(env, stack, object) + field_offset);
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%g", field_value);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE: {
                double field_value = *(double*)(GET_DATA_ADDRESS(env, stack, object) + field_offset);
                snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%g", field_value);
                SPVM_STRING_BUFFER_add(string_buffer, (const char*)tmp_buffer);
                break;
              }
              default : {
                assert(0);
              }
            }
          }
          else  {
            SPVM_OBJECT* field_value = *(SPVM_OBJECT**)(GET_DATA_ADDRESS(env, stack, object) + field_offset);
            field_value = SPVM_API_drop_tag_pointers(env, stack, field_value);
            (*depth)++;
            SPVM_API_dump_recursive(env, stack, field_value, depth, string_buffer, address_symtable);
            (*depth)--;
          }
          
          SPVM_STRING_BUFFER_add(string_buffer, ",\n");
        }
        
        for (int32_t depth_index = 0; depth_index < *depth; depth_index++) {
          SPVM_STRING_BUFFER_add(string_buffer, "  ");
        }
        SPVM_STRING_BUFFER_add(string_buffer, "}");
      }
    }
  }
}

void SPVM_API_make_read_only(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string) {
  SPVM_RUNTIME_BASIC_TYPE* string_basic_type = SPVM_API_get_object_basic_type(env, stack, string);
  if (string && string_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_STRING && string->type_dimension == 0) {
    __sync_fetch_and_or(&string->flag, SPVM_OBJECT_C_FLAG_IS_READ_ONLY);
  }
}

void SPVM_API_make_fixed_length(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
  if (object && object_basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_STRING || object->type_dimension > 0) {
    __sync_fetch_and_or(&object->flag, SPVM_OBJECT_C_FLAG_IS_FIXED_LENGTH);
  }
}

int32_t SPVM_API_is_read_only(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string) {
  
  int32_t is_read_only;
  if (string) {
    if (string->flag & SPVM_OBJECT_C_FLAG_IS_READ_ONLY) {
      is_read_only = 1;
    }
    else {
      is_read_only = 0;
    }
  }
  else {
    is_read_only = 0;
  }
  
  return is_read_only;
}

int32_t SPVM_API_is_fixed_length(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t is_fixed_length;
  if (object) {
    if (object->flag & SPVM_OBJECT_C_FLAG_IS_FIXED_LENGTH) {
      is_fixed_length = 1;
    }
    else {
      is_fixed_length = 0;
    }
  }
  else {
    is_fixed_length = 0;
  }
  
  return is_fixed_length;
}

int32_t SPVM_API_set_exception(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* exception) {
  
  SPVM_OBJECT** current_exception_ptr = (SPVM_OBJECT**)&stack[SPVM_API_C_STACK_INDEX_EXCEPTION];
  
  SPVM_API_assign_object(env, stack, current_exception_ptr, exception);
  
  return 0;
}

SPVM_OBJECT* SPVM_API_get_exception(SPVM_ENV* env, SPVM_VALUE* stack){

  SPVM_OBJECT** current_exception_ptr = (SPVM_OBJECT**)&stack[SPVM_API_C_STACK_INDEX_EXCEPTION];
  SPVM_OBJECT* current_exception = *current_exception_ptr;
  
  return current_exception;
}

SPVM_OBJECT* SPVM_API_new_byte_array(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  SPVM_OBJECT* object = SPVM_API_new_byte_array_no_mortal(env, stack, length);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_short_array(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  SPVM_OBJECT* object = SPVM_API_new_short_array_no_mortal(env, stack, length);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_int_array(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  SPVM_OBJECT* object = SPVM_API_new_int_array_no_mortal(env, stack, length);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_long_array(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  SPVM_OBJECT* object = SPVM_API_new_long_array_no_mortal(env, stack, length);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_float_array(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  SPVM_OBJECT* object = SPVM_API_new_float_array_no_mortal(env, stack, length);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_double_array(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  SPVM_OBJECT* object = SPVM_API_new_double_array_no_mortal(env, stack, length);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_string_array(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  SPVM_OBJECT* string_array = SPVM_API_new_string_array_no_mortal(env, stack, length);
  
  SPVM_API_push_mortal(env, stack, string_array);
  
  return string_array;
}

SPVM_OBJECT* SPVM_API_new_string_array_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  SPVM_RUNTIME_BASIC_TYPE* string_basic_type = SPVM_API_RUNTIME_get_basic_type_by_id((SPVM_RUNTIME*)env->runtime, SPVM_NATIVE_C_BASIC_TYPE_ID_STRING);
  
  SPVM_OBJECT* object = SPVM_API_new_object_array_no_mortal(env, stack, string_basic_type, length);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type) {
  
  SPVM_OBJECT* object = SPVM_API_new_object_no_mortal(env, stack, basic_type);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_object_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (!basic_type) {
    assert(0);
  }
  
  if (basic_type->category != SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_CLASS) {
    assert(0);
  }
  
  // Alloc body length + 1
  int32_t fields_length = basic_type->fields_length;
  
  int32_t exists_byte_blocks_length = (fields_length + 7) / 8;
  
  size_t data_size = (size_t)basic_type->fields_size +(sizeof(uint8_t) * exists_byte_blocks_length);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, 0, fields_length, 0);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_pointer_object_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type, void* pointer) {
  
  SPVM_OBJECT* obj_object = SPVM_API_new_object_no_mortal(env, stack, basic_type);
  
  if (obj_object) {
    SPVM_API_set_pointer(env, stack, obj_object, pointer);
  }
  
  return obj_object;
}

SPVM_OBJECT* SPVM_API_new_pointer_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type, void* pointer) {
  
  
  SPVM_OBJECT* object = SPVM_API_new_pointer_object_no_mortal(env, stack, basic_type, pointer);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_string_nolen_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, const char* bytes) {
  
  
  int32_t length = strlen((char*)bytes);
  
  SPVM_OBJECT* object = SPVM_API_new_string_no_mortal(env, stack, NULL, length);
  
  if (object) {
    if (bytes != NULL && length > 0) {
      memcpy((void*)(GET_DATA_ADDRESS(env, stack, object)), (char*)bytes, length);
    }
  }
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_string_nolen(SPVM_ENV* env, SPVM_VALUE* stack, const char* bytes) {
  
  
  SPVM_OBJECT* object = SPVM_API_new_string_nolen_no_mortal(env, stack, bytes);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_string_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, const char* bytes, int32_t length) {
  
  if (length < 0) {
    return NULL;
  }
  
  size_t data_size = (size_t)sizeof(char) * (length + 1);
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type_by_id(env, stack, SPVM_NATIVE_C_BASIC_TYPE_ID_STRING);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, 0, length, 0);
  
  if (object) {
    if (bytes != NULL && length > 0) {
      memcpy((void*)(GET_DATA_ADDRESS(env, stack, object)), (char*)bytes, length);
    }
  }
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_string(SPVM_ENV* env, SPVM_VALUE* stack, const char* bytes, int32_t length) {
  
  
  SPVM_OBJECT* object = SPVM_API_new_string_no_mortal(env, stack, bytes, length);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_byte_array_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  if (length < 0) {
    return NULL;
  }
  
  size_t data_size = (size_t)sizeof(int8_t) * (length + 1);
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type_by_id(env, stack, SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, 1, length, 0);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_short_array_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  if (length < 0) {
    return NULL;
  }
  
  size_t data_size = (size_t)sizeof(int16_t) * (length + 1);
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type_by_id(env, stack, SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, 1, length, 0);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_int_array_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  if (length < 0) {
    return NULL;
  }
  
  size_t data_size = (size_t)sizeof(int32_t) * (length + 1);
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type_by_id(env, stack, SPVM_NATIVE_C_BASIC_TYPE_ID_INT);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, 1, length, 0);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_long_array_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  if (length < 0) {
    return NULL;
  }
  
  size_t data_size = (size_t)sizeof(int64_t) * (length + 1);
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type_by_id(env, stack, SPVM_NATIVE_C_BASIC_TYPE_ID_LONG);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, 1, length, 0);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_float_array_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  if (length < 0) {
    return NULL;
  }
  
  size_t data_size = (size_t)sizeof(float) * (length + 1);
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type_by_id(env, stack, SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, 1, length, 0);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_double_array_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, int32_t length) {
  
  if (length < 0) {
    return NULL;
  }
  
  size_t data_size = (size_t)sizeof(double) * (length + 1);
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type_by_id(env, stack, SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, 1, length, 0);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_object_array_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t length) {
  
  if (length < 0) {
    return NULL;
  }
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_OBJECT object_for_type_check;
  object_for_type_check.basic_type = basic_type;
  object_for_type_check.type_dimension = 1;
  
  int32_t is_object_array = SPVM_API_is_object_array(env, stack, &object_for_type_check);
  
  if (!is_object_array) {
    return NULL;
  }
  
  if (!basic_type) {
    return NULL;
  }
  
  size_t data_size = (size_t)sizeof(SPVM_OBJECT*) * (length + 1);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, 1, length, 0);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_object_array(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t length) {
  
  SPVM_OBJECT* object = SPVM_API_new_object_array_no_mortal(env, stack, basic_type, length);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_muldim_array_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t length) {
  
  if (length < 0) {
    return NULL;
  }
  
  if (type_dimension < 2) {
    return NULL;
  }
  else if (basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_ANY_OBJECT) {
    return NULL;
  }
  
  if (!basic_type) {
    return NULL;
  }
  
  size_t data_size = (size_t)sizeof(SPVM_OBJECT*) * (length + 1);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, type_dimension, length, 0);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_muldim_array(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t length) {
  
  SPVM_OBJECT* object = SPVM_API_new_muldim_array_no_mortal(env, stack, basic_type, type_dimension, length);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_mulnum_array_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t length) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  // valut_t array dimension must be 1
  const char* basic_type_name = basic_type->name;
  
  int32_t fields_length = basic_type->fields_length;
  SPVM_RUNTIME_FIELD* field_first = SPVM_API_BASIC_TYPE_get_field_by_index(runtime, basic_type, 0);
  
  int32_t field_basic_type_id = field_first->basic_type->id;
  
  int32_t unit_size;
  if (field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE) {
    unit_size = sizeof(int8_t);
  }
  else if (field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT) {
    unit_size = sizeof(int16_t);
  }
  else if (field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_INT) {
    unit_size = sizeof(int32_t);
  }
  else if (field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_LONG) {
    unit_size = sizeof(int64_t);
  }
  else if (field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT) {
    unit_size = sizeof(float);
  }
  else if (field_basic_type_id == SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE) {
    unit_size = sizeof(double);
  }
  else {
    assert(0);
  }
  
  size_t data_size = (size_t)unit_size * fields_length * (length + 1);
  
  SPVM_OBJECT* object = SPVM_API_new_object_common(env, stack, data_size, basic_type, 1, length, 0);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_mulnum_array(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t length) {
  
  SPVM_OBJECT* object = SPVM_API_new_mulnum_array_no_mortal(env, stack, basic_type, length);
  
  SPVM_API_push_mortal(env, stack, object);
  
  return object;
}

SPVM_OBJECT* SPVM_API_new_array_proto_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* array, int32_t length) {
  
  if (array == NULL) {
    return NULL;
  }
  
  if (!SPVM_API_is_array(env, stack, array)) {
    return NULL;
  }
  
  if (length < 0) {
    return NULL;
  }
  
  size_t element_size = SPVM_API_get_elem_size(env, stack, array);
  
  size_t data_size = (size_t)element_size * (length + 1);
  
  SPVM_RUNTIME_BASIC_TYPE* array_basic_type = SPVM_API_get_object_basic_type(env, stack, array);
  SPVM_OBJECT* new_array = SPVM_API_new_object_common(env, stack, data_size, array_basic_type, array->type_dimension, length, 0);
  
  return new_array;
}

SPVM_OBJECT* SPVM_API_new_array_proto(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* array, int32_t length) {
  
  
  SPVM_OBJECT* new_array = SPVM_API_new_array_proto_no_mortal(env, stack, array, length);
  
  SPVM_API_push_mortal(env, stack, new_array);
  
  return new_array;
}

int32_t SPVM_API_get_object_basic_type_id(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  return object->basic_type->id;
}

SPVM_RUNTIME_BASIC_TYPE* SPVM_API_get_object_basic_type(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  return object->basic_type;
}

const char* SPVM_API_get_object_basic_type_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
  
  return object_basic_type->name;
}

int32_t SPVM_API_get_object_type_dimension(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  return object->type_dimension;
}

int32_t SPVM_API_length(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t length = object->length;
  
  return length;
}

int32_t SPVM_API_capacity(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t capacity = object->capacity;
  
  return capacity;
}

int32_t SPVM_API_set_length(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t length) {
  
  int32_t error_id = 0;
  
  if (!object) {
    return SPVM_API_die(env, stack, "set_length failed: the object must be defined.", __func__, FILE_NAME, __LINE__);
  }
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = object->basic_type;
  int32_t type_dimension = object->type_dimension;
  if (!SPVM_API_is_dynamic_data_type(env, stack, basic_type, type_dimension, 0)) {
    return SPVM_API_die(env, stack, "set_length failed: the type of the object must be string type or an array type.", __func__, FILE_NAME, __LINE__);
  }
  
  int32_t flag = object->flag;
  if (flag & SPVM_OBJECT_C_FLAG_IS_FIXED_LENGTH) {
    return SPVM_API_die(env, stack, "set_length failed: the object must not be a fixed length object.", __func__, FILE_NAME, __LINE__);
  }
  if (flag & SPVM_OBJECT_C_FLAG_IS_READ_ONLY) {
    return SPVM_API_die(env, stack, "set_capacity failed: the object must not be a read-only object.", __func__, FILE_NAME, __LINE__);
  }
  
  if (!(length >= 0)) {
    return SPVM_API_die(env, stack, "set_length failed: the length must be a non-negative number.", __func__, FILE_NAME, __LINE__);
  }
  
  if (length + 1 > object->capacity) {
    error_id = SPVM_API_set_capacity(env, stack, object, length + 1);
    if (error_id) {
      return error_id;
    }
  }
  
  if (length < object->length) {
    if (SPVM_API_is_object_array(env, stack, object)) {
      for (int32_t i = length; i < object->length; i++) {
        SPVM_API_set_elem_object(env, stack, object, i, NULL);
      }
    }
    else {
      int32_t elem_size = SPVM_API_get_elem_size(env, stack, object);
      char* data = (char*)SPVM_API_get_chars(env, stack, object);
      memset(data + (elem_size * length), 0, elem_size * (object->length - length));
    }
  }
  
  object->length = length;
  
  return error_id;
}

int32_t SPVM_API_set_capacity(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t capacity) {
  
  int32_t error_id = 0;
  
  if (!object) {
    return SPVM_API_die(env, stack, "set_capacity failed: the object must be defined.", __func__, FILE_NAME, __LINE__);
  }
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = object->basic_type;
  int32_t type_dimension = object->type_dimension;
  
  if (!SPVM_API_is_dynamic_data_type(env, stack, basic_type, type_dimension, 0)) {
    return SPVM_API_die(env, stack, "set_capacity failed: the type of the object must be string type or an array type.", __func__, FILE_NAME, __LINE__);
  }
  
  int32_t flag = object->flag;
  if (flag & SPVM_OBJECT_C_FLAG_IS_FIXED_LENGTH) {
    return SPVM_API_die(env, stack, "set_capacity failed: the object must not be a fixed length object.", __func__, FILE_NAME, __LINE__);
  }
  if (flag & SPVM_OBJECT_C_FLAG_IS_READ_ONLY) {
    return SPVM_API_die(env, stack, "set_capacity failed: the object must not be a read-only object.", __func__, FILE_NAME, __LINE__);
  }
  
  if (capacity < object->length + 1) {
    return SPVM_API_die(env, stack, "set_capacity failed: too small capacity.", __func__, FILE_NAME, __LINE__);
  }
  
  int32_t elem_size = SPVM_API_get_elem_size(env, stack, object);
  char* new_data = SPVM_API_new_memory_block(env, stack, elem_size * capacity);
  if (!new_data) {
    return SPVM_API_die(env, stack, "set_capacity failed: cannot allocate memory.", __func__, FILE_NAME, __LINE__);
  }
  char* data = (char*)SPVM_API_get_chars(env, stack, object);
  memcpy(new_data, data, elem_size * object->length);
  SPVM_API_free_memory_block(env, stack, data);
  object->data = new_data;;
  
  object->capacity = capacity;
  
  return error_id;
}

int8_t* SPVM_API_get_elems_byte(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  assert(object);
  
  return (int8_t*)(GET_DATA_ADDRESS(env, stack, object));
}

const char* SPVM_API_get_chars(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string) {
  
  assert(string);
  
  return (const char*)(GET_DATA_ADDRESS(env, stack, string));
}

int16_t* SPVM_API_get_elems_short(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  assert(object);
  
  return (int16_t*)(GET_DATA_ADDRESS(env, stack, object));
}

int32_t* SPVM_API_get_elems_int(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  assert(object);
  
  return (int32_t*)(GET_DATA_ADDRESS(env, stack, object));
}

int64_t* SPVM_API_get_elems_long(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  assert(object);
  
  return (int64_t*)(GET_DATA_ADDRESS(env, stack, object));
}

float* SPVM_API_get_elems_float(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  assert(object);
  
  return (float*)(GET_DATA_ADDRESS(env, stack, object));
}

double* SPVM_API_get_elems_double(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  assert(object);
  
  return (double*)(GET_DATA_ADDRESS(env, stack, object));
}

SPVM_OBJECT* SPVM_API_get_elem_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* array, int32_t index) {
  
  assert(array);
  
  SPVM_OBJECT* object_maybe_weaken = ((SPVM_OBJECT**)(GET_DATA_ADDRESS(env, stack, array)))[index];
  SPVM_OBJECT* object = SPVM_API_drop_tag_pointers(env, stack, object_maybe_weaken);
  
  return object;
}

void SPVM_API_set_elem_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* array, int32_t index, SPVM_OBJECT* object) {
  
  assert(array);
  
  SPVM_OBJECT** ref = &((SPVM_OBJECT**)(GET_DATA_ADDRESS(env, stack, array)))[index];
  
  SPVM_API_assign_object(env, stack, ref, object);
}

SPVM_OBJECT* SPVM_API_get_elem_string(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* array, int32_t index) {
  
  return SPVM_API_get_elem_object(env, stack, array, index);
}

void SPVM_API_set_elem_string(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* array, int32_t index, SPVM_OBJECT* string) {
  
  SPVM_API_set_elem_object(env, stack, array, index, string);
}

void* SPVM_API_get_pointer(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  void* pointer = SPVM_NATIVE_GET_POINTER(object);
  
  return pointer;
}

void SPVM_API_set_pointer(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, void* pointer) {
  
  SPVM_NATIVE_SET_POINTER(object, pointer);
}

SPVM_RUNTIME_FIELD* SPVM_API_get_field(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (!object) {
    return NULL;
  }
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
  
  if (object->type_dimension != 0) {
    return NULL;
  }
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_BASIC_TYPE_get_field_by_name(runtime, object_basic_type, field_name);
  
  return field;
}

SPVM_RUNTIME_FIELD* SPVM_API_get_field_static(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* field_name) {
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  
  if (!basic_type) {
    return NULL;
  }
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_BASIC_TYPE_get_field_by_name((SPVM_RUNTIME*)env->runtime, basic_type, field_name);
  
  return field;
}

SPVM_RUNTIME_CLASS_VAR* SPVM_API_get_class_var(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, const char* class_var_name) {
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  
  if (!basic_type) {
    return NULL;
  }
  
  SPVM_RUNTIME_CLASS_VAR* class_var = SPVM_API_BASIC_TYPE_get_class_var_by_name((SPVM_RUNTIME*)env->runtime, basic_type, class_var_name);
  
  return class_var;
}

void* SPVM_API_new_memory_block(SPVM_ENV* env, SPVM_VALUE* stack, size_t size) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  void* block = SPVM_ALLOCATOR_alloc_memory_block_unmanaged((size_t)size);
  
  if (block) {
    int32_t save_memory_blocks_count = __sync_fetch_and_add(&runtime->memory_blocks_count, 1);
    
#ifdef SPVM_DEBUG_MEMORY
    fprintf(runtime->spvm_stderr, "[Debug]Function : new_memory_block, Block Address: %p, Stack Address : %p, Memory Blocks Count : %d.\n", block, stack, save_memory_blocks_count + 1);
#endif
  }
  
  return block;
}

// Deprecated
void SPVM_API_free_memory_block(SPVM_ENV* env, SPVM_VALUE* stack, void* block) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (block) {
    SPVM_ALLOCATOR_free_memory_block_unmanaged(block);
    
    int32_t save_memory_blocks_count = __sync_fetch_and_add(&runtime->memory_blocks_count, -1);
    
#ifdef SPVM_DEBUG_MEMORY
    fprintf(runtime->spvm_stderr, "[Debug]Function : free_memory_block, Block Address: %p, Stack Address : %p, Memory Blocks Count : %d.\n", block, stack, save_memory_blocks_count - 1);
#endif
      
  }
}

int32_t SPVM_API_get_memory_blocks_count(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  int32_t memory_blocks_count = runtime->memory_blocks_count;
  
  return memory_blocks_count;
}

SPVM_OBJECT* SPVM_API_copy_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  if (!object) {
    return NULL;
  }
  
  SPVM_OBJECT* new_object;
  
  int32_t length = SPVM_API_length(env, stack, object);
  
  if (SPVM_API_is_string(env, stack, object)) {
    new_object = SPVM_API_new_string_no_mortal(env, stack, NULL, length);
    
    const char* object_chars = SPVM_API_get_chars(env, stack, object);
    char* new_object_chars = (char*)SPVM_API_get_chars(env, stack, new_object);
    
    memcpy(new_object_chars, object_chars, length);
  }
  else if (SPVM_API_is_numeric_array(env, stack, object) || SPVM_API_is_mulnum_array(env, stack, object)) {
    new_object = SPVM_API_new_array_proto_no_mortal(env, stack, object, length);
    
    const char* object_bytes = (const char*)SPVM_API_get_elems_byte(env, stack, object);
    char* new_object_bytes = (char*)SPVM_API_get_elems_byte(env, stack, new_object);
    
    size_t element_size = SPVM_API_get_elem_size(env, stack, object);
    
    memcpy(new_object_bytes, object_bytes, element_size * length);
  }
  else {
    assert(0);
  }
  
  return new_object;
}

SPVM_OBJECT* SPVM_API_copy(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  
  SPVM_OBJECT* new_object = SPVM_API_copy_no_mortal(env, stack, object);
  
  SPVM_API_push_mortal(env, stack, new_object);
  
  return new_object;
}

void SPVM_API_shorten(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* string, int32_t new_length) {
  
  if (!string) {
    return;
  }
  
  if (!SPVM_API_is_string(env, stack, string)) {
    return;
  }
  
  if (SPVM_API_is_read_only(env, stack, string)) {
    return;
  }
  
  int32_t length = SPVM_API_length(env, stack, string);
  
  if (!(new_length >= 0)) {
    return;
  }
  
  if (!(new_length < length)) {
    return;
  }
  
  string->length = new_length;
  
  char* chars = (char*)SPVM_API_get_chars(env, stack, string);
  
  assert(length - new_length > 0);
  
  memset(chars + new_length, 0, length - new_length);
}

int32_t SPVM_API_elem_isa(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* array, SPVM_OBJECT* element) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  assert(array);
  
  SPVM_RUNTIME_BASIC_TYPE* array_basic_type = SPVM_API_get_object_basic_type(env, stack,array);
  
  int32_t array_type_dimension = array->type_dimension;
  
  assert(array_type_dimension > 0);
  int32_t elem_isa = SPVM_API_isa(env, stack, element, array_basic_type, array_type_dimension - 1);
  
  return elem_isa;
}

int32_t SPVM_API_is_type(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension) {
  
  assert(object);
  
  int32_t is_type = 0;
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = SPVM_API_get_object_basic_type(env, stack, object);
  if (object_basic_type->id == basic_type->id && object->type_dimension == type_dimension) {
    is_type = 1;
  }
  
  return is_type;
}

int32_t SPVM_API_is_type_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* basic_type_name, int32_t type_dimension) {
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  
  if (!basic_type) {
    return 0;
  };
  
  int32_t is_type = SPVM_API_is_type(env, stack, object, basic_type, type_dimension);
  
  return is_type;
}

int32_t SPVM_API_isa(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  int32_t isa = 0;
  if (object == NULL) {
    isa = 1;
  }
  else {
    SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
    int32_t object_type_dimension = object->type_dimension;
    if (!basic_type) {
      isa = 0;
    }
    else {
      // Check type by an easy way at first because SPVM_API_can_assign uses hash and mutex lock.
      if (type_dimension == object_type_dimension) {
        if (object_basic_type->id == basic_type->id) {
          isa = 1;
        }
        else {
          if (basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_ANY_OBJECT && type_dimension == 1) {
            if (object_basic_type->is_object_type && object->type_dimension > 0) {
              isa = 1;
            }
          }
          else {
            SPVM_RUNTIME_BASIC_TYPE* parent_object_basic_type = object_basic_type->parent;
            while (1) {
              if (parent_object_basic_type) {
                if (parent_object_basic_type->id == basic_type->id) {
                  isa = 1;
                  break;
                }
                else {
                  parent_object_basic_type = parent_object_basic_type->parent;
                }
              }
              else {
                break;
              }
            }
          }
        }
      }
      
      if (!isa) {
        isa = SPVM_API_can_assign((SPVM_RUNTIME*)env->runtime, basic_type, type_dimension, 0, object_basic_type, object_type_dimension, 0);
      }
    }
  }
  
  return isa;
}

int32_t SPVM_API_isa_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* basic_type_name, int32_t type_dimension) {
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    return 0;
  };
  
  int32_t isa = SPVM_API_isa(env, stack, object, basic_type, type_dimension);
  
  return isa;
}

int32_t SPVM_API_get_basic_type_id_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  *error_id = 0;
  
  int32_t basic_type_id = SPVM_API_get_basic_type_id(env, stack, basic_type_name);
  if (basic_type_id < 0) {
    *error_id = SPVM_API_die(env, stack, "The %s basic type is not found.", func_name, file, line, basic_type_name);
  };
  return basic_type_id;
}

SPVM_RUNTIME_BASIC_TYPE* SPVM_API_get_basic_type_by_name(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  *error_id = 0;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type(env, stack, basic_type_name);
  if (!basic_type) {
    *error_id = SPVM_API_die(env, stack, "The %s basic type is not found.", func_name, file, line, basic_type_name);
  };
  
  return basic_type;
}

SPVM_RUNTIME_BASIC_TYPE* SPVM_API_get_basic_type_by_id(SPVM_ENV* env, SPVM_VALUE* stack, int32_t basic_type_id) {
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_RUNTIME_get_basic_type_by_id((SPVM_RUNTIME*)env->runtime, basic_type_id);
  
  return basic_type;
}

SPVM_OBJECT* SPVM_API_strerror_string(SPVM_ENV* env, SPVM_VALUE* stack, int32_t errno_value, int32_t length) {
  
  assert(length >= 0);
  
  if (length == 0) {
    // Linux maybe needs at least 49.
    // Windows needs at least 94.
    length = 128;
  }
  
  SPVM_OBJECT* obj_strerror_value = SPVM_API_new_string(env, stack, NULL, length);
  char* strerror_value = (char*)SPVM_API_get_chars(env, stack, obj_strerror_value);
  
  int32_t status = SPVM_STRERROR_strerror(errno_value, strerror_value, length);
  
  if (!(status == 0)) {
    errno = status;
    sprintf(strerror_value, "strerror failed. errno is %d.", errno);
  }
  
  SPVM_API_shorten(env, stack, obj_strerror_value, strlen(strerror_value));
  
  return obj_strerror_value;
}

const char* SPVM_API_strerror(SPVM_ENV* env, SPVM_VALUE* stack, int32_t errno_value, int32_t length) {
  
  SPVM_OBJECT* obj_strerror_value = SPVM_API_strerror_string(env, stack, errno_value, length);
  
  if (obj_strerror_value) {
    char* strerror_value = (char*)SPVM_API_get_chars(env, stack, obj_strerror_value);
    return strerror_value;
  }
  else {
    return NULL;
  }
}

SPVM_OBJECT* SPVM_API_strerror_string_nolen(SPVM_ENV* env, SPVM_VALUE* stack, int32_t errno_value) {
  return SPVM_API_strerror_string(env, stack, errno_value, 0);
}

const char* SPVM_API_strerror_nolen(SPVM_ENV* env, SPVM_VALUE* stack, int32_t errno_value) {
  return SPVM_API_strerror(env, stack, errno_value, 0);
}

const char* SPVM_API_get_spvm_version_string(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  const char* spvm_version_string = SPVM_NATIVE_VERSION_STRING;
  
  return spvm_version_string;
}

double SPVM_API_get_spvm_version_number(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  const char* spvm_version_string = SPVM_API_get_spvm_version_string(env, stack);
  
  assert(spvm_version_string);
  
  int32_t spvm_version_string_length = strlen(spvm_version_string);
  
  char *end;
  errno = 0;
  double version_number = strtod(spvm_version_string, &end);
  assert(*end == '\0');
  assert(errno == 0);
  
  return version_number;
}

const char* SPVM_API_get_version_string(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type){
  
  return basic_type->version_string;
}

double SPVM_API_get_version_number(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type) {
  
  const char* version_string = SPVM_API_get_version_string(env, stack, basic_type);
  
  if (!version_string) {
    return -1;
  }
  
  int32_t version_string_length = strlen(version_string);
  
  char *end;
  errno = 0;
  double version_number = strtod(version_string, &end);
  assert(*end == '\0');
  assert(errno == 0);
  
  return version_number;
}

SPVM_OBJECT* SPVM_API_new_object_common(SPVM_ENV* env, SPVM_VALUE* stack, size_t data_size, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t length, int32_t flag) {
  
  SPVM_OBJECT* object = NULL;
  
  // string or array
  if (SPVM_API_is_dynamic_data_type(env, stack, basic_type, type_dimension, flag)) {
    object = SPVM_API_new_memory_block(env, stack, sizeof(SPVM_OBJECT));
    if (object) {
      void* data = SPVM_API_new_memory_block(env, stack, data_size);
      if (data) {
        object->data = data;
      }
      else {
        SPVM_API_free_memory_block(env, stack, object);
      }
    }
  }
  // Class
  else {
    object = SPVM_API_new_memory_block(env, stack, sizeof(SPVM_OBJECT) + data_size);
    if (object) {
      object->data = (void*)((intptr_t)object + sizeof(SPVM_OBJECT));
    }
  }
  
  if (object) {
    object->basic_type = basic_type;
    object->type_dimension = type_dimension;
    object->flag = flag;
    object->length = length;
    object->capacity = length + 1;
  }
  
  return object;
}

int32_t SPVM_API_is_binary_compatible_stack(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  int32_t is_binary_compatible_stack = 0;
  if (stack[SPVM_API_C_STACK_INDEX_ENV].address == env) {
    is_binary_compatible_stack = 1;
  }
  
  return is_binary_compatible_stack;
}

int32_t SPVM_API_push_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  if (object != NULL) {
    SPVM_OBJECT*** current_mortal_stack_ptr = (SPVM_OBJECT***)&stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK];
    int32_t* current_mortal_stack_top_ptr = (int32_t*)&stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK_LENGTH];
    int32_t* current_mortal_stack_capacity_ptr = (int32_t*)&stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK_CAPACITY];
    
    // Extend mortal stack
    if (*current_mortal_stack_top_ptr >= *current_mortal_stack_capacity_ptr) {
      int32_t new_mortal_stack_capacity = *current_mortal_stack_capacity_ptr * 2;
      SPVM_OBJECT** new_mortal_stack = SPVM_API_new_memory_block(env, stack, sizeof(SPVM_OBJECT*) * new_mortal_stack_capacity);
      if (new_mortal_stack == NULL) {
        return SPVM_NATIVE_C_BASIC_TYPE_ID_ERROR_CLASS;
      }
      memcpy(new_mortal_stack, *current_mortal_stack_ptr, sizeof(SPVM_OBJECT*) * *current_mortal_stack_capacity_ptr);
      *current_mortal_stack_capacity_ptr = new_mortal_stack_capacity;
      SPVM_API_free_memory_block(env, stack, *current_mortal_stack_ptr);
      *current_mortal_stack_ptr = NULL;
      *current_mortal_stack_ptr = new_mortal_stack;
    }
    
    SPVM_API_assign_object(env, stack, &(*current_mortal_stack_ptr)[*current_mortal_stack_top_ptr], object);
    
    *current_mortal_stack_top_ptr = *current_mortal_stack_top_ptr + 1;
  }
  
  return 0;
}

void SPVM_API_leave_scope(SPVM_ENV* env, SPVM_VALUE* stack, int32_t original_mortal_stack_top) {
  
  SPVM_OBJECT*** current_mortal_stack_ptr = (SPVM_OBJECT***)&stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK];
  int32_t* current_mortal_stack_top_ptr = (int32_t*)&stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK_LENGTH];
  
  for (int32_t mortal_stack_index = original_mortal_stack_top; mortal_stack_index < *current_mortal_stack_top_ptr; mortal_stack_index++) {
    
    SPVM_OBJECT** ref = &(*current_mortal_stack_ptr)[mortal_stack_index];
    
    SPVM_API_assign_object(env, stack, ref, NULL);
    
  }
  
  *current_mortal_stack_top_ptr = original_mortal_stack_top;
}

int32_t SPVM_API_enter_scope(SPVM_ENV* env, SPVM_VALUE* stack){
  
  int32_t mortal_stack_top = *(int32_t*)&stack[SPVM_API_C_STACK_INDEX_MORTAL_STACK_LENGTH];
  
  return mortal_stack_top;
}

void SPVM_API_leave_scope_local(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT** object_vars, int32_t* mortal_stack_typed_var_index, int32_t* mortal_stack_top_ptr, int32_t* mortal_stack_tops, int32_t mortal_stack_tops_index) {
  
  assert(mortal_stack_tops[mortal_stack_tops_index] != -1);
  
  for (int32_t mortal_stack_index = mortal_stack_tops[mortal_stack_tops_index]; mortal_stack_index < *mortal_stack_top_ptr; mortal_stack_index++) {
    int32_t var_index = mortal_stack_typed_var_index[mortal_stack_index];
    
    assert(var_index != -1);
    
    SPVM_OBJECT** ref = (SPVM_OBJECT**)&object_vars[var_index];
    if (*ref != NULL) {
      SPVM_API_assign_object(env, stack, ref, NULL);
    }
    mortal_stack_typed_var_index[mortal_stack_index] = -1;
  }
  *mortal_stack_top_ptr = mortal_stack_tops[mortal_stack_tops_index];
  mortal_stack_tops[mortal_stack_tops_index] = -1;
}

SPVM_OBJECT* SPVM_API_drop_tag_pointers(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  object = (SPVM_OBJECT*)SPVM_IMPLEMENT_DROP_TAG_POINTERS((SPVM_OBJ*)object);
  
  return object;
}

int32_t SPVM_API_isweak_only_check_flag(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT** ref) {
  int32_t isweak = (intptr_t)*ref & 1;
  return isweak;
}

int32_t SPVM_API_isweak(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT** ref) {
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  assert(ref);
  
  if (*ref == NULL) {
    return 0;
  }
  
  SPVM_OBJECT* object = SPVM_API_drop_tag_pointers(env, stack, *ref);
  
  int32_t isweak = SPVM_API_isweak_only_check_flag(env, stack, ref);
  
  return isweak;
}

int32_t SPVM_API_weaken(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT** ref) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_MUTEX* runtime_mutex = runtime->mutex;
  
  SPVM_OBJECT* destroied_referent = NULL;
  {
    SPVM_MUTEX_lock(runtime_mutex);
    
    assert(ref);
    
    if (!(*ref == NULL)) {
      
      SPVM_OBJECT* object = SPVM_API_drop_tag_pointers(env, stack, *ref);
      
      int32_t isweak = SPVM_API_isweak_only_check_flag(env, stack, ref);
      
      if (!isweak) {
        
        int32_t ref_count = SPVM_API_get_ref_count(env, stack, object);
        
        assert(ref_count > 0);
        
        // If reference count is 1, the object is destroied
        if (ref_count == 1) {
          destroied_referent = object;
          *ref = NULL;
        }
        else {
          // Weaken is implemented by tag pointer.
          // If pointer most right bit is 1, object is weaken.
          *ref = (SPVM_OBJECT*)((intptr_t)*ref | 1);
          
          SPVM_API_dec_ref_count(env, stack, object);
          
          SPVM_WEAKEN_BACKREF* new_weaken_backref = SPVM_API_new_memory_block(env, stack, sizeof(SPVM_WEAKEN_BACKREF));
          new_weaken_backref->ref = ref;
          
          // Create a new weaken back refference
          if (object->weaken_backref_head == NULL) {
            object->weaken_backref_head = new_weaken_backref;
          }
          // Add weaken back refference
          else {
            SPVM_WEAKEN_BACKREF* weaken_backref_next = object->weaken_backref_head;
            
            while (weaken_backref_next->next != NULL){
              weaken_backref_next = weaken_backref_next->next;
            }
            weaken_backref_next->next = new_weaken_backref;
          }
        }
      }
    }
    
    SPVM_MUTEX_unlock(runtime_mutex);
  }
  
  if (destroied_referent) {
    SPVM_API_assign_object(env, stack, &destroied_referent, NULL);
  }
  
  return 0;
}

void SPVM_API_unweaken_thread_unsafe(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT** ref) {
  
  assert(ref);
  
  if (*ref == NULL) {
    return;
  }
  
  SPVM_OBJECT* object = SPVM_API_drop_tag_pointers(env, stack, *ref);
  
  int32_t isweak = SPVM_API_isweak_only_check_flag(env, stack, ref);
  
  if (isweak) {
    
    assert(object->weaken_backref_head);
    
    // Drop weaken flag
    *ref = SPVM_API_drop_tag_pointers(env, stack, *ref);
    
    SPVM_API_inc_ref_count(env, stack, object);
    
    // Remove a weaken back reference
    SPVM_WEAKEN_BACKREF** weaken_backref_cur_ptr = &object->weaken_backref_head;
    
    while (*weaken_backref_cur_ptr != NULL){
      if ((*weaken_backref_cur_ptr)->ref == ref) {
        SPVM_WEAKEN_BACKREF* weaken_backref_next = (*weaken_backref_cur_ptr)->next;
        
        SPVM_WEAKEN_BACKREF* weaken_backref_cur = *weaken_backref_cur_ptr;
        
        SPVM_API_free_memory_block(env, stack, weaken_backref_cur);
        weaken_backref_cur = NULL;
        
        *weaken_backref_cur_ptr = weaken_backref_next;
        break;
      }
      weaken_backref_cur_ptr = &((*weaken_backref_cur_ptr)->next);
    }
  }
  
}

void SPVM_API_unweaken(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT** ref) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_MUTEX* runtime_mutex = runtime->mutex;
  
  {
    SPVM_MUTEX_lock(runtime_mutex);
    
    assert(ref);
    
    if (!(*ref == NULL)) {
      SPVM_OBJECT* object = SPVM_API_drop_tag_pointers(env, stack, *ref);
      
      SPVM_API_unweaken_thread_unsafe(env, stack, ref);
    }
    
    SPVM_MUTEX_unlock(runtime_mutex);
  }
  
}

void SPVM_API_free_weaken_backrefs(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_WEAKEN_BACKREF* weaken_backref_head) {
  
  SPVM_WEAKEN_BACKREF* weaken_backref_cur = weaken_backref_head;
  while (weaken_backref_cur != NULL){
    int32_t isweak = SPVM_API_isweak_only_check_flag(env, stack, weaken_backref_cur->ref);
    
    assert(isweak);
    
    *(weaken_backref_cur->ref) = NULL;
    
    SPVM_WEAKEN_BACKREF* weaken_backref_head_next = weaken_backref_cur->next;
    
    SPVM_API_free_memory_block(env, stack, weaken_backref_cur);
    weaken_backref_cur = NULL;
    
    weaken_backref_cur = weaken_backref_head_next;
  }
}

void SPVM_API_assign_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT** ref, SPVM_OBJECT* object) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_MUTEX* runtime_mutex = runtime->mutex;
  
  SPVM_OBJECT* released_object = NULL;
  {
    SPVM_MUTEX_lock(runtime_mutex);
    
    released_object = SPVM_API_drop_tag_pointers(env, stack, *ref);
    
    assert(!((intptr_t)object & 1));
    
    SPVM_API_unweaken_thread_unsafe(env, stack, ref);
    
    if (object) {
      SPVM_API_inc_ref_count(env, stack, object);
    }
    
    *ref = object;
    
    SPVM_MUTEX_unlock(runtime_mutex);
  }
  
  if (released_object) {
    int32_t do_dec_ref_count_only = 0;
    
    {
      SPVM_MUTEX_lock(runtime_mutex);
      
      int32_t released_object_ref_count = SPVM_API_get_ref_count(env, stack, released_object);
      
      assert(released_object_ref_count > 0);
      
      if (released_object_ref_count > 1) {
        SPVM_API_dec_ref_count(env, stack, released_object);
        do_dec_ref_count_only = 1;
      }
      
      SPVM_MUTEX_unlock(runtime_mutex);
    }
    
    if (!do_dec_ref_count_only) {
      // Free released_object array
      if (SPVM_API_is_object_array(env, stack, released_object)) {
        int32_t length = SPVM_API_length(env, stack, released_object);
        for (int32_t index = 0; index < length; index++) {
          SPVM_OBJECT** ref = &(((SPVM_OBJECT**)(GET_DATA_ADDRESS(env, stack, released_object)))[index]);
          SPVM_API_assign_object(env, stack, ref, NULL);
        }
      }
      // Free released_object
      else {
        SPVM_RUNTIME_BASIC_TYPE* released_object_basic_type = SPVM_API_get_object_basic_type(env, stack, released_object);
        int32_t released_object_basic_type_category = released_object_basic_type->category;
        if (released_object_basic_type_category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_CLASS) {
          // Class
          SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
          
          // Call destructor
          if (released_object_basic_type->destroy_method) {
            
            // Save return value and exception variable
            SPVM_VALUE save_stack_ret = stack[0];
            SPVM_OBJ* save_stack_exception_var = stack[SPVM_API_C_STACK_INDEX_EXCEPTION].oval;
            stack[SPVM_API_C_STACK_INDEX_EXCEPTION].oval = NULL;
            
            SPVM_RUNTIME_METHOD* destroy_method = released_object_basic_type->destroy_method;
            
            stack[0].oval = (SPVM_OBJ*)released_object;
            int32_t args_width = 1;
            int32_t error_id = SPVM_API_call_method(env, stack, destroy_method, args_width, __func__, FILE_NAME, __LINE__);
            
            // An exception thrown in a destructor is converted to a warning message
            if (error_id) {
              SPVM_OBJECT* exception = SPVM_API_get_exception(env, stack);
              
              assert(exception);
              
              const char* exception_chars = SPVM_API_get_chars(env, stack, exception);
              
              fprintf(runtime->spvm_stderr, "[An exception thrown in DESTROY method is converted to a warning message]\n%s\n", exception_chars);
            }
            
            SPVM_API_set_exception(env, stack, NULL);
            
            // Restore return value and exception variable
            stack[0] = save_stack_ret;
            stack[SPVM_API_C_STACK_INDEX_EXCEPTION].oval = save_stack_exception_var;
          }
          
          // Free released_object fields
          int32_t released_object_fields_length = released_object_basic_type->fields_length;
          for (int32_t field_index = 0; field_index < released_object_fields_length; field_index++) {
            SPVM_RUNTIME_FIELD* field = SPVM_API_BASIC_TYPE_get_field_by_index(runtime, released_object_basic_type, field_index);
            
            SPVM_RUNTIME_BASIC_TYPE* field_basic_type = field->basic_type;
            int32_t field_type_dimension = field->type_dimension;
            int32_t field_type_flag = field->type_flag;
            int32_t field_type_is_released_object = SPVM_API_is_object_type(runtime, field_basic_type, field_type_dimension, field_type_flag);
            
            if (field_type_is_released_object) {
              SPVM_OBJECT** ref = (SPVM_OBJECT**)(GET_DATA_ADDRESS(env, stack, released_object) + field->offset);
              SPVM_API_assign_object(env, stack, ref, NULL);
            }
          }
        }
      
      }
      
      {
        SPVM_MUTEX_lock(runtime_mutex);
        
        SPVM_API_dec_ref_count(env, stack, released_object);
        
        int32_t released_object_ref_count = SPVM_API_get_ref_count(env, stack, released_object);
        
        if (released_object_ref_count == 0) {
          
          // Free weak back refenreces
          if (released_object->weaken_backref_head != NULL) {
            SPVM_API_free_weaken_backrefs(env, stack, released_object->weaken_backref_head);
            released_object->weaken_backref_head = NULL;
          }
          
          // Free released_object
          SPVM_RUNTIME_BASIC_TYPE* released_object_basic_type = SPVM_API_get_object_basic_type(env, stack, released_object);
          int32_t released_object_type_dimension = SPVM_API_get_object_type_dimension(env, stack, released_object);
          if (SPVM_API_is_dynamic_data_type(env, stack, released_object_basic_type, released_object_type_dimension, 0)) {
            SPVM_API_free_memory_block(env, stack, released_object->data);
            released_object->data = NULL;
          }
          SPVM_API_free_memory_block(env, stack, released_object);
          released_object = NULL;
        }
        
        SPVM_MUTEX_unlock(runtime_mutex);
      }
    }
  }
}

void SPVM_API_inc_ref_count(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  if (object != NULL) {
    int32_t ref_count = SPVM_API_get_ref_count(env, stack, object);
    assert(ref_count >= 0);
    
    __sync_fetch_and_add(&object->ref_count, 1);
  }
}

void SPVM_API_dec_ref_count(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object != NULL) {
    int32_t ref_count = SPVM_API_get_ref_count(env, stack, object);
    assert(ref_count > 0);
    
    __sync_fetch_and_add(&object->ref_count, -1);
  }
}

int32_t SPVM_API_get_ref_count(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t ref_count = object->ref_count;
  
  return ref_count;
}

FILE* SPVM_API_spvm_stdin(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  FILE* spvm_stdin = SPVM_API_RUNTIME_get_spvm_stdin((SPVM_RUNTIME*)env->runtime);
  
  return spvm_stdin;
}

FILE* SPVM_API_spvm_stdout(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  FILE* spvm_stdout = SPVM_API_RUNTIME_get_spvm_stdout((SPVM_RUNTIME*)env->runtime);
  
  return spvm_stdout;
}

FILE* SPVM_API_spvm_stderr(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  FILE* spvm_stderr = SPVM_API_RUNTIME_get_spvm_stderr((SPVM_RUNTIME*)env->runtime);
  
  return spvm_stderr;
}

int32_t SPVM_API_check_bootstrap_method(SPVM_ENV* env, SPVM_VALUE* stack, const char* basic_type_name) {
  
  int32_t error_id = 0;
  
  SPVM_RUNTIME_BASIC_TYPE* class_basic_type = SPVM_API_RUNTIME_get_basic_type_by_name((SPVM_RUNTIME*)env->runtime, basic_type_name);
  SPVM_RUNTIME_METHOD* method = SPVM_API_BASIC_TYPE_get_method_by_name((SPVM_RUNTIME*)env->runtime, class_basic_type, "main");
  
  if (method) {
    int32_t is_class_method = SPVM_API_METHOD_is_class_method((SPVM_RUNTIME*)env->runtime, method);
    
    if (is_class_method) {
      int32_t args_length = SPVM_API_METHOD_get_args_length((SPVM_RUNTIME*)env->runtime, method);
      
      if (!(args_length == 0)) {
        error_id = SPVM_API_die(env, stack, "The length of the arguments of %s#main method must be 0.", __func__, FILE_NAME, __LINE__, basic_type_name);
      }
      else {
        SPVM_RUNTIME_BASIC_TYPE* return_basic_type = SPVM_API_METHOD_get_return_basic_type((SPVM_RUNTIME*)env->runtime, method);
        const char* return_basic_type_name = SPVM_API_BASIC_TYPE_get_name((SPVM_RUNTIME*)env->runtime, return_basic_type);
        
        if (!(strcmp(return_basic_type_name, "void") == 0)) {
          error_id = SPVM_API_die(env, stack, "The return type of %s#main method must be the void type.", __func__, FILE_NAME, __LINE__, basic_type_name);
        }
      }
    }
    else {
      error_id = SPVM_API_die(env, stack, "%s#main method must be a class method.", __func__, FILE_NAME, __LINE__, basic_type_name);
    }
  }
  else {
    error_id = SPVM_API_die(env, stack, "%s#main method must be defined.", __func__, FILE_NAME, __LINE__, basic_type_name);
  }
  
  return error_id;
}

SPVM_OBJECT* SPVM_API_new_array_proto_element_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* element, int32_t length) {
  
  if (element == NULL) {
    return NULL;
  }
  
  if (length < 0) {
    return NULL;
  }
  
  size_t element_size = 1;
  
  size_t data_size = (size_t)sizeof(SPVM_OBJECT*) * (length + 1);
  
  SPVM_RUNTIME_BASIC_TYPE* element_basic_type = SPVM_API_get_object_basic_type(env, stack, element);
  SPVM_OBJECT* new_array = SPVM_API_new_object_common(env, stack, data_size, element_basic_type, element->type_dimension + 1, length, 0);
  
  return new_array;
}

SPVM_OBJECT* SPVM_API_new_array_proto_element(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* element, int32_t length) {
  
  
  SPVM_OBJECT* new_array = SPVM_API_new_array_proto_element_no_mortal(env, stack, element, length);
  
  SPVM_API_push_mortal(env, stack, new_array);
  
  return new_array;
}

int32_t SPVM_API_is_binary_compatible_object(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t is_binary_compatible_object = 0;
  
  int32_t is_binary_compatible_stack = SPVM_API_is_binary_compatible_stack(env, stack);
  
  if (is_binary_compatible_stack) {
    int32_t basic_type_id = object->basic_type->id;
    
    int32_t is_shareable_type = 0;
    switch (basic_type_id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE:
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT:
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT:
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG:
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT:
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE:
      case SPVM_NATIVE_C_BASIC_TYPE_ID_STRING:
      {
        is_shareable_type = 1;
      }
    }
    
    if (is_shareable_type || object->basic_type->current_runtime == (SPVM_RUNTIME*)env->runtime) {
      is_binary_compatible_object = 1;
    }
  }
  
  return is_binary_compatible_object;
}

int32_t SPVM_API_get_bool_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* bool_object) {
  
  int32_t value = *(int8_t*)(GET_DATA_ADDRESS(env, stack, bool_object));
  
  return value;
}

int32_t SPVM_API_get_byte_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* byte_object) {
  
  int32_t value = *(int8_t*)(GET_DATA_ADDRESS(env, stack, byte_object));
  
  return value;
}

int32_t SPVM_API_get_short_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* short_object) {
  
  int32_t value = *(int16_t*)(GET_DATA_ADDRESS(env, stack, short_object));
  
  return value;
}

int32_t SPVM_API_get_int_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* int_object) {
  
  int32_t value = *(int32_t*)(GET_DATA_ADDRESS(env, stack, int_object));
  
  return value;
}

int64_t SPVM_API_get_long_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* long_object) {
  
  int64_t value = *(int64_t*)(GET_DATA_ADDRESS(env, stack, long_object));
  
  return value;
}

float SPVM_API_get_float_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* float_object) {
  
  float value = *(float*)(GET_DATA_ADDRESS(env, stack, float_object));
  
  return value;
}

double SPVM_API_get_double_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* double_object) {
  
  double value = *(double*)(GET_DATA_ADDRESS(env, stack, double_object));
  
  return value;
}

void SPVM_API_set_byte_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* byte_object, int8_t value) {
  
  // Set the byte value to the data address of the Byte object
  *(int8_t*)(GET_DATA_ADDRESS(env, stack, byte_object)) = value;
}

void SPVM_API_set_short_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* short_object, int16_t value) {
  
  // Set the short value to the data address of the Short object
  *(int16_t*)(GET_DATA_ADDRESS(env, stack, short_object)) = value;
}

void SPVM_API_set_int_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* int_object, int32_t value) {
  
  // Set the int value to the data address of the Int object
  *(int32_t*)(GET_DATA_ADDRESS(env, stack, int_object)) = value;
}

void SPVM_API_set_long_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* long_object, int64_t value) {
  
  // Set the long value to the data address of the Long object
  *(int64_t*)(GET_DATA_ADDRESS(env, stack, long_object)) = value;
}

void SPVM_API_set_float_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* float_object, float value) {
  
  // Set the float value to the data address of the Float object
  *(float*)(GET_DATA_ADDRESS(env, stack, float_object)) = value;
}

void SPVM_API_set_double_object_value(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* double_object, double value) {
  
  // Set the double value to the data address of the Double object
  *(double*)(GET_DATA_ADDRESS(env, stack, double_object)) = value;
}

char* SPVM_API_get_stack_tmp_buffer(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  char* tmp_buffer = (char*)&stack[SPVM_API_C_STACK_INDEX_TMP_BUFFER];
  
  return tmp_buffer;
}

int32_t SPVM_API_no_free(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t no_free = !!(object->flag & SPVM_OBJECT_C_FLAG_NO_FREE);
  
  return no_free;
}

void SPVM_API_set_no_free(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t no_free) {
  
  if (no_free) {
    __sync_fetch_and_or(&object->flag, SPVM_OBJECT_C_FLAG_NO_FREE);
  }
  else {
    __sync_fetch_and_xor(&object->flag, SPVM_OBJECT_C_FLAG_NO_FREE);
  }
}

void SPVM_API_print_exception_to_stderr(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  SPVM_OBJECT* obj_exception = SPVM_API_get_exception(env, stack);
  const char* exception = SPVM_API_get_chars(env, stack, obj_exception);
  
  fprintf(SPVM_API_RUNTIME_get_spvm_stderr((SPVM_RUNTIME*)env->runtime), "[An exception is converted to a warning]\n");
  
  SPVM_API_print_stderr(env, stack, obj_exception);
  
  fprintf(SPVM_API_RUNTIME_get_spvm_stderr((SPVM_RUNTIME*)env->runtime), "\n");
}

SPVM_OBJECT* SPVM_API_dump_object_internal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  SPVM_OBJECT* obj_dump = NULL;
  char* tmp_buffer = SPVM_API_get_stack_tmp_buffer(env, stack);
  if (object) {
    void* pointer = object->pointer;
    
    int32_t weaken_backrefs_length = 0;
    
    SPVM_WEAKEN_BACKREF* weaken_backref_cur = object->weaken_backref_head;
    
    while (weaken_backref_cur) {
      weaken_backrefs_length++;
      weaken_backref_cur = weaken_backref_cur->next;
    }
    
    int32_t ref_count = object->ref_count;
    
    SPVM_RUNTIME_BASIC_TYPE* basic_type = object->basic_type;
    
    const char* basic_type_name = basic_type->name;
    
    uint8_t type_dimension = object->type_dimension;
    
    uint8_t flag = object->flag;
    const char* is_read_only_flag_str = "";
    if (flag & SPVM_OBJECT_C_FLAG_IS_READ_ONLY) {
      is_read_only_flag_str = "is_read_only";
    }
    
    const char* no_free_flag_str = "";
    if (flag & SPVM_OBJECT_C_FLAG_NO_FREE) {
      no_free_flag_str = "no_free";
    }
    
    int32_t length = SPVM_API_length(env, stack, object);
    
    snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "[Object Intenal:%p]\npointer:%p\nweaken_backrefs_length:%d\nref_count:%d\nbasic_type_name:%s\ntype_dimension:%d\nflag:%s %s\nlength:%d", object, pointer, weaken_backrefs_length, ref_count, basic_type_name, type_dimension, is_read_only_flag_str, no_free_flag_str, length);
    
    obj_dump = SPVM_API_new_string_nolen(env, stack, tmp_buffer);
  }
  else {
    obj_dump = SPVM_API_new_string_nolen(env, stack, "undef");
  }
  
  return obj_dump;
}

int32_t SPVM_API_get_seed(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  int32_t seed = *(int32_t*)&stack[SPVM_API_C_STACK_INDEX_SEED];
  
  return seed;
}

void SPVM_API_set_seed(SPVM_ENV* env, SPVM_VALUE* stack, int32_t seed) {
  
  *(int32_t*)&stack[SPVM_API_C_STACK_INDEX_SEED] = seed;
  
  if (!*(int32_t*)&stack[SPVM_API_C_STACK_INDEX_SEED_INITIALIZED]) {
    *(int32_t*)&stack[SPVM_API_C_STACK_INDEX_SEED_INITIALIZED] = 1;
  }
}

int32_t SPVM_API_seed_initialized(SPVM_ENV* env, SPVM_VALUE* stack) {
  
  int32_t seed_initialized = !!*(int32_t*)&stack[SPVM_API_C_STACK_INDEX_SEED_INITIALIZED];
  
  return seed_initialized;
}

const char* SPVM_API_get_basic_type_name_in_version_from(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type) {
  
  const char* basic_type_name_in_version_from = NULL;
  if (basic_type->basic_type_in_version_from) {
    basic_type_name_in_version_from = basic_type->basic_type_in_version_from->name;
  }
  
  return basic_type_name_in_version_from;
}

int32_t SPVM_API_set_command_info_warning(SPVM_ENV* env, SPVM_VALUE* stack, int32_t warning) {
  
  int32_t error_id = 0;
  
  SPVM_API_set_class_var_byte_by_name(env, stack, "CommandInfo", "$WARNING", warning, &error_id, __func__, FILE_NAME, __LINE__);
  if (error_id) { return error_id; }
  
  return 0;
}


/*
  Type native APIs
*/

int32_t SPVM_API_is_numeric_type(SPVM_RUNTIME* runtime, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag) {
  
  if (basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_NUMERIC && type_dimension == 0 && !(type_flag & SPVM_NATIVE_C_TYPE_FLAG_REF)) {
    return 1;
  }
  else {
    return 0;
  }
}

int32_t SPVM_API_is_object_type(SPVM_RUNTIME* runtime, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag) {
  
  if (type_dimension > 0) {
    return 1;
  }
  else if (basic_type->is_object_type && !(type_flag & SPVM_NATIVE_C_TYPE_FLAG_REF)) {
    return 1;
  }
  else {
    return 0;
  }
}

int32_t SPVM_API_is_class_type(SPVM_RUNTIME* runtime, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag) {
  
  SPVM_COMPILER* compiler = runtime->compiler;
  
  int32_t is_class_type = SPVM_TYPE_is_class_type(compiler, basic_type->id, type_dimension, type_flag);
  
  return is_class_type;
}

int32_t SPVM_API_is_any_object_type(SPVM_RUNTIME* runtime, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag) {
  
  if (basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_ANY_OBJECT && type_dimension == 0 && !(type_flag & SPVM_NATIVE_C_TYPE_FLAG_REF)) {
    return 1;
  }
  else {
    return 0;
  }
}

int32_t SPVM_API_is_object_array_type(SPVM_RUNTIME* runtime, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag) {
  
  if (basic_type->is_object_type && type_dimension > 0 && !(type_flag & SPVM_NATIVE_C_TYPE_FLAG_REF)) {
    return 1;
  }
  else {
    return 0;
  }
}

int32_t SPVM_API_is_any_object_array_type(SPVM_RUNTIME* runtime, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag) {
  
  if (basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_ANY_OBJECT && type_dimension > 0 && !(type_flag & SPVM_NATIVE_C_TYPE_FLAG_REF)) {
    return 1;
  }
  else {
    return 0;
  }
}

int32_t SPVM_API_is_mulnum_type(SPVM_RUNTIME* runtime, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag) {
  
  if (basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_MULNUM && type_dimension == 0 && !(type_flag & SPVM_NATIVE_C_TYPE_FLAG_REF)) {
    return 1;
  }
  else {
    return 0;
  }
}

int32_t SPVM_API_is_dynamic_data_type(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag) {
  
  int32_t is_dynamic_data_type = 0;
  if (basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_STRING || type_dimension > 0) {
    is_dynamic_data_type = 1;
  }
  
  return is_dynamic_data_type;
}

int32_t SPVM_API_get_type_width(SPVM_RUNTIME* runtime, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag) {
  
  int32_t is_mulnum_type = SPVM_API_is_mulnum_type(runtime, basic_type, type_dimension, type_flag);
  
  int32_t type_width = -1;
  if (is_mulnum_type) {
    type_width = basic_type->fields_length;
  }
  else {
    type_width = 1;
  }
  
  return type_width;
}

int32_t SPVM_API_can_assign(SPVM_RUNTIME* runtime, SPVM_RUNTIME_BASIC_TYPE* dist_basic_type, int32_t dist_type_dimension, int32_t dist_type_flag, SPVM_RUNTIME_BASIC_TYPE* src_basic_type, int32_t src_type_dimension, int32_t src_type_flag) {
  
  int32_t isa = 0;
  
  char assinability_key[256] = {0};
  snprintf(assinability_key, 255, "%d-%d-%d-%d-%d-%d", dist_basic_type->id, dist_type_dimension, dist_type_flag, src_basic_type->id, src_type_dimension, src_type_flag);
  
  SPVM_MUTEX* runtime_mutex = runtime->mutex;
  
  SPVM_MUTEX_reader_lock(runtime_mutex);
  
  int32_t can_assign = (intptr_t)SPVM_HASH_get(runtime->assignment_requirement_symtable, assinability_key, strlen(assinability_key));
  
  SPVM_MUTEX_reader_unlock(runtime_mutex);
  
  if (can_assign > 0) {
    isa = 1;
  }
  else if (can_assign < 0) {
    isa = 0;
  }
  else {
    
    SPVM_COMPILER* compiler = runtime->compiler;
    
    isa = SPVM_TYPE_satisfy_assignment_requirement_without_data_conversion(
      compiler,
      dist_basic_type->id, dist_type_dimension, dist_type_flag,
      src_basic_type->id, src_type_dimension, src_type_flag,
      NULL
    );
    
    SPVM_MUTEX_lock(runtime_mutex);
    
    SPVM_HASH_set(runtime->assignment_requirement_symtable, assinability_key, strlen(assinability_key), (void*)(intptr_t)(isa ? 1 : -1));
    
    SPVM_MUTEX_unlock(runtime_mutex);
  }
  
  return isa;
}

/*
  The end of type native APIs
*/

int32_t SPVM_API_get_call_stack_frame_size(SPVM_RUNTIME_METHOD* method) {
  
  int32_t call_stack_frame_size = 0;
  call_stack_frame_size += (method->long_vars_width + 1) * sizeof(int64_t);
  call_stack_frame_size += (method->double_vars_width + 1) * sizeof(double);
  call_stack_frame_size += (method->object_vars_width + 1) * sizeof(SPVM_OBJECT*);
  call_stack_frame_size += (method->ref_vars_width + 1) * sizeof(SPVM_REF*);
  call_stack_frame_size += (method->int_vars_width + 1) * sizeof(int32_t);
  call_stack_frame_size += (method->float_vars_width + 1) * sizeof(float);
  call_stack_frame_size += (method->mortal_stack_length + 1) * sizeof(int32_t);
  call_stack_frame_size += (method->mortal_stack_tops_length + 1) * sizeof(int32_t);
  call_stack_frame_size += (method->short_vars_width + 1) * sizeof(int16_t);
  call_stack_frame_size += (method->byte_vars_width + 1) * sizeof(int8_t);
  
  // Adjust allignment
  int32_t alignment = sizeof(void*) > sizeof(int64_t) ? sizeof(void*) : sizeof(int64_t);
  assert(alignment >= 8);
  call_stack_frame_size = (call_stack_frame_size + (alignment - 1)) & ~(alignment - 1);
  assert(call_stack_frame_size % alignment == 0);
  
  return call_stack_frame_size;
}

void SPVM_API_set_call_stack_frame_info(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CALL_STACK_FRAME_INFO* call_stack_frame_info, char* call_stack_frame) {
  
  SPVM_RUNTIME_METHOD* method = call_stack_frame_info->method;
  
  // Alignment is important for performance
  // + 1 is needed for pointing a different address when width is 0.
  int32_t call_stack_frame_offset = 0;
  
  // Long varialbes. 8 bytes
  *call_stack_frame_info->long_vars_address = (int64_t*)&call_stack_frame[call_stack_frame_offset];
  call_stack_frame_offset += (method->long_vars_width + 1) * sizeof(int64_t);
  
  // Double variables. 8 bytes
  *call_stack_frame_info->double_vars_address = (double*)&call_stack_frame[call_stack_frame_offset];
  call_stack_frame_offset += (method->double_vars_width + 1) * sizeof(double);
  
  // Object variables. 8 bytes in 64bit architecture, 4 bytes in 32bit architecture
  *call_stack_frame_info->object_vars_address = (SPVM_OBJ**)&call_stack_frame[call_stack_frame_offset];
  call_stack_frame_offset += (method->object_vars_width + 1) * sizeof(SPVM_OBJ*);
  
  // Refernce variables. 8 bytes in 64bit architecture, 4 bytes in 32bit architecture
  *call_stack_frame_info->ref_vars_address = (SPVM_REF**)&call_stack_frame[call_stack_frame_offset];
  call_stack_frame_offset += (method->ref_vars_width + 1) * sizeof(SPVM_REF*);
  
  // Int variables. 4 bytes
  *call_stack_frame_info->int_vars_address = (int32_t*)&call_stack_frame[call_stack_frame_offset];
  call_stack_frame_offset += (method->int_vars_width + 1) * sizeof(int32_t);
  
  // Float variables. 4 bytes
  *call_stack_frame_info->float_vars_address = (float*)&call_stack_frame[call_stack_frame_offset];
  call_stack_frame_offset += (method->float_vars_width + 1) * sizeof(float);
  
  // Mortal stack. 4 bytes
  *call_stack_frame_info->mortal_stack_address = (int32_t*)&call_stack_frame[call_stack_frame_offset];
  call_stack_frame_offset += (method->mortal_stack_length + 1) * sizeof(int32_t);
  
  // Mortal stack tops. 4 bytes
  *call_stack_frame_info->mortal_stack_tops_address = (int32_t*)&call_stack_frame[call_stack_frame_offset];
  call_stack_frame_offset += (method->mortal_stack_tops_length + 1) * sizeof(int32_t);
  
  // Short variables. 2 bytes
  *call_stack_frame_info->short_vars_address = (int16_t*)&call_stack_frame[call_stack_frame_offset];
  call_stack_frame_offset += (method->short_vars_width + 1) * sizeof(int16_t);
  
  // Byte variables. 1 bytes
  *call_stack_frame_info->byte_vars_address = (int8_t*)&call_stack_frame[call_stack_frame_offset];
  call_stack_frame_offset += (method->byte_vars_width + 1) * sizeof(int8_t);
  
  call_stack_frame_info->call_stack_frame = call_stack_frame;

}

int32_t SPVM_API_push_call_stack_frame(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CALL_STACK_FRAME_INFO* call_stack_frame_info) {
  
  SPVM_RUNTIME_METHOD* method = call_stack_frame_info->method;
  
  int32_t call_stack_frame_size = method->call_stack_frame_size;
  
  char* call_stack_frame = NULL;
  if (call_stack_frame_size > SPVM_API_C_CALL_STACK_MEMORY_BLOCK_SIZE) {
    call_stack_frame = SPVM_API_new_memory_block_for_call_stack(env, stack, call_stack_frame_size);
    if (!call_stack_frame) {
      return -1;
    }
    call_stack_frame_info->on_heap = 1;
  }
  else {
    char** call_stack_memory_blocks = stack[SPVM_API_C_STACK_INDEX_CALL_STACK_MEMORY_BLOCKS].address;
    
    int32_t call_stack_offset = stack[SPVM_API_C_STACK_INDEX_CALL_STACK_OFFSET].ival;
    
    int32_t call_stack_memory_blocks_index = call_stack_offset / SPVM_API_C_CALL_STACK_MEMORY_BLOCK_SIZE;
    
    int32_t call_stack_memory_block_offset = call_stack_offset - (SPVM_API_C_CALL_STACK_MEMORY_BLOCK_SIZE * call_stack_memory_blocks_index);
    
    if (call_stack_memory_block_offset + call_stack_frame_size >= SPVM_API_C_CALL_STACK_MEMORY_BLOCK_SIZE) {
      call_stack_memory_blocks_index++;
      call_stack_memory_block_offset = 0;
    }
    
    int32_t call_stack_memory_blocks_capacity = stack[SPVM_API_C_STACK_INDEX_CALL_STACK_MEMORY_BLOCKS_CAPACITY].ival;
    
    if (call_stack_memory_blocks_index >= call_stack_memory_blocks_capacity) {
      int32_t new_call_stack_memory_blocks_capacity = call_stack_memory_blocks_capacity * 2;
      char** new_call_stack_memory_blocks = SPVM_API_new_memory_block_for_call_stack(env, stack, sizeof(char*) * new_call_stack_memory_blocks_capacity);
      if (!new_call_stack_memory_blocks) {
        return -1;
      }
      
      memcpy(new_call_stack_memory_blocks, call_stack_memory_blocks, sizeof(char*) * call_stack_memory_blocks_capacity);
      
      stack[SPVM_API_C_STACK_INDEX_CALL_STACK_MEMORY_BLOCKS].address = new_call_stack_memory_blocks;
      stack[SPVM_API_C_STACK_INDEX_CALL_STACK_MEMORY_BLOCKS_CAPACITY].ival = new_call_stack_memory_blocks_capacity;
      
      SPVM_API_free_memory_block_for_call_stack(env, stack, call_stack_memory_blocks);
      
      for (int32_t i = 0; i < new_call_stack_memory_blocks_capacity; i++) {
        if (i >= call_stack_memory_blocks_capacity) {
          new_call_stack_memory_blocks[i] = SPVM_API_new_memory_block_for_call_stack(env, stack, SPVM_API_C_CALL_STACK_MEMORY_BLOCK_SIZE);
        }
      }
      
      call_stack_memory_blocks = new_call_stack_memory_blocks;
    }
    
    char* call_stck_memory_block = call_stack_memory_blocks[call_stack_memory_blocks_index];
    
    call_stack_frame = &call_stck_memory_block[call_stack_memory_block_offset];
    
    call_stack_frame_info->call_stack_offset = stack[SPVM_API_C_STACK_INDEX_CALL_STACK_OFFSET].ival;
    
    int32_t new_call_stack_offset = SPVM_API_C_CALL_STACK_MEMORY_BLOCK_SIZE * call_stack_memory_blocks_index + call_stack_memory_block_offset + call_stack_frame_size;
    
    stack[SPVM_API_C_STACK_INDEX_CALL_STACK_OFFSET].ival = new_call_stack_offset;
  }
  
  memset(call_stack_frame, 0, call_stack_frame_size);
  
  assert((intptr_t)call_stack_frame % 8 == 0);
  
  SPVM_API_set_call_stack_frame_info(env, stack, call_stack_frame_info, call_stack_frame);
  
  memset(*call_stack_frame_info->mortal_stack_address, -1, method->mortal_stack_length * sizeof(int32_t));
  memset(*call_stack_frame_info->mortal_stack_tops_address, -1, method->mortal_stack_tops_length * sizeof(int32_t));
  
  return 0;
}

void SPVM_API_pop_call_stack_frame(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_CALL_STACK_FRAME_INFO* call_stack_frame_info) {
  
  if (call_stack_frame_info->on_heap) {
    SPVM_API_free_memory_block_for_call_stack(env, stack, call_stack_frame_info->call_stack_frame);
  }
  else {
    assert(stack[SPVM_API_C_STACK_INDEX_CALL_STACK_OFFSET].ival > 0);
    stack[SPVM_API_C_STACK_INDEX_CALL_STACK_OFFSET].ival = call_stack_frame_info->call_stack_offset;
  }
}

void* SPVM_API_new_memory_block_for_call_stack(SPVM_ENV* env, SPVM_VALUE* stack, int32_t size) {
  
  void* memory_block = SPVM_ALLOCATOR_alloc_memory_block_unmanaged(size);
  if (memory_block) {
    stack[SPVM_API_C_STACK_INDEX_MEMORY_BLOCKS_FOR_CALL_STACK].ival++;
  }
  
  return memory_block;
}

void SPVM_API_free_memory_block_for_call_stack(SPVM_ENV* env, SPVM_VALUE* stack, void* block) {
  
  if (block) {
    SPVM_ALLOCATOR_free_memory_block_unmanaged(block);
    stack[SPVM_API_C_STACK_INDEX_MEMORY_BLOCKS_FOR_CALL_STACK].ival--;
  }
  
}

void SPVM_API_enable_options(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  if (object) {
    if (SPVM_API_is_object_array(env, stack, object)) {
      __sync_fetch_and_or(&object->flag, SPVM_OBJECT_C_FLAG_IS_OPTIONS);
    }
  }
}

void SPVM_API_disable_options(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  if (object) {
    __sync_fetch_and_and(&object->flag, ~SPVM_OBJECT_C_FLAG_IS_OPTIONS);
  }
}

int32_t SPVM_API_is_options(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object) {
  
  int32_t is_options;
  if (object) {
    if (object->flag & SPVM_OBJECT_C_FLAG_IS_OPTIONS) {
      is_options = 1;
    }
    else {
      is_options = 0;
    }
  }
  else {
    is_options = 0;
  }
  
  return is_options;
}

int32_t SPVM_API_exists_field(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  int32_t value = SPVM_IMPLEMENT_GET_EXISTS_FLAG(env, stack, (SPVM_OBJ*)object, field->exists_offset, field->exists_bit);
  
  return value;
}

int32_t SPVM_API_exists_field_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return 0;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return 0;
  };
  
  int32_t exists = SPVM_API_exists_field(env, stack, object, field);
  
  return exists;
}

void SPVM_API_delete_field(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, SPVM_RUNTIME_FIELD* field) {
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  int32_t is_numeric_type = SPVM_API_is_numeric_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
  
  if (is_numeric_type) {
    switch (field->basic_type->id) {
      case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE : {
        SPVM_API_set_field_byte(env, stack, object, field, 0);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT : {
        SPVM_API_set_field_short(env, stack, object, field, 0);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_INT : {
        SPVM_API_set_field_int(env, stack, object, field, 0);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG : {
        SPVM_API_set_field_long(env, stack, object, field, 0);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT : {
        SPVM_API_set_field_float(env, stack, object, field, 0);
        break;
      }
      case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE : {
        SPVM_API_set_field_double(env, stack, object, field, 0);
        break;
      }
      default : {
        assert(0);
      }
    }
  }
  else {
    int32_t is_object_type = SPVM_API_is_object_type(runtime, field->basic_type, field->type_dimension, field->type_flag);
    if (is_object_type) {
      SPVM_API_set_field_object(env, stack, object, field, NULL);
    }
    else {
      assert(0);
    }
  }
  
  SPVM_IMPLEMENT_DISABLE_EXISTS_FLAG(env, stack, (SPVM_OBJ*)object, field->exists_offset, field->exists_bit);
}

void SPVM_API_delete_field_by_name(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, const char* field_name, int32_t* error_id, const char* func_name, const char* file, int32_t line) {
  
  *error_id = 0;
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  if (object == NULL) {
    *error_id = SPVM_API_die(env, stack, "The object must be defined.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_BASIC_TYPE* object_basic_type = object->basic_type;
  
  int32_t object_type_dimension = object->type_dimension;
  
  int32_t object_is_class_type = SPVM_API_is_class_type(runtime, object_basic_type, object_type_dimension, 0);
  
  if (!object_is_class_type) {
    *error_id = SPVM_API_die(env, stack, "The type of the invocant must be a class type.", func_name, file, line);
    return;
  };
  
  SPVM_RUNTIME_FIELD* field = SPVM_API_get_field(env, stack, object, field_name);
  if (!field) {
    const char* basic_type_name = SPVM_API_get_object_basic_type_name(env, stack, object);
    *error_id = SPVM_API_die(env, stack, "%s#%s field is not found in the class or its super classes.", func_name, file, line, basic_type_name, field_name);
    return;
  };
  
  SPVM_API_delete_field(env, stack, object, field);
}

int32_t SPVM_API_is_numeric_object_type(SPVM_RUNTIME* runtime, SPVM_RUNTIME_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag) {
  
  if (basic_type->id >= SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE_CLASS && basic_type->id <= SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE_CLASS && type_dimension == 0 && !(type_flag & SPVM_NATIVE_C_TYPE_FLAG_REF)) {
    return 1;
  }
  else {
    return 0;
  }
}

int8_t SPVM_API_numeric_object_to_byte(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t* error_id) {
  
  *error_id = 0;
  
  if (object == NULL) {
    return 0;
  }
  
  int32_t is_numeric_object_type = SPVM_API_is_numeric_object_type((SPVM_RUNTIME*)env->runtime, object->basic_type, object->type_dimension, 0);
  
  if (!is_numeric_object_type) {
    *error_id = SPVM_API_die(env, stack, "Type conversion failed. The type of the object must be a numeric object type.", __func__, FILE_NAME, __LINE__);
    return 0;
  }
  
  int8_t out;
  switch (object->basic_type->id) {
    case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE_CLASS : {
      int8_t** fields = (int8_t**)object->data;
      out = (int8_t)*(int8_t*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT_CLASS : {
      int16_t** fields = (int16_t**)object->data;
      out = (int8_t)*(int16_t*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_INT_CLASS : {
      int32_t** fields = (int32_t**)object->data;
      out = (int8_t)*(int32_t*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG_CLASS : {
      int64_t** fields = (int64_t**)object->data;
      out = (int8_t)*(int64_t*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT_CLASS : {
      float** fields = (float**)object->data;
      out = (int8_t)*(float*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE_CLASS : {
      double** fields = (double**)object->data;
      out = (int8_t)*(double*)&fields[0];
      break;
    }
    default : {
      assert(0);
    }
  }
  
  return out;
}

int16_t SPVM_API_numeric_object_to_short(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t* error_id) {
  
  *error_id = 0;
  
  if (object == NULL) {
    return 0;
  }
  
  int32_t is_numeric_object_type = SPVM_API_is_numeric_object_type((SPVM_RUNTIME*)env->runtime, object->basic_type, object->type_dimension, 0);
  
  if (!is_numeric_object_type) {
    *error_id = SPVM_API_die(env, stack, "Type conversion failed. The type of the object must be a numeric object type.", __func__, FILE_NAME, __LINE__);
    return 0;
  }
  
  int16_t out;
  switch (object->basic_type->id) {
    case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int8_t** fields = (int8_t**)object->data;
      int8_t val = *(int8_t*)&fields[0];
      out = is_unsigned ? (int16_t)(uint8_t)val : (int16_t)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT_CLASS : {
      int16_t** fields = (int16_t**)object->data;
      out = (int16_t)*(int16_t*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_INT_CLASS : {
      int32_t** fields = (int32_t**)object->data;
      out = (int16_t)*(int32_t*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG_CLASS : {
      int64_t** fields = (int64_t**)object->data;
      out = (int16_t)*(int64_t*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT_CLASS : {
      float** fields = (float**)object->data;
      out = (int16_t)*(float*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE_CLASS : {
      double** fields = (double**)object->data;
      out = (int16_t)*(double*)&fields[0];
      break;
    }
    default : {
      assert(0);
    }
  }
  
  return out;
}

int32_t SPVM_API_numeric_object_to_int(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t* error_id) {
  
  *error_id = 0;
  
  if (object == NULL) {
    return 0;
  }
  
  int32_t is_numeric_object_type = SPVM_API_is_numeric_object_type((SPVM_RUNTIME*)env->runtime, object->basic_type, object->type_dimension, 0);
  
  if (!is_numeric_object_type) {
    *error_id = SPVM_API_die(env, stack, "Type conversion failed. The type of the object must be a numeric object type.", __func__, FILE_NAME, __LINE__);
    return 0;
  }
  
  int32_t out;
  switch (object->basic_type->id) {
    case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int8_t** fields = (int8_t**)object->data;
      int8_t val = *(int8_t*)&fields[0];
      out = is_unsigned ? (int32_t)(uint8_t)val : (int32_t)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int16_t** fields = (int16_t**)object->data;
      int16_t val = *(int16_t*)&fields[0];
      out = is_unsigned ? (int32_t)(uint16_t)val : (int32_t)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_INT_CLASS : {
      int32_t** fields = (int32_t**)object->data;
      out = (int32_t)*(int32_t*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG_CLASS : {
      int64_t** fields = (int64_t**)object->data;
      out = (int32_t)*(int64_t*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT_CLASS : {
      float** fields = (float**)object->data;
      out = (int32_t)*(float*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE_CLASS : {
      double** fields = (double**)object->data;
      out = (int32_t)*(double*)&fields[0];
      break;
    }
    default : {
      assert(0);
    }
  }
  
  return out;
}

int64_t SPVM_API_numeric_object_to_long(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t* error_id) {
  
  *error_id = 0;
  
  if (object == NULL) {
    return 0;
  }
  
  int32_t is_numeric_object_type = SPVM_API_is_numeric_object_type((SPVM_RUNTIME*)env->runtime, object->basic_type, object->type_dimension, 0);
  
  if (!is_numeric_object_type) {
    *error_id = SPVM_API_die(env, stack, "Type conversion failed. The type of the object must be a numeric object type.", __func__, FILE_NAME, __LINE__);
    return 0;
  }
  
  int64_t out;
  switch (object->basic_type->id) {
    case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int8_t** fields = (int8_t**)object->data;
      int8_t val = *(int8_t*)&fields[0];
      out = is_unsigned ? (int64_t)(uint8_t)val : (int64_t)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int16_t** fields = (int16_t**)object->data;
      int16_t val = *(int16_t*)&fields[0];
      out = is_unsigned ? (int64_t)(uint16_t)val : (int64_t)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_INT_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int32_t** fields = (int32_t**)object->data;
      int32_t val = *(int32_t*)&fields[0];
      out = is_unsigned ? (int64_t)(uint32_t)val : (int64_t)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG_CLASS : {
      int64_t** fields = (int64_t**)object->data;
      out = (int64_t)*(int64_t*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT_CLASS : {
      float** fields = (float**)object->data;
      out = (int64_t)*(float*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE_CLASS : {
      double** fields = (double**)object->data;
      out = (int64_t)*(double*)&fields[0];
      break;
    }
    default : {
      assert(0);
    }
  }
  
  return out;
}

float SPVM_API_numeric_object_to_float(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t* error_id) {
  
  *error_id = 0;
  
  if (object == NULL) {
    return 0;
  }
  
  int32_t is_numeric_object_type = SPVM_API_is_numeric_object_type((SPVM_RUNTIME*)env->runtime, object->basic_type, object->type_dimension, 0);
  
  if (!is_numeric_object_type) {
    *error_id = SPVM_API_die(env, stack, "Type conversion failed. The type of the object must be a numeric object type.", __func__, FILE_NAME, __LINE__);
    return 0;
  }
  
  float out;
  switch (object->basic_type->id) {
    case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int8_t** fields = (int8_t**)object->data;
      int8_t val = *(int8_t*)&fields[0];
      out = is_unsigned ? (float)(uint8_t)val : (float)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int16_t** fields = (int16_t**)object->data;
      int16_t val = *(int16_t*)&fields[0];
      out = is_unsigned ? (float)(uint16_t)val : (float)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_INT_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int32_t** fields = (int32_t**)object->data;
      int32_t val = *(int32_t*)&fields[0];
      out = is_unsigned ? (float)(uint32_t)val : (float)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int64_t** fields = (int64_t**)object->data;
      int64_t val = *(int64_t*)&fields[0];
      out = is_unsigned ? (float)(uint64_t)val : (float)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT_CLASS : {
      float** fields = (float**)object->data;
      out = (float)*(float*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE_CLASS : {
      double** fields = (double**)object->data;
      out = (float)*(double*)&fields[0];
      break;
    }
    default : {
      assert(0);
    }
  }
  
  return out;
}

double SPVM_API_numeric_object_to_double(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t* error_id) {
  
  *error_id = 0;
  
  if (object == NULL) {
    return 0;
  }
  
  int32_t is_numeric_object_type = SPVM_API_is_numeric_object_type((SPVM_RUNTIME*)env->runtime, object->basic_type, object->type_dimension, 0);
  
  if (!is_numeric_object_type) {
    SPVM_OBJECT* obj_exception = SPVM_API_new_string_nolen_no_mortal(env, stack, "Type conversion failed. The type of the object must be a numeric object type.");
    SPVM_API_set_exception(env, stack, obj_exception);
    *error_id = SPVM_NATIVE_C_BASIC_TYPE_ID_ERROR_CLASS;
    return 0;
  }
  
  double out;
  switch (object->basic_type->id) {
    case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int8_t** fields = (int8_t**)object->data;
      int8_t val = *(int8_t*)&fields[0];
      out = is_unsigned ? (double)(uint8_t)val : (double)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int16_t** fields = (int16_t**)object->data;
      int16_t val = *(int16_t*)&fields[0];
      out = is_unsigned ? (double)(uint16_t)val : (double)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_INT_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int32_t** fields = (int32_t**)object->data;
      int32_t val = *(int32_t*)&fields[0];
      out = is_unsigned ? (double)(uint32_t)val : (double)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, FILE_NAME, __LINE__);
      if (*error_id) { return 0; }
      int64_t** fields = (int64_t**)object->data;
      int64_t val = *(int64_t*)&fields[0];
      out = is_unsigned ? (double)(uint64_t)val : (double)val;
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT_CLASS : {
      float** fields = (float**)object->data;
      out = (double)*(float*)&fields[0];
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE_CLASS : {
      double** fields = (double**)object->data;
      out = (double)*(double*)&fields[0];
      break;
    }
    default : {
      assert(0);
    }
  }
  
  return out;
}

SPVM_OBJECT* SPVM_API_numeric_object_to_string_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t* error_id) {
  
  *error_id = 0;
  
  if (object == NULL) {
    return NULL;
  }
  
  int32_t is_numeric_object_type = SPVM_API_is_numeric_object_type((SPVM_RUNTIME*)env->runtime, object->basic_type, object->type_dimension, 0);
  
  if (!is_numeric_object_type) {
    SPVM_OBJECT* obj_exception = SPVM_API_new_string_nolen_no_mortal(env, stack, "Type conversion failed. The type of the object must be a numeric object type.");
    SPVM_API_set_exception(env, stack, obj_exception);
    *error_id = SPVM_NATIVE_C_BASIC_TYPE_ID_ERROR_CLASS;
    return 0;
  }
  
  char* tmp_buffer = SPVM_API_get_stack_tmp_buffer(env, stack);
  
  switch (object->basic_type->id) {
    case SPVM_NATIVE_C_BASIC_TYPE_ID_BYTE_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, __FILE__, __LINE__);
      if (*error_id) { return 0; }
      
      int8_t** fields = (int8_t**)object->data;
      if (is_unsigned) {
        snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%" PRIu8, *(uint8_t*)&fields[0]);
      } else {
        snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%" PRId8, *(int8_t*)&fields[0]);
      }
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_SHORT_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, __FILE__, __LINE__);
      if (*error_id) { return 0; }

      int16_t** fields = (int16_t**)object->data;
      if (is_unsigned) {
        snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%" PRIu16, *(uint16_t*)&fields[0]);
      } else {
        snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%" PRId16, *(int16_t*)&fields[0]);
      }
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_INT_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, __FILE__, __LINE__);
      if (*error_id) { return 0; }

      int32_t** fields = (int32_t**)object->data;
      if (is_unsigned) {
        snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%" PRIu32, *(uint32_t*)&fields[0]);
      } else {
        snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%" PRId32, *(int32_t*)&fields[0]);
      }
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_LONG_CLASS : {
      int8_t is_unsigned = SPVM_API_get_field_byte_by_name(env, stack, object, "unsigned", error_id, __func__, __FILE__, __LINE__);
      if (*error_id) { return 0; }

      int64_t** fields = (int64_t**)object->data;
      if (is_unsigned) {
        snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%" PRIu64, *(uint64_t*)&fields[0]);
      } else {
        snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%" PRId64, *(int64_t*)&fields[0]);
      }
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_FLOAT_CLASS : {
      float** fields = (float**)object->data;
      snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%g", *(float*)&fields[0]);
      break;
    }
    case SPVM_NATIVE_C_BASIC_TYPE_ID_DOUBLE_CLASS : {
      double** fields = (double**)object->data;
      snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%g", *(double*)&fields[0]);
      break;
    }
    default : {
      assert(0);
    }
  }
  
  int32_t string_length = strlen(tmp_buffer);
  SPVM_OBJECT* string = SPVM_API_new_string_no_mortal(env, stack, tmp_buffer, string_length);
  
  return string;
}

SPVM_OBJECT* SPVM_API_numeric_object_to_string(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* object, int32_t* error_id) {
  
  SPVM_OBJECT* new_object = SPVM_API_numeric_object_to_string_no_mortal(env, stack, object, error_id);
  
  SPVM_API_push_mortal(env, stack, new_object);
  
  return new_object;
}

int32_t SPVM_API_push_caller_info(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_RUNTIME_METHOD* current_method, const char* caller_method_abs_name, const char* caller_file, int32_t caller_line) {
  
  // Touch pointers to ensure they are valid and prevent optimization using volatile
  if (caller_method_abs_name) {
    volatile char c = *caller_method_abs_name;
  }
  if (caller_file) {
    volatile char c = *caller_file;
  }
  
  SPVM_VALUE** current_caller_info_stack_ptr = (SPVM_VALUE**)&stack[SPVM_API_C_STACK_INDEX_CALLER_INFO_STACK].address;
  int32_t* current_call_depth_ptr = &stack[SPVM_API_C_STACK_INDEX_CALL_DEPTH].ival;
  int32_t* current_capacity_ptr = &stack[SPVM_API_C_STACK_INDEX_CALLER_INFO_STACK_CAPACITY].ival;
  int32_t record_size = stack[SPVM_API_C_STACK_INDEX_CALLER_INFO_STACK_RECORD_SIZE].ival;
  
  // Extend caller info stack if the current call depth reaches the capacity
  // Use __builtin_expect to hint that stack extension is unlikely (unlikely branch)
  if (__builtin_expect(*current_call_depth_ptr >= *current_capacity_ptr, 0)) {
    int32_t new_capacity = *current_capacity_ptr * 2;
    SPVM_VALUE* new_caller_info_stack = (SPVM_VALUE*)SPVM_API_new_memory_block(env, stack, sizeof(SPVM_VALUE) * record_size * new_capacity);
    
    if (__builtin_expect(new_caller_info_stack == NULL, 0)) {
      return -1; // Error
    }
    
    // Copy existing data
    memcpy(new_caller_info_stack, *current_caller_info_stack_ptr, sizeof(SPVM_VALUE) * record_size * *current_capacity_ptr);
    
    // Update capacity and free old block
    *current_capacity_ptr = new_capacity;
    SPVM_API_free_memory_block(env, stack, *current_caller_info_stack_ptr);
    *current_caller_info_stack_ptr = new_caller_info_stack;
  }
  
  // Push the record at the index indicated by the current call depth
  int32_t offset = *current_call_depth_ptr * record_size;
  (*current_caller_info_stack_ptr)[offset + 0].address = (char*)caller_method_abs_name;
  (*current_caller_info_stack_ptr)[offset + 1].address = (char*)caller_file;
  (*current_caller_info_stack_ptr)[offset + 2].ival = caller_line;
  (*current_caller_info_stack_ptr)[offset + 3].address = current_method;
  
  return 0;
}

int32_t SPVM_API_get_call_depth(SPVM_ENV* env, SPVM_VALUE* stack) {
  int32_t index = (int32_t)(intptr_t)env->api->runtime->stack_index_call_depth;
  int32_t call_depth = *(int32_t*)&stack[index];
  return call_depth;
}

SPVM_VALUE* SPVM_API_get_caller_info_stack(SPVM_ENV* env, SPVM_VALUE* stack) {
  int32_t index = (int32_t)(intptr_t)env->api->runtime->stack_index_caller_info_stack;
  SPVM_VALUE* caller_info_stack = (SPVM_VALUE*)stack[index].address;
  return caller_info_stack;
}

int32_t SPVM_API_get_caller_info_stack_record_size(SPVM_ENV* env, SPVM_VALUE* stack) {
  int32_t index = (int32_t)(intptr_t)env->api->runtime->stack_index_caller_info_stack_record_size;
  int32_t record_size = stack[index].ival;
  return record_size;
}

SPVM_RUNTIME_METHOD* SPVM_API_get_current_method(SPVM_ENV* env, SPVM_VALUE* stack, int32_t level, int32_t* error_id) {
  
  /* Initialize error_id to 0 */
  *error_id = 0;
  
  /* Get the current call depth */
  int32_t call_depth = SPVM_API_get_call_depth(env, stack);
  
  /* Check if level is negative */
  if (level < 0) {
    return NULL;
  }

  /* Calculate the target call depth */
  int32_t target_call_depth = call_depth - level;

  /* Check if the depth is out of range (Level 0 is the current method) */
  if (target_call_depth < 0) {
    return NULL;
  }
  
  /* Get the raw pointer to the caller information stack and the record size */
  SPVM_VALUE* caller_info_stack = SPVM_API_get_caller_info_stack(env, stack);
  int32_t record_size = SPVM_API_get_caller_info_stack_record_size(env, stack);
  
  /* The method information is stored at index 3 of each record */
  int32_t offset = target_call_depth * record_size;
  
  SPVM_RUNTIME_METHOD* method = (SPVM_RUNTIME_METHOD*)caller_info_stack[offset + 3].address;
  
  /* method must not be NULL during method execution if target_call_depth is valid. */
  if (target_call_depth <= call_depth) {
    assert(method != NULL);
  }
  
  return method;
}

SPVM_OBJECT* SPVM_API_caller_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, int32_t level, int32_t* error_id) {
  
  *error_id = 0;
  
  /* Get the current call depth */
  int32_t call_depth = SPVM_API_get_call_depth(env, stack);
  
  /* Check if level is negative */
  if (level < 0) {
    *error_id = env->die(env, stack, "The level must be greater than or equal to 0.", __func__, FILE_NAME, __LINE__);
    return NULL;
  }
  
  /* Calculate the target call depth. */
  int32_t target_call_depth = call_depth - level;
  
  /* Check if the depth is out of range */
  if (target_call_depth < 0) {
    *error_id = env->die(env, stack, "The level exceeds the call depth.", __func__, FILE_NAME, __LINE__);
    return NULL;
  }
  
  SPVM_VALUE* caller_info_stack = SPVM_API_get_caller_info_stack(env, stack);
  int32_t record_size = SPVM_API_get_caller_info_stack_record_size(env, stack);
  
  /* Calculate the target index in the caller info stack */
  int32_t target_index = target_call_depth * record_size;
  
  /* Extract information directly from the record. */
  const char* caller_func_name = (const char*)caller_info_stack[target_index + 0].address;
  const char* caller_file = (const char*)caller_info_stack[target_index + 1].address;
  int32_t caller_line = caller_info_stack[target_index + 2].ival;
  
  /* Create CallerInfo object without mortal (Unified creation) */
  SPVM_RUNTIME_BASIC_TYPE* basic_type = SPVM_API_get_basic_type_by_id(env, stack, SPVM_NATIVE_C_BASIC_TYPE_ID_CALLER_INFO_CLASS);
  SPVM_OBJECT* obj_caller_info = SPVM_API_new_object_no_mortal(env, stack, basic_type);
  if (!obj_caller_info) {
    *error_id = env->die(env, stack, "Failed to create a new CallerInfo object.", __func__, FILE_NAME, __LINE__);
    return NULL;
  }
  if (*error_id) { return NULL; }
  
  /* Set fields to the new object (Strings are created as non-mortal for now, 
     as they are owned by the obj_caller_info fields) */
  if (caller_func_name) {
    SPVM_OBJECT* obj_func_name = SPVM_API_new_string_nolen_no_mortal(env, stack, caller_func_name);
    SPVM_API_set_field_string_by_name(env, stack, obj_caller_info, "method_abs_name", obj_func_name, error_id, __func__, FILE_NAME, __LINE__);
    if (*error_id) { return NULL; }
  }
  
  if (caller_file) {
    SPVM_OBJECT* obj_file = SPVM_API_new_string_nolen_no_mortal(env, stack, caller_file);
    SPVM_API_set_field_string_by_name(env, stack, obj_caller_info, "file", obj_file, error_id, __func__, FILE_NAME, __LINE__);
    if (*error_id) { return NULL; }
  }
  
  SPVM_API_set_field_int_by_name(env, stack, obj_caller_info, "line", caller_line, error_id, __func__, FILE_NAME, __LINE__);
  if (*error_id) { return NULL; }
  
  return obj_caller_info;
}

SPVM_OBJECT* SPVM_API_caller(SPVM_ENV* env, SPVM_VALUE* stack, int32_t level, int32_t* error_id) {
  
  SPVM_OBJECT* obj_caller_info = SPVM_API_caller_no_mortal(env, stack, level, error_id);
  
  if (*error_id) { return NULL; }
  
  if (obj_caller_info) {
    SPVM_API_push_mortal(env, stack, obj_caller_info);
  }
  
  return obj_caller_info;
}

int32_t SPVM_API_die(SPVM_ENV* env, SPVM_VALUE* stack, const char* exception_format, const char* func_name, const char* file, int32_t line, ...) {
  
  /* 0. Touch pointers to ensure they are valid and prevent optimization using volatile */
  if (func_name) {
    volatile char c = *func_name;
  }
  if (file) {
    volatile char c = *file;
  }

  va_list args;
  
  /* 1. Calculate the required length for the exception message */
  va_start(args, line);
  int32_t length = vsnprintf(NULL, 0, exception_format, args);
  va_end(args);

  /* 2. Create the exception string object */
  SPVM_OBJECT* obj_exception = SPVM_API_new_string_no_mortal(env, stack, NULL, length);
  char* exception_chars = (char*)SPVM_API_get_chars(env, stack, obj_exception);

  /* 3. Write the formatted message into the string object */
  va_start(args, line);
  vsnprintf(exception_chars, length + 1, exception_format, args);
  va_end(args);

  /* 4. Delegate to die_with_string for common exception setting logic */
  return SPVM_API_die_with_string(env, stack, obj_exception, func_name, file, line);
}

int32_t SPVM_API_die_with_string(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* obj_exception, const char* func_name, const char* file, int32_t line) {
  
  /* Set the exception object */
  SPVM_API_set_exception(env, stack, obj_exception);

  /* Set exception metadata to the stack indices */
  stack[SPVM_API_C_STACK_INDEX_EXCEPTION_METHOD_ABS_NAME].address = (char*)func_name;
  stack[SPVM_API_C_STACK_INDEX_EXCEPTION_FILE].address = (char*)file;
  stack[SPVM_API_C_STACK_INDEX_EXCEPTION_LINE].ival = line;
  stack[SPVM_API_C_STACK_INDEX_EXCEPTION_CALL_DEPTH].ival = stack[SPVM_API_C_STACK_INDEX_CALL_DEPTH].ival;

  return SPVM_NATIVE_C_BASIC_TYPE_ID_ERROR_CLASS;
}

SPVM_OBJECT* SPVM_API_new_stack_trace_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* exception, SPVM_RUNTIME_METHOD* method, int32_t line) {
  
  if (stack[SPVM_API_C_STACK_INDEX_CALL_DEPTH].ival > 100) {
    return exception;
  }
  
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)env->runtime;
  
  SPVM_RUNTIME_BASIC_TYPE* basic_type = method->current_basic_type;
  const char* basic_type_name = basic_type->name;
  const char* method_name = method->name;
  
  const char* class_file = basic_type->file;
  
  // Basic type name and method name
  const char* new_line_part = "\n  ";
  const char* arrow_part = "->";
  const char* at_part = " at ";
  
  // Exception
  const char* exception_bytes = SPVM_API_get_chars(env, stack, exception);
  int32_t exception_length = SPVM_API_length(env, stack, exception);
  
  // Total string length
  int32_t total_length = 0;
  total_length += exception_length;
  total_length += strlen(new_line_part);
  total_length += strlen(basic_type_name);
  total_length += strlen(arrow_part);
  total_length += strlen(method_name);
  total_length += strlen(at_part);
  total_length += strlen(class_file);
  
  const char* line_part = " line ";
  
  total_length += strlen(line_part);
  char tmp_buffer[SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE] = {0};
  snprintf(tmp_buffer, SPVM_NATIVE_C_STACK_TMP_BUFFER_SIZE, "%" PRId32, line);
  total_length += strlen(tmp_buffer);
  
  // Create exception message
  SPVM_OBJECT* new_exception = SPVM_API_new_string_no_mortal(env, stack, NULL, total_length);
  
  if (new_exception) {
    const char* new_exception_bytes = SPVM_API_get_chars(env, stack, new_exception);
    
    memcpy(
      (char*)(new_exception_bytes),
      (char*)(exception_bytes),
      exception_length
    );
    
    sprintf(
      (char*)new_exception_bytes + exception_length,
      "%s%s%s%s%s%s%s%" PRId32,
      new_line_part,
      basic_type_name,
      arrow_part,
      method_name,
      at_part,
      class_file,
      line_part,
      line
    );
  }
  
  return new_exception;
}

SPVM_OBJECT* SPVM_API_new_stack_trace(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* exception, SPVM_RUNTIME_METHOD* method, int32_t line) {
  
  
  SPVM_OBJECT* stack_trace = SPVM_API_new_stack_trace_no_mortal(env, stack, exception, method, line);
  
  SPVM_API_push_mortal(env, stack, stack_trace);
  
  return stack_trace;
}

/* File scope helper for portability */
static int32_t SPVM_API_strnlen(const char *s, int32_t maxlen) {
  if (!s) return 0;
  int32_t len = 0;
  while (len < maxlen && s[len] != '\0') {
    len++;
  }
  return len;
}

/* Helper to format a single line of the stack trace */
static int32_t SPVM_API_build_caller_stack_line(char* buffer, const char* func_name, const char* file, int32_t line) {
  
  if (buffer) {
    return sprintf(buffer, "\n  %s at %s line %" PRId32, func_name, file, line);
  }
  else {
    char tmp_line[64];
    snprintf(tmp_line, sizeof(tmp_line), "%" PRId32, line);
    return (int32_t)(strlen("\n  ") + strlen(func_name) + strlen(" at ") + strlen(file) + strlen(" line ") + strlen(tmp_line));
  }
}

SPVM_OBJECT* SPVM_API_build_exception_message_no_mortal(SPVM_ENV* env, SPVM_VALUE* stack, int32_t level) {
  
  const char* unknown_func_name = "(Method name unknown)";
  const char* unknown_file = "(File name unknown)";
  const char* too_long_func_name = "(Method name too long)";
  const char* too_long_file = "(File name too long)";
  const int32_t max_func_name_len = 511;
  const int32_t max_file_len = 1023;
  
  SPVM_OBJECT* obj_exception = SPVM_API_get_exception(env, stack);
  const char* exception_bytes = SPVM_API_get_chars(env, stack, obj_exception);
  int32_t exception_length = SPVM_API_length(env, stack, obj_exception);

  const char* exception_func_name = (const char*)stack[SPVM_API_C_STACK_INDEX_EXCEPTION_METHOD_ABS_NAME].oval;
  if (!exception_func_name) {
    exception_func_name = unknown_func_name;
  }
  else if (SPVM_API_strnlen(exception_func_name, max_func_name_len + 1) > max_func_name_len) {
    exception_func_name = too_long_func_name;
  }
  
  const char* exception_file = (const char*)stack[SPVM_API_C_STACK_INDEX_EXCEPTION_FILE].oval;
  if (!exception_file) {
    exception_file = unknown_file;
  }
  else if (SPVM_API_strnlen(exception_file, max_file_len + 1) > max_file_len) {
    exception_file = too_long_file;
  }

  int32_t exception_line = stack[SPVM_API_C_STACK_INDEX_EXCEPTION_LINE].ival;
  int32_t exception_call_depth = stack[SPVM_API_C_STACK_INDEX_EXCEPTION_CALL_DEPTH].ival;

  SPVM_VALUE* caller_info_stack = (SPVM_VALUE*)stack[SPVM_API_C_STACK_INDEX_CALLER_INFO_STACK].address;
  int32_t record_size = stack[SPVM_API_C_STACK_INDEX_CALLER_INFO_STACK_RECORD_SIZE].ival;

  /* Calculate the target depth with clamping */
  int32_t current_call_depth = stack[SPVM_API_C_STACK_INDEX_CALL_DEPTH].ival;
  int32_t target_call_depth = current_call_depth - level;
  
  if (target_call_depth < 0) {
    target_call_depth = 0;
  }
  else if (target_call_depth > exception_call_depth) {
    target_call_depth = exception_call_depth;
  }
  
  /* 1. Calculate total length */
  int32_t total_length = exception_length;
  
  // Origin
  total_length += SPVM_API_build_caller_stack_line(NULL, exception_func_name, exception_file, exception_line);

  // Callers
  for (int32_t depth = exception_call_depth; depth >= target_call_depth; depth--) {
    int32_t offset = depth * record_size;
    const char* func_name = (const char*)caller_info_stack[offset + 0].address;
    if (!func_name) {
      func_name = unknown_func_name;
    }
    else if (SPVM_API_strnlen(func_name, max_func_name_len + 1) > max_func_name_len) {
      func_name = too_long_func_name;
    }
    
    const char* file = (const char*)caller_info_stack[offset + 1].address;
    if (!file) {
      file = unknown_file;
    }
    else if (SPVM_API_strnlen(file, max_file_len + 1) > max_file_len) {
      file = too_long_file;
    }
    
    int32_t line = (int32_t)(intptr_t)caller_info_stack[offset + 2].ival;
    
    total_length += SPVM_API_build_caller_stack_line(NULL, func_name, file, line);
  }

  /* 2. Allocate */
  SPVM_OBJECT* obj_new_exception = SPVM_API_new_string_no_mortal(env, stack, NULL, total_length);
  char* new_exception_bytes = (char*)SPVM_API_get_chars(env, stack, obj_new_exception);
  
  /* 3. Fill */
  memcpy(new_exception_bytes, exception_bytes, exception_length);
  int32_t current_offset = exception_length;

  // Write Origin
  current_offset += SPVM_API_build_caller_stack_line(new_exception_bytes + current_offset, exception_func_name, exception_file, exception_line);

  // Write Callers
  for (int32_t depth = exception_call_depth; depth >= target_call_depth; depth--) {
    int32_t offset = depth * record_size;
    const char* func_name = (const char*)caller_info_stack[offset + 0].address;
    if (!func_name) {
      func_name = unknown_func_name;
    }
    else if (SPVM_API_strnlen(func_name, max_func_name_len + 1) > max_func_name_len) {
      func_name = too_long_func_name;
    }
    
    const char* file = (const char*)caller_info_stack[offset + 1].address;
    if (!file) {
      file = unknown_file;
    }
    else if (SPVM_API_strnlen(file, max_file_len + 1) > max_file_len) {
      file = too_long_file;
    }
    
    int32_t line = (int32_t)(intptr_t)caller_info_stack[offset + 2].ival;
    
    current_offset += SPVM_API_build_caller_stack_line(new_exception_bytes + current_offset, func_name, file, line);
  }

  return obj_new_exception;
}

SPVM_OBJECT* SPVM_API_build_exception_message(SPVM_ENV* env, SPVM_VALUE* stack, int32_t level) {
  SPVM_OBJECT* obj_message = SPVM_API_build_exception_message_no_mortal(env, stack, level);
  SPVM_API_push_mortal(env, stack, obj_message);
  return obj_message;
}

int32_t SPVM_API_is_utf8(SPVM_ENV* env, SPVM_VALUE* stack, SPVM_OBJECT* obj_string, int32_t* error_id) {
  
  *error_id = 0;
  
  // A string obj_string must not be NULL
  if (!obj_string) {
    *error_id = env->die(env, stack, "The string obj_string must be defined.", __func__, FILE_NAME, __LINE__);
    return 0;
  }

  // Get the string pointer and its length
  const char* str = SPVM_API_get_chars(env, stack, obj_string);
  int32_t int_len = SPVM_API_length(env, stack, obj_string);
  
  // Call the UTF-8 validation function directly
  int32_t is_utf8 = SPVM_UTF8_is_utf8(str, (size_t)int_len);

  return is_utf8;
}

size_t SPVM_API_c_strlen(SPVM_ENV* env, SPVM_VALUE* stack, const char* str) {
  return strlen(str);
}

void* SPVM_API_c_memcpy(SPVM_ENV* env, SPVM_VALUE* stack, void* dest, const void* src, size_t n) {
  return memcpy(dest, src, n);
}

void* SPVM_API_c_memset(SPVM_ENV* env, SPVM_VALUE* stack, void* s, int c, size_t n) {
  return memset(s, c, n);
}

int SPVM_API_c_memcmp(SPVM_ENV* env, SPVM_VALUE* stack, const void* s1, const void* s2, size_t n) {
  return memcmp(s1, s2, n);
}

long long SPVM_API_c_strtoll(SPVM_ENV* env, SPVM_VALUE* stack, const char* str, char** endptr, int base) {
  return strtoll(str, endptr, base);
}

float SPVM_API_c_strtof(SPVM_ENV* env, SPVM_VALUE* stack, const char* str, char** endptr) {
  return strtof(str, endptr);
}

double SPVM_API_c_strtod(SPVM_ENV* env, SPVM_VALUE* stack, const char* str, char** endptr) {
  return strtod(str, endptr);
}

int SPVM_API_c_fputs(SPVM_ENV* env, SPVM_VALUE* stack, const char* s, void* stream) {
  return fputs(s, (FILE*)stream);
}

int SPVM_API_c_fgetc(SPVM_ENV* env, SPVM_VALUE* stack, void* stream) {
  return fgetc((FILE*)stream);
}

int SPVM_API_c_snprintf_len(SPVM_ENV* env, SPVM_VALUE* stack, char* str, size_t size, const char* format, SPVM_VALUE* args, int32_t args_length) {
  if (!format) {
    return -1;
  }

  int32_t total_len = 0;
  int32_t arg_index = 0;
  const char* p = format;
  char* dst = str;
  size_t left = size;

  while (*p) {
    if (*p == '%' && *(p + 1) != '%') {
      /* Start parsing */
      const char* start = p;
      p++;

      /* Flags, width, precision */
      while (*p && (strchr("-+ #0123456789.", *p))) {
        p++;
      }

      /* Length modifiers */
      int32_t is_64bit = 0;
      if (*p == 'l') {
        p++;
        is_64bit = 1; /* long or long long */
        if (*p == 'l') {
          p++;
        }
      } else if (*p == 'j' || *p == 'z' || *p == 't') {
        is_64bit = 1; /* intmax_t, size_t, ptrdiff_t */
        p++;
      } else if (*p == 'h') {
        p++;
        if (*p == 'h') p++;
      } else if (*p == 'L') {
        p++;
      }

      /* Specifier */
      char type = *p;
      if (type) p++;

      if (arg_index >= args_length) {
        return -1;
      }

      /* Sub-format string */
      char sub_fmt[64];
      size_t fmt_len = p - start;
      if (fmt_len > 63) fmt_len = 63;
      memcpy(sub_fmt, start, fmt_len);
      sub_fmt[fmt_len] = '\0';

      int32_t written = 0;
      SPVM_VALUE value = args[arg_index++];

      /* Dispatch by type */
      if (type == 's') {
        written = snprintf(dst, left, sub_fmt, (char*)value.address);
      } else if (type == 'd' || type == 'i') {
        if (is_64bit) written = snprintf(dst, left, sub_fmt, value.lval);
        else written = snprintf(dst, left, sub_fmt, value.ival);
      } else if (type == 'u' || type == 'x' || type == 'X' || type == 'o') {
        if (is_64bit) written = snprintf(dst, left, sub_fmt, (unsigned long long)value.lval);
        else written = snprintf(dst, left, sub_fmt, (unsigned int)value.ival);
      } else if (type == 'f' || type == 'g' || type == 'G' || type == 'e' || type == 'E') {
        written = snprintf(dst, left, sub_fmt, value.dval);
      } else if (type == 'p') {
        written = snprintf(dst, left, sub_fmt, value.address);
      } else if (type == 'c') {
        written = snprintf(dst, left, sub_fmt, value.ival);
      } else {
        return -1;
      }

      if (written < 0) return -1;
      total_len += written;
      
      if ((size_t)written < left) {
        dst += written;
        left -= written;
      } else {
        if (left > 0) {
          dst += (left - 1);
          left = 1;
        }
      }
    } else {
      /* Literal */
      if (*p == '%' && *(p + 1) == '%') p++;
      
      if (left > 1) {
        *dst++ = *p;
        left--;
      }
      p++;
      total_len++;
    }
  }

  /* Null-termination */
  if (size > 0) {
    if (left > 0) {
      *dst = '\0';
    } else {
      str[size - 1] = '\0';
    }
  }

  return total_len;
}
