[루아1.1] opcode.c 읽기 (3)

6 분 소요

Opcode.c (3)

Opcode.c 파일은 크게 두 부분으로 나뉩니다. 전반부는 루아 VM 구현이고 후반부는 루아 외부 API 구현입니다. 호스트 프로그램에서 Lua.h를 통해 호출하는 루아 API들을 구현한 것이죠. 그래서 아직 다 읽지 않은 함수가 많습니다. 이번 글에서 모조리 다 읽도록하죠. 다행인건 개별 함수가 그리 길지 않습니다.

void lua_travstack (void (*fn)(Object *))
{
 Object *o;
 for (o = top-1; o >= stack; o--)
  fn (o);
}

가비지 컬랙션을 할 때 lua_pack() 함수에서 부르는 함수입니다. 스택에 있는 Object 인스턴스들에 대해 마킹하는데 동원됩니다. 확장하면 여러 용도로 쓸 수 있겠으나 루아 1.1에서는 일단 마킹하는데만 씁니다.

/*
** Open file, generate opcode and execute global statement. Return 0 on
** success or 1 on error.
*/
int lua_dofile (char *filename)
{
 if (lua_openfile (filename)) return 1;
 if (lua_parse ()) { lua_closefile (); return 1; }
 lua_closefile ();
 return 0;
}

/*
** Generate opcode stored on string and execute global statement. Return 0 on
** success or 1 on error.
*/
int lua_dostring (char *string)
{
 if (lua_openstring (string)) return 1;
 if (lua_parse ()) return 1;
 lua_closestring();
 return 0;
}

아마 호스트 프로그램에서 루아 코드를 돌리고 싶을때 호출하는 게이트웨이 함수가 아닐까 싶습니다. 루아 파일을 읽어서 루아를 돌릴 땐 lua_dofile() 함수를 호출하고 호스트 프로그램에서 문자열로 루아 코드를 넘겨서 실행합니다.

/*
** Execute the given function. Return 0 on success or 1 on error.
*/
int lua_call (char *functionname, int nparam)
{
 static Byte startcode[] = {CALLFUNC, HALT};
 int i; 
 Object func = s_object(lua_findsymbol(functionname));
 if (tag(&func) != T_FUNCTION) return 1;
 for (i=1; i<=nparam; i++)
  *(top-i+2) = *(top-i);
 top += 2;
 tag(top-nparam-1) = T_MARK;
 *(top-nparam-2) = func;
 return (lua_execute (startcode));
}

루아 함수를 별도로 실행할 때 호출하는 함수입니다. 파라메터로 넘어오는 함수 이름으로 심볼 테이블에서 함수 객체를 가져옵니다. 그리고 파라메터 개수만큼 스택을 조정하고 스택에 함수 객체를 넣습니다. 그리고 VM에서는 CALLFUNC 명령어를 실행합니다.

/*
** Get a parameter, returning the object handle or NULL on error.
** 'number' must be 1 to get the first parameter.
*/
Object *lua_getparam (int number)
{
 if (number <= 0 || number > top-base) return NULL;
 return (base+number-1);
}

/*
** Given an object handle, return its number value. On error, return 0.0.
*/
real lua_getnumber (Object *object)
{
 if (object == NULL || tag(object) == T_NIL) return 0.0;
 if (tonumber (object)) return 0.0;
 else                   return (nvalue(object));
}

/*
** Given an object handle, return its string pointer. On error, return NULL.
*/
char *lua_getstring (Object *object)
{
 if (object == NULL || tag(object) == T_NIL) return NULL;
 if (tostring (object)) return NULL;
 else                   return (svalue(object));
}

/*
** Given an object handle, return a copy of its string. On error, return NULL.
*/
char *lua_copystring (Object *object)
{
 if (object == NULL || tag(object) == T_NIL) return NULL;
 if (tostring (object)) return NULL;
 else                   return (strdup(svalue(object)));
}

/*
** Given an object handle, return its cfuntion pointer. On error, return NULL.
*/
lua_CFunction lua_getcfunction (Object *object)
{
 if (object == NULL) return NULL;
 if (tag(object) != T_CFUNCTION) return NULL;
 else                            return (fvalue(object));
}

/*
** Given an object handle, return its user data. On error, return NULL.
*/
void *lua_getuserdata (Object *object)
{
 if (object == NULL) return NULL;
 if (tag(object) != T_USERDATA) return NULL;
 else                           return (uvalue(object));
}

코드 패턴이 모두 같아서 한 번에 읽어보겠습니다. 함수 이름도 getxxx()로 아마 호스트 프로그램에서 루아 오브젝트 객체에서 필요한 자료형 값을 받는 함수입니다.

/*
** Given an object handle and an index, return its indexed object.
** On error, return NULL.
*/
Object *lua_getindexed (Object *object, float index)
{
 if (object == NULL) return NULL;
 if (tag(object) != T_ARRAY)
  return NULL;
 else
 {
  Object ref;
  tag(&ref) = T_NUMBER;
  nvalue(&ref) = index;
  return (lua_hashdefine(avalue(object), &ref));
 }
}

/*
** Get a global object. Return the object handle or NULL on error.
*/
Object *lua_getglobal (char *name)
{
 int n = lua_findsymbol(name);
 if (n < 0) return NULL;
 return &s_object(n);
}

lua_getindexed() 함수와 lua_getglobal() 함수도 getxxx 계열 함수입니다만 코드 패턴이 조금 다릅니다. lua_getindexed() 함수는 루아 오브젝트 인스턴스가 배열일 경우에 index 위치의 아이템 오브젝트 인스턴스를 찾아서 포인터를 리턴합니다. lua_getglobal() 함수는 심볼 이름으로 심볼 테이블에서 오브젝트 인스턴스 포인터를 받아서 리턴합니다.

/*
** Pop and return an object
*/
Object *lua_pop (void)
{
 if (top <= base) return NULL;
 top--;
 return top;
}

/*
** Push a nil object
*/
int lua_pushnil (void)
{
 if ((top-stack) >= MAXSTACK-1)
 {
  lua_error ("stack overflow");
  return 1;
 }
 tag(top) = T_NIL;
 return 0;
}

/*
** Push an object (tag=number) to stack. Return 0 on success or 1 on error.
*/
int lua_pushnumber (real n)
{
 if ((top-stack) >= MAXSTACK-1)
 {
  lua_error ("stack overflow");
  return 1;
 }
 tag(top) = T_NUMBER; nvalue(top++) = n;
 return 0;
}

/*
** Push an object (tag=string) to stack. Return 0 on success or 1 on error.
*/
int lua_pushstring (char *s)
{
 if ((top-stack) >= MAXSTACK-1)
 {
  lua_error ("stack overflow");
  return 1;
 }
 tag(top) = T_STRING; 
 svalue(top++) = lua_createstring(lua_strdup(s));
 return 0;
}

/*
** Push an object (tag=cfunction) to stack. Return 0 on success or 1 on error.
*/
int lua_pushcfunction (lua_CFunction fn)
{
 if ((top-stack) >= MAXSTACK-1)
 {
  lua_error ("stack overflow");
  return 1;
 }
 tag(top) = T_CFUNCTION; fvalue(top++) = fn;
 return 0;
}

/*
** Push an object (tag=userdata) to stack. Return 0 on success or 1 on error.
*/
int lua_pushuserdata (void *u)
{
 if ((top-stack) >= MAXSTACK-1)
 {
  lua_error ("stack overflow");
  return 1;
 }
 tag(top) = T_USERDATA; uvalue(top++) = u;
 return 0;
}

/*
** Push an object to stack.
*/
int lua_pushobject (Object *o)
{
 if ((top-stack) >= MAXSTACK-1)
 {
  lua_error ("stack overflow");
  return 1;
 }
 *top++ = *o;
 return 0;
}

lua_pop() 함수는 스택 팝하는 함수입니다. 나머지 push 계열 함수들은 스택에 필요한 값을 넣는 함수입니다. 호스트 프로그램에서 루아 VM 스택에 값을 직접 넣을 수 있도록 제공하는 API인가봅니다.

/*
** Store top of the stack at a global variable array field. 
** Return 1 on error, 0 on success.
*/
int lua_storeglobal (char *name)
{
 int n = lua_findsymbol (name);
 if (n < 0) return 1;
 if (tag(top-1) == T_MARK) return 1;
 s_object(n) = *(--top);
 return 0;
}

/*
** Store top of the stack at an array field. Return 1 on error, 0 on success.
*/
int lua_storefield (lua_Object object, char *field)
{
 if (tag(object) != T_ARRAY)
  return 1;
 else
 {
  Object ref, *h;
  tag(&ref) = T_STRING;
  svalue(&ref) = lua_createstring(lua_strdup(field));
  h = lua_hashdefine(avalue(object), &ref);
  if (h == NULL) return 1;
  if (tag(top-1) == T_MARK) return 1;
  *h = *(--top);
 }
 return 0;
}


/*
** Store top of the stack at an array index. Return 1 on error, 0 on success.
*/
int lua_storeindexed (lua_Object object, float index)
{
 if (tag(object) != T_ARRAY)
  return 1;
 else
 {
  Object ref, *h;
  tag(&ref) = T_NUMBER;
  nvalue(&ref) = index;
  h = lua_hashdefine(avalue(object), &ref);
  if (h == NULL) return 1;
  if (tag(top-1) == T_MARK) return 1;
  *h = *(--top);
 }
 return 0;
}

store 계열 함수는 스택에서 값을 읽는 함수입니다. 스택 자체도 조정하네요.

/*
** Given an object handle, return if it is nil.
*/
int lua_isnil (Object *object)
{
 return (object != NULL && tag(object) == T_NIL);
}

/*
** Given an object handle, return if it is a number one.
*/
int lua_isnumber (Object *object)
{
 return (object != NULL && tag(object) == T_NUMBER);
}

/*
** Given an object handle, return if it is a string one.
*/
int lua_isstring (Object *object)
{
 return (object != NULL && tag(object) == T_STRING);
}

/*
** Given an object handle, return if it is an array one.
*/
int lua_istable (Object *object)
{
 return (object != NULL && tag(object) == T_ARRAY);
}

/*
** Given an object handle, return if it is a cfunction one.
*/
int lua_iscfunction (Object *object)
{
 return (object != NULL && tag(object) == T_CFUNCTION);
}

/*
** Given an object handle, return if it is an user data one.
*/
int lua_isuserdata (Object *object)
{
 return (object != NULL && tag(object) == T_USERDATA);
}

is계열 함수는 파라메터로 전달한 오브젝트 인스턴스의 루아 자료형을 판단합니다. 역시 호스트 프로그램에서 루아 객체 포인터가 어떤 타입인지 알고 싶을 때 쓰는 API 함수입니다.

다 읽었다.

이렇게해서 루아 1.1 소스 코드를 다 읽었습니다. 코드를 읽으면서 동시에 글을 썼습니다. 그래서 이 글 내용이 제가 코드를 읽으면서 하는 생각 그 자체라고 보시면 됩니다. 네, 저는 다른 사람이 작성한 코드를 읽으면서 이런 생각을 합니다. :) 그러다 보니 제가 키보드를 두드리면서도 제일 많이 하는 말이 모르겠다, 넘어가겠다, 다음에 보겠다.. 등등 아무튼 모르겠다는 말이었네요. 앞으로 계속 읽을 것이니 점점 모르겠다는 말이 줄어들다가 어느 시점에서 그 말이 없어지길 바랍니다.

댓글남기기