/* core.c - 가상 머신의 모든 루틴이 들어 있는 곳.
   Copyright (C) 2006 Weongyo Jeong (weongyo@gmail.com)

This file is part of ROVM.

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

ROVM is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

#include <stdlib.h>
#include <math.h>

#include "types.h"
#include "mpool.h"
#include "tktree.h"
#include "clstree.h"
#include "sha1.h"
#include "listen.h"
#include "rovm.h"

#include "thread.h"
#include "thread_mutex.h"
#include "thread_cond.h"

#include "mpm_worker_fdqueue.h"
#include "mpm_worker_pod.h"
#include "mpm_worker.h"

#include "connection.h"
#include "request.h"
#include "proc_rc.h"

#include "log.h"
#include "common.h"
#include "ticket.h"
#include "utils.h"

#include "class.h"
#include "file.h"
#include "el.h"

#define ROVM_CODE_DEBUG(EXPN)           /* nothing */

#define GETPC(OFFSET)                   CORE_OP_IDX (ri, pc + 1 + (OFFSET))

#define PUSH(N)                         TICKET_SP (CORE_TICKET (ri)) += (N); _MAX_CB_ (0);
#define POP(N)                          TICKET_SP (CORE_TICKET (ri)) -= (N); _MIN_CB_ (0);
/** Stack 번호에 따라 - (마이너스) 가 붙는 것을 명심하기 바랍니다.  */
#define	STACK(N)                        (&TICKET_SP (CORE_TICKET (ri))[-(N)])
#define RSTACK                          STACK

/** 로컬 변수에 접근할 때 사용되는 매크로입니다.  */
#define LOCAL(N)                        (&TICKET_LOCAL (CORE_TICKET (ri))[(N)])

/** Delayed Stack Slot 을 초기화하는 매크로입니다.  */
#define _CLEAR_(UNUSED)                 ticket_clear_delay_slot (CORE_TICKET (ri))

/**
   스택의 경계점을 검사하는 매크로로써, PUSH 혹은 POP Operation 이 발생하였을 때,
   수행되는 부분입니다.
   
   _MAX_CB_ 는 Stack 의 최대 포인터를 검사합니다.
*/
#define _MAX_CB_(NOT)                                                   \
  if (TICKET_SP (CORE_TICKET (ri)) >= TICKET_MAX_SP (CORE_TICKET (ri))) \
    {                                                                   \
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,    \
                         "Overflow of Stack Boundary");                 \
      return -1;                                                        \
    }
/**
   스택의 경계점을 검사하는 매크로로써, PUSH 혹은 POP Operation 이 발생하였을 때,
   수행되는 부분입니다.  
   
   _MIN_CB_ 는 Stack 의 최소 포인터를 검사합니다.
*/
#define _MIN_CB_(NOT)                                                   \
  if (TICKET_SP (CORE_TICKET (ri)) < TICKET_MIN_SP (CORE_TICKET (ri)))  \
    {                                                                   \
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,    \
                         "Underflow of Stack Boundary");                \
      return -1;                                                        \
    }
/**
   스택의 경계점을 검사하는 매크로이다.  MINNUM 은 스택상에 최소 존재해야 하는 
   채워진 slot 의 갯수를 의미하며 만약 그것보다 작다면 -1 을 반환해 버린다. 
*/
#define _CB_(MINNUM)                                                    \
  if ((TICKET_STACK (CORE_TICKET (ri)) + (MINNUM - 1)) > TICKET_SP (CORE_TICKET (ri))) \
    {                                                                   \
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,    \
                         "Overflow of Stack Boundary");                 \
      return -1;                                                        \
    }

/**
   Local 변수의 경계점을 검사하는 매크로이다.
 */
#define _CLB_(IDX)                                                      \
  if (IDX < 0                                                           \
      || &(TICKET_LOCAL (CORE_TICKET (ri))[(IDX)]) >= TICKET_MAXLOCAL (CORE_TICKET (ri))) \
    {                                                                   \
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,    \
                         "Overflow of Local Variable Boundary");        \
      return -1;                                                        \
    }

/**
   만약 SLOT 이 STACK_TYPE_OBJREF 일 경우, 오류를 기록하고 빠져나가 버린다. 
   예를 들면, `ADD' opcode 시 인자가 들어있는 stack slot 은 objectref 여서는
   안되기 때문이다.
*/
#define _DENY_OBJREF(SLOT)                                              \
  if (STACK_TYPE (SLOT) == STACK_TYPE_OBJREF)                           \
    {                                                                   \
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,    \
                         "Invalid operation");                          \
      return -1;                                                        \
    }

/**
   만약 slot 이 float 가 아닐 경우, 오류를 출력한 후 실행을 빠져 나옵니다.
 */
#define _MUST_FLOAT(SLOT)                                               \
  if (STACK_TYPE (SLOT) != STACK_TYPE_FLOAT)                            \
    {                                                                   \
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,    \
                         "Must float type");                            \
      return -1;                                                        \
    }

#define _MUST_DOUBLE(SLOT)                                              \
  if (STACK_TYPE (SLOT) != STACK_TYPE_DOUBLE)                           \
    {                                                                   \
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,    \
                         "Must float type");                            \
      return -1;                                                        \
    }

/** SRC1 과 SRC2 의 값을 더하여 DST 에 저장하게 됩니다.  */
#define	ADD_INT(DST, SRC1, SRC2)                                        \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  SET_STACK_VALUE (DST, INT, GET_STACK_VALUE (INT, SRC1) + GET_STACK_VALUE (INT, SRC2));

/** SRC1 과 SRC2 의 값을 뺀 후, DST 에 저장합니다.  */
#define	MINUS_INT(DST, SRC1, SRC2)                                      \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  SET_STACK_VALUE (DST, INT, GET_STACK_VALUE (INT, SRC1) - GET_STACK_VALUE (INT, SRC2));

/** SRC1 과 SRC2 의 값을 곱한 후, DST 에 저장합니다.  */
#define	MULTIPLY_INT(DST, SRC1, SRC2)                                   \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  SET_STACK_VALUE (DST, INT, GET_STACK_VALUE (INT, SRC1) * GET_STACK_VALUE (INT, SRC2));

/** SRC1 과 SRC2 의 값을 나눈 후, DST 에 저장합니다.  */
#define	DIVIDE_INT(DST, SRC1, SRC2)                                     \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  SET_STACK_VALUE (DST, INT, GET_STACK_VALUE (INT, SRC1) / GET_STACK_VALUE (INT, SRC2));

/** SRC1 과 SRC2 의 값을 나눈 후 그에 대한 나머지를 DST 에 저장합니다.  */
#define	REMAINDER_INT(DST, SRC1, SRC2)                                  \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  SET_STACK_VALUE (DST, INT, GET_STACK_VALUE (INT, SRC1) % GET_STACK_VALUE (INT, SRC2));

/** SRC1 과 SRC2 의 값을 더하여 DST 에 저장하게 됩니다.  */
#define	ADD_FLOAT(DST, SRC1, SRC2)                                      \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  _MUST_FLOAT (SRC1);                                                   \
  _MUST_FLOAT (SRC2);                                                   \
  SET_STACK_VALUE (DST, FLOAT, GET_STACK_VALUE (FLOAT, SRC1) + GET_STACK_VALUE (FLOAT, SRC2));

#define	MINUS_FLOAT(DST, SRC1, SRC2)                                    \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  _MUST_FLOAT (SRC1);                                                   \
  _MUST_FLOAT (SRC2);                                                   \
  SET_STACK_VALUE (DST, FLOAT, GET_STACK_VALUE (FLOAT, SRC1) - GET_STACK_VALUE (FLOAT, SRC2));

#define	MULTIPLY_FLOAT(DST, SRC1, SRC2)                                 \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  _MUST_FLOAT (SRC1);                                                   \
  _MUST_FLOAT (SRC2);                                                   \
  SET_STACK_VALUE (DST, FLOAT, GET_STACK_VALUE (FLOAT, SRC1) * GET_STACK_VALUE (FLOAT, SRC2));

#define	DIVIDE_FLOAT(DST, SRC1, SRC2)                                   \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  _MUST_FLOAT (SRC1);                                                   \
  _MUST_FLOAT (SRC2);                                                   \
  SET_STACK_VALUE (DST, FLOAT, GET_STACK_VALUE (FLOAT, SRC1) / GET_STACK_VALUE (FLOAT, SRC2));

#define	REMAINDER_FLOAT(DST, SRC1, SRC2)                                \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  _MUST_FLOAT (SRC1);                                                   \
  _MUST_FLOAT (SRC2);                                                   \
  SET_STACK_VALUE (DST, FLOAT, fmodf (GET_STACK_VALUE (FLOAT, SRC1), GET_STACK_VALUE (FLOAT, SRC2)));

#define	ADD_DOUBLE(DST, SRC1, SRC2)                                      \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  _MUST_DOUBLE (SRC1);                                                   \
  _MUST_DOUBLE (SRC2);                                                   \
  SET_STACK_VALUE (DST, DOUBLE, GET_STACK_VALUE (DOUBLE, SRC1) + GET_STACK_VALUE (DOUBLE, SRC2));

#define	MINUS_DOUBLE(DST, SRC1, SRC2)                                    \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  _MUST_DOUBLE (SRC1);                                                   \
  _MUST_DOUBLE (SRC2);                                                   \
  SET_STACK_VALUE (DST, DOUBLE, GET_STACK_VALUE (DOUBLE, SRC1) - GET_STACK_VALUE (DOUBLE, SRC2));

#define	MULTIPLY_DOUBLE(DST, SRC1, SRC2)                                 \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  _MUST_DOUBLE (SRC1);                                                   \
  _MUST_DOUBLE (SRC2);                                                   \
  SET_STACK_VALUE (DST, DOUBLE, GET_STACK_VALUE (DOUBLE, SRC1) * GET_STACK_VALUE (DOUBLE, SRC2));

#define	DIVIDE_DOUBLE(DST, SRC1, SRC2)                                   \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  _MUST_DOUBLE (SRC1);                                                   \
  _MUST_DOUBLE (SRC2);                                                   \
  SET_STACK_VALUE (DST, DOUBLE, GET_STACK_VALUE (DOUBLE, SRC1) / GET_STACK_VALUE (DOUBLE, SRC2));

#define	REMAINDER_DOUBLE(DST, SRC1, SRC2)                                \
  _CB_ (2);                                                             \
  _DENY_OBJREF (SRC1);                                                  \
  _DENY_OBJREF (SRC2);                                                  \
  _MUST_DOUBLE (SRC1);                                                   \
  _MUST_DOUBLE (SRC2);                                                   \
  SET_STACK_VALUE (DST, DOUBLE, fmod (GET_STACK_VALUE (DOUBLE, SRC1), GET_STACK_VALUE (DOUBLE, SRC2)));

#define MOVE_BOOLEAN_CONST(DST, SRC)            SET_STACK_VALUE (DST, BOOLEAN, SRC)
#define MOVE_CHAR_CONST(DST, SRC)               SET_STACK_VALUE (DST, CHAR, SRC)
#define MOVE_SHORT_CONST(DST, SRC)              SET_STACK_VALUE (DST, SHORT, SRC)
#define MOVE_INT_CONST(DST, SRC)                SET_STACK_VALUE (DST, INT, SRC)
#define MOVE_FLOAT_CONST(DST, SRC)              SET_STACK_VALUE (DST, FLOAT, SRC);
#define MOVE_DOUBLE_CONST(DST, SRC)             SET_STACK_VALUE (DST, DOUBLE, SRC);

#define MOVE_INT(DST, SRC)                      SET_STACK_VALUE (DST, INT, GET_STACK_VALUE (INT, SRC));
#define MOVE_OBJREF(DST, SRC)                   SET_STACK_VALUE (DST, OBJREF, (void *) (SRC))
#define MOVE_STRINGREF(DST, SRC)                SET_STACK_VALUE (DST, STRINGREF, (void *) (SRC))
#define MOVE_ARRAYREF(DST, SRC)                 SET_STACK_VALUE (DST, ARRAYREF, (void *) (SRC))

typedef char            rovm_int8_t;
typedef unsigned char   rovm_uint8_t;

/** request_rec 에 존재하는 pool 을 편리하게 쓸수 있도록 한 매크로이다.  */
#define CORE_POOL(NODE)                 (REQUEST_POOL (CORE_REQUEST (NODE)))

#define CORE_ROVM(NODE)                 REQUEST_ROVM (CORE_REQUEST (NODE))
#define CORE_REQUEST(NODE)              ((NODE)->r)
#define CORE_TICKET(NODE)               ((NODE)->tk)
#define CORE_OP(NODE)                   ((NODE)->op)
#define CORE_OP_IDX(NODE, IDX)          ((NODE)->op[(IDX)])
#define CORE_OPLEN(NODE)                ((NODE)->oplen)
#define CORE_NPC(NODE)                  ((NODE)->npc)
#define CORE_LOW(NODE)                  ((NODE)->low)
#define CORE_HIGH(NODE)                 ((NODE)->high)
#define CORE_IDX(NODE)                  ((NODE)->idx)

/**
   가상 머신을 호출하는 것의 정보를 보관하기 위해 사용하는 구조체.  이 구조체가 생성되게 된
   이유는 가상 머신의 각각의 opcode 를 호출할 때 전달되는 argument 수를 최소로 줄이기 위해서
   이다.
 */
struct rovmcore_info
{
  /** 사용자 request 에 대한 구조체.  */
  request_rec *r;
  /** 현재 가상 머신이 실행되는 ticket 기반.  */
  struct ticket *tk;

  /** 현재 처리 중인 OPCODE.  */
  rc_opcode_t *op;
  /** 현재 처리 중인 OPCODE 의 길이.  */
  rc_size_t oplen;

  /** 다음 PC (Program Counter).  */
  uint32_t npc;

  /** 가상 머신상에서 계산시 자유롭게 사용하는 변수.  대부분 값의 lower 파트를 다루는데,
      사용된다.  */
  int low;
  /** 가상 머신상에서 계산시 자유롭게 사용하는 변수.  대부분 값의 higher 파트를 다루는데,
      사용된다.  */
  int high;
  /** 가상 머신상에서 계산시 자유롭게 사용하는 변수.  Index 번호를 다룰 때 사용된다.  */
  int idx;  
};

enum opcode
  {
#define DEF_OPCODE(ENUM, NAME, NUMBER, OPLEN, OPFUNC)   ENUM ,
#include "opcode.def"
#undef DEF_OPCODE
    MAX_OPCODE
  };

/**
   각 Opcode 가 가지는 길이에 대한 배열.  Opcode 순서대로 나열되어 있으며,
   virtual machine 에서 현재 PC (Program Counter) 의 다음 PC 를
   계산할 때 사용된다.  
*/
const int opcode_len[MAX_OPCODE] = 
  {
#define DEF_OPCODE(ENUM, NAME, NUMBER, OPLEN, OPFUNC)   OPLEN ,
#include "opcode.def"
#undef DEF_OPCODE
  };

const char *opcode_name[MAX_OPCODE] = 
  {
#define DEF_OPCODE(ENUM, NAME, NUMBER, OPLEN, OPFUNC)   NAME ,
#include "opcode.def"
#undef DEF_OPCODE
  };

/**
   OPCODEFUNC_* 함수들이 공통적으로 가지게 되는 인자들을 정의한 매크로이다.
   이 값이 수정되면 전체 함수 모양이 수정되게 된다.
 */
#define OPCODEFUNC_ARGUMENT     struct rovmcore_info *ri, unsigned int pc
/**
   OPCODEFUNC_* 함수들의 리턴 타입을 정의하는 매크로이다.
 */
#define OPCODEFUNC_RET_T        static int

/**
   OPCODEFUNC_* 함수들의 prototype 정의
 */
#define DEF_OPCODE(ENUM, NAME, NUMBER, OPLEN, OPFUNC)   OPCODEFUNC_RET_T OPFUNC (OPCODEFUNC_ARGUMENT);
#include "opcode.def"
#undef DEF_OPCODE

/**
   OPCODEFUNC_* 함수 포인터 배열
 */
OPCODEFUNC_RET_T (*opcode_func[MAX_OPCODE]) (OPCODEFUNC_ARGUMENT) = 
{
#define DEF_OPCODE(ENUM, NAME, NUMBER, OPLEN, OPFUNC)   OPFUNC ,
#include "opcode.def"
#undef DEF_OPCODE  
};

/**
   각 stack type 에 대한 문자를 지정해 주는 배열입니다.
*/
const char rovm_stack_char[MAX_STACK_TYPE] = 
  {
#define DEF_TYPE(ENUM, NAME, TYPE, SIZE)   TYPE ,
#include "type.def"
#undef DEF_TYPE
  };

/** 각 stack type 에 대한 이름 정보를 가지고 있는 배열입니다.  */
const char *rovm_stack_name[MAX_STACK_TYPE] = 
  {
#define DEF_TYPE(ENUM, NAME, TYPE, SIZE)   NAME ,
#include "type.def"
#undef DEF_TYPE
  };

/** 각 stack type 에 대한 크기를 가르키는 배열입니다.  */
int rovm_stack_size[MAX_STACK_TYPE] = 
  {
#define DEF_TYPE(ENUM, NAME, TYPE, SIZE)   SIZE ,
#include "type.def"
#undef DEF_TYPE
  };

/** [0 (0x0)] OPCODE `NOP' 을 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_nop (OPCODEFUNC_ARGUMENT)
{
  /* nothing to do.  */
  return 0;
}

/** [13 (0xd)] OPCODE `BPUSH' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_bpush (OPCODEFUNC_ARGUMENT)
{
  CORE_LOW (ri) = (rovm_uint8_t) GETPC (0);

  ROVM_CODE_DEBUG (("bpush\t%d", CORE_LOW (ri)));

  PUSH (1);
  MOVE_BOOLEAN_CONST (STACK (0), CORE_LOW (ri));

  return 0;
}

/** [14 (0x0e)] OPCODE `FPUSH' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_fpush (OPCODEFUNC_ARGUMENT)
{
  float val = (float) ((float *) (&GETPC (0)))[0];

  ROVM_CODE_DEBUG (("fpush\t%f", val));

  PUSH (1);
  MOVE_FLOAT_CONST (STACK (0), val);

  return 0;
}

/** [15 (0x0f)] OPCODE `DPUSH' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_dpush (OPCODEFUNC_ARGUMENT)
{
  double val = (double) ((double *) (&GETPC (0)))[0];

  ROVM_CODE_DEBUG (("dpush\t%f", val));

  PUSH (1);
  MOVE_DOUBLE_CONST (STACK (0), val);

  return 0;
}

/** [16 (0x10)] OPCODE `CPUSH' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_cpush (OPCODEFUNC_ARGUMENT)
{
  CORE_LOW (ri) = (rovm_int8_t) GETPC (0);

  ROVM_CODE_DEBUG (("cpush\t%d", CORE_LOW (ri)));

  PUSH (1);
  MOVE_CHAR_CONST (STACK (0), CORE_LOW (ri));

  return 0;
}

/** [17 (0x11)] OPCODE `HPUSH' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_hpush (OPCODEFUNC_ARGUMENT)
{
  CORE_LOW (ri) = (short) ((short *) (&GETPC (0)))[0];

  ROVM_CODE_DEBUG (("hpush\t%d", CORE_LOW (ri)));

  PUSH (1);
  MOVE_SHORT_CONST (STACK (0), CORE_LOW (ri));

  return 0;
}

/** [18 (0x12)] OPCODE `IPUSH' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_ipush (OPCODEFUNC_ARGUMENT)
{
  int val = (int) ((int *) (&GETPC (0)))[0];

  ROVM_CODE_DEBUG (("ipush\t%d", val));

  PUSH (1);
  MOVE_INT_CONST (STACK (0), val);

  return 0;
}

/** [19 (0x13)] OPCODE `SPUSH' 를 처리한다.  가변길이 opcode 입니다.

    Locking 메커니즘의 적용이 필요한 opcode 입니다.  */
OPCODEFUNC_RET_T
opcodefunc_spush (OPCODEFUNC_ARGUMENT)
{
  size_t len;
  RvObject *obj;

  ROVM_CODE_DEBUG (("spush"));

  len = (unsigned int) ((unsigned int *) (&GETPC (0)))[0];

  if ((pc + 4 + len) >= (CORE_OPLEN (ri)))
    return -1;

  CORE_NPC (ri) = pc + (5 + len);

  obj = NewRvStringObject (CORE_ROVM (ri), CORE_TICKET (ri), (const char *) (&GETPC (4)), len);
  if (!obj)
    return -1;

  PUSH (1);
  MOVE_STRINGREF (STACK (0), obj);

  _CLEAR_ ();

  return 0;
}

/** [21 (0x15)] OPCODE `ILOAD' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_iload (OPCODEFUNC_ARGUMENT)
{
  CORE_IDX (ri) = (rovm_uint8_t) GETPC (0);

  ROVM_CODE_DEBUG (("iload\t%d", CORE_IDX (ri)));

  _CLB_ (CORE_IDX (ri));
  PUSH (1);
  MOVE_INT (STACK (0), LOCAL (CORE_IDX (ri)));

  return 0;
}

/** [87 (0x57)] OPCODE `POP' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_pop (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("pop"));

  POP (1);

  return 0;
}

/** [89 (0x59)] OPCODE `DUP' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_dup (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("dup"));

  /* Stack 이 <empty> 상태일 때, DUP 를 시도할 경우, 이를 처리합니다.  */
  _MIN_CB_ (0);

  PUSH (1);
  *STACK (0) = *RSTACK (1);

  return 0;
}

/** [95 (0x5f)] OPCODE `SWAP' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_swap (OPCODEFUNC_ARGUMENT)
{
  rovm_stack_t tmp;

  ROVM_CODE_DEBUG (("swap"));

  tmp = *STACK (0);
  *STACK (0) = *STACK (1);
  *STACK (1) = tmp;

  return 0;
}

/** [96 (0x60)] OPCODE `IADD' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_iadd (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("iadd"));

  ADD_INT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [98 (0x62)] OPCODE `FADD' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_fadd (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("fadd"));

  ADD_FLOAT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [99 (0x63)] OPCODE `DADD' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_dadd (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("dadd"));

  ADD_DOUBLE (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [100 (0x64)] OPCODE `ISUB' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_isub (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("isub"));

  MINUS_INT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [102 (0x66)] OPCODE `FSUB' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_fsub (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("fsub"));

  MINUS_FLOAT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [103 (0x67)] OPCODE `DSUB' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_dsub (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("dsub"));

  MINUS_DOUBLE (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [104 (0x68)] OPCODE `IMUL' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_imul (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("imul"));

  MULTIPLY_INT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [106 (0x6a)] OPCODE `FMUL' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_fmul (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("fmul"));

  MULTIPLY_FLOAT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [107 (0x6b)] OPCODE `DMUL' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_dmul (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("dmul"));

  MULTIPLY_DOUBLE (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [108 (0x6c)] OPCODE `IDIV' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_idiv (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("idiv"));

  DIVIDE_INT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [110 (0x6e)] OPCODE `FDIV' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_fdiv (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("fdiv"));

  DIVIDE_FLOAT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [111 (0x6f)] OPCODE `DDIV' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_ddiv (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("ddiv"));

  DIVIDE_DOUBLE (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [112 (0x70)] OPCODE `IREM' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_irem (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("irem"));

  REMAINDER_INT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [114 (0x72)] OPCODE `FREM' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_frem (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("frem"));

  REMAINDER_FLOAT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [115 (0x73)] OPCODE `DREM' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_drem (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("drem"));

  REMAINDER_DOUBLE (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/** [172 (0xac)] OPCODE `IRETURN' 를 처리한다.  반환값은 반드시 1 로 처리를
    해야 합니다.  그래야 return 값이 존재한다는 것으로 인식이 될 수 있습니다.  */
OPCODEFUNC_RET_T
opcodefunc_ireturn (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("ireturn"));

  if (!(STACK_TYPE (STACK (0)) == STACK_TYPE_BOOLEAN
        || STACK_TYPE (STACK (0)) == STACK_TYPE_CHAR
        || STACK_TYPE (STACK (0)) == STACK_TYPE_SHORT
        || STACK_TYPE (STACK (0)) == STACK_TYPE_INT))
    return -1;

  *TICKET_RET (CORE_TICKET (ri)) = *STACK (0);
  POP (1);

  return 1;
}

/** [174 (0xae)] OPCODE `FRETURN' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_freturn (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("freturn"));

  if (STACK_TYPE (STACK (0)) != STACK_TYPE_FLOAT)
    return -1;

  *TICKET_RET (CORE_TICKET (ri)) = *STACK (0);
  POP (1);

  return 1;
}

/** [175 (0xaf)] OPCODE `DRETURN' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_dreturn (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("dreturn"));

  if (STACK_TYPE (STACK (0)) != STACK_TYPE_DOUBLE)
    return -1;

  *TICKET_RET (CORE_TICKET (ri)) = *STACK (0);
  POP (1);

  return 1;
}

/** [176 (0xb0)] OPCODE `ARETURN' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_areturn (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("areturn"));

  if (!(STACK_TYPE (STACK (0)) == STACK_TYPE_ARRAYREF
        || STACK_TYPE (STACK (0)) == STACK_TYPE_OBJREF
        || STACK_TYPE (STACK (0)) == STACK_TYPE_STRINGREF))
    return -1;

  *TICKET_RET (CORE_TICKET (ri)) = *STACK (0);
  POP (1);

  return 1;
}

/** [177 (0xb1)] OPCODE `RETURN' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_return (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("return"));

  STACK_TYPE (TICKET_RET (CORE_TICKET (ri))) = STACK_TYPE_VOID;

  return 1;
}

#define _CHECK_METHOD_TYPE_(UNUSED)             \
  /* 스택 타입 검사.  */                        \
  for (i=0; i<argc; i++)                        \
    {                                           \
      if (type[argv[argc-i-1]] == 'S'                           \
          || type[argv[argc-i-1]] == 'T'                        \
          || type[argv[argc-i-1]] == '[')                               \
        {                                                               \
          if (!(rovm_stack_char[STACK_TYPE (STACK (i))] == 'S'          \
                || rovm_stack_char[STACK_TYPE (STACK (i))] == 'T'       \
                || rovm_stack_char[STACK_TYPE (STACK (i))] == '['))     \
            {                                                           \
              request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK, "Method type don't match."); \
              return -1;                                                \
            }                                                           \
        }                                                               \
      else if (rovm_stack_char[STACK_TYPE (STACK (i))] != type[argv[argc-i-1]]) \
        {                                                               \
          request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK, "Method type don't match."); \
          return -1;                                                    \
        }                                                               \
    }

/** [182 (0xb6)] OPCODE `CALL' 를 처리한다.  `CALL' opcode 의 경우, 가변길이를
    가지는 opcode 이다.  */
OPCODEFUNC_RET_T
opcodefunc_call (OPCODEFUNC_ARGUMENT)
{
  int i, argc, hlen, tlen, ret = 0;
  char *name, *type;
  u2 *argv;

  hlen = (int) GETPC (0);
  name = rc_pstrndup (CORE_POOL (ri), (char *) (&GETPC (1)), hlen);
  tlen = (unsigned short) ((unsigned short *) (&GETPC (1 + hlen)))[0];
  type = rc_pstrndup (CORE_POOL (ri), (char *) (&GETPC (1 + hlen + 2)), tlen);

  ROVM_CODE_DEBUG (("call %s %s", name, type));

  CORE_NPC (ri) = pc + (4 + hlen+ tlen);

  argc = (int) calctype_argc ((u1 *) type);
  argv = calctype_argv ((u1 *) type, (u2) argc);
  if (!argv)
    {
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK, 
                         "Method Type Error");      
      return -1;
    }

  _CHECK_METHOD_TYPE_ ();
  
  if (rc_callmethod (CORE_REQUEST (ri), CORE_TICKET (ri), name, type, argc, argv))
    ret = -1;

  POP (argc);

  if (!ret)
    {
      int retoffset;

      /* 정상적으로 rc_callmethod () 함수가 수행되었을 경우만 반환값 설정을 합니다.  */
      retoffset = calctype_ret ((u1 *) type);
      switch (type[retoffset])
        {
        case 'V':
          break;
        case '[':
        case 'B':
        case 'C':
        case 'H':
        case 'I':
        case 'F':
        case 'D':
        case 'T':
        case 'S':
          PUSH (1);
          RvSetReturnValue (STACK (0), TICKET_RET (CORE_TICKET (ri)));
          break;
        default:
          abort ();
        }
    }

  rc_free (argv);
  _CLEAR_ ();

  return ret;
}

/** [187 (0xbb)] OPCODE `NEW' 를 처리한다.  `NEW' opcode 의 경우, 가변길이를
    가지는 opcode 이다.

    Locking 메커니즘의 적용이 필요한 opcode 입니다.  */
OPCODEFUNC_RET_T
opcodefunc_new (OPCODEFUNC_ARGUMENT)
{
  int len, len2, iptype;
  char *hostname, *path;
  unsigned short port;
  RvObject *obj;
  RvClass *cls;
 
  iptype = (int) GETPC (0);
  len = (unsigned short) ((unsigned short *) (&GETPC (1)))[0];
  hostname = rc_pstrndup (CORE_POOL (ri), (char *) (&GETPC (3)), len);
  port = (unsigned short) ((unsigned short *) (&GETPC (3 + len)))[0];
  len2 = (unsigned short) ((unsigned short *) (&GETPC (5 + len)))[0];
  path = rc_pstrndup (CORE_POOL (ri), (char *) (&GETPC (7 + len)), len2);

  ROVM_CODE_DEBUG (("new e://%s:%d%s (%s)", 
                    hostname, 
                    port ? port : DEFAULT_ROVM_LISTENING_PORT,
                    path,
                    iptype ? "IPv6", "IPv4"));

  CORE_NPC (ri) = pc + (8 + len + len2);

  cls = rc_getclassbyhost (CORE_REQUEST (ri), iptype, hostname, port, path);
  if (cls == NULL_CLASS)
    {
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK, "Can't find class.");
      return -1;
    }
  else if (cls == REMOTE_CLASS)
    {
      obj = NewRemoteRvObject (CORE_REQUEST (ri), iptype, hostname, port, path);
      if (!obj)
        {
          request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,
                             "NewRemoteRvObject is under construction.");
          return -1;
        }
    }
  else
    obj = NewRvObject (CORE_ROVM (ri), CORE_TICKET (ri), cls);

  if (!obj)
    {
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK, "Can't allocate ObjectRef.");
      return -1;
    }

  PUSH (1);
  MOVE_OBJREF (STACK (0), obj);

  _CLEAR_ ();

  return 0;
}

/** [188 (0xbc)] OPCODE `NEWARRAY' 를 처리한다.  

    Locking 메커니즘의 적용이 필요한 opcode 입니다.  */
OPCODEFUNC_RET_T
opcodefunc_newarray (OPCODEFUNC_ARGUMENT)
{
  RvObject *obj;

  ROVM_CODE_DEBUG (("newarray"));

  obj = NewRvArray (CORE_ROVM (ri), CORE_TICKET (ri), GET_STACK_VALUE (INT, STACK (0)));
  if (!obj)
    return -1;

  MOVE_ARRAYREF (STACK (0), obj);

  _CLEAR_ ();

  return 0;
}

/**
   OPCODE 중 아직 만들어지지 않았거나, 나중에 사용할 목적으로 미리 예약해 놓은
   값이 호출되었을 때, 사용되는 callback 함수이다.  이 함수는 호출되어서는 
   않된다.
 */
OPCODEFUNC_RET_T
opcodefunc_reserved (OPCODEFUNC_ARGUMENT)
{
  /* 여기에 도달했다는 것은 opcode 값이 예약된 opcode 값을 가지고 있다는 것을
     뜻한다.  이것은 잘못된 수행 과정이다.  */

  request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,
                     "Invalid Opcode");

  return -1;
}

/**
   가상 머신 상에서 실제 opcode 들을 실행시키는 함수.  핵심 코드이다.  단지 좀
   짧게 보인다는 것 외엔.

   @param ri    항상 opcode callback 함수에 인자로 넘어가는 변수이다.
   @return      만약 실행 과정에서 아무런 문제가 없다면 0 을 반환하게 되며,
                하나의 오류가 났을 경우는 -1 을 반환하게 된다.
 */
rc_status_t
rovmcore (ri)
     struct rovmcore_info *ri;
{
  /* ??? 현재 구조로는 아래의 PC 변수를 register 로 등록한다고 하더라도 실질적인
         속도 개선에 별로 영향을 줄 것 같지 않은 느낌이 든다.  저의 의견이 맞나요?  */
  register unsigned int pc;
  register int rv;

  while (1)
    {
      pc = CORE_NPC (ri);
      
      CORE_NPC (ri) = pc + opcode_len[(CORE_OP_IDX (ri, pc))];

      rv = opcode_func[(CORE_OP_IDX (ri, pc))] (ri, pc);
      if (rv)
        {
          /* 만약 opcode_func () 함수에 의한 반환값이 0 이 아닐 경우에 대한 의미들을
             아래에서 다룹니다.  */
          switch (rv)
            {
            case -1:
              return -1;
            case 1:
              /* ireturn 과 같은 opcode 에 의해 반환될 경우, 반환값이 1 입니다.  */
              return 0;
            default:
              rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK,
                        "Weird return value. Check it.");
              break;
            }
          return -1;
        }

      if (CORE_NPC (ri) >= CORE_OPLEN (ri))
        break;
    }

  return 0;
}

/**
   ROVMCORE_INFO 구조체를 새롭게 할당하여 반환한다.

   @param r     Request 구조체
   @param tk    사용자가 사용하고자 하는 Ticket 구조체
   @return      새롭게 할당된 ROVMCORE_INFO 구조체를 반환한다.
 */
struct rovmcore_info *
init_rovmcore_info (r, tk, op, oplen)
     request_rec *r;
     struct ticket *tk;
     rc_opcode_t *op;
     rc_size_t oplen;
{
  struct rovmcore_info *ri;

  /* 기존 mp_alloc 에서 rc_malloc 으로 대체.  이유는 계속되는 함수 호출로 인한 메모리
     증가를 막기 위해서.  */
  ri = (struct rovmcore_info *) rc_malloc (sizeof (struct rovmcore_info));
  CORE_REQUEST (ri) = r;
  CORE_TICKET (ri) = tk;
  CORE_NPC (ri) = 0;

  CORE_OP (ri) = op;
  CORE_OPLEN (ri) = oplen;

  return ri;
}

/**
   사용을 완료한 ROVMCORE_INFO 구조체를 완료한다.

   @param ri    마무리하길 원하는 ROVMCORE_INFO 구조체.
 */
int
finish_rovmcore_info (ri)
     struct rovmcore_info *ri;
{
  rc_free (ri);

  return 0;
}

/**
   Request `OPCODE' 를 처리하기 위한 메인 VM 함수 중 하나이다.  원격에서 전송된
   길이가 OPLEN 이고 OP 인 opcode 를 처리하게 된다.

   @param r     Request 구조체
   @param tk    사용자가 사용하고자 하는 Ticket 구조체
   @param op    사용자가 보낸 opcode
   @param oplen 사용자가 보낸 opcode 의 총 길이
   @return      만약 성공적으로 VM 이 실행되었다면 0 을 반환하고 그렇지 않다면
                -1 을 반환하게 된다.
 */
int
rovmcore_main (r, tk, op, oplen)
     request_rec *r;
     struct ticket *tk;
     rc_opcode_t *op;
     rc_size_t oplen;
{
  int rv;
  struct rovmcore_info *ri;

  ri = init_rovmcore_info (r, tk, op, oplen);
  if (!ri)
    return -1;

  rv = rovmcore (ri);

  finish_rovmcore_info (ri);

  return rv;
}
