/js/src/ctypes/Library.cpp

http://github.com/zpao/v8monkey · C++ · 382 lines · 246 code · 57 blank · 79 comment · 51 complexity · aa102e7b81cc261ce737fc81b942f8b5 MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * The Original Code is js-ctypes.
  16. *
  17. * The Initial Developer of the Original Code is
  18. * The Mozilla Foundation <http://www.mozilla.org/>.
  19. * Portions created by the Initial Developer are Copyright (C) 2009
  20. * the Initial Developer. All Rights Reserved.
  21. *
  22. * Contributor(s):
  23. * Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
  24. * Fredrik Larsson <nossralf@gmail.com>
  25. * Dan Witte <dwitte@mozilla.com>
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either the GNU General Public License Version 2 or later (the "GPL"), or
  29. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. #include "jscntxt.h"
  41. #include "jsstr.h"
  42. #include "Library.h"
  43. #include "CTypes.h"
  44. #include "prlink.h"
  45. namespace js {
  46. namespace ctypes {
  47. /*******************************************************************************
  48. ** JSAPI function prototypes
  49. *******************************************************************************/
  50. namespace Library
  51. {
  52. static void Finalize(JSContext* cx, JSObject* obj);
  53. static JSBool Close(JSContext* cx, uintN argc, jsval* vp);
  54. static JSBool Declare(JSContext* cx, uintN argc, jsval* vp);
  55. }
  56. /*******************************************************************************
  57. ** JSObject implementation
  58. *******************************************************************************/
  59. static JSClass sLibraryClass = {
  60. "Library",
  61. JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
  62. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
  63. JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, Library::Finalize,
  64. JSCLASS_NO_OPTIONAL_MEMBERS
  65. };
  66. #define CTYPESFN_FLAGS \
  67. (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
  68. static JSFunctionSpec sLibraryFunctions[] = {
  69. JS_FN("close", Library::Close, 0, CTYPESFN_FLAGS),
  70. JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
  71. JS_FS_END
  72. };
  73. JSBool
  74. Library::Name(JSContext* cx, uintN argc, jsval *vp)
  75. {
  76. if (argc != 1) {
  77. JS_ReportError(cx, "libraryName takes one argument");
  78. return JS_FALSE;
  79. }
  80. jsval arg = JS_ARGV(cx, vp)[0];
  81. JSString* str = NULL;
  82. if (JSVAL_IS_STRING(arg)) {
  83. str = JSVAL_TO_STRING(arg);
  84. }
  85. else {
  86. JS_ReportError(cx, "name argument must be a string");
  87. return JS_FALSE;
  88. }
  89. AutoString resultString;
  90. AppendString(resultString, DLL_PREFIX);
  91. AppendString(resultString, str);
  92. AppendString(resultString, DLL_SUFFIX);
  93. JSString *result = JS_NewUCStringCopyN(cx, resultString.begin(),
  94. resultString.length());
  95. if (!result)
  96. return JS_FALSE;
  97. JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
  98. return JS_TRUE;
  99. }
  100. JSObject*
  101. Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks)
  102. {
  103. JSObject* libraryObj = JS_NewObject(cx, &sLibraryClass, NULL, NULL);
  104. if (!libraryObj)
  105. return NULL;
  106. js::AutoObjectRooter root(cx, libraryObj);
  107. // initialize the library
  108. JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
  109. // attach API functions
  110. if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
  111. return NULL;
  112. if (!JSVAL_IS_STRING(path)) {
  113. JS_ReportError(cx, "open takes a string argument");
  114. return NULL;
  115. }
  116. PRLibSpec libSpec;
  117. JSFlatString* pathStr = JS_FlattenString(cx, JSVAL_TO_STRING(path));
  118. if (!pathStr)
  119. return NULL;
  120. #ifdef XP_WIN
  121. // On Windows, converting to native charset may corrupt path string.
  122. // So, we have to use Unicode path directly.
  123. const PRUnichar* pathChars = JS_GetFlatStringChars(pathStr);
  124. if (!pathChars)
  125. return NULL;
  126. libSpec.value.pathname_u = pathChars;
  127. libSpec.type = PR_LibSpec_PathnameU;
  128. #else
  129. // Convert to platform native charset if the appropriate callback has been
  130. // provided.
  131. char* pathBytes;
  132. if (callbacks && callbacks->unicodeToNative) {
  133. pathBytes =
  134. callbacks->unicodeToNative(cx, pathStr->chars(), pathStr->length());
  135. if (!pathBytes)
  136. return NULL;
  137. } else {
  138. // Fallback: assume the platform native charset is UTF-8. This is true
  139. // for Mac OS X, Android, and probably Linux.
  140. size_t nbytes =
  141. GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length());
  142. if (nbytes == (size_t) -1)
  143. return NULL;
  144. pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
  145. if (!pathBytes)
  146. return NULL;
  147. ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStr->chars(),
  148. pathStr->length(), pathBytes, &nbytes));
  149. pathBytes[nbytes] = 0;
  150. }
  151. libSpec.value.pathname = pathBytes;
  152. libSpec.type = PR_LibSpec_Pathname;
  153. #endif
  154. PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0);
  155. #ifndef XP_WIN
  156. JS_free(cx, pathBytes);
  157. #endif
  158. if (!library) {
  159. JS_ReportError(cx, "couldn't open library");
  160. return NULL;
  161. }
  162. // stash the library
  163. JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library));
  164. return libraryObj;
  165. }
  166. bool
  167. Library::IsLibrary(JSObject* obj)
  168. {
  169. return JS_GetClass(obj) == &sLibraryClass;
  170. }
  171. PRLibrary*
  172. Library::GetLibrary(JSObject* obj)
  173. {
  174. JS_ASSERT(IsLibrary(obj));
  175. jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
  176. return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
  177. }
  178. void
  179. Library::Finalize(JSContext* cx, JSObject* obj)
  180. {
  181. // unload the library
  182. PRLibrary* library = GetLibrary(obj);
  183. if (library)
  184. PR_UnloadLibrary(library);
  185. }
  186. JSBool
  187. Library::Open(JSContext* cx, uintN argc, jsval *vp)
  188. {
  189. JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
  190. if (!ctypesObj || !IsCTypesGlobal(ctypesObj)) {
  191. JS_ReportError(cx, "not a ctypes object");
  192. return JS_FALSE;
  193. }
  194. if (argc != 1 || JSVAL_IS_VOID(JS_ARGV(cx, vp)[0])) {
  195. JS_ReportError(cx, "open requires a single argument");
  196. return JS_FALSE;
  197. }
  198. JSObject* library = Create(cx, JS_ARGV(cx, vp)[0], GetCallbacks(ctypesObj));
  199. if (!library)
  200. return JS_FALSE;
  201. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(library));
  202. return JS_TRUE;
  203. }
  204. JSBool
  205. Library::Close(JSContext* cx, uintN argc, jsval* vp)
  206. {
  207. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  208. if (!obj || !IsLibrary(obj)) {
  209. JS_ReportError(cx, "not a library");
  210. return JS_FALSE;
  211. }
  212. if (argc != 0) {
  213. JS_ReportError(cx, "close doesn't take any arguments");
  214. return JS_FALSE;
  215. }
  216. // delete our internal objects
  217. Finalize(cx, obj);
  218. JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
  219. JS_SET_RVAL(cx, vp, JSVAL_VOID);
  220. return JS_TRUE;
  221. }
  222. JSBool
  223. Library::Declare(JSContext* cx, uintN argc, jsval* vp)
  224. {
  225. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  226. if (!obj || !IsLibrary(obj)) {
  227. JS_ReportError(cx, "not a library");
  228. return JS_FALSE;
  229. }
  230. PRLibrary* library = GetLibrary(obj);
  231. if (!library) {
  232. JS_ReportError(cx, "library not open");
  233. return JS_FALSE;
  234. }
  235. // We allow two API variants:
  236. // 1) library.declare(name, abi, returnType, argType1, ...)
  237. // declares a function with the given properties, and resolves the symbol
  238. // address in the library.
  239. // 2) library.declare(name, type)
  240. // declares a symbol of 'type', and resolves it. The object that comes
  241. // back will be of type 'type', and will point into the symbol data.
  242. // This data will be both readable and writable via the usual CData
  243. // accessors. If 'type' is a PointerType to a FunctionType, the result will
  244. // be a function pointer, as with 1).
  245. if (argc < 2) {
  246. JS_ReportError(cx, "declare requires at least two arguments");
  247. return JS_FALSE;
  248. }
  249. jsval* argv = JS_ARGV(cx, vp);
  250. if (!JSVAL_IS_STRING(argv[0])) {
  251. JS_ReportError(cx, "first argument must be a string");
  252. return JS_FALSE;
  253. }
  254. JSObject* fnObj = NULL;
  255. JSObject* typeObj;
  256. js::AutoObjectRooter root(cx);
  257. bool isFunction = argc > 2;
  258. if (isFunction) {
  259. // Case 1).
  260. // Create a FunctionType representing the function.
  261. fnObj = FunctionType::CreateInternal(cx,
  262. argv[1], argv[2], &argv[3], argc - 3);
  263. if (!fnObj)
  264. return JS_FALSE;
  265. root.setObject(fnObj);
  266. // Make a function pointer type.
  267. typeObj = PointerType::CreateInternal(cx, fnObj);
  268. if (!typeObj)
  269. return JS_FALSE;
  270. root.setObject(typeObj);
  271. } else {
  272. // Case 2).
  273. if (JSVAL_IS_PRIMITIVE(argv[1]) ||
  274. !CType::IsCType(JSVAL_TO_OBJECT(argv[1])) ||
  275. !CType::IsSizeDefined(JSVAL_TO_OBJECT(argv[1]))) {
  276. JS_ReportError(cx, "second argument must be a type of defined size");
  277. return JS_FALSE;
  278. }
  279. typeObj = JSVAL_TO_OBJECT(argv[1]);
  280. if (CType::GetTypeCode(typeObj) == TYPE_pointer) {
  281. fnObj = PointerType::GetBaseType(typeObj);
  282. isFunction = fnObj && CType::GetTypeCode(fnObj) == TYPE_function;
  283. }
  284. }
  285. void* data;
  286. PRFuncPtr fnptr;
  287. JSString* nameStr = JSVAL_TO_STRING(argv[0]);
  288. AutoCString symbol;
  289. if (isFunction) {
  290. // Build the symbol, with mangling if necessary.
  291. FunctionType::BuildSymbolName(nameStr, fnObj, symbol);
  292. AppendString(symbol, "\0");
  293. // Look up the function symbol.
  294. fnptr = PR_FindFunctionSymbol(library, symbol.begin());
  295. if (!fnptr) {
  296. JS_ReportError(cx, "couldn't find function symbol in library");
  297. return JS_FALSE;
  298. }
  299. data = &fnptr;
  300. } else {
  301. // 'typeObj' is another data type. Look up the data symbol.
  302. AppendString(symbol, nameStr);
  303. AppendString(symbol, "\0");
  304. data = PR_FindSymbol(library, symbol.begin());
  305. if (!data) {
  306. JS_ReportError(cx, "couldn't find symbol in library");
  307. return JS_FALSE;
  308. }
  309. }
  310. JSObject* result = CData::Create(cx, typeObj, obj, data, isFunction);
  311. if (!result)
  312. return JS_FALSE;
  313. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  314. // Seal the CData object, to prevent modification of the function pointer.
  315. // This permanently associates this object with the library, and avoids
  316. // having to do things like reset SLOT_REFERENT when someone tries to
  317. // change the pointer value.
  318. // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
  319. // could be called on a sealed object.
  320. if (isFunction && !JS_FreezeObject(cx, result))
  321. return JS_FALSE;
  322. return JS_TRUE;
  323. }
  324. }
  325. }