core/systems/ForwardSys.cpp
author Scott Ehlert <ds@alliedmods.net>
Tue Jul 08 09:20:47 2008 +0000 (2008-07-08)
changeset 2181 7c85684253ed
parent 2142 d9d876e97a66
child 2183 4dde8a7f95a8
permissions -rw-r--r--
- Fixed amb1802: Crash when client was disconnected as a result of false being returned in OnClientConnect and a function that operated on this client was used. A client's connection state was not reset when this happened.
- Removed IForwardFilter due to overall horribleness (should be safe since no one seems to use it). Perhaps it might be back one day?
- Added ET_LowEvent forward exec type which is exactly the same as ET_Event, except that it returns the lowest value rather than the highest
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <assert.h>
    33 #include <stdarg.h>
    34 #include <string.h>
    35 #include "ForwardSys.h"
    36 #include "PluginSys.h"
    37 #include "ShareSys.h"
    38 #include "DebugReporter.h"
    39 
    40 CForwardManager g_Forwards;
    41 
    42 /**
    43  * Genesis turns to its source, reduction occurs stepwise although the essence is all one.
    44  * End of line.  FTL system check.
    45  *
    46  * :TODO: WHAT NEEDS TO BE TESTED IN THIS BEAST
    47  * VARARG FUNCTIONS:
    48  * - Pushing no varargs
    49  */
    50 
    51 // :TODO: IMPORTANT!!! The result pointer arg in the execute function maybe invalid if the forward fails
    52 // so later evaluation of this result may cause problems on higher levels of abstraction. DOCUMENT OR FIX ALL FORWARDS!
    53 
    54 CForwardManager::~CForwardManager()
    55 {
    56 	CStack<CForward *>::iterator iter;
    57 	for (iter=m_FreeForwards.begin(); iter!=m_FreeForwards.end(); iter++)
    58 	{
    59 		delete (*iter);
    60 	}
    61 	m_FreeForwards.popall();
    62 }
    63 
    64 void CForwardManager::OnSourceModAllInitialized()
    65 {
    66 	g_PluginSys.AddPluginsListener(this);
    67 	g_ShareSys.AddInterface(NULL, this);
    68 }
    69 
    70 IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, ...)
    71 {
    72 	CForward *fwd;
    73 	va_list ap;
    74 	va_start(ap, types);
    75 	
    76 	fwd = CForward::CreateForward(name, et, num_params, types, ap);
    77 
    78 	va_end(ap);
    79 
    80 	if (fwd)
    81 	{
    82 		g_PluginSys.AddFunctionsToForward(name, fwd);
    83 
    84 		m_managed.push_back(fwd);
    85 	}
    86 
    87 	return fwd;
    88 }
    89 
    90 IChangeableForward *CForwardManager::CreateForwardEx(const char *name, ExecType et, int num_params, const ParamType *types, ...)
    91 {
    92 	CForward *fwd;
    93 	va_list ap;
    94 	va_start(ap, types);
    95 
    96 	fwd = CForward::CreateForward(name, et, num_params, types, ap);
    97 
    98 	va_end(ap);
    99 
   100 	if (fwd)
   101 	{
   102 		m_unmanaged.push_back(fwd);
   103 	}
   104 
   105 	return fwd;
   106 }
   107 
   108 void CForwardManager::OnPluginLoaded(IPlugin *plugin)
   109 {
   110 	/* Attach any globally managed forwards */
   111 	List<CForward *>::iterator iter;
   112 	CForward *fwd;
   113 
   114 	for (iter=m_managed.begin(); iter!=m_managed.end(); iter++)
   115 	{
   116 		fwd = (*iter);
   117 		IPluginFunction *pFunc = plugin->GetBaseContext()->GetFunctionByName(fwd->GetForwardName());
   118 		if (pFunc)
   119 		{
   120 			fwd->AddFunction(pFunc);
   121 		}
   122 	}
   123 }
   124 
   125 void CForwardManager::OnPluginUnloaded(IPlugin *plugin)
   126 {
   127 	List<CForward *>::iterator iter;
   128 	CForward *fwd;
   129 
   130 	for (iter=m_managed.begin(); iter!=m_managed.end(); iter++)
   131 	{
   132 		fwd = (*iter);
   133 		fwd->RemoveFunctionsOfPlugin(plugin);
   134 	}
   135 
   136 	for (iter=m_unmanaged.begin(); iter!=m_unmanaged.end(); iter++)
   137 	{
   138 		fwd = (*iter);
   139 		fwd->RemoveFunctionsOfPlugin(plugin);
   140 	}
   141 }
   142 
   143 IForward *CForwardManager::FindForward(const char *name, IChangeableForward **ifchng)
   144 {
   145 	List<CForward *>::iterator iter;
   146 	CForward *fwd;
   147 
   148 	for (iter=m_managed.begin(); iter!=m_managed.end(); iter++)
   149 	{
   150 		fwd = (*iter);
   151 		if (strcmp(fwd->GetForwardName(), name) == 0)
   152 		{
   153 			if (ifchng)
   154 			{
   155 				*ifchng = NULL;
   156 			}
   157 			return fwd;
   158 		}
   159 	}
   160 
   161 	for (iter=m_unmanaged.begin(); iter!=m_unmanaged.end(); iter++)
   162 	{
   163 		fwd = (*iter);
   164 		if (strcmp(fwd->GetForwardName(), name) == 0)
   165 		{
   166 			if (ifchng)
   167 			{
   168 				*ifchng = fwd;
   169 			}
   170 			return fwd;
   171 		}
   172 	}
   173 
   174 	if (ifchng)
   175 	{
   176 		*ifchng = NULL;
   177 	}
   178 
   179 	return NULL;
   180 }
   181 
   182 void CForwardManager::ReleaseForward(IForward *forward)
   183 {
   184 	ForwardFree(static_cast<CForward *>(forward));
   185 }
   186 
   187 void CForwardManager::ForwardFree(CForward *fwd)
   188 {
   189 	m_FreeForwards.push(fwd);
   190 	m_managed.remove(fwd);
   191 }
   192 
   193 CForward *CForwardManager::ForwardMake()
   194 {
   195 	CForward *fwd;
   196 	if (m_FreeForwards.empty())
   197 	{
   198 		fwd = new CForward;
   199 	} else {
   200 		fwd = m_FreeForwards.front();
   201 		m_FreeForwards.pop();
   202 	}
   203 	return fwd;
   204 }
   205 
   206 void CForwardManager::OnPluginPauseChange(IPlugin *plugin, bool paused)
   207 {
   208 	/* No longer used */
   209 }
   210 
   211 /*************************************
   212  * ACTUAL FORWARD API IMPLEMENTATION *
   213  *************************************/
   214 
   215 CForward *CForward::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, va_list ap)
   216 {
   217 	ParamType _types[SP_MAX_EXEC_PARAMS];
   218 
   219 	if (num_params > SP_MAX_EXEC_PARAMS)
   220 	{
   221 		return NULL;
   222 	}
   223 	
   224 	if (types == NULL && num_params)
   225 	{
   226 		for (unsigned int i=0; i<num_params; i++)
   227 		{
   228 			_types[i] = (ParamType)va_arg(ap, int);
   229 			if (_types[i] == Param_VarArgs && (i != num_params - 1))
   230 			{
   231 				return NULL;
   232 			}
   233 		}
   234 		types = _types;
   235 	} else {
   236 		for (unsigned int i=0; i<num_params; i++)
   237 		{
   238 			_types[i] = types[i];
   239 			if (types[i] == Param_VarArgs && (i != num_params - 1))
   240 			{
   241 				return NULL;
   242 			}
   243 		}
   244 	}
   245 
   246 	/* First parameter can never be varargs */
   247 	if (num_params && _types[0] == Param_VarArgs)
   248 	{
   249 		return NULL;
   250 	}
   251 
   252 	CForward *pForward = g_Forwards.ForwardMake();
   253 	pForward->m_curparam = 0;
   254 	pForward->m_ExecType = et;
   255 	snprintf(pForward->m_name, FORWARDS_NAME_MAX, "%s", name ? name : "");
   256 	
   257 	for (unsigned int i=0; i<num_params; i++)
   258 	{
   259 		pForward->m_types[i] = _types[i];
   260 	}
   261 
   262 	if (num_params && _types[num_params-1] == Param_VarArgs)
   263 	{
   264 		pForward->m_varargs = num_params--;
   265 	} else {
   266 		pForward->m_varargs = false;
   267 	}
   268 
   269 	pForward->m_numparams = num_params;
   270 	pForward->m_errstate = SP_ERROR_NONE;
   271 
   272 	pForward->m_functions.clear();
   273 
   274 	return pForward;
   275 }
   276 
   277 int CForward::Execute(cell_t *result, IForwardFilter *filter)
   278 {
   279 	if (m_errstate)
   280 	{
   281 		int err = m_errstate;
   282 		Cancel();
   283 		return err;
   284 	}
   285 
   286 	FuncIter iter = m_functions.begin();
   287 	IPluginFunction *func;
   288 	cell_t cur_result = 0;
   289 	cell_t high_result = 0;
   290 	cell_t low_result = 0;
   291 	int err;
   292 	unsigned int failed=0, success=0;
   293 	unsigned int num_params = m_curparam;
   294 	FwdParamInfo temp_info[SP_MAX_EXEC_PARAMS];
   295 	FwdParamInfo *param;
   296 	ParamType type;
   297 
   298 	/* Save local, reset */
   299 	memcpy(temp_info, m_params, sizeof(m_params));
   300 	m_curparam = 0;
   301 
   302 	for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
   303 	{
   304 		func = (*iter);
   305 
   306 		for (unsigned int i=0; i<num_params; i++)
   307 		{
   308 			int err = SP_ERROR_PARAM;
   309 			param = &temp_info[i];
   310 
   311 			if (i >= m_numparams || m_types[i] == Param_Any)
   312 			{
   313 				type = param->pushedas;
   314 			}
   315 			else
   316 			{
   317 				type = m_types[i];
   318 			}
   319 
   320 			if ((i >= m_numparams) || (type & SP_PARAMFLAG_BYREF))
   321 			{
   322 				/* If we're byref or we're vararg, we always push everything by ref.
   323 				 * Even if they're byval, we must push them byref.
   324 				 */
   325 				if (type == Param_String)
   326 				{
   327 					err = func->PushStringEx((char *)param->byref.orig_addr, param->byref.cells, param->byref.sz_flags, param->byref.flags);
   328 				}
   329 				else if (type == Param_Float || type == Param_Cell)
   330 				{
   331 					err = func->PushCellByRef(&param->val); 
   332 				}
   333 				else
   334 				{
   335 					err = func->PushArray(param->byref.orig_addr, param->byref.cells, param->byref.flags);
   336 					assert(type == Param_Array || type == Param_FloatByRef || type == Param_CellByRef);
   337 				}
   338 			}
   339 			else
   340 			{
   341 				/* If we're not byref or not vararg, our job is a bit easier. */
   342 				assert(type == Param_Cell || type == Param_Float);
   343 				err = func->PushCell(param->val);
   344 			}
   345 
   346 			if (err != SP_ERROR_NONE)
   347 			{
   348 				g_DbgReporter.GenerateError(func->GetParentContext(), 
   349 					func->GetFunctionID(), 
   350 					err, 
   351 					"Failed to push parameter while executing forward");
   352 				continue;
   353 			}
   354 		}
   355 		
   356 		/* Call the function and deal with the return value. */
   357 		if ((err=func->Execute(&cur_result)) != SP_ERROR_NONE)
   358 		{
   359 			failed++;
   360 		}
   361 		else
   362 		{
   363 			success++;
   364 			switch (m_ExecType)
   365 			{
   366 			case ET_Event:
   367 				{
   368 					if (cur_result > high_result)
   369 					{
   370 						high_result = cur_result;
   371 					}
   372 					break;
   373 				}
   374 			case ET_Hook:
   375 				{
   376 					if (cur_result > high_result)
   377 					{
   378 						high_result = cur_result;
   379 						if ((ResultType)high_result == Pl_Stop)
   380 						{
   381 							goto done;
   382 						}
   383 					}
   384 					break;
   385 				}
   386 			case ET_LowEvent:
   387 				{
   388 					if (cur_result < low_result)
   389 					{
   390 						low_result = cur_result;
   391 					}
   392 					break;
   393 				}
   394 			default:
   395 				{
   396 					break;
   397 				}
   398 			}
   399 		}
   400 	}
   401 
   402 done:
   403 
   404 	if (success)
   405 	{
   406 		switch (m_ExecType)
   407 		{
   408 		case ET_Ignore:
   409 			{
   410 				cur_result = 0;
   411 				break;
   412 			}
   413 		case ET_Event:
   414 		case ET_Hook:
   415 			{
   416 				cur_result = high_result;
   417 				break;
   418 			}
   419 		case ET_LowEvent:
   420 			{
   421 				cur_result = low_result;
   422 				break;
   423 			}
   424 		default:
   425 			{
   426 				break;
   427 			}
   428 		}
   429 
   430 		if (result)
   431 		{
   432 			*result = cur_result;
   433 		}
   434 	}
   435 
   436 	return SP_ERROR_NONE;
   437 }
   438 
   439 int CForward::PushCell(cell_t cell)
   440 {
   441 	if (m_curparam < m_numparams)
   442 	{
   443 		if (m_types[m_curparam] == Param_Any)
   444 		{
   445 			m_params[m_curparam].pushedas = Param_Cell;
   446 		} else if (m_types[m_curparam] != Param_Cell) {
   447 			return SetError(SP_ERROR_PARAM);
   448 		}
   449 	} else {
   450 		if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS)
   451 		{
   452 			return SetError(SP_ERROR_PARAMS_MAX);
   453 		}
   454 		m_params[m_curparam].pushedas = Param_Cell;
   455 	}
   456 
   457 	m_params[m_curparam++].val = cell;
   458 
   459 	return SP_ERROR_NONE;
   460 }
   461 
   462 int CForward::PushFloat(float number)
   463 {
   464 	if (m_curparam < m_numparams)
   465 	{
   466 		if (m_types[m_curparam] == Param_Any)
   467 		{
   468 			m_params[m_curparam].pushedas = Param_Float;
   469 		} else if (m_types[m_curparam] != Param_Float) {
   470 			return SetError(SP_ERROR_PARAM);
   471 		}
   472 	} else {
   473 		if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS)
   474 		{
   475 			return SetError(SP_ERROR_PARAMS_MAX);
   476 		}
   477 		m_params[m_curparam].pushedas = Param_Float;
   478 	}
   479 
   480 	m_params[m_curparam++].val = *(cell_t *)&number;
   481 
   482 	return SP_ERROR_NONE;
   483 }
   484 
   485 int CForward::PushCellByRef(cell_t *cell, int flags)
   486 {
   487 	if (m_curparam < m_numparams)
   488 	{
   489 		if (m_types[m_curparam] == Param_Any)
   490 		{
   491 			m_params[m_curparam].pushedas = Param_CellByRef;
   492 		} else if (m_types[m_curparam] != Param_CellByRef) {
   493 			return SetError(SP_ERROR_PARAM);
   494 		}
   495 	} else {
   496 		if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS)
   497 		{
   498 			return SetError(SP_ERROR_PARAMS_MAX);
   499 		}
   500 		m_params[m_curparam].pushedas = Param_CellByRef;
   501 	}
   502 
   503 	_Int_PushArray(cell, 1, flags);
   504 	m_curparam++;
   505 
   506 	return SP_ERROR_NONE;
   507 }
   508 
   509 int CForward::PushFloatByRef(float *num, int flags)
   510 {
   511 	if (m_curparam < m_numparams)
   512 	{
   513 		if (m_types[m_curparam] == Param_Any)
   514 		{
   515 			m_params[m_curparam].pushedas = Param_FloatByRef;
   516 		} else if (m_types[m_curparam] != Param_FloatByRef) {
   517 			return SetError(SP_ERROR_PARAM);
   518 		}
   519 	} else {
   520 		if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS)
   521 		{
   522 			return SetError(SP_ERROR_PARAMS_MAX);
   523 		}
   524 		m_params[m_curparam].pushedas = Param_FloatByRef;
   525 	}
   526 
   527 	_Int_PushArray((cell_t *)num, 1, flags);
   528 	m_curparam++;
   529 
   530 	return SP_ERROR_NONE;
   531 }
   532 
   533 void CForward::_Int_PushArray(cell_t *inarray, unsigned int cells, int flags)
   534 {
   535 	m_params[m_curparam].byref.cells = cells;
   536 	m_params[m_curparam].byref.flags = flags;
   537 	m_params[m_curparam].byref.orig_addr = inarray;
   538 }
   539 
   540 int CForward::PushArray(cell_t *inarray, unsigned int cells, int flags)
   541 {
   542 	/* We don't allow this here */
   543 	if (!inarray)
   544 	{
   545 		return SetError(SP_ERROR_PARAM);
   546 	}
   547 
   548 	if (m_curparam < m_numparams)
   549 	{
   550 		if (m_types[m_curparam] == Param_Any)
   551 		{
   552 			m_params[m_curparam].pushedas = Param_Array;
   553 		} else if (m_types[m_curparam] != Param_Array) {
   554 			return SetError(SP_ERROR_PARAM);
   555 		}
   556 	} else {
   557 		if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS)
   558 		{
   559 			return SetError(SP_ERROR_PARAMS_MAX);
   560 		}
   561 		m_params[m_curparam].pushedas = Param_Array;
   562 	}
   563 
   564 	_Int_PushArray(inarray, cells, flags);
   565 
   566 	m_curparam++;
   567 
   568 	return SP_ERROR_NONE;
   569 }
   570 
   571 void CForward::_Int_PushString(cell_t *inarray, unsigned int cells, int sz_flags, int cp_flags)
   572 {
   573 	m_params[m_curparam].byref.cells = cells;	/* Notice this contains the char count not cell count */
   574 	m_params[m_curparam].byref.flags = cp_flags;
   575 	m_params[m_curparam].byref.orig_addr = inarray;
   576 	m_params[m_curparam].byref.sz_flags = sz_flags;
   577 }
   578 
   579 int CForward::PushString(const char *string)
   580 {
   581 	if (m_curparam < m_numparams)
   582 	{
   583 		if (m_types[m_curparam] == Param_Any)
   584 		{
   585 			m_params[m_curparam].pushedas = Param_String;
   586 		} else if (m_types[m_curparam] != Param_String) {
   587 			return SetError(SP_ERROR_PARAM);
   588 		}
   589 	} else {
   590 		if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS)
   591 		{
   592 			return SetError(SP_ERROR_PARAMS_MAX);
   593 		}
   594 		m_params[m_curparam].pushedas = Param_String;
   595 	}
   596 
   597 	_Int_PushString((cell_t *)string, strlen(string)+1, SM_PARAM_STRING_COPY, 0);
   598 	m_curparam++;
   599 
   600 	return SP_ERROR_NONE;
   601 }
   602 
   603 int CForward::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
   604 {
   605 	if (m_curparam < m_numparams)
   606 	{
   607 		if (m_types[m_curparam] == Param_Any)
   608 		{
   609 			m_params[m_curparam].pushedas = Param_String;
   610 		} else if (m_types[m_curparam] != Param_String) {
   611 			return SetError(SP_ERROR_PARAM);
   612 		}
   613 	} else {
   614 		if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS)
   615 		{
   616 			return SetError(SP_ERROR_PARAMS_MAX);
   617 		}
   618 		m_params[m_curparam].pushedas = Param_String;
   619 	}
   620 
   621 	_Int_PushString((cell_t *)buffer, length, sz_flags, cp_flags);
   622 	m_curparam++;
   623 
   624 	return SP_ERROR_NONE;
   625 }
   626 
   627 void CForward::Cancel()
   628 {
   629 	if (!m_curparam)
   630 	{
   631 		return;
   632 	}
   633 
   634 	m_curparam = 0;
   635 	m_errstate = SP_ERROR_NONE;
   636 }
   637 
   638 bool CForward::AddFunction(IPluginContext *pContext, funcid_t index)
   639 {
   640 	IPluginFunction *pFunc = pContext->GetFunctionById(index);
   641 
   642 	if (!pFunc)
   643 	{
   644 		return false;
   645 	}
   646 
   647 	return AddFunction(pFunc);
   648 }
   649 
   650 bool CForward::RemoveFunction(IPluginContext *pContext, funcid_t index)
   651 {
   652 	IPluginFunction *pFunc = pContext->GetFunctionById(index);
   653 
   654 	if (!pFunc)
   655 	{
   656 		return false;
   657 	}
   658 
   659 	return RemoveFunction(pFunc);
   660 }
   661 
   662 bool CForward::RemoveFunction(IPluginFunction *func)
   663 {
   664 	bool found = false;
   665 	FuncIter iter;
   666 	List<IPluginFunction *> *lst;
   667 
   668 	if (func->IsRunnable())
   669 	{
   670 		lst = &m_functions;
   671 	} else {
   672 		lst = &m_paused;
   673 	}
   674 
   675 	for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
   676 	{
   677 		if ((*iter) == func)
   678 		{
   679 			found = true;
   680 			lst->erase(iter);
   681 			break;
   682 		}
   683 	}
   684 
   685 	/* Cancel a call, if any */
   686 	if (found || m_curparam)
   687 	{
   688 		func->Cancel();
   689 	}
   690 
   691 	return found;
   692 }
   693 
   694 unsigned int CForward::RemoveFunctionsOfPlugin(IPlugin *plugin)
   695 {
   696 	FuncIter iter;
   697 	IPluginFunction *func;
   698 	unsigned int removed = 0;
   699 	IPluginContext *pContext = plugin->GetBaseContext();
   700 	for (iter=m_functions.begin(); iter!=m_functions.end();)
   701 	{
   702 		func = (*iter);
   703 		if (func->GetParentContext() == pContext)
   704 		{
   705 			iter = m_functions.erase(iter);
   706 			removed++;
   707 		} else {
   708 			iter++;
   709 		}
   710 	}
   711 
   712 	return removed;
   713 }
   714 
   715 bool CForward::AddFunction(IPluginFunction *func)
   716 {
   717 	if (m_curparam)
   718 	{
   719 		return false;
   720 	}
   721 
   722 	if (func->IsRunnable())
   723 	{
   724 		m_functions.push_back(func);
   725 	} else {
   726 		m_paused.push_back(func);
   727 	}
   728 
   729 	return true;
   730 }
   731 
   732 const char *CForward::GetForwardName()
   733 {
   734 	return m_name;
   735 }
   736 
   737 unsigned int CForward::GetFunctionCount()
   738 {
   739 	return m_functions.size();
   740 }
   741 
   742 ExecType CForward::GetExecType()
   743 {
   744 	return m_ExecType;
   745 }