gmMachineLib.cpp 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313
  1. /*
  2. _____ __ ___ __ ____ _ __
  3. / ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
  4. / (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
  5. \___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
  6. /___/ /_/
  7. See Copyright Notice in gmMachine.h
  8. */
  9. #include "gmConfig.h"
  10. #include "gmMachineLib.h"
  11. #include "gmThread.h"
  12. #include "gmMachine.h"
  13. #include "gmUtil.h"
  14. //
  15. // machine
  16. //
  17. static int GM_CDECL gmVersion(gmThread * a_thread)
  18. {
  19. a_thread->PushNewString(GM_VERSION);
  20. return GM_OK;
  21. }
  22. static int GM_CDECL gmTypeId(gmThread * a_thread) // return int, or null
  23. {
  24. if(a_thread->GetNumParams() > 0)
  25. {
  26. a_thread->PushInt((gmptr) a_thread->Param(0).m_type);
  27. }
  28. return GM_OK;
  29. }
  30. static int GM_CDECL gmTypeName(gmThread * a_thread) // return string, or null
  31. {
  32. if(a_thread->GetNumParams() > 0)
  33. {
  34. const char * name = a_thread->GetMachine()->GetTypeName(a_thread->Param(0).m_type);
  35. a_thread->PushNewString(name);
  36. }
  37. return GM_OK;
  38. }
  39. static int GM_CDECL gmRegisterTypeOperator(gmThread * a_thread) // typeid, operatorname, function, returns true on success
  40. {
  41. GM_CHECK_NUM_PARAMS(3);
  42. GM_CHECK_INT_PARAM(typeId, 0);
  43. GM_CHECK_STRING_PARAM(operatorName, 1);
  44. GM_CHECK_FUNCTION_PARAM(function, 2);
  45. gmOperator op = gmGetOperator(operatorName);
  46. if(op != O_MAXOPERATORS)
  47. {
  48. a_thread->PushInt(a_thread->GetMachine()->RegisterTypeOperator((gmType) typeId, op, function) ? 1 : 0);
  49. return GM_OK;
  50. }
  51. a_thread->PushInt(0);
  52. return GM_OK;
  53. }
  54. static int GM_CDECL gmRegisterTypeVariable(gmThread * a_thread) // typeid, key (string), value
  55. {
  56. GM_CHECK_NUM_PARAMS(3);
  57. GM_CHECK_INT_PARAM(typeId, 0);
  58. GM_CHECK_STRING_PARAM(variable, 1);
  59. a_thread->GetMachine()->RegisterTypeVariable((gmType) typeId, variable, a_thread->Param(2));
  60. return GM_OK;
  61. }
  62. static int GM_CDECL gmCollectGarbage(gmThread * a_thread) // returns true if gc was run
  63. {
  64. GM_INT_PARAM(forceFullCollect, 0, false);
  65. a_thread->PushInt(a_thread->GetMachine()->CollectGarbage(forceFullCollect != 0) ? 1 : 0);
  66. return GM_OK;
  67. }
  68. static int GM_CDECL gmGetCurrentMemoryUsage(gmThread * a_thread) // returns current memory usage in bytes
  69. {
  70. a_thread->PushInt(a_thread->GetMachine()->GetCurrentMemoryUsage());
  71. return GM_OK;
  72. }
  73. static int GM_CDECL gmSetDesiredMemoryUsageHard(gmThread * a_thread) // mem usage in bytes
  74. {
  75. GM_CHECK_NUM_PARAMS(1);
  76. GM_CHECK_INT_PARAM(mem, 0);
  77. a_thread->GetMachine()->SetDesiredByteMemoryUsageHard(mem);
  78. return GM_OK;
  79. }
  80. static int GM_CDECL gmSetDesiredMemoryUsageSoft(gmThread * a_thread) // mem usage in bytes
  81. {
  82. GM_CHECK_NUM_PARAMS(1);
  83. GM_CHECK_INT_PARAM(mem, 0);
  84. a_thread->GetMachine()->SetDesiredByteMemoryUsageSoft(mem);
  85. return GM_OK;
  86. }
  87. static int GM_CDECL gmGetDesiredMemoryUsageHard(gmThread * a_thread)
  88. {
  89. a_thread->PushInt(a_thread->GetMachine()->GetDesiredByteMemoryUsageHard());
  90. return GM_OK;
  91. }
  92. static int GM_CDECL gmGetDesiredMemoryUsageSoft(gmThread * a_thread)
  93. {
  94. a_thread->PushInt(a_thread->GetMachine()->GetDesiredByteMemoryUsageSoft());
  95. return GM_OK;
  96. }
  97. static int GM_CDECL gmSetDesiredMemoryUsageAuto(gmThread * a_thread) // mem usage in bytes
  98. {
  99. GM_CHECK_NUM_PARAMS(1);
  100. GM_CHECK_INT_PARAM(autoEnable, 0);
  101. a_thread->GetMachine()->SetAutoMemoryUsage(autoEnable != 0);
  102. return GM_OK;
  103. }
  104. static int GM_CDECL gmSysGetStatsGCNumFullCollects(gmThread * a_thread)
  105. {
  106. a_thread->PushInt(a_thread->GetMachine()->GetStatsGCNumFullCollects());
  107. return GM_OK;
  108. }
  109. static int GM_CDECL gmSysGetStatsGCNumIncCollects(gmThread * a_thread)
  110. {
  111. a_thread->PushInt(a_thread->GetMachine()->GetStatsGCNumIncCollects());
  112. return GM_OK;
  113. }
  114. static int GM_CDECL gmSysGetStatsGCNumWarnings(gmThread * a_thread)
  115. {
  116. a_thread->PushInt(a_thread->GetMachine()->GetStatsGCNumWarnings());
  117. return GM_OK;
  118. }
  119. static int GM_CDECL gmSysIsGCRunning(gmThread * a_thread)
  120. {
  121. a_thread->PushInt(a_thread->GetMachine()->IsGCRunning());
  122. return GM_OK;
  123. }
  124. static int GM_CDECL gmDoString(gmThread * a_thread) // string, now(int), returns thread id, null on error, exception on compile error
  125. {
  126. GM_CHECK_NUM_PARAMS(1); // Need at least 1 parameter
  127. GM_CHECK_STRING_PARAM(script, 0); // 1st param is script string
  128. GM_INT_PARAM(now, 1, 1); // 2nd param is execute now flag
  129. gmVariable paramThis = a_thread->Param(2, gmVariable::s_null); // 3rd param is 'this'
  130. int id = GM_INVALID_THREAD;
  131. if( script )
  132. {
  133. int errors = a_thread->GetMachine()->ExecuteString(script, &id, (now) ? true : false, NULL, &paramThis);
  134. if( errors )
  135. {
  136. return GM_EXCEPTION;
  137. }
  138. a_thread->PushInt(id);
  139. }
  140. return GM_OK;
  141. }
  142. static int GM_CDECL gmGlobals(gmThread * a_thread) // return table
  143. {
  144. a_thread->PushTable(a_thread->GetMachine()->GetGlobals());
  145. return GM_OK;
  146. }
  147. static int GM_CDECL gmMachineTime(gmThread * a_thread) // return machine time
  148. {
  149. a_thread->PushInt(a_thread->GetMachine()->GetTime());
  150. return GM_OK;
  151. }
  152. //
  153. // thread
  154. //
  155. static int GM_CDECL gmSleep(gmThread * a_thread) // float\int param time in seconds
  156. {
  157. GM_CHECK_NUM_PARAMS(1);
  158. gmType type = a_thread->ParamType(0);
  159. gmuint32 ms = 0;
  160. if(type == GM_INT) ms = a_thread->Param(0).m_value.m_int * 1000;
  161. else if(type == GM_FLOAT) ms = (gmuint32) floorf(a_thread->Param(0).m_value.m_float * 1000.0f);
  162. a_thread->Sys_SetTimeStamp(a_thread->GetMachine()->GetTime() + ms);
  163. return GM_SYS_SLEEP;
  164. }
  165. static int GM_CDECL gmYield(gmThread * a_thread)
  166. {
  167. return GM_SYS_YIELD;
  168. }
  169. static int GM_CDECL gmThreadTime(gmThread * a_thread)
  170. {
  171. a_thread->PushInt(a_thread->GetThreadTime());
  172. return GM_OK;
  173. }
  174. static int GM_CDECL gmThreadId(gmThread * a_thread) // return thread id
  175. {
  176. a_thread->PushInt(a_thread->GetId());
  177. return GM_OK;
  178. }
  179. // Callback iteration function for gmThreadAllIds
  180. static bool gmThreadIdIter(gmThread * a_thread, void * a_context)
  181. {
  182. gmTableObject* table = (gmTableObject*)a_context;
  183. gmVariable threadId;
  184. threadId.SetInt(a_thread->GetId());
  185. table->Set(a_thread->GetMachine(), table->Count(), threadId);
  186. return true;
  187. }
  188. static int GM_CDECL gmThreadAllIds(gmThread * a_thread) // thread id
  189. {
  190. gmTableObject * threadIds = a_thread->PushNewTable();
  191. a_thread->GetMachine()->ForEachThread(gmThreadIdIter, threadIds);
  192. return GM_OK;
  193. }
  194. static int GM_CDECL gmExit(gmThread * a_thread)
  195. {
  196. return GM_SYS_KILL;
  197. }
  198. static int GM_CDECL gmKillThread(gmThread * a_thread) // thread id
  199. {
  200. GM_INT_PARAM(id, 0, GM_INVALID_THREAD); // 1 optional param, default is this thread
  201. // Kill this thread
  202. if( (id == GM_INVALID_THREAD) || (id == a_thread->GetId()) )
  203. {
  204. return GM_SYS_KILL; // Kill this thread
  205. }
  206. // Attempt to kill other thread by Id
  207. gmThread * thread = a_thread->GetMachine()->GetThread(id);
  208. if( thread )
  209. {
  210. thread->GetMachine()->Sys_SwitchState(thread, gmThread::KILLED); // Kill other thread
  211. }
  212. return GM_OK;
  213. }
  214. // Callback iteration function for gmKillAllThreads()
  215. // Kills all threads except current, current thread is passed in as context.
  216. static bool threadIterKill(gmThread * a_thread, void * a_context)
  217. {
  218. gmThread* caller = (gmThread*)a_context;
  219. if(a_thread != caller) // Ignore calling thread
  220. {
  221. switch(a_thread->GetState())
  222. {
  223. case gmThread::RUNNING:
  224. case gmThread::SLEEPING:
  225. case gmThread::BLOCKED:
  226. {
  227. // Kill the thread
  228. a_thread->GetMachine()->Sys_SwitchState(a_thread, gmThread::KILLED);
  229. break;
  230. }
  231. case gmThread::EXCEPTION:
  232. case gmThread::KILLED:
  233. default:
  234. {
  235. // Ignore threads of these states
  236. break;
  237. }
  238. }
  239. }
  240. return true;
  241. }
  242. static int GM_CDECL gmKillAllThreads(gmThread * a_thread) // thread id
  243. {
  244. GM_INT_PARAM(killCurrent, 0, 0);
  245. a_thread->GetMachine()->ForEachThread(threadIterKill, a_thread);
  246. if(killCurrent)
  247. {
  248. return GM_SYS_KILL;
  249. }
  250. return GM_OK;
  251. }
  252. static int GM_CDECL gmfThread(gmThread * a_thread) // fn, params, returns thread id (0) on error, else returns new thread id
  253. {
  254. GM_CHECK_NUM_PARAMS(1);
  255. GM_CHECK_FUNCTION_PARAM(function, 0);
  256. int id, i;
  257. gmThread * thread = a_thread->GetMachine()->CreateThread(&id);
  258. if(thread)
  259. {
  260. thread->Push(*a_thread->GetThis());
  261. thread->PushFunction(function);
  262. int numParameters = a_thread->GetNumParams() - 1;
  263. for(i = 0; i < numParameters; ++i)
  264. thread->Push(a_thread->Param(i + 1));
  265. thread->PushStackFrame(numParameters, 0);
  266. }
  267. a_thread->PushInt(id);
  268. return GM_OK;
  269. }
  270. static int GM_CDECL gmAssert(gmThread * a_thread)
  271. {
  272. if(a_thread->GetNumParams() > 0)
  273. {
  274. if(a_thread->Param(0).m_value.m_int)
  275. {
  276. return GM_OK;
  277. }
  278. }
  279. GM_STRING_PARAM(message, 1, "assert failed");
  280. a_thread->GetMachine()->GetLog().LogEntry("%s", message);
  281. return GM_EXCEPTION;
  282. }
  283. static gmType s_gmStateUserType = GM_NULL;
  284. struct gmStateUserType
  285. {
  286. gmFunctionObject * m_lastState; // last state
  287. gmFunctionObject * m_currentState; // current state
  288. gmFunctionObject * m_setExitState; // leave hanlder
  289. };
  290. static int GM_CDECL gmSetState(gmThread * a_thread) // fp, params
  291. {
  292. GM_CHECK_NUM_PARAMS(GM_STATE_NUM_PARAMS);
  293. GM_CHECK_FUNCTION_PARAM(function, 0);
  294. // make sure we have our state type.
  295. GM_ASSERT(s_gmStateUserType != GM_NULL);
  296. // save off the parameters to the new state
  297. gmVariable thisVar = *a_thread->GetThis();
  298. int i, numParameters = a_thread->GetNumParams() - GM_STATE_NUM_PARAMS;
  299. gmVariable * params = (gmVariable *) alloca(sizeof(gmVariable) * numParameters);
  300. for(i = 0; i < numParameters; ++i)
  301. {
  302. params[i] = a_thread->Param(i + GM_STATE_NUM_PARAMS);
  303. }
  304. // get the current state
  305. gmVariable newStateVariable;
  306. gmVariable * currentStateVariable = a_thread->GetBottom();
  307. if(currentStateVariable->m_type == s_gmStateUserType)
  308. {
  309. gmUserObject * userObj = (gmUserObject *) GM_OBJECT(currentStateVariable->m_value.m_ref);
  310. gmStateUserType * currentState = (gmStateUserType *) userObj->m_user;
  311. // call the on state leave if one exists.
  312. if(currentState->m_setExitState)
  313. {
  314. gmThread * thread = a_thread->GetMachine()->CreateThread(thisVar, gmVariable(GM_FUNCTION, currentState->m_setExitState->GetRef()));
  315. if(thread)
  316. {
  317. thread->Sys_Execute();
  318. }
  319. }
  320. currentState->m_setExitState = NULL;
  321. currentState->m_lastState = currentState->m_currentState;
  322. currentState->m_currentState = function;
  323. newStateVariable = *currentStateVariable;
  324. }
  325. else
  326. {
  327. gmStateUserType * state = (gmStateUserType *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmStateUserType));
  328. state->m_setExitState = NULL;
  329. state->m_currentState = function;
  330. state->m_lastState = NULL;
  331. // create a new state variable
  332. newStateVariable.SetUser(a_thread->GetMachine()->AllocUserObject(state, s_gmStateUserType));
  333. }
  334. // reset the stack. and push new state
  335. int user = a_thread->m_user;
  336. a_thread->Sys_Reset(a_thread->GetId());
  337. a_thread->m_user = user;
  338. a_thread->Sys_SetStartTime(a_thread->GetMachine()->GetTime());
  339. a_thread->Touch(4 + numParameters);
  340. a_thread->Push(newStateVariable);
  341. a_thread->Push(thisVar);
  342. a_thread->PushFunction(function);
  343. for(i = 0; i < numParameters; ++i)
  344. {
  345. a_thread->Push(params[i]);
  346. }
  347. return GM_SYS_STATE;
  348. }
  349. static int GM_CDECL gmSetStateOnThread(gmThread * a_thread) // (threadid, fp, params...) returns true or false.
  350. {
  351. GM_CHECK_NUM_PARAMS(2);
  352. GM_CHECK_INT_PARAM(threadId, 0);
  353. GM_CHECK_FUNCTION_PARAM(function, 1);
  354. // make sure we have our state type.
  355. GM_ASSERT(s_gmStateUserType != GM_NULL);
  356. // get the target thread
  357. gmThread * thread = a_thread->GetMachine()->GetThread(threadId);
  358. if(thread == a_thread)
  359. {
  360. a_thread->GetMachine()->GetLog().LogEntry("use setstate() on own thread");
  361. return GM_EXCEPTION;
  362. }
  363. if(thread == NULL)
  364. {
  365. return GM_OK;
  366. }
  367. // get the current state of the thread
  368. gmVariable newStateVariable;
  369. gmVariable thisVar = *thread->GetThis();
  370. gmVariable * currentStateVariable = thread->GetBottom();
  371. if(currentStateVariable->m_type == s_gmStateUserType)
  372. {
  373. gmUserObject * userObj = (gmUserObject *) GM_OBJECT(currentStateVariable->m_value.m_ref);
  374. gmStateUserType * currentState = (gmStateUserType *) userObj->m_user;
  375. // call the on state leave if one exists.
  376. if(currentState->m_setExitState)
  377. {
  378. gmThread * thread = a_thread->GetMachine()->CreateThread(thisVar, gmVariable(GM_FUNCTION, currentState->m_setExitState->GetRef()));
  379. if(thread)
  380. {
  381. thread->Sys_Execute();
  382. }
  383. }
  384. currentState->m_setExitState = NULL;
  385. currentState->m_lastState = currentState->m_currentState;
  386. currentState->m_currentState = function;
  387. newStateVariable = *currentStateVariable;
  388. }
  389. else
  390. {
  391. gmStateUserType * state = (gmStateUserType *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmStateUserType));
  392. state->m_setExitState = NULL;
  393. state->m_currentState = function;
  394. state->m_lastState = NULL;
  395. // create a new state variable
  396. newStateVariable.SetUser(a_thread->GetMachine()->AllocUserObject(state, s_gmStateUserType));
  397. }
  398. // reset the stack. and push new state
  399. int numParameters = a_thread->GetNumParams() - 2;
  400. int user = thread->m_user;
  401. thread->Sys_Reset(thread->GetId());
  402. thread->m_user = user;
  403. thread->Sys_SetStartTime(thread->GetMachine()->GetTime());
  404. thread->Touch(4 + numParameters);
  405. thread->Push(newStateVariable);
  406. thread->Push(thisVar);
  407. thread->PushFunction(function);
  408. int i;
  409. for(i = 0; i < numParameters; ++i)
  410. {
  411. thread->Push(a_thread->Param(i+2));
  412. }
  413. thread->PushStackFrame(numParameters);
  414. a_thread->GetMachine()->Sys_SwitchState(thread, gmThread::RUNNING);
  415. return GM_OK;
  416. }
  417. static int GM_CDECL gmGetState(gmThread * a_thread) // return var
  418. {
  419. GM_ASSERT(s_gmStateUserType != GM_NULL);
  420. gmThread * testThread = a_thread;
  421. //Optional parameter, threadId
  422. if(a_thread->GetNumParams() >= 1)
  423. {
  424. GM_CHECK_INT_PARAM(testThreadId, 0);
  425. testThread = a_thread->GetMachine()->GetThread(testThreadId);
  426. if(!testThread)
  427. {
  428. a_thread->PushNull();
  429. return GM_OK;
  430. }
  431. }
  432. gmVariable * currentStateVariable = testThread->GetBottom();
  433. if(currentStateVariable->m_type == s_gmStateUserType)
  434. {
  435. gmUserObject * userObj = (gmUserObject *) GM_OBJECT(currentStateVariable->m_value.m_ref);
  436. gmStateUserType * currentState = (gmStateUserType *) userObj->m_user;
  437. a_thread->PushFunction(currentState->m_currentState);
  438. }
  439. return GM_OK;
  440. }
  441. static int GM_CDECL gmGetLastState(gmThread * a_thread) // return var
  442. {
  443. GM_ASSERT(s_gmStateUserType != GM_NULL);
  444. gmThread * testThread = a_thread;
  445. //Optional parameter, threadId
  446. if(a_thread->GetNumParams() >= 1)
  447. {
  448. GM_CHECK_INT_PARAM(testThreadId, 0);
  449. testThread = a_thread->GetMachine()->GetThread(testThreadId);
  450. if(!testThread)
  451. {
  452. a_thread->PushNull();
  453. return GM_OK;
  454. }
  455. }
  456. gmVariable * currentStateVariable = testThread->GetBottom();
  457. if(currentStateVariable->m_type == s_gmStateUserType)
  458. {
  459. gmUserObject * userObj = (gmUserObject *) GM_OBJECT(currentStateVariable->m_value.m_ref);
  460. gmStateUserType * currentState = (gmStateUserType *) userObj->m_user;
  461. if(currentState->m_lastState)
  462. {
  463. a_thread->PushFunction(currentState->m_lastState);
  464. }
  465. }
  466. return GM_OK;
  467. }
  468. static int GM_CDECL gmSetExitState(gmThread * a_thread) // function
  469. {
  470. GM_CHECK_NUM_PARAMS(1);
  471. GM_CHECK_FUNCTION_PARAM(function, 0);
  472. GM_ASSERT(s_gmStateUserType != GM_NULL);
  473. gmVariable * currentStateVariable = a_thread->GetBottom();
  474. if(currentStateVariable->m_type == s_gmStateUserType)
  475. {
  476. gmUserObject * userObj = (gmUserObject *) GM_OBJECT(currentStateVariable->m_value.m_ref);
  477. gmStateUserType * currentState = (gmStateUserType *) userObj->m_user;
  478. currentState->m_setExitState = function;
  479. }
  480. return GM_OK;
  481. }
  482. static int GM_CDECL gmSignal(gmThread * a_thread) // var, dest thread id
  483. {
  484. GM_CHECK_NUM_PARAMS(1);
  485. GM_INT_PARAM(dstThreadId, 1, GM_INVALID_THREAD);
  486. a_thread->GetMachine()->Signal(a_thread->Param(0), dstThreadId, a_thread->GetId());
  487. return GM_OK;
  488. }
  489. static int GM_CDECL gmBlock(gmThread * a_thread) // var, ...
  490. {
  491. GM_CHECK_NUM_PARAMS(1);
  492. int res = a_thread->GetMachine()->Sys_Block(a_thread, a_thread->GetNumParams(), a_thread->GetBase());
  493. if(res == -1)
  494. {
  495. return GM_SYS_BLOCK;
  496. }
  497. else if(res == -2)
  498. {
  499. // Failed to block, so exception to prevent undefined or unexpected behavior
  500. GM_EXCEPTION_MSG("cannot block on null");
  501. return GM_EXCEPTION;
  502. }
  503. a_thread->Push(a_thread->Param(res));
  504. return GM_OK;
  505. }
  506. #if GM_USE_ENDON
  507. static int GM_CDECL gmEndOn(gmThread * a_thread)
  508. {
  509. GM_CHECK_NUM_PARAMS(1);
  510. int res = a_thread->GetMachine()->Sys_Block(a_thread, a_thread->GetNumParams(), a_thread->GetBase(), true);
  511. if(res == -1)
  512. {
  513. return GM_OK;
  514. }
  515. else if(res == -3)
  516. {
  517. return GM_EXCEPTION;
  518. }
  519. a_thread->Push(a_thread->Param(res));
  520. return GM_OK;
  521. }
  522. #endif //GM_USE_ENDON
  523. #if GM_USE_INCGC
  524. static void GM_CDECL gmGCDestructStateUserType(gmMachine * a_machine, gmUserObject* a_object)
  525. {
  526. gmStateUserType * state = (gmStateUserType *) a_object->m_user;
  527. a_machine->Sys_Free(state);
  528. }
  529. static bool GM_CDECL gmGCTraceStateUserType(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
  530. {
  531. gmStateUserType * state = (gmStateUserType *) a_object->m_user;
  532. if(state->m_currentState) a_gc->GetNextObject(state->m_currentState);
  533. if(state->m_lastState) a_gc->GetNextObject(state->m_lastState);
  534. if(state->m_setExitState) a_gc->GetNextObject(state->m_setExitState);
  535. a_workDone += 4; //contents + this
  536. return true;
  537. }
  538. #else //GM_USE_INCGC
  539. static void GM_CDECL gmGCStateUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
  540. {
  541. gmStateUserType * state = (gmStateUserType *) a_object->m_user;
  542. a_machine->Sys_Free(state);
  543. }
  544. static void GM_CDECL gmMarkStateUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
  545. {
  546. gmStateUserType * state = (gmStateUserType *) a_object->m_user;
  547. if(state->m_currentState && state->m_currentState->NeedsMark(a_mark)) state->m_currentState->Mark(a_machine, a_mark);
  548. if(state->m_lastState && state->m_lastState->NeedsMark(a_mark)) state->m_lastState->Mark(a_machine, a_mark);
  549. if(state->m_setExitState && state->m_setExitState->NeedsMark(a_mark)) state->m_setExitState->Mark(a_machine, a_mark);
  550. }
  551. #endif //GM_USE_INCGC
  552. //
  553. // table
  554. //
  555. static int GM_CDECL gmTableCount(gmThread * a_thread)
  556. {
  557. GM_CHECK_NUM_PARAMS(1);
  558. GM_CHECK_TABLE_PARAM(table, 0);
  559. a_thread->PushInt(table->Count());
  560. return GM_OK;
  561. }
  562. static int GM_CDECL gmTableDuplicate(gmThread * a_thread)
  563. {
  564. GM_CHECK_NUM_PARAMS(1);
  565. GM_CHECK_TABLE_PARAM(table, 0);
  566. a_thread->PushTable(table->Duplicate(a_thread->GetMachine()));
  567. return GM_OK;
  568. }
  569. //
  570. // std
  571. //
  572. void gmConcat(gmMachine * a_machine, char * &a_dst, int &a_len, int &a_size, const char * a_src, int a_growBy = 32)
  573. {
  574. int len = (int)strlen(a_src);
  575. if((a_len + len + 1) >= a_size)
  576. {
  577. a_size = a_len + len + a_growBy + 1;
  578. char * str = (char *) a_machine->Sys_Alloc(a_size);
  579. if(a_dst != NULL)
  580. {
  581. memcpy(str, a_dst, a_len);
  582. a_machine->Sys_Free(a_dst);
  583. }
  584. a_dst = str;
  585. a_dst[a_len] = '\0';
  586. }
  587. memcpy(a_dst + a_len, a_src, len);
  588. a_len += len;
  589. a_dst[a_len] = '\0';
  590. }
  591. static int GM_CDECL gmPrint(gmThread * a_thread)
  592. {
  593. const int bufferSize = 256;
  594. int len = 0, size = 0, i;
  595. char * str = NULL, buffer[bufferSize];
  596. // build the string
  597. for(i = 0; i < a_thread->GetNumParams(); ++i)
  598. {
  599. gmConcat(a_thread->GetMachine(), str, len, size, a_thread->Param(i).AsString(a_thread->GetMachine(), buffer, bufferSize), 64);
  600. if(str)
  601. {
  602. GM_ASSERT(len < size);
  603. str[len++] = ' ';
  604. str[len] = '\0';
  605. }
  606. }
  607. // print the string
  608. if(str)
  609. {
  610. if(gmMachine::s_printCallback)
  611. {
  612. gmMachine::s_printCallback(a_thread->GetMachine(), str);
  613. }
  614. a_thread->GetMachine()->Sys_Free(str);
  615. }
  616. return GM_OK;
  617. }
  618. static int GM_CDECL gmfFormat(gmThread * a_thread) // string, params ...
  619. {
  620. GM_CHECK_NUM_PARAMS(1);
  621. GM_CHECK_STRING_PARAM(format, 0);
  622. int param = 1;
  623. int len = 0, size = 0;
  624. const int bufferSize = 1024;
  625. char * str = NULL, buffer[bufferSize] = {};
  626. while(*format)
  627. {
  628. if(*format == '%')
  629. {
  630. switch(format[1])
  631. {
  632. case 'S' :
  633. case 's' :
  634. {
  635. //GM_STRING_PARAM(pstr, param, "");
  636. const char *pstr = a_thread->Param(param).GetCStringSafe();
  637. if(!pstr)
  638. {
  639. a_thread->GetMachine()->Sys_Free(str);
  640. GM_EXCEPTION_MSG("expected string as param %d",param);
  641. return GM_EXCEPTION;
  642. }
  643. gmConcat(a_thread->GetMachine(), str, len, size, pstr, 64);
  644. ++param;
  645. break;
  646. }
  647. case 'C' :
  648. case 'c' :
  649. {
  650. //GM_INT_PARAM(ival, param, 0);
  651. if(!a_thread->Param(param).IsInt())
  652. {
  653. a_thread->GetMachine()->Sys_Free(str);
  654. GM_EXCEPTION_MSG("expected int as param %d",param);
  655. return GM_EXCEPTION;
  656. }
  657. sprintf(buffer, "%c", a_thread->Param(param).GetInt());
  658. gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
  659. ++param;
  660. break;
  661. }
  662. case 'D' :
  663. case 'd' :
  664. {
  665. //GM_INT_PARAM(ival, param, 0);
  666. if(!a_thread->Param(param).IsInt())
  667. {
  668. a_thread->GetMachine()->Sys_Free(str);
  669. GM_EXCEPTION_MSG("expected int as param %d",param);
  670. return GM_EXCEPTION;
  671. }
  672. sprintf(buffer, "%d", a_thread->Param(param).GetInt());
  673. gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
  674. ++param;
  675. break;
  676. }
  677. case 'U' :
  678. case 'u' :
  679. {
  680. //GM_INT_PARAM(ival, param, 0);
  681. if(!a_thread->Param(param).IsInt())
  682. {
  683. a_thread->GetMachine()->Sys_Free(str);
  684. GM_EXCEPTION_MSG("expected int as param %d",param);
  685. return GM_EXCEPTION;
  686. }
  687. sprintf(buffer, "%u", a_thread->Param(param).GetInt());
  688. gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
  689. ++param;
  690. break;
  691. }
  692. case 'B' :
  693. case 'b' :
  694. {
  695. //GM_INT_PARAM(ival, param, 0);
  696. if(!a_thread->Param(param).IsInt())
  697. {
  698. a_thread->GetMachine()->Sys_Free(str);
  699. GM_EXCEPTION_MSG("expected int as param %d",param);
  700. return GM_EXCEPTION;
  701. }
  702. gmItoa(a_thread->Param(param).GetInt(), buffer, 2);
  703. gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
  704. ++param;
  705. break;
  706. }
  707. case 'X' :
  708. case 'x' :
  709. {
  710. //GM_INT_PARAM(ival, param, 0);
  711. if(!a_thread->Param(param).IsInt())
  712. {
  713. a_thread->GetMachine()->Sys_Free(str);
  714. GM_EXCEPTION_MSG("expected int as param %d",param);
  715. return GM_EXCEPTION;
  716. }
  717. sprintf(buffer, "%x", a_thread->Param(param).GetInt());
  718. gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
  719. ++param;
  720. break;
  721. }
  722. case 'F' :
  723. case 'f' :
  724. {
  725. //GM_FLOAT_PARAM(fval, param, 0);
  726. if(!a_thread->Param(param).IsFloat())
  727. {
  728. a_thread->GetMachine()->Sys_Free(str);
  729. GM_EXCEPTION_MSG("expected float as param %d",param);
  730. return GM_EXCEPTION;
  731. }
  732. sprintf(buffer, "%f", a_thread->Param(param).GetFloat());
  733. gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
  734. ++param;
  735. break;
  736. }
  737. case 'G' :
  738. case 'g' :
  739. {
  740. //GM_FLOAT_PARAM(fval, param, 0);
  741. if(!a_thread->Param(param).IsFloat())
  742. {
  743. a_thread->GetMachine()->Sys_Free(str);
  744. GM_EXCEPTION_MSG("expected float as param %d",param);
  745. return GM_EXCEPTION;
  746. }
  747. sprintf(buffer, "%g", a_thread->Param(param).GetFloat());
  748. gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
  749. ++param;
  750. break;
  751. }
  752. case 'e' :
  753. case 'E' :
  754. {
  755. //GM_FLOAT_PARAM(fval, param, 0);
  756. if(!a_thread->Param(param).IsFloat())
  757. {
  758. a_thread->GetMachine()->Sys_Free(str);
  759. GM_EXCEPTION_MSG("expected float as param %d",param);
  760. return GM_EXCEPTION;
  761. }
  762. sprintf(buffer, "%e", a_thread->Param(param).GetFloat());
  763. gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
  764. ++param;
  765. break;
  766. }
  767. case '%' :
  768. {
  769. if(len + 2 < size)
  770. str[len++] = '%';
  771. else
  772. gmConcat(a_thread->GetMachine(), str, len, size, "%", 64);
  773. break;
  774. }
  775. default :
  776. break;
  777. }
  778. format += 2;
  779. }
  780. else
  781. {
  782. if(len + 2 < size)
  783. str[len++] = *(format++);
  784. else
  785. {
  786. buffer[0] = *(format++);
  787. buffer[1] = '\0';
  788. gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
  789. }
  790. }
  791. }
  792. if(str)
  793. {
  794. str[len] = '\0';
  795. a_thread->PushNewString(str);
  796. a_thread->GetMachine()->Sys_Free(str);
  797. }
  798. return GM_OK;
  799. }
  800. //
  801. // lib
  802. //
  803. static gmFunctionEntry s_binding[] =
  804. {
  805. /*gm
  806. \lib gm
  807. \brief functions in the gm lib are all global scope
  808. */
  809. /*gm
  810. \function gmVersion
  811. \brief gmVersion will return the gmMachine version string. version string is major type . minor type as a string
  812. and was added at version 1.1
  813. \return string
  814. */
  815. {"gmVersion", gmVersion},
  816. /*gm
  817. \function typeId
  818. \brief typeId will return the type id of the passed var
  819. \param var
  820. \return integer type
  821. */
  822. {"typeId", gmTypeId},
  823. /*gm
  824. \function typeName
  825. \brief typeName will return the type name of the passed var
  826. \param var
  827. \return string
  828. */
  829. {"typeName", gmTypeName},
  830. /*gm
  831. \function typeRegisterOperator
  832. \brief typeRegisterOperator will register an operator for a type
  833. \param int typeid
  834. \param string operator name is one of
  835. "getdot", "setdot", "getind", "setind", "add", "sub", "mul", "div", "mod",
  836. "inc", "dec", "bitor", "bitxor", "bitand", "shiftleft", "shiftright", "bitinv",
  837. "lt", "gt", "lte", "gte", "eq", "neq", "neg", "pos", "not"
  838. \param function
  839. \return 1 on success, otherwise 0
  840. */
  841. {"typeRegisterOperator", gmRegisterTypeOperator},
  842. /*gm
  843. \function typeRegisterVariable
  844. \brief typeRegisterVariable will register a variable with a type such that (type).varname will return the variable
  845. \param int typeid
  846. \param string var name
  847. \param var
  848. \return 1 on success, otherwise 0
  849. */
  850. {"typeRegisterVariable", gmRegisterTypeVariable},
  851. /*gm
  852. \function sysCollectGarbage
  853. \brief sysCollectGarbage will run the garbage collector iff the current mem used is over the desired mem used
  854. \param forceFullCollect (false) Optionally perform full garbage collection immediately if garbage collection is not disabled.
  855. \return 1 if the gc was run, 0 otherwise
  856. */
  857. {"sysCollectGarbage", gmCollectGarbage},
  858. /*gm
  859. \function sysGetMemoryUsage
  860. \brief sysGetMemoryUsage will return the current memory used in bytes
  861. \return int memory usage
  862. */
  863. {"sysGetMemoryUsage", gmGetCurrentMemoryUsage},
  864. /*gm
  865. \function sysSetDesiredMemoryUsageHard
  866. \brief sysSetDesiredMemoryUsageHard will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run.
  867. \param int desired mem usage in bytes
  868. */
  869. {"sysSetDesiredMemoryUsageHard", gmSetDesiredMemoryUsageHard},
  870. /*gm
  871. \function sysSetDesiredMemoryUsageSoft
  872. \brief sysSetDesiredMemoryUsageSoft will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run.
  873. \param int desired mem usage in bytes
  874. */
  875. {"sysSetDesiredMemoryUsageSoft", gmSetDesiredMemoryUsageSoft},
  876. /*gm
  877. \function sysGetDesiredMemoryUsageHard
  878. \brief sysGetDesiredMemoryUsageHard will get the desired memory useage in bytes.
  879. Note that this value is used to start garbage collection, it is not a strict limit.
  880. \return int Desired memory usage in bytes.
  881. */
  882. {"sysGetDesiredMemoryUsageHard", gmGetDesiredMemoryUsageHard},
  883. /*gm
  884. \function sysGetDesiredMemoryUsageSoft
  885. \brief sysGetDesiredMemoryUsageSoft will get the desired memory useage in bytes.
  886. Note that this value is used to start garbage collection, it is not a strict limit.
  887. \return int Desired memory usage in bytes.
  888. */
  889. {"sysGetDesiredMemoryUsageSoft", gmGetDesiredMemoryUsageSoft},
  890. /*gm
  891. \function sysSetDesiredMemoryUsageAuto
  892. \brief sysSetDesiredMemoryUsageAuto will enable auto adjustment of the memory limit(s) for subsequent garbage collections.
  893. \param int enable or disable flag
  894. */
  895. {"sysSetDesiredMemoryUsageAuto", gmSetDesiredMemoryUsageAuto},
  896. /*gm
  897. \function sysGetStatsGCNumFullCollects
  898. \brief sysGetStatsGCNumFullCollects Return the number of times full garbage collection has occured.
  899. \return int Number of times full collect has occured.
  900. */
  901. {"sysGetStatsGCNumFullCollects", gmSysGetStatsGCNumFullCollects},
  902. /*gm
  903. \function sysGetStatsGCNumIncCollects
  904. \brief sysGetStatsGCNumIncCollects Return the number of times incremental garbage collection has occured.
  905. This number may increase in twos as the GC has multiple phases which appear as restarts.
  906. \return int Number of times incremental collect has occured.
  907. */
  908. {"sysGetStatsGCNumIncCollects", gmSysGetStatsGCNumIncCollects},
  909. /*gm
  910. \function sysGetStatsGCNumWarnings
  911. \brief sysGetStatsGCNumWarnings Return the number of warnings because the GC or VM thought the GC was poorly configured.
  912. If this number is large and growing rapidly, the GC soft and hard limits need to be configured better.
  913. Do not be concerned if this number grows slowly.
  914. \return int Number of warnings garbage collect has generated.
  915. */
  916. {"sysGetStatsGCNumWarnings", gmSysGetStatsGCNumWarnings},
  917. /*gm
  918. \function sysIsGCRunning
  919. \brief Returns true if GC is running a cycle.
  920. */
  921. {"sysIsGCRunning", gmSysIsGCRunning},
  922. /*gm
  923. \function sysTime
  924. \brief sysTime will return the machine time in milli seconds
  925. \return int
  926. */
  927. {"sysTime", gmMachineTime},
  928. /*gm
  929. \function doString
  930. \brief doString will execute the passed gm script
  931. \param string script
  932. \param int optional (1) set as true and the string will execute before returning to this thread
  933. \param ref optional (null) set 'this'
  934. \return int thread id of thread created for string execution
  935. */
  936. {"doString", gmDoString},
  937. /*gm
  938. \function globals
  939. \brief globals will return the globals table
  940. \return table containing all global variables
  941. */
  942. {"globals", gmGlobals},
  943. /*gm
  944. \function threadTime
  945. \brief threadTime will return the thread execution time in milliseconds
  946. \return int
  947. */
  948. {"threadTime", gmThreadTime},
  949. /*gm
  950. \function threadId
  951. \brief threadId will return the thread id of the current executing script
  952. \return int
  953. */
  954. {"threadId", gmThreadId},
  955. /*gm
  956. \function threadAllIds
  957. \brief threadIds returns a table of thread Ids
  958. \return table of thread Ids
  959. */
  960. {"threadAllIds", gmThreadAllIds},
  961. /*gm
  962. \function threadKill
  963. \brief threadKill will kill the thread with the given id
  964. \param int threadId optional (0) will kill this thread
  965. */
  966. {"threadKill", gmKillThread},
  967. /*gm
  968. \function threadKillAll
  969. \brief threadKillAll will kill all the threads except the current one
  970. \param bool optional (false) will kill this thread if true
  971. */
  972. {"threadKillAll", gmKillAllThreads},
  973. /*gm
  974. \function thread
  975. \brief thread will start a new thread
  976. \param function entry point of the thread
  977. \param ... parameters to pass to the entry function
  978. \return int threadid
  979. */
  980. {"thread", gmfThread},
  981. /*gm
  982. \function yield
  983. \brief yield will hand execution control to the next thread
  984. */
  985. {"yield", gmYield},
  986. /*gm
  987. \function exit
  988. \brief exit will kill this thread
  989. */
  990. {"exit", gmExit},
  991. /*gm
  992. \function assert
  993. \brief assert
  994. \param int expression if true, will do nothing, if false, will cause an exception
  995. */
  996. {"assert", gmAssert},
  997. /*gm
  998. \function sleep
  999. \brief sleep will sleep this thread for the given number of seconds
  1000. \param int\float seconds
  1001. */
  1002. {"sleep", gmSleep},
  1003. /*gm
  1004. \function signal
  1005. \brief signal will signal the given variable, this will unblock dest threads that are blocked on the same variable.
  1006. \param var
  1007. \param int destThreadId optional (0) 0 will signal all threads
  1008. */
  1009. {"signal", gmSignal},
  1010. /*gm
  1011. \function block
  1012. \brief block will block on all passed vars, execution will halt until another thread signals one of the block variables. Will yield on null and return null.
  1013. \param ... vars
  1014. \return the unblocking var
  1015. */
  1016. {"block", gmBlock},
  1017. #if GM_USE_ENDON
  1018. /*gm
  1019. \function endon
  1020. \brief endon will kill the thread when a signal is sent from those matching the list
  1021. \param ... vars
  1022. \return the killing var
  1023. */
  1024. {"endon", gmEndOn},
  1025. #endif //GM_USE_ENDON
  1026. /*gm
  1027. \function stateSet
  1028. \brief stateSet will collapse the stack to nothing, and push the passed functions.
  1029. \param function new state function to execute
  1030. \param ... params for new state function
  1031. */
  1032. {"stateSet", gmSetState},
  1033. /*gm
  1034. \function stateSetOnThread
  1035. \brief stateSetOnThread will collapse the stack of the given thread id to nothing, and push the passed functions.
  1036. \param int thread id
  1037. \param function new state function to execute
  1038. \param ... params for new state function
  1039. */
  1040. {"stateSetOnThread", gmSetStateOnThread},
  1041. /*gm
  1042. \function stateGet
  1043. \brief stateGet will return the function on the bottom of this threads execution stack iff it was pushed using stateSet
  1044. \param a_threadId Optional Id of thread to get state on.
  1045. \reutrn function \ null
  1046. */
  1047. {"stateGet", gmGetState},
  1048. /*gm
  1049. \function stateGetLast
  1050. \brief stateGetLast will return the last state function of this thread
  1051. \param a_threadId Optional Id of thread to get last state on.
  1052. \reutrn function \ null
  1053. */
  1054. {"stateGetLast", gmGetLastState},
  1055. /*gm
  1056. \function stateSetExitFunction
  1057. \brief stateSetExitFunction will set an exit function for this state, that will be called with no parameters if this thread
  1058. switches state
  1059. \param function
  1060. */
  1061. {"stateSetExitFunction", gmSetExitState},
  1062. /*gm
  1063. \function tableCount
  1064. \brief tableCount will return the number of elements in a table object
  1065. \param table
  1066. \return int
  1067. */
  1068. {"tableCount", gmTableCount},
  1069. /*gm
  1070. \function tableDuplicate
  1071. \brief tableDuplicate will duplicate the passed table object
  1072. \param table
  1073. \return table
  1074. */
  1075. {"tableDuplicate", gmTableDuplicate},
  1076. /*gm
  1077. \function print
  1078. \brief print will print the given vars to the print handler. passed strings are concatinated together with a seperating space.
  1079. \param ... strings
  1080. */
  1081. {"print", gmPrint},
  1082. /*gm
  1083. \function format
  1084. \brief format (like sprintf, but returns a string) %d, %s, %f, %c, %b, %x, %e
  1085. \param string
  1086. */
  1087. {"format", gmfFormat},
  1088. };
  1089. void gmMachineLib(gmMachine * a_machine)
  1090. {
  1091. // create the state type
  1092. s_gmStateUserType = a_machine->CreateUserType("gmState");
  1093. #if GM_USE_INCGC
  1094. a_machine->RegisterUserCallbacks(s_gmStateUserType, gmGCTraceStateUserType, gmGCDestructStateUserType);
  1095. #else //GM_USE_INCGC
  1096. a_machine->RegisterUserCallbacks(s_gmStateUserType, gmMarkStateUserType, gmGCStateUserType);
  1097. #endif //GM_USE_INCGC
  1098. // default lib
  1099. a_machine->RegisterLibrary(s_binding, sizeof(s_binding) / sizeof(gmFunctionEntry), NULL);
  1100. }