Procházet zdrojové kódy

* Changes to the script engine:
- new stack datatype script_retinfo for C_RETINFO to hold all the return state info that was being stored in the stack. (the script engine in 64bit-ready now)
- pop_stack is responsible for adjusting all the stack pointers.
- push_* returns the created script_data.
- 'return' only converts to value scope variables of the current scope.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@13427 54d463be-8e91-2dee-dedb-b68131a5f0ec

FlavioJS před 16 roky
rodič
revize
89d683d668
3 změnil soubory, kde provedl 219 přidání a 177 odebrání
  1. 7 0
      Changelog-Trunk.txt
  2. 202 176
      src/map/script.c
  3. 10 1
      src/map/script.h

+ 7 - 0
Changelog-Trunk.txt

@@ -3,6 +3,13 @@ Date	Added
 AS OF SVN REV. 5091, WE ARE NOW USING TRUNK.  ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK.
 IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 
+2008/12/31
+	* Changes to the script engine: [FlavioJS]
+	- new stack datatype script_retinfo for C_RETINFO to hold all the return state info 
+	  that was being stored in the stack. (the script engine in 64bit-ready now)
+	- pop_stack is responsible for adjusting all the stack pointers.
+	- push_* returns the created script_data.
+	- 'return' only converts to value scope variables of the current scope.
 2008/12/30
 	* Changes to the script engine: [FlavioJS]
 	- script ends when run_func can't find the buildin command. (script engine error)

+ 202 - 176
src/map/script.c

@@ -5,6 +5,7 @@
 //#define DEBUG_DISASM
 //#define DEBUG_RUN
 //#define DEBUG_HASH
+//#define DEBUG_DUMP_STACK
 
 #include "../common/cbasetypes.h"
 #include "../common/malloc.h"
@@ -108,7 +109,7 @@
 /// Pushes a copy of the data in the target index relative to the top of the stack
 #define script_pushcopytop(st,i) push_copy((st)->stack, (st)->stack->sp + (i))
 /// Removes the range of values [start,end[ relative to the top of the stack
-#define script_removetop(st,start,end) ( pop_stack((st)->stack, ((st)->stack->sp + (start)), (st)->stack->sp + (end)) )
+#define script_removetop(st,start,end) ( pop_stack((st), ((st)->stack->sp + (start)), (st)->stack->sp + (end)) )
 
 //
 // struct script_data* data;
@@ -381,6 +382,48 @@ const char* script_op2name(int op)
 #undef RETURN_OP_NAME
 }
 
+#ifdef DEBUG_DUMP_STACK
+static void script_dump_stack(struct script_state* st)
+{
+	int i;
+	ShowMessage("\tstart = %d\n", st->start);
+	ShowMessage("\tend   = %d\n", st->end);
+	ShowMessage("\tdefsp = %d\n", st->stack->defsp);
+	ShowMessage("\tsp    = %d\n", st->stack->sp);
+	for( i = 0; i < st->stack->sp; ++i )
+	{
+		struct script_data* data = &st->stack->stack_data[i];
+		ShowMessage("\t[%d] %s", i, script_op2name(data->type));
+		switch( data->type )
+		{
+		case C_INT:
+		case C_POS:
+			ShowMessage(" %d\n", data->u.num);
+			break;
+
+		case C_STR:
+		case C_CONSTSTR:
+			ShowMessage(" \"%s\"\n", data->u.str);
+			break;
+
+		case C_NAME:
+			ShowMessage(" \"%s\" (id=%d ref=%p subtype=%s)\n", reference_getname(data), data->u.num, data->ref, script_op2name(str_data[data->u.num].type));
+			break;
+
+		case C_RETINFO:
+			{
+				struct script_retinfo* ri = data->u.ri;
+				ShowMessage(" %p {var_function=%p, script=%p, pos=%d, nargs=%d, defsp=%d}\n", ri, ri->var_function, ri->script, ri->pos, ri->nargs, ri->defsp);
+			}
+			break;
+		default:
+			ShowMessage("\n");
+			break;
+		}
+	}
+}
+#endif
+
 /// Reports on the console the src of a script error.
 static void script_reportsrc(struct script_state *st)
 {
@@ -2222,7 +2265,7 @@ void get_val(struct script_state* st, struct script_data* data)
 	return;
 }
 
-void push_val2(struct script_stack* stack, enum c_op type, int val, struct linkdb_node** ref);
+struct script_data* push_val2(struct script_stack* stack, enum c_op type, int val, struct linkdb_node** ref);
 
 /// Retrieves the value of a reference identified by uid (variable, constant, param)
 /// The value is left in the top of the stack and needs to be removed manually.
@@ -2428,7 +2471,7 @@ void stack_expand(struct script_stack* stack)
 #define push_val(stack,type,val) push_val2(stack, type, val, NULL)
 
 /// Pushes a value into the stack (with reference)
-void push_val2(struct script_stack* stack, enum c_op type, int val, struct linkdb_node** ref)
+struct script_data* push_val2(struct script_stack* stack, enum c_op type, int val, struct linkdb_node** ref)
 {
 	if( stack->sp >= stack->sp_max )
 		stack_expand(stack);
@@ -2436,10 +2479,11 @@ void push_val2(struct script_stack* stack, enum c_op type, int val, struct linkd
 	stack->stack_data[stack->sp].u.num = val;
 	stack->stack_data[stack->sp].ref   = ref;
 	stack->sp++;
+	return &stack->stack_data[stack->sp-1];
 }
 
 /// Pushes a string into the stack
-void push_str(struct script_stack* stack, enum c_op type, char* str)
+struct script_data* push_str(struct script_stack* stack, enum c_op type, char* str)
 {
 	if( stack->sp >= stack->sp_max )
 		stack_expand(stack);
@@ -2447,21 +2491,38 @@ void push_str(struct script_stack* stack, enum c_op type, char* str)
 	stack->stack_data[stack->sp].u.str = str;
 	stack->stack_data[stack->sp].ref   = NULL;
 	stack->sp++;
+	return &stack->stack_data[stack->sp-1];
+}
+
+/// Pushes a retinfo into the stack
+struct script_data* push_retinfo(struct script_stack* stack, struct script_retinfo* ri)
+{
+	if( stack->sp >= stack->sp_max )
+		stack_expand(stack);
+	stack->stack_data[stack->sp].type = C_RETINFO;
+	stack->stack_data[stack->sp].u.ri = ri;
+	stack->stack_data[stack->sp].ref  = NULL;
+	stack->sp++;
+	return &stack->stack_data[stack->sp-1];
 }
 
 /// Pushes a copy of the target position into the stack
-void push_copy(struct script_stack* stack, int pos)
+struct script_data* push_copy(struct script_stack* stack, int pos)
 {
 	switch( stack->stack_data[pos].type )
 	{
 	case C_CONSTSTR:
-		push_str(stack, C_CONSTSTR, stack->stack_data[pos].u.str);
+		return push_str(stack, C_CONSTSTR, stack->stack_data[pos].u.str);
 		break;
 	case C_STR:
-		push_str(stack, C_STR, aStrdup(stack->stack_data[pos].u.str));
+		return push_str(stack, C_STR, aStrdup(stack->stack_data[pos].u.str));
+		break;
+	case C_RETINFO:
+		ShowFatalError("script:push_copy: can't create copies of C_RETINFO. Exiting...\n");
+		exit(1);
 		break;
 	default:
-		push_val2(
+		return push_val2(
 			stack,stack->stack_data[pos].type,
 			stack->stack_data[pos].u.num,
 			stack->stack_data[pos].ref
@@ -2470,16 +2531,18 @@ void push_copy(struct script_stack* stack, int pos)
 	}
 }
 
-/// Removes the values in indexes [start,end[ from the stack
-void pop_stack(struct script_stack* stack, int start, int end)
+/// Removes the values in indexes [start,end[ from the stack.
+/// Adjusts all stack pointers.
+void pop_stack(struct script_state* st, int start, int end)
 {
+	struct script_stack* stack = st->stack;
 	struct script_data* data;
 	int i;
 
 	if( start < 0 )
 		start = 0;
-	if( end > stack->sp_max )
-		end = stack->sp_max;
+	if( end > stack->sp )
+		end = stack->sp;
 	if( start >= end )
 		return;// nothing to pop
 
@@ -2489,11 +2552,32 @@ void pop_stack(struct script_stack* stack, int start, int end)
 		data = &stack->stack_data[i];
 		if( data->type == C_STR )
 			aFree(data->u.str);
+		if( data->type == C_RETINFO )
+		{
+			struct script_retinfo* ri = data->u.ri;
+			if( ri->var_function )
+			{
+				script_free_vars(ri->var_function);
+				aFree(ri->var_function);
+			}
+			aFree(ri);
+		}
 		data->type = C_NOP;
 	}
 	// move the rest of the elements
 	if( stack->sp > end )
+	{
 		memmove(&stack->stack_data[start], &stack->stack_data[end], sizeof(stack->stack_data[0])*(stack->sp - end));
+		for( i = start + stack->sp - end; i < stack->sp; ++i )
+			stack->stack_data[i].type = C_NOP;
+	}
+	// adjust stack pointers
+	     if( st->start > end )   st->start -= end - start;
+	else if( st->start > start ) st->start = start;
+	     if( st->end > end )   st->end -= end - start;
+	else if( st->end > start ) st->end = start;
+	     if( stack->defsp > end )   stack->defsp -= end - start;
+	else if( stack->defsp > start ) stack->defsp = start;
 	stack->sp -= end - start;
 }
 
@@ -2517,29 +2601,6 @@ void script_free_vars(struct linkdb_node **node)
 	linkdb_final( node );
 }
 
-/*==========================================
- * Free's the whole stack. Invoked when clearing a character. [Skotlex]
- *------------------------------------------*/
-/// @deprecated will be removed later [FlavioJS]
-static void script_free_stack(struct script_stack *stack)
-{
-	int i;
-	for(i = 0; i < stack->sp; i++) {
-		if( stack->stack_data[i].type == C_STR ) {
-			aFree(stack->stack_data[i].u.str);
-			stack->stack_data[i].type = C_INT;
-		} else if( i > 0 && stack->stack_data[i].type == C_RETINFO ) {
-			struct linkdb_node** n = (struct linkdb_node**)stack->stack_data[i-1].u.num;
-			script_free_vars( n );
-			aFree( n );
-		}
-	}
-	script_free_vars( stack->var_function );
-	aFree(stack->var_function);
-	aFree(stack->stack_data);
-	aFree(stack);
-}
-
 void script_free_code(struct script_code* code)
 {
 	script_free_vars( &code->script_vars );
@@ -2581,7 +2642,11 @@ void script_free_state(struct script_state* st)
 {
 	if( st->sleep.timer != INVALID_TIMER )
 		delete_timer(st->sleep.timer, run_script_timer);
-	script_free_stack(st->stack);
+	script_free_vars(st->stack->var_function);
+	aFree(st->stack->var_function);
+	pop_stack(st, 0, st->stack->sp);
+	aFree(st->stack->stack_data);
+	aFree(st->stack);
 	st->pos = -1;
 	aFree(st);
 }
@@ -2858,108 +2923,83 @@ void op_1(struct script_state* st, int op)
 }
 
 
-/*==========================================
- * 関数の実行
- *------------------------------------------*/
+
+/// Executes a buildin command.
+/// Stack: C_NAME(<command>) C_ARG <arg0> <arg1> ... <argN>
 int run_func(struct script_state *st)
 {
+	struct script_data* data;
 	int i,start_sp,end_sp,func;
 
-	end_sp=st->stack->sp;
-	for(i=end_sp-1;i>=0 && st->stack->stack_data[i].type!=C_ARG;i--);
-	if(i<=0){ //Crash fix when missing "push_val" causes current pointer to become -1. from Rayce (jA)
-		ShowError("function not found\n");
-//		st->stack->sp=0;
-		st->state=END;
+	end_sp = st->stack->sp;// position after the last argument
+	for( i = end_sp-1; i > 0 ; --i )
+		if( st->stack->stack_data[i].type == C_ARG )
+			break;
+	if( i == 0 )
+	{
+		ShowError("script:run_func: C_ARG not found. please report this!!!\n");
+		st->state = END;
 		script_reportsrc(st);
 		return 1;
 	}
-	start_sp=i-1;
-	st->start=i-1;
-	st->end=end_sp;
-	func=st->stack->stack_data[st->start].u.num;
-
-#ifdef DEBUG_RUN
-	if(battle_config.etc_log) {
-		ShowDebug("run_func : %s? (%d(%d)) sp=%d (%d...%d)\n", get_str(func), func, str_data[func].type, st->stack->sp, st->start, st->end);
-		ShowDebug("stack dump :");
-		for(i=0;i<end_sp;i++){
-			switch(st->stack->stack_data[i].type){
-			case C_INT:
-				ShowMessage(" int(%d)", st->stack->stack_data[i].u.num);
-				break;
-			case C_NAME:
-				ShowMessage(" name(%s)", get_str(st->stack->stack_data[i].u.num & 0xffffff);
-				break;
-			case C_ARG:
-				ShowMessage(" arg");
-				break;
-			case C_POS:
-				ShowMessage(" pos(%d)",st->stack->stack_data[i].u.num);
-				break;
-			case C_STR:
-				ShowMessage(" str(%s)",st->stack->stack_data[i].u.str);
-				break;
-			case C_CONSTSTR:
-				ShowMessage(" cstr(%s)",st->stack->stack_data[i].u.str);
-				break;
-			default:
-				ShowMessage(" etc(%d,%d)",st->stack->stack_data[i].type,st->stack->stack_data[i].u.num);
-			}
-		}
-		ShowMessage("\n");
-	}
-#endif
+	start_sp = i-1;// C_NAME of the command
+	st->start = start_sp;
+	st->end = end_sp;
 
-	if(str_data[func].type!=C_FUNC ){
-		ShowError("run_func: '"CL_WHITE"%s"CL_RESET"' (type %d) is not function and command!\n", get_str(func), str_data[func].type);
-//		st->stack->sp=0;
-		st->state=END;
+	data = &st->stack->stack_data[st->start];
+	if( data->type == C_NAME && str_data[data->u.num].type == C_FUNC )
+		func = data->u.num;
+	else
+	{
+		ShowError("script:run_func: not a buildin command.\n");
+		script_reportdata(data);
 		script_reportsrc(st);
+		st->state = END;
 		return 1;
 	}
-#ifdef DEBUG_RUN
-	ShowDebug("run_func : %s (func_no : %d , func_type : %d pos : 0x%x)\n", get_str(func),func,str_data[func].type,st->pos);
-#endif
+
 	if(str_data[func].func){
 		if (str_data[func].func(st)) //Report error
 			script_reportsrc(st);
 	} else {
-		ShowError("script:run_func: missing buildin command '%s' (id=%d type=%s)\n", get_str(func), func, script_op2name(str_data[func].type));
-		script_pushnil(st);
+		ShowError("script:run_func: '%s' (id=%d type=%s) has no C function. please report this!!!\n", get_str(func), func, script_op2name(str_data[func].type));
 		script_reportsrc(st);
 		st->state = END;
 	}
 
-	// Stack's datum are used when re-run functions [Eoe]
-	if(st->state != RERUNLINE) {
-		pop_stack(st->stack,start_sp,end_sp);
-	}
+	// Stack's datum are used when re-running functions [Eoe]
+	if( st->state == RERUNLINE )
+		return 0;
 
-	if(st->state==RETFUNC){
-		// ユーザー定義関数からの復帰
-		int olddefsp=st->stack->defsp;
-		int i;
+	pop_stack(st, st->start, st->end);
+	if( st->state == RETFUNC )
+	{// return from a user-defined function
+		struct script_retinfo* ri;
+		int olddefsp = st->stack->defsp;
+		int nargs;
 
-		pop_stack(st->stack,st->stack->defsp,start_sp);	// 復帰に邪魔なスタック削除
-		if(st->stack->defsp<5 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO){
-			ShowWarning("script:run_func(return) return without callfunc or callsub!\n");
-			st->state=END;
+		pop_stack(st, st->stack->defsp, st->start);// pop distractions from the stack
+		if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp-1].type != C_RETINFO )
+		{
+			ShowWarning("script:run_func: return without callfunc or callsub!\n");
 			script_reportsrc(st);
+			st->state = END;
 			return 1;
 		}
 		script_free_vars( st->stack->var_function );
 		aFree(st->stack->var_function);
 
-		i = conv_num(st,& (st->stack->stack_data[st->stack->defsp-5]));					// 引数の数所得
-		st->pos=conv_num(st,& (st->stack->stack_data[st->stack->defsp-1]));				// スクリプト位置の復元
-		st->script=(struct script_code*)conv_num(st,& (st->stack->stack_data[st->stack->defsp-3]));	// スクリプトを復元
-		st->stack->var_function = (struct linkdb_node**)st->stack->stack_data[st->stack->defsp-2].u.num; // 関数依存変数
+		ri = st->stack->stack_data[st->stack->defsp-1].u.ri;
+		nargs = ri->nargs;
+		st->pos = ri->pos;
+		st->script = ri->script;
+		st->stack->var_function = ri->var_function;
+		st->stack->defsp = ri->defsp;
+		memset(ri, 0, sizeof(struct script_retinfo));
 
-		st->stack->defsp=conv_num(st,& (st->stack->stack_data[st->stack->defsp-4]));	// 基準スタックポインタを復元
-		pop_stack(st->stack,olddefsp-5-i,olddefsp);		// 要らなくなったスタック(引数と復帰用データ)削除
+		pop_stack(st, olddefsp-nargs-1, olddefsp);// pop arguments and retinfo
 
-		st->state=GOTO;
+		st->state = GOTO;
 	}
 
 	return 0;
@@ -3080,16 +3120,10 @@ void run_script_main(struct script_state *st)
 		enum c_op c = get_com(st->script->script_buf,&st->pos);
 		switch(c){
 		case C_EOL:
-			if( stack->sp != stack->defsp )
-			{
-				if( stack->sp > stack->defsp )
-				{	//sp > defsp is valid in cases when you invoke functions and don't use the returned value. [Skotlex]
-					//Since sp is supposed to be defsp in these cases, we could assume the extra stack elements are unneeded.
-					pop_stack(stack, stack->defsp, stack->sp); //Clear out the unused stack-section.
-				} else
-					ShowError("script:run_script_main: unexpected stack position stack.sp(%d) != default(%d)\n", stack->sp, stack->defsp);
-				stack->sp = stack->defsp;
-			}
+			if( stack->defsp > stack->sp )
+				ShowError("script:run_script_main: unexpected stack position (defsp=%d sp=%d). please report this!!!\n", stack->defsp, stack->sp);
+			else
+				pop_stack(st, stack->defsp, stack->sp);// pop unused stack data. (unused return value)
 			break;
 		case C_INT:
 			push_val(stack,C_INT,get_num(st->script->script_buf,&st->pos));
@@ -3770,8 +3804,8 @@ BUILDIN_FUNC(goto)
 BUILDIN_FUNC(callfunc)
 {
 	int i, j;
-	struct linkdb_node** oldval;
-	struct script_code *scr, *oldscr;
+	struct script_retinfo* ri;
+	struct script_code* scr;
 	const char* str = script_getstr(st,2);
 
 	scr = (struct script_code*)strdb_get(userfunc_db, str);
@@ -3783,36 +3817,32 @@ BUILDIN_FUNC(callfunc)
 	}
 
 	for( i = st->start+3, j = 0; i < st->end; i++, j++ )
-		push_copy(st->stack,i);
-
-	script_pushint(st,j);                            // push argument count
-	script_pushint(st,st->stack->defsp);             // push current stack pointer
-	script_pushint(st,(int)st->script);              // push current script
-	script_pushint(st,(int)st->stack->var_function); // push function-dependent variables
-	push_val(st->stack,C_RETINFO,st->pos);           // push current script location
-
-	oldscr = st->script;
-	oldval = st->stack->var_function;
-
-	st->pos = 0;
-	st->script = scr;
-	st->stack->defsp = st->start+5+j;
-	st->state = GOTO;
-	st->stack->var_function = (struct linkdb_node**)aCalloc(1, sizeof(struct linkdb_node*));
-
-	for( i = 0; i < j; i++ )
 	{
-		struct script_data* s = script_getdatatop(st, -6-i);
-		if( data_isreference(s) && !s->ref )
+		struct script_data* data = push_copy(st->stack,i);
+		if( data_isreference(data) && !data->ref )
 		{
-			const char* name = reference_getname(s);
+			const char* name = reference_getname(data);
 			if( name[0] == '.' && name[1] == '@' )
-				s->ref = oldval;
+				data->ref = st->stack->var_function;
 			else if( name[0] == '.' )
-				s->ref = &oldscr->script_vars;
+				data->ref = &st->script->script_vars;
 		}
 	}
 
+	CREATE(ri, struct script_retinfo, 1);
+	ri->script       = st->script;// script code
+	ri->var_function = st->stack->var_function;// scope variables
+	ri->pos          = st->pos;// script location
+	ri->nargs        = j;// argument count
+	ri->defsp        = st->stack->defsp;// default stack pointer
+	push_retinfo(st->stack, ri);
+
+	st->pos = 0;
+	st->script = scr;
+	st->stack->defsp = st->stack->sp;
+	st->state = GOTO;
+	st->stack->var_function = (struct linkdb_node**)aCalloc(1, sizeof(struct linkdb_node*));
+
 	return 0;
 }
 /*==========================================
@@ -3821,7 +3851,7 @@ BUILDIN_FUNC(callfunc)
 BUILDIN_FUNC(callsub)
 {
 	int i,j;
-	struct linkdb_node** oldval;
+	struct script_retinfo* ri;
 	int pos = script_getnum(st,2);
 
 	if( !data_islabel(script_getdata(st,2)) && !data_isfunclabel(script_getdata(st,2)) )
@@ -3832,33 +3862,30 @@ BUILDIN_FUNC(callsub)
 		return 1;
 	}
 
-	oldval = st->stack->var_function;
-
 	for( i = st->start+3, j = 0; i < st->end; i++, j++ )
-		push_copy(st->stack,i);
-
-	script_pushint(st,j);                            // push argument count
-	script_pushint(st,st->stack->defsp);             // push current stack pointer
-	script_pushint(st,(int)st->script);              // push current script
-	script_pushint(st,(int)st->stack->var_function); // push function-dependent variables
-	push_val(st->stack,C_RETINFO,st->pos);           // push current script location
-
-	st->pos = pos;
-	st->stack->defsp = st->start+5+j;
-	st->state = GOTO;
-	st->stack->var_function = (struct linkdb_node**)aCalloc(1, sizeof(struct linkdb_node*));
-
-	for(i = 0; i < j; i++)
 	{
-		struct script_data* s = script_getdatatop(st, -6-i);
-		if( data_isreference(s) && !s->ref )
+		struct script_data* data = push_copy(st->stack,i);
+		if( data_isreference(data) && !data->ref )
 		{
-			const char* name = reference_getname(s);
+			const char* name = reference_getname(data);
 			if( name[0] == '.' && name[1] == '@' )
-				s->ref = oldval;
+				data->ref = st->stack->var_function;
 		}
 	}
 
+	CREATE(ri, struct script_retinfo, 1);
+	ri->script       = st->script;// script code
+	ri->var_function = st->stack->var_function;// scope variables
+	ri->pos          = st->pos;// script location
+	ri->nargs        = j;// argument count
+	ri->defsp        = st->stack->defsp;// default stack pointer
+	push_retinfo(st->stack, ri);
+
+	st->pos = pos;
+	st->stack->defsp = st->stack->sp;
+	st->state = GOTO;
+	st->stack->var_function = (struct linkdb_node**)aCalloc(1, sizeof(struct linkdb_node*));
+
 	return 0;
 }
 
@@ -3868,28 +3895,26 @@ BUILDIN_FUNC(callsub)
 /// getarg(<index>{,<default_value>}) -> <value>
 BUILDIN_FUNC(getarg)
 {
+	struct script_retinfo* ri;
 	int idx;
-	int count;
-	int stsp;
 
-	if( st->stack->defsp < 5 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO )
+	if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO )
 	{
 		ShowError("script:getarg: no callfunc or callsub!\n");
 		st->state = END;
 		return 1;
 	}
-	count = conv_num(st, &(st->stack->stack_data[st->stack->defsp - 5]));
-	stsp = st->stack->defsp - count - 5;
+	ri = st->stack->stack_data[st->stack->defsp - 1].u.ri;
 
 	idx = script_getnum(st,2);
 
-	if( idx < count )
-		push_copy(st->stack, stsp + idx);
+	if( idx >= 0 && idx < ri->nargs )
+		push_copy(st->stack, st->stack->defsp - 1 - ri->nargs + idx);
 	else if( script_hasdata(st,3) )
 		script_pushcopy(st, 3);
 	else
 	{
-		ShowError("script:getarg: index (idx=%d) out of range (count=%d) and no default value found\n", idx, count);
+		ShowError("script:getarg: index (idx=%d) out of range (nargs=%d) and no default value found\n", idx, ri->nargs);
 		st->state = END;
 		return 1;
 	}
@@ -3913,8 +3938,9 @@ BUILDIN_FUNC(return)
 		{
 			const char* name = reference_getname(data);
 			if( name[0] == '.' && name[1] == '@' )
-			{// temporary script variable, convert to value
-				get_val(st, data);
+			{// scope variable
+				if( !data->ref || data->ref == st->stack->var_function )
+					get_val(st, data);// current scope, convert to value
 			}
 			else if( name[0] == '.' && !data->ref )
 			{// script variable, link to current script
@@ -4379,7 +4405,7 @@ BUILDIN_FUNC(input)
 		sd->state.menu_or_input = 0;
 		if( is_string_variable(name) )
 		{
-			size_t len = strlen(sd->npc_str);
+			int len = (int)strlen(sd->npc_str);
 			set_reg(st, sd, uid, name, (void*)sd->npc_str, script_getref(st,2));
 			script_pushint(st, (len > max ? 1 : len < min ? -1 : 0));
 		}

+ 10 - 1
src/map/script.h

@@ -67,11 +67,20 @@ typedef enum c_op {
 	C_L_SHIFT // a << b
 } c_op;
 
+struct script_retinfo {
+	struct linkdb_node** var_function;// scope variables
+	struct script_code* script;// script code
+	int pos;// script location
+	int nargs;// argument count
+	int defsp;// default stack pointer
+};
+
 struct script_data {
 	enum c_op type;
 	union script_data_val {
 		int num;
 		char *str;
+		struct script_retinfo* ri;
 	} u;
 	struct linkdb_node** ref;
 };
@@ -89,7 +98,7 @@ struct script_stack {
 	int sp_max;// capacity of the stack
 	int defsp;
 	struct script_data *stack_data;// stack
-	struct linkdb_node **var_function;	// ֐ˑϐ
+	struct linkdb_node** var_function;// scope variables
 };