/LuaCocoa/LuaSelectorBridge.m

https://bitbucket.org/ewing/luacocoa/ · Objective C · 272 lines · 182 code · 44 blank · 46 comment · 23 complexity · c7d31ea8e83f6e08616d657b63f741bb MD5 · raw file

  1. //
  2. // LuaSelectorBridge.m
  3. // LuaCocoa
  4. //
  5. // Created by Eric Wing on 11/11/09.
  6. // Copyright 2009 PlayControl Software, LLC. All rights reserved.
  7. //
  8. #import <Foundation/Foundation.h>
  9. #import "LuaSelectorBridge.h"
  10. #include "LuaCocoaWeakTable.h"
  11. #include "lua.h"
  12. #include "lauxlib.h"
  13. #import "LuaObjectBridge.h"
  14. const char* LUACOCOA_SELECTOR_METATABLE_ID = "LuaCocoa.Selector";
  15. // returns pointer if a LuaCocoa.Selector, null otherwise
  16. static LuaUserDataContainerForSelector* LuaSelectorBridge_LuaIsSelector(lua_State *L, int stack_index)
  17. {
  18. void *p = lua_touserdata(L, stack_index);
  19. if (p != NULL) { /* value is a userdata? */
  20. if (lua_getmetatable(L, stack_index))
  21. { /* does it have a metatable? */
  22. lua_getfield(L, LUA_REGISTRYINDEX, LUACOCOA_SELECTOR_METATABLE_ID); /* get correct metatable */
  23. if (lua_rawequal(L, -1, -2))
  24. { /* does it have the correct mt? */
  25. lua_pop(L, 2); /* remove both metatables */
  26. return p;
  27. }
  28. else
  29. {
  30. lua_pop(L, 1); /* pop getfield */
  31. }
  32. }
  33. lua_pop(L, 1); /* pop metatable */
  34. }
  35. return NULL; /* to avoid warnings */
  36. }
  37. // returns pointer if LuaCocoa.Object, lua_error otherwise
  38. static LuaUserDataContainerForSelector* LuaSelectorBridge_LuaCheckSelectorContainer(lua_State* lua_state, int stack_index)
  39. {
  40. return luaL_checkudata(lua_state, stack_index, LUACOCOA_SELECTOR_METATABLE_ID);
  41. }
  42. bool LuaSelectorBridge_isselector(lua_State* lua_state, int stack_index)
  43. {
  44. // Check if boxed NSValue
  45. if(LuaObjectBridge_isidinstance(lua_state, stack_index))
  46. {
  47. id the_object = LuaObjectBridge_toid(lua_state, stack_index);
  48. if([the_object isKindOfClass:[NSValue class]])
  49. {
  50. if(!strcmp([the_object objCType], @encode(SEL)))
  51. {
  52. return true;
  53. }
  54. else
  55. {
  56. return false;
  57. }
  58. }
  59. else
  60. {
  61. return false;
  62. }
  63. }
  64. LuaUserDataContainerForSelector* the_container = LuaSelectorBridge_LuaIsSelector(lua_state, stack_index);
  65. if(NULL == the_container)
  66. {
  67. return false;
  68. }
  69. else
  70. {
  71. return true;
  72. }
  73. }
  74. SEL LuaSelectorBridge_checkselector(lua_State* lua_state, int stack_index)
  75. {
  76. // Will handle both string and NSString
  77. if(LuaObjectBridge_isnsstring(lua_state, stack_index))
  78. {
  79. return NSSelectorFromString(LuaObjectBridge_tonsstring(lua_state, stack_index));
  80. }
  81. // Check if boxed NSValue
  82. if(LuaObjectBridge_isidinstance(lua_state, stack_index))
  83. {
  84. id the_object = LuaObjectBridge_toid(lua_state, stack_index);
  85. if([the_object isKindOfClass:[NSValue class]])
  86. {
  87. if(!strcmp([the_object objCType], @encode(SEL)))
  88. {
  89. SEL return_selector;
  90. [the_object getValue:&return_selector];
  91. return return_selector;
  92. }
  93. else
  94. {
  95. luaL_error(lua_state, "Not an selector type in NSValue");
  96. }
  97. }
  98. else
  99. {
  100. luaL_error(lua_state, "Not an instance type");
  101. }
  102. }
  103. LuaUserDataContainerForSelector* the_container = LuaSelectorBridge_LuaCheckSelectorContainer(lua_state, stack_index);
  104. return the_container->theSelector;
  105. }
  106. // Warning: will convert strings and nsstrings and NSValue's with correct encoding to selectors
  107. SEL LuaSelectorBridge_toselector(lua_State* lua_state, int stack_index)
  108. {
  109. // Will handle both string and NSString
  110. if(LuaObjectBridge_isnsstring(lua_state, stack_index))
  111. {
  112. return NSSelectorFromString(LuaObjectBridge_tonsstring(lua_state, stack_index));
  113. }
  114. // Check if boxed NSValue
  115. if(LuaObjectBridge_isidinstance(lua_state, stack_index))
  116. {
  117. id the_object = LuaObjectBridge_toid(lua_state, stack_index);
  118. if([the_object isKindOfClass:[NSValue class]])
  119. {
  120. if(!strcmp([the_object objCType], @encode(SEL)))
  121. {
  122. SEL return_selector;
  123. [the_object getValue:&return_selector];
  124. return return_selector;
  125. }
  126. else
  127. {
  128. return NULL;
  129. }
  130. }
  131. else
  132. {
  133. return NULL;
  134. }
  135. }
  136. LuaUserDataContainerForSelector* the_container = LuaSelectorBridge_LuaIsSelector(lua_state, stack_index);
  137. if(NULL == the_container)
  138. {
  139. return NULL;
  140. }
  141. return the_container->theSelector;
  142. }
  143. // Will push an existing lua container userdata onto the stack for the associated object,
  144. // or will create a new userdata container for the object if it does not exist and push it on the stack.
  145. static void LuaSelectorBridge_PushOrCreateUserData(lua_State* lua_state, SEL the_selector)
  146. {
  147. if(NULL == the_selector)
  148. {
  149. lua_pushnil(lua_state);
  150. return;
  151. }
  152. // First check to see if we already have the object in our global weak table.
  153. // This will leave the userdata or nil on top of the stack
  154. void* return_class_userdata = LuaCocoaWeakTable_GetObjectInGlobalWeakTable(lua_state, the_selector);
  155. // If it is not there, we need to create the new userdata container
  156. if(NULL == return_class_userdata)
  157. {
  158. lua_pop(lua_state, 1); // pop the nil value left from LuaCocoaWeakTable_GetObjectInGlobalWeakTable
  159. // Create the new container
  160. void* return_selector_userdata = lua_newuserdata(lua_state, sizeof(LuaUserDataContainerForSelector));
  161. LuaUserDataContainerForSelector* lua_selector_container = (LuaUserDataContainerForSelector*)return_selector_userdata;
  162. // Set the metatable identifier on our new userdata
  163. luaL_getmetatable(lua_state, LUACOCOA_SELECTOR_METATABLE_ID);
  164. lua_setmetatable(lua_state, -2);
  165. // Add the object to the container
  166. lua_selector_container->theSelector = the_selector;
  167. // finally, add this container and object to the global weak table
  168. LuaCocoaWeakTable_InsertObjectInGlobalWeakTable(lua_state, -1, the_selector);
  169. }
  170. else
  171. {
  172. // NSLog(@"PushID Found object in our weak table");
  173. }
  174. }
  175. void LuaSelectorBridge_pushselector(lua_State* lua_state, SEL the_selector)
  176. {
  177. LuaSelectorBridge_PushOrCreateUserData(lua_state, the_selector);
  178. }
  179. static int LuaSelectorBridge_ToString(lua_State* lua_state)
  180. {
  181. LuaUserDataContainerForSelector* lua_class_container = LuaSelectorBridge_LuaCheckSelectorContainer(lua_state, -1);
  182. lua_pushstring(lua_state, [NSStringFromSelector(lua_class_container->theSelector) UTF8String]);
  183. return 1;
  184. }
  185. /* FIXME: This function won't work unless I change the global weak table to reflect the new selector address.
  186. static int LuaSelectorBridge_Call(lua_State* lua_state)
  187. {
  188. LuaUserDataContainerForSelector* lua_class_container = LuaSelectorBridge_LuaCheckSelectorContainer(lua_state, -1);
  189. // TODO: Probably should check the number of arguments
  190. NSString* new_string = LuaObjectBridge_checknsstring(lua_state, -1);
  191. // This line will break things without changes to the weak table
  192. lua_class_container->theSelector = NSSelectorFromString(new_string);
  193. return 0;
  194. }
  195. */
  196. static int LuaSelectorBridge_ToSelector(lua_State* lua_state)
  197. {
  198. NSString* the_string = LuaObjectBridge_checknsstring(lua_state, -1);
  199. LuaSelectorBridge_pushselector(lua_state, NSSelectorFromString(the_string));
  200. return 1;
  201. }
  202. static const struct luaL_reg LuaSelectorBridge_MethodsForSelectorMetatable[] =
  203. {
  204. // Don't think I need much for selectors.
  205. {"__tostring", LuaSelectorBridge_ToString},
  206. // {"__call", LuaSelectorBridge_Call},
  207. // For __eq, since we use the global weak table to unique things, I think the default comparison will just work.
  208. // If not, I think I read Obj-C makes SEL's unique so it is just a simple pointer comparison there too.
  209. // {"__eq", LuaSelectorBridge_IsEqual},
  210. // I don't have to worry about memory management with selectors
  211. // {"__gc", LuaSelectorBridge_GarbageCollect},
  212. {NULL,NULL},
  213. };
  214. static const luaL_reg LuaSelectorBridge_LuaFunctions[] =
  215. {
  216. {"toselector", LuaSelectorBridge_ToSelector},
  217. {NULL,NULL},
  218. };
  219. int luaopen_LuaSelectorBridge(lua_State* lua_state)
  220. {
  221. luaL_newmetatable(lua_state, LUACOCOA_SELECTOR_METATABLE_ID);
  222. // lua_pushvalue(lua_state, -1);
  223. // lua_setfield(lua_state, -2, "__index");
  224. luaL_register(lua_state, NULL, LuaSelectorBridge_MethodsForSelectorMetatable);
  225. luaL_register(lua_state, "LuaCocoa", LuaSelectorBridge_LuaFunctions);
  226. return 1;
  227. }