ObjectTranslator.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. namespace LuaInterface
  2. {
  3. using System;
  4. using System.IO;
  5. using System.Collections;
  6. using System.Reflection;
  7. using System.Runtime.InteropServices;
  8. using System.Collections.Generic;
  9. using System.Diagnostics;
  10. /*
  11. * Passes objects from the CLR to Lua and vice-versa
  12. *
  13. * Author: Fabio Mascarenhas
  14. * Version: 1.0
  15. */
  16. public class ObjectTranslator
  17. {
  18. internal CheckType typeChecker;
  19. // object # to object (FIXME - it should be possible to get object address as an object #)
  20. public readonly Dictionary<int, object> objects = new Dictionary<int, object>();
  21. // object to object #
  22. public readonly Dictionary<object, int> objectsBackMap = new Dictionary<object, int>();
  23. internal LuaState interpreter;
  24. public MetaFunctions metaFunctions;
  25. public List<Assembly> assemblies;
  26. private LuaCSFunction registerTableFunction,unregisterTableFunction,getMethodSigFunction,
  27. getConstructorSigFunction,importTypeFunction,loadAssemblyFunction, ctypeFunction, enumFromIntFunction;
  28. internal EventHandlerContainer pendingEvents = new EventHandlerContainer();
  29. public static ObjectTranslator FromState(IntPtr luaState)
  30. {
  31. LuaDLL.lua_getglobal(luaState, "_translator");
  32. IntPtr thisptr = LuaDLL.lua_touserdata(luaState, -1);
  33. LuaDLL.lua_pop(luaState, 1);
  34. GCHandle handle = GCHandle.FromIntPtr(thisptr);
  35. ObjectTranslator translator = (ObjectTranslator)handle.Target;
  36. return translator;
  37. }
  38. public ObjectTranslator(LuaState interpreter,IntPtr luaState)
  39. {
  40. this.interpreter=interpreter;
  41. typeChecker=new CheckType(this);
  42. metaFunctions=new MetaFunctions(this);
  43. assemblies=new List<Assembly>();
  44. assemblies.Add(Assembly.GetExecutingAssembly());
  45. importTypeFunction=new LuaCSFunction(importType);
  46. loadAssemblyFunction=new LuaCSFunction(loadAssembly);
  47. registerTableFunction=new LuaCSFunction(registerTable);
  48. unregisterTableFunction=new LuaCSFunction(unregisterTable);
  49. getMethodSigFunction=new LuaCSFunction(getMethodSignature);
  50. getConstructorSigFunction=new LuaCSFunction(getConstructorSignature);
  51. ctypeFunction = new LuaCSFunction(ctype);
  52. enumFromIntFunction = new LuaCSFunction(enumFromInt);
  53. createLuaObjectList(luaState);
  54. createIndexingMetaFunction(luaState);
  55. createBaseClassMetatable(luaState);
  56. createClassMetatable(luaState);
  57. createFunctionMetatable(luaState);
  58. setGlobalFunctions(luaState);
  59. }
  60. /*
  61. * Sets up the list of objects in the Lua side
  62. */
  63. private void createLuaObjectList(IntPtr luaState)
  64. {
  65. LuaDLL.lua_pushstring(luaState,"luaNet_objects");
  66. LuaDLL.lua_newtable(luaState);
  67. LuaDLL.lua_newtable(luaState);
  68. LuaDLL.lua_pushstring(luaState,"__mode");
  69. LuaDLL.lua_pushstring(luaState,"v");
  70. LuaDLL.lua_settable(luaState,-3);
  71. LuaDLL.lua_setmetatable(luaState,-2);
  72. LuaDLL.lua_settable(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
  73. }
  74. /*
  75. * Registers the indexing function of CLR objects
  76. * passed to Lua
  77. */
  78. private void createIndexingMetaFunction(IntPtr luaState)
  79. {
  80. LuaDLL.lua_pushstring(luaState,"luaNet_indexfunction");
  81. LuaDLL.luaL_dostring(luaState,MetaFunctions.luaIndexFunction);
  82. //LuaDLL.lua_pushstdcallcfunction(luaState,indexFunction);
  83. LuaDLL.lua_rawset(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
  84. }
  85. /*
  86. * Creates the metatable for superclasses (the base
  87. * field of registered tables)
  88. */
  89. private void createBaseClassMetatable(IntPtr luaState)
  90. {
  91. LuaDLL.luaL_newmetatable(luaState,"luaNet_searchbase");
  92. LuaDLL.lua_pushstring(luaState,"__gc");
  93. LuaDLL.lua_pushstdcallcfunction(luaState, metaFunctions.gcFunction);
  94. LuaDLL.lua_settable(luaState,-3);
  95. LuaDLL.lua_pushstring(luaState,"__tostring");
  96. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction);
  97. LuaDLL.lua_settable(luaState,-3);
  98. LuaDLL.lua_pushstring(luaState,"__index");
  99. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.baseIndexFunction);
  100. LuaDLL.lua_settable(luaState,-3);
  101. LuaDLL.lua_pushstring(luaState,"__newindex");
  102. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.newindexFunction);
  103. LuaDLL.lua_settable(luaState,-3);
  104. LuaDLL.lua_settop(luaState,-2);
  105. }
  106. /*
  107. * Creates the metatable for type references
  108. */
  109. private void createClassMetatable(IntPtr luaState)
  110. {
  111. LuaDLL.luaL_newmetatable(luaState,"luaNet_class");
  112. LuaDLL.lua_pushstring(luaState,"__gc");
  113. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
  114. LuaDLL.lua_settable(luaState,-3);
  115. LuaDLL.lua_pushstring(luaState,"__tostring");
  116. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction);
  117. LuaDLL.lua_settable(luaState,-3);
  118. LuaDLL.lua_pushstring(luaState,"__index");
  119. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.classIndexFunction);
  120. LuaDLL.lua_settable(luaState,-3);
  121. LuaDLL.lua_pushstring(luaState,"__newindex");
  122. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.classNewindexFunction);
  123. LuaDLL.lua_settable(luaState,-3);
  124. LuaDLL.lua_pushstring(luaState,"__call");
  125. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.callConstructorFunction);
  126. LuaDLL.lua_settable(luaState,-3);
  127. LuaDLL.lua_settop(luaState,-2);
  128. }
  129. /*
  130. * Registers the global functions used by LuaInterface
  131. */
  132. private void setGlobalFunctions(IntPtr luaState)
  133. {
  134. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.indexFunction);
  135. LuaDLL.lua_setglobal(luaState,"get_object_member");
  136. LuaDLL.lua_pushstdcallcfunction(luaState,importTypeFunction);
  137. LuaDLL.lua_setglobal(luaState,"import_type");
  138. LuaDLL.lua_pushstdcallcfunction(luaState,loadAssemblyFunction);
  139. LuaDLL.lua_setglobal(luaState,"load_assembly");
  140. LuaDLL.lua_pushstdcallcfunction(luaState,registerTableFunction);
  141. LuaDLL.lua_setglobal(luaState,"make_object");
  142. LuaDLL.lua_pushstdcallcfunction(luaState,unregisterTableFunction);
  143. LuaDLL.lua_setglobal(luaState,"free_object");
  144. LuaDLL.lua_pushstdcallcfunction(luaState,getMethodSigFunction);
  145. LuaDLL.lua_setglobal(luaState,"get_method_bysig");
  146. LuaDLL.lua_pushstdcallcfunction(luaState,getConstructorSigFunction);
  147. LuaDLL.lua_setglobal(luaState,"get_constructor_bysig");
  148. LuaDLL.lua_pushstdcallcfunction(luaState,ctypeFunction);
  149. LuaDLL.lua_setglobal(luaState,"ctype");
  150. LuaDLL.lua_pushstdcallcfunction(luaState,enumFromIntFunction);
  151. LuaDLL.lua_setglobal(luaState,"enum");
  152. }
  153. /*
  154. * Creates the metatable for delegates
  155. */
  156. private void createFunctionMetatable(IntPtr luaState)
  157. {
  158. LuaDLL.luaL_newmetatable(luaState,"luaNet_function");
  159. LuaDLL.lua_pushstring(luaState,"__gc");
  160. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
  161. LuaDLL.lua_settable(luaState,-3);
  162. LuaDLL.lua_pushstring(luaState,"__call");
  163. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.execDelegateFunction);
  164. LuaDLL.lua_settable(luaState,-3);
  165. LuaDLL.lua_settop(luaState,-2);
  166. }
  167. /*
  168. * Passes errors (argument e) to the Lua interpreter
  169. */
  170. internal void throwError(IntPtr luaState, object e)
  171. {
  172. // We use this to remove anything pushed by luaL_where
  173. int oldTop = LuaDLL.lua_gettop(luaState);
  174. // Stack frame #1 is our C# wrapper, so not very interesting to the user
  175. // Stack frame #2 must be the lua code that called us, so that's what we want to use
  176. LuaDLL.luaL_where(luaState, 1);
  177. object[] curlev = popValues(luaState, oldTop);
  178. // Determine the position in the script where the exception was triggered
  179. string errLocation = "";
  180. if (curlev.Length > 0)
  181. errLocation = curlev[0].ToString();
  182. string message = e as string;
  183. if (message != null)
  184. {
  185. // Wrap Lua error (just a string) and store the error location
  186. e = new LuaScriptException(message, errLocation);
  187. }
  188. else
  189. {
  190. Exception ex = e as Exception;
  191. if (ex != null)
  192. {
  193. // Wrap generic .NET exception as an InnerException and store the error location
  194. e = new LuaScriptException(ex, errLocation);
  195. }
  196. }
  197. push(luaState, e);
  198. LuaDLL.lua_error(luaState);
  199. }
  200. /*
  201. * Implementation of load_assembly. Throws an error
  202. * if the assembly is not found.
  203. */
  204. [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
  205. public static int loadAssembly(IntPtr luaState)
  206. {
  207. ObjectTranslator translator = ObjectTranslator.FromState(luaState);
  208. try
  209. {
  210. string assemblyName=LuaDLL.lua_tostring(luaState,1);
  211. Assembly assembly = null;
  212. //assembly = Assembly.GetExecutingAssembly();
  213. try
  214. {
  215. assembly = Assembly.Load(assemblyName);
  216. }
  217. catch (BadImageFormatException)
  218. {
  219. // The assemblyName was invalid. It is most likely a path.
  220. }
  221. if (assembly == null)
  222. {
  223. assembly = Assembly.Load(AssemblyName.GetAssemblyName(assemblyName));
  224. }
  225. if (assembly != null && !translator.assemblies.Contains(assembly))
  226. {
  227. translator.assemblies.Add(assembly);
  228. }
  229. }
  230. catch(Exception e)
  231. {
  232. translator.throwError(luaState,e);
  233. }
  234. return 0;
  235. }
  236. internal Type FindType(string className)
  237. {
  238. foreach(Assembly assembly in assemblies)
  239. {
  240. Type klass=assembly.GetType(className);
  241. if(klass!=null)
  242. {
  243. return klass;
  244. }
  245. }
  246. return null;
  247. }
  248. /*
  249. * Implementation of import_type. Returns nil if the
  250. * type is not found.
  251. */
  252. [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
  253. public static int importType(IntPtr luaState)
  254. {
  255. ObjectTranslator translator = ObjectTranslator.FromState(luaState);
  256. string className=LuaDLL.lua_tostring(luaState,1);
  257. Type klass=translator.FindType(className);
  258. if(klass!=null)
  259. translator.pushType(luaState,klass);
  260. else
  261. LuaDLL.lua_pushnil(luaState);
  262. return 1;
  263. }
  264. /*
  265. * Implementation of make_object. Registers a table (first
  266. * argument in the stack) as an object subclassing the
  267. * type passed as second argument in the stack.
  268. */
  269. [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
  270. public static int registerTable(IntPtr luaState)
  271. {
  272. ObjectTranslator translator = ObjectTranslator.FromState(luaState);
  273. #if __NOGEN__
  274. translator.throwError(luaState, "Tables as Objects not implemnented");
  275. #else
  276. if(LuaDLL.lua_type(luaState,1)==LuaTypes.LUA_TTABLE)
  277. {
  278. LuaTable luaTable=translator.getTable(luaState,1);
  279. string superclassName = LuaDLL.lua_tostring(luaState, 2);
  280. if (superclassName != null)
  281. {
  282. Type klass = translator.FindType(superclassName);
  283. if (klass != null)
  284. {
  285. // Creates and pushes the object in the stack, setting
  286. // it as the metatable of the first argument
  287. object obj = CodeGeneration.Instance.GetClassInstance(klass, luaTable);
  288. translator.pushObject(luaState, obj, "luaNet_metatable");
  289. LuaDLL.lua_newtable(luaState);
  290. LuaDLL.lua_pushstring(luaState, "__index");
  291. LuaDLL.lua_pushvalue(luaState, -3);
  292. LuaDLL.lua_settable(luaState, -3);
  293. LuaDLL.lua_pushstring(luaState, "__newindex");
  294. LuaDLL.lua_pushvalue(luaState, -3);
  295. LuaDLL.lua_settable(luaState, -3);
  296. LuaDLL.lua_setmetatable(luaState, 1);
  297. // Pushes the object again, this time as the base field
  298. // of the table and with the luaNet_searchbase metatable
  299. LuaDLL.lua_pushstring(luaState, "base");
  300. int index = translator.addObject(obj);
  301. translator.pushNewObject(luaState, obj, index, "luaNet_searchbase");
  302. LuaDLL.lua_rawset(luaState, 1);
  303. }
  304. else
  305. translator.throwError(luaState, "register_table: can not find superclass '" + superclassName + "'");
  306. }
  307. else
  308. translator.throwError(luaState, "register_table: superclass name can not be null");
  309. }
  310. else translator.throwError(luaState,"register_table: first arg is not a table");
  311. #endif
  312. return 0;
  313. }
  314. /*
  315. * Implementation of free_object. Clears the metatable and the
  316. * base field, freeing the created object for garbage-collection
  317. */
  318. [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
  319. public static int unregisterTable(IntPtr luaState)
  320. {
  321. ObjectTranslator translator = ObjectTranslator.FromState(luaState);
  322. try
  323. {
  324. if(LuaDLL.lua_getmetatable(luaState,1)!=0)
  325. {
  326. LuaDLL.lua_pushstring(luaState,"__index");
  327. LuaDLL.lua_gettable(luaState,-2);
  328. object obj=translator.getRawNetObject(luaState,-1);
  329. if(obj==null) translator.throwError(luaState,"unregister_table: arg is not valid table");
  330. FieldInfo luaTableField=obj.GetType().GetField("__luaInterface_luaTable");
  331. if(luaTableField==null) translator.throwError(luaState,"unregister_table: arg is not valid table");
  332. luaTableField.SetValue(obj,null);
  333. LuaDLL.lua_pushnil(luaState);
  334. LuaDLL.lua_setmetatable(luaState,1);
  335. LuaDLL.lua_pushstring(luaState,"base");
  336. LuaDLL.lua_pushnil(luaState);
  337. LuaDLL.lua_settable(luaState,1);
  338. }
  339. else translator.throwError(luaState,"unregister_table: arg is not valid table");
  340. }
  341. catch(Exception e)
  342. {
  343. translator.throwError(luaState,e.Message);
  344. }
  345. return 0;
  346. }
  347. /*
  348. * Implementation of get_method_bysig. Returns nil
  349. * if no matching method is not found.
  350. */
  351. [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
  352. public static int getMethodSignature(IntPtr luaState)
  353. {
  354. ObjectTranslator translator = ObjectTranslator.FromState(luaState);
  355. IReflect klass; object target;
  356. int udata=LuaDLL.luanet_checkudata(luaState,1,"luaNet_class");
  357. if(udata!=-1)
  358. {
  359. klass=(IReflect)translator.objects[udata];
  360. target=null;
  361. }
  362. else
  363. {
  364. target=translator.getRawNetObject(luaState,1);
  365. if(target==null)
  366. {
  367. translator.throwError(luaState,"get_method_bysig: first arg is not type or object reference");
  368. LuaDLL.lua_pushnil(luaState);
  369. return 1;
  370. }
  371. klass=target.GetType();
  372. }
  373. string methodName=LuaDLL.lua_tostring(luaState,2);
  374. Type[] signature=new Type[LuaDLL.lua_gettop(luaState)-2];
  375. for(int i=0;i<signature.Length;i++)
  376. signature[i]=translator.FindType(LuaDLL.lua_tostring(luaState,i+3));
  377. try
  378. {
  379. //CP: Added ignore case
  380. MethodInfo method=klass.GetMethod(methodName,BindingFlags.Public | BindingFlags.Static |
  381. BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase, null, signature, null);
  382. translator.pushFunction(luaState,new LuaCSFunction((new LuaMethodWrapper(translator,target,klass,method)).call));
  383. }
  384. catch(Exception e)
  385. {
  386. translator.throwError(luaState,e);
  387. LuaDLL.lua_pushnil(luaState);
  388. }
  389. return 1;
  390. }
  391. /*
  392. * Implementation of get_constructor_bysig. Returns nil
  393. * if no matching constructor is found.
  394. */
  395. [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
  396. public static int getConstructorSignature(IntPtr luaState)
  397. {
  398. ObjectTranslator translator = ObjectTranslator.FromState(luaState);
  399. IReflect klass=null;
  400. int udata=LuaDLL.luanet_checkudata(luaState,1,"luaNet_class");
  401. if(udata!=-1)
  402. {
  403. klass=(IReflect)translator.objects[udata];
  404. }
  405. if(klass==null)
  406. {
  407. translator.throwError(luaState,"get_constructor_bysig: first arg is invalid type reference");
  408. }
  409. Type[] signature=new Type[LuaDLL.lua_gettop(luaState)-1];
  410. for(int i=0;i<signature.Length;i++)
  411. signature[i]=translator.FindType(LuaDLL.lua_tostring(luaState,i+2));
  412. try
  413. {
  414. ConstructorInfo constructor=klass.UnderlyingSystemType.GetConstructor(signature);
  415. translator.pushFunction(luaState,new LuaCSFunction((new LuaMethodWrapper(translator,null,klass,constructor)).call));
  416. }
  417. catch(Exception e)
  418. {
  419. translator.throwError(luaState,e);
  420. LuaDLL.lua_pushnil(luaState);
  421. }
  422. return 1;
  423. }
  424. private Type typeOf(IntPtr luaState, int idx)
  425. {
  426. int udata=LuaDLL.luanet_checkudata(luaState,1,"luaNet_class");
  427. if (udata == -1) {
  428. return null;
  429. } else {
  430. ProxyType pt = (ProxyType)objects[udata];
  431. return pt.UnderlyingSystemType;
  432. }
  433. }
  434. public int pushError(IntPtr luaState, string msg)
  435. {
  436. LuaDLL.lua_pushnil(luaState);
  437. LuaDLL.lua_pushstring(luaState,msg);
  438. return 2;
  439. }
  440. [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
  441. public static int ctype(IntPtr luaState)
  442. {
  443. ObjectTranslator translator = ObjectTranslator.FromState(luaState);
  444. Type t = translator.typeOf(luaState,1);
  445. if (t == null) {
  446. return translator.pushError(luaState,"not a CLR class");
  447. }
  448. translator.pushObject(luaState,t,"luaNet_metatable");
  449. return 1;
  450. }
  451. [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
  452. public static int enumFromInt(IntPtr luaState)
  453. {
  454. ObjectTranslator translator = ObjectTranslator.FromState(luaState);
  455. Type t = translator.typeOf(luaState,1);
  456. if (t == null || ! t.IsEnum) {
  457. return translator.pushError(luaState,"not an enum");
  458. }
  459. object res = null;
  460. LuaTypes lt = LuaDLL.lua_type(luaState,2);
  461. if (lt == LuaTypes.LUA_TNUMBER) {
  462. int ival = (int)LuaDLL.lua_tonumber(luaState,2);
  463. res = Enum.ToObject(t,ival);
  464. } else
  465. if (lt == LuaTypes.LUA_TSTRING) {
  466. string sflags = LuaDLL.lua_tostring(luaState,2);
  467. string err = null;
  468. try {
  469. res = Enum.Parse(t,sflags);
  470. } catch (ArgumentException e) {
  471. err = e.Message;
  472. }
  473. if (err != null) {
  474. return translator.pushError(luaState,err);
  475. }
  476. } else {
  477. return translator.pushError(luaState,"second argument must be a integer or a string");
  478. }
  479. translator.pushObject(luaState,res,"luaNet_metatable");
  480. return 1;
  481. }
  482. /*
  483. * Pushes a type reference into the stack
  484. */
  485. internal void pushType(IntPtr luaState, Type t)
  486. {
  487. pushObject(luaState,new ProxyType(t),"luaNet_class");
  488. }
  489. /*
  490. * Pushes a delegate into the stack
  491. */
  492. internal void pushFunction(IntPtr luaState, LuaCSFunction func)
  493. {
  494. pushObject(luaState,func,"luaNet_function");
  495. }
  496. /*
  497. * Pushes a CLR object into the Lua stack as an userdata
  498. * with the provided metatable
  499. */
  500. internal void pushObject(IntPtr luaState, object o, string metatable)
  501. {
  502. int index = -1;
  503. // Pushes nil
  504. if(o==null)
  505. {
  506. LuaDLL.lua_pushnil(luaState);
  507. return;
  508. }
  509. // Object already in the list of Lua objects? Push the stored reference.
  510. bool found = objectsBackMap.TryGetValue(o, out index);
  511. if(found)
  512. {
  513. LuaDLL.luaL_getmetatable(luaState,"luaNet_objects");
  514. LuaDLL.lua_rawgeti(luaState,-1,index);
  515. // Note: starting with lua5.1 the garbage collector may remove weak reference items (such as our luaNet_objects values) when the initial GC sweep
  516. // occurs, but the actual call of the __gc finalizer for that object may not happen until a little while later. During that window we might call
  517. // this routine and find the element missing from luaNet_objects, but collectObject() has not yet been called. In that case, we go ahead and call collect
  518. // object here
  519. // did we find a non nil object in our table? if not, we need to call collect object
  520. LuaTypes type = LuaDLL.lua_type(luaState, -1);
  521. if (type != LuaTypes.LUA_TNIL)
  522. {
  523. LuaDLL.lua_remove(luaState, -2); // drop the metatable - we're going to leave our object on the stack
  524. return;
  525. }
  526. // MetaFunctions.dumpStack(this, luaState);
  527. LuaDLL.lua_remove(luaState, -1); // remove the nil object value
  528. LuaDLL.lua_remove(luaState, -1); // remove the metatable
  529. collectObject(o, index); // Remove from both our tables and fall out to get a new ID
  530. }
  531. index = addObject(o);
  532. pushNewObject(luaState,o,index,metatable);
  533. }
  534. /*
  535. * Pushes a new object into the Lua stack with the provided
  536. * metatable
  537. */
  538. private void pushNewObject(IntPtr luaState,object o,int index,string metatable)
  539. {
  540. if(metatable=="luaNet_metatable")
  541. {
  542. // Gets or creates the metatable for the object's type
  543. LuaDLL.luaL_getmetatable(luaState,o.GetType().AssemblyQualifiedName);
  544. if(LuaDLL.lua_isnil(luaState,-1))
  545. {
  546. LuaDLL.lua_settop(luaState,-2);
  547. LuaDLL.luaL_newmetatable(luaState,o.GetType().AssemblyQualifiedName);
  548. LuaDLL.lua_pushstring(luaState,"cache");
  549. LuaDLL.lua_newtable(luaState);
  550. LuaDLL.lua_rawset(luaState,-3);
  551. LuaDLL.lua_pushlightuserdata(luaState,LuaDLL.luanet_gettag());
  552. LuaDLL.lua_pushnumber(luaState,1);
  553. LuaDLL.lua_rawset(luaState,-3);
  554. LuaDLL.lua_pushstring(luaState,"__index");
  555. LuaDLL.lua_pushstring(luaState,"luaNet_indexfunction");
  556. LuaDLL.lua_rawget(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
  557. LuaDLL.lua_rawset(luaState,-3);
  558. LuaDLL.lua_pushstring(luaState,"__gc");
  559. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
  560. LuaDLL.lua_rawset(luaState,-3);
  561. LuaDLL.lua_pushstring(luaState,"__tostring");
  562. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction);
  563. LuaDLL.lua_rawset(luaState,-3);
  564. LuaDLL.lua_pushstring(luaState,"__newindex");
  565. LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.newindexFunction);
  566. LuaDLL.lua_rawset(luaState,-3);
  567. }
  568. }
  569. else
  570. {
  571. LuaDLL.luaL_getmetatable(luaState,metatable);
  572. }
  573. // Stores the object index in the Lua list and pushes the
  574. // index into the Lua stack
  575. LuaDLL.luaL_getmetatable(luaState,"luaNet_objects");
  576. LuaDLL.luanet_newudata(luaState,index);
  577. LuaDLL.lua_pushvalue(luaState,-3);
  578. LuaDLL.lua_remove(luaState,-4);
  579. LuaDLL.lua_setmetatable(luaState,-2);
  580. LuaDLL.lua_pushvalue(luaState,-1);
  581. LuaDLL.lua_rawseti(luaState,-3,index);
  582. LuaDLL.lua_remove(luaState,-2);
  583. }
  584. /*
  585. * Gets an object from the Lua stack with the desired type, if it matches, otherwise
  586. * returns null.
  587. */
  588. internal object getAsType(IntPtr luaState,int stackPos,Type paramType)
  589. {
  590. ExtractValue extractor=typeChecker.checkType(luaState,stackPos,paramType);
  591. if(extractor!=null) return extractor(luaState,stackPos);
  592. return null;
  593. }
  594. /// <summary>
  595. /// Given the Lua int ID for an object remove it from our maps
  596. /// </summary>
  597. /// <param name="udata"></param>
  598. internal void collectObject(int udata)
  599. {
  600. object o;
  601. bool found = objects.TryGetValue(udata, out o);
  602. // The other variant of collectObject might have gotten here first, in that case we will silently ignore the missing entry
  603. if (found)
  604. {
  605. // Debug.WriteLine("Removing " + o.ToString() + " @ " + udata);
  606. objects.Remove(udata);
  607. objectsBackMap.Remove(o);
  608. }
  609. }
  610. /// <summary>
  611. /// Given an object reference, remove it from our maps
  612. /// </summary>
  613. /// <param name="udata"></param>
  614. void collectObject(object o, int udata)
  615. {
  616. // Debug.WriteLine("Removing " + o.ToString() + " @ " + udata);
  617. objects.Remove(udata);
  618. objectsBackMap.Remove(o);
  619. }
  620. /// <summary>
  621. /// We want to ensure that objects always have a unique ID
  622. /// </summary>
  623. int nextObj = 0;
  624. int addObject(object obj)
  625. {
  626. // New object: inserts it in the list
  627. int index = nextObj++;
  628. // Debug.WriteLine("Adding " + obj.ToString() + " @ " + index);
  629. objects[index] = obj;
  630. objectsBackMap[obj] = index;
  631. return index;
  632. }
  633. /*
  634. * Gets an object from the Lua stack according to its Lua type.
  635. */
  636. internal object getObject(IntPtr luaState,int index)
  637. {
  638. LuaTypes type=LuaDLL.lua_type(luaState,index);
  639. switch(type)
  640. {
  641. case LuaTypes.LUA_TNUMBER:
  642. {
  643. return LuaDLL.lua_tonumber(luaState,index);
  644. }
  645. case LuaTypes.LUA_TSTRING:
  646. {
  647. return LuaDLL.lua_tostring(luaState,index);
  648. }
  649. case LuaTypes.LUA_TBOOLEAN:
  650. {
  651. return LuaDLL.lua_toboolean(luaState,index);
  652. }
  653. case LuaTypes.LUA_TTABLE:
  654. {
  655. return getTable(luaState,index);
  656. }
  657. case LuaTypes.LUA_TFUNCTION:
  658. {
  659. return getFunction(luaState,index);
  660. }
  661. case LuaTypes.LUA_TUSERDATA:
  662. {
  663. int udata=LuaDLL.luanet_tonetobject(luaState,index);
  664. if(udata!=-1)
  665. return objects[udata];
  666. else
  667. return getUserData(luaState,index);
  668. }
  669. default:
  670. return null;
  671. }
  672. }
  673. /*
  674. * Gets the table in the index positon of the Lua stack.
  675. */
  676. internal LuaTable getTable(IntPtr luaState,int index)
  677. {
  678. LuaDLL.lua_pushvalue(luaState,index);
  679. return new LuaTable(LuaDLL.luaL_ref(luaState,LuaIndexes.LUA_REGISTRYINDEX),interpreter);
  680. }
  681. /*
  682. * Gets the userdata in the index positon of the Lua stack.
  683. */
  684. internal LuaUserData getUserData(IntPtr luaState,int index)
  685. {
  686. LuaDLL.lua_pushvalue(luaState,index);
  687. return new LuaUserData(LuaDLL.luaL_ref(luaState,LuaIndexes.LUA_REGISTRYINDEX),interpreter);
  688. }
  689. /*
  690. * Gets the function in the index positon of the Lua stack.
  691. */
  692. public LuaFunction getFunction(IntPtr luaState,int index)
  693. {
  694. LuaDLL.lua_pushvalue(luaState,index);
  695. return new LuaFunction(LuaDLL.luaL_ref(luaState,LuaIndexes.LUA_REGISTRYINDEX),interpreter);
  696. }
  697. /*
  698. * Gets the CLR object in the index positon of the Lua stack. Returns
  699. * delegates as Lua functions.
  700. */
  701. internal object getNetObject(IntPtr luaState,int index)
  702. {
  703. int idx=LuaDLL.luanet_tonetobject(luaState,index);
  704. if(idx!=-1)
  705. return objects[idx];
  706. else
  707. return null;
  708. }
  709. /*
  710. * Gets the CLR object in the index positon of the Lua stack. Returns
  711. * delegates as is.
  712. */
  713. internal object getRawNetObject(IntPtr luaState,int index)
  714. {
  715. int udata=LuaDLL.luanet_rawnetobj(luaState,index);
  716. if(udata!=-1)
  717. {
  718. return objects[udata];
  719. }
  720. return null;
  721. }
  722. /*
  723. * Pushes the entire array into the Lua stack and returns the number
  724. * of elements pushed.
  725. */
  726. internal int returnValues(IntPtr luaState, object[] returnValues)
  727. {
  728. if(LuaDLL.lua_checkstack(luaState,returnValues.Length+5))
  729. {
  730. for(int i=0;i<returnValues.Length;i++)
  731. {
  732. push(luaState,returnValues[i]);
  733. }
  734. return returnValues.Length;
  735. } else
  736. return 0;
  737. }
  738. /*
  739. * Gets the values from the provided index to
  740. * the top of the stack and returns them in an array.
  741. */
  742. internal object[] popValues(IntPtr luaState,int oldTop)
  743. {
  744. int newTop=LuaDLL.lua_gettop(luaState);
  745. if(oldTop==newTop)
  746. {
  747. return null;
  748. }
  749. else
  750. {
  751. ArrayList returnValues=new ArrayList();
  752. for(int i=oldTop+1;i<=newTop;i++)
  753. {
  754. returnValues.Add(getObject(luaState,i));
  755. }
  756. LuaDLL.lua_settop(luaState,oldTop);
  757. return returnValues.ToArray();
  758. }
  759. }
  760. /*
  761. * Gets the values from the provided index to
  762. * the top of the stack and returns them in an array, casting
  763. * them to the provided types.
  764. */
  765. internal object[] popValues(IntPtr luaState,int oldTop,Type[] popTypes)
  766. {
  767. int newTop=LuaDLL.lua_gettop(luaState);
  768. if(oldTop==newTop)
  769. {
  770. return null;
  771. }
  772. else
  773. {
  774. int iTypes;
  775. ArrayList returnValues=new ArrayList();
  776. if(popTypes[0] == typeof(void))
  777. iTypes=1;
  778. else
  779. iTypes=0;
  780. for(int i=oldTop+1;i<=newTop;i++)
  781. {
  782. returnValues.Add(getAsType(luaState,i,popTypes[iTypes]));
  783. iTypes++;
  784. }
  785. LuaDLL.lua_settop(luaState,oldTop);
  786. return returnValues.ToArray();
  787. }
  788. }
  789. // kevinh - the following line doesn't work for remoting proxies - they always return a match for 'is'
  790. // else if(o is ILuaGeneratedType)
  791. static bool IsILua(object o)
  792. {
  793. #if ! __NOGEN__
  794. if(o is ILuaGeneratedType)
  795. {
  796. // Make sure we are _really_ ILuaGenerated
  797. Type typ = o.GetType();
  798. return (typ.GetInterface("ILuaGeneratedType") != null);
  799. }
  800. else
  801. #endif
  802. return false;
  803. }
  804. /*
  805. * Pushes the object into the Lua stack according to its type.
  806. */
  807. internal void push(IntPtr luaState, object o)
  808. {
  809. if((object)o==(object)null)
  810. {
  811. LuaDLL.lua_pushnil(luaState);
  812. }
  813. else if(o is sbyte || o is byte || o is short || o is ushort ||
  814. o is int || o is uint || o is long || o is float ||
  815. o is ulong || o is decimal || o is double)
  816. {
  817. double d=Convert.ToDouble(o);
  818. LuaDLL.lua_pushnumber(luaState,d);
  819. }
  820. else if(o is char)
  821. {
  822. double d = (char)o;
  823. LuaDLL.lua_pushnumber(luaState,d);
  824. }
  825. else if(o is string)
  826. {
  827. string str=(string)o;
  828. LuaDLL.lua_pushstring(luaState,str);
  829. }
  830. else if(o is bool)
  831. {
  832. bool b=(bool)o;
  833. LuaDLL.lua_pushboolean(luaState,b);
  834. }
  835. else if(IsILua(o))
  836. {
  837. #if ! __NOGEN__
  838. (((ILuaGeneratedType)o).__luaInterface_getLuaTable()).push(luaState);
  839. #endif
  840. }
  841. else if(o is LuaTable)
  842. {
  843. ((LuaTable)o).push(luaState);
  844. }
  845. else if(o is LuaCSFunction)
  846. {
  847. pushFunction(luaState,(LuaCSFunction)o);
  848. }
  849. else if(o is LuaFunction)
  850. {
  851. ((LuaFunction)o).push(luaState);
  852. }
  853. else
  854. {
  855. pushObject(luaState,o,"luaNet_metatable");
  856. }
  857. }
  858. /*
  859. * Checks if the method matches the arguments in the Lua stack, getting
  860. * the arguments if it does.
  861. */
  862. internal bool matchParameters(IntPtr luaState,MethodBase method,ref MethodCache methodCache)
  863. {
  864. return metaFunctions.matchParameters(luaState,method,ref methodCache);
  865. }
  866. internal Array tableToArray(object luaParamValue, Type paramArrayType) {
  867. return metaFunctions.TableToArray(luaParamValue,paramArrayType);
  868. }
  869. }
  870. }