LCOV - code coverage report
Current view: top level - js/src/ctypes - Library.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 177 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 9 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2             :  * vim: set ts=2 sw=2 et tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "ctypes/Library.h"
       8             : 
       9             : #include "prerror.h"
      10             : #include "prlink.h"
      11             : 
      12             : #include "ctypes/CTypes.h"
      13             : 
      14             : namespace js {
      15             : namespace ctypes {
      16             : 
      17             : /*******************************************************************************
      18             : ** JSAPI function prototypes
      19             : *******************************************************************************/
      20             : 
      21             : namespace Library
      22             : {
      23             :   static void Finalize(JSFreeOp* fop, JSObject* obj);
      24             : 
      25             :   static bool Close(JSContext* cx, unsigned argc, Value* vp);
      26             :   static bool Declare(JSContext* cx, unsigned argc, Value* vp);
      27             : } // namespace Library
      28             : 
      29             : /*******************************************************************************
      30             : ** JSObject implementation
      31             : *******************************************************************************/
      32             : 
      33             : typedef Rooted<JSFlatString*>    RootedFlatString;
      34             : 
      35             : static const JSClassOps sLibraryClassOps = {
      36             :   nullptr, nullptr, nullptr, nullptr,
      37             :   nullptr, nullptr, nullptr, nullptr,
      38             :   Library::Finalize
      39             : };
      40             : 
      41             : static const JSClass sLibraryClass = {
      42             :   "Library",
      43             :   JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS) |
      44             :   JSCLASS_FOREGROUND_FINALIZE,
      45             :   &sLibraryClassOps
      46             : };
      47             : 
      48             : #define CTYPESFN_FLAGS \
      49             :   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
      50             : 
      51             : static const JSFunctionSpec sLibraryFunctions[] = {
      52             :   JS_FN("close",   Library::Close,   0, CTYPESFN_FLAGS),
      53             :   JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
      54             :   JS_FS_END
      55             : };
      56             : 
      57             : bool
      58           0 : Library::Name(JSContext* cx, unsigned argc, Value* vp)
      59             : {
      60           0 :   CallArgs args = CallArgsFromVp(argc, vp);
      61           0 :   if (args.length() != 1) {
      62           0 :     JS_ReportErrorASCII(cx, "libraryName takes one argument");
      63           0 :     return false;
      64             :   }
      65             : 
      66           0 :   Value arg = args[0];
      67           0 :   JSString* str = nullptr;
      68           0 :   if (arg.isString()) {
      69           0 :     str = arg.toString();
      70             :   } else {
      71           0 :     JS_ReportErrorASCII(cx, "name argument must be a string");
      72           0 :     return false;
      73             :   }
      74             : 
      75           0 :   AutoString resultString;
      76           0 :   AppendString(resultString, DLL_PREFIX);
      77           0 :   AppendString(resultString, str);
      78           0 :   AppendString(resultString, DLL_SUFFIX);
      79             : 
      80           0 :   JSString* result = JS_NewUCStringCopyN(cx, resultString.begin(),
      81           0 :                                          resultString.length());
      82           0 :   if (!result)
      83           0 :     return false;
      84             : 
      85           0 :   args.rval().setString(result);
      86           0 :   return true;
      87             : }
      88             : 
      89             : JSObject*
      90           0 : Library::Create(JSContext* cx, HandleValue path, const JSCTypesCallbacks* callbacks)
      91             : {
      92           0 :   RootedObject libraryObj(cx, JS_NewObject(cx, &sLibraryClass));
      93           0 :   if (!libraryObj)
      94           0 :     return nullptr;
      95             : 
      96             :   // initialize the library
      97           0 :   JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PrivateValue(nullptr));
      98             : 
      99             :   // attach API functions
     100           0 :   if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
     101           0 :     return nullptr;
     102             : 
     103           0 :   if (!path.isString()) {
     104           0 :     JS_ReportErrorASCII(cx, "open takes a string argument");
     105           0 :     return nullptr;
     106             :   }
     107             : 
     108             :   PRLibSpec libSpec;
     109           0 :   RootedFlatString pathStr(cx, JS_FlattenString(cx, path.toString()));
     110           0 :   if (!pathStr)
     111           0 :     return nullptr;
     112           0 :   AutoStableStringChars pathStrChars(cx);
     113           0 :   if (!pathStrChars.initTwoByte(cx, pathStr))
     114           0 :     return nullptr;
     115             : #ifdef XP_WIN
     116             :   // On Windows, converting to native charset may corrupt path string.
     117             :   // So, we have to use Unicode path directly.
     118             :   char16ptr_t pathChars = pathStrChars.twoByteChars();
     119             :   libSpec.value.pathname_u = pathChars;
     120             :   libSpec.type = PR_LibSpec_PathnameU;
     121             : #else
     122             :   // Convert to platform native charset if the appropriate callback has been
     123             :   // provided.
     124             :   char* pathBytes;
     125           0 :   if (callbacks && callbacks->unicodeToNative) {
     126             :     pathBytes =
     127           0 :       callbacks->unicodeToNative(cx, pathStrChars.twoByteChars(), pathStr->length());
     128           0 :     if (!pathBytes)
     129           0 :       return nullptr;
     130             : 
     131             :   } else {
     132             :     // Fallback: assume the platform native charset is UTF-8. This is true
     133             :     // for Mac OS X, Android, and probably Linux.
     134             :     size_t nbytes =
     135           0 :       GetDeflatedUTF8StringLength(cx, pathStrChars.twoByteChars(), pathStr->length());
     136           0 :     if (nbytes == (size_t) -1)
     137           0 :       return nullptr;
     138             : 
     139           0 :     pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
     140           0 :     if (!pathBytes)
     141           0 :       return nullptr;
     142             : 
     143           0 :     ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStrChars.twoByteChars(),
     144           0 :                 pathStr->length(), pathBytes, &nbytes));
     145           0 :     pathBytes[nbytes] = 0;
     146             :   }
     147             : 
     148           0 :   libSpec.value.pathname = pathBytes;
     149           0 :   libSpec.type = PR_LibSpec_Pathname;
     150             : #endif
     151             : 
     152           0 :   PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW);
     153             : 
     154             : #ifndef XP_WIN
     155           0 :   JS_free(cx, pathBytes);
     156             : #endif
     157             : 
     158           0 :   if (!library) {
     159             : #define MAX_ERROR_LEN 1024
     160           0 :     char error[MAX_ERROR_LEN] = "Cannot get error from NSPR.";
     161           0 :     uint32_t errorLen = PR_GetErrorTextLength();
     162           0 :     if (errorLen && errorLen < MAX_ERROR_LEN)
     163           0 :       PR_GetErrorText(error);
     164             : #undef MAX_ERROR_LEN
     165             : 
     166           0 :     if (JS::StringIsASCII(error)) {
     167           0 :       JSAutoByteString pathCharsUTF8;
     168           0 :       if (pathCharsUTF8.encodeUtf8(cx, pathStr))
     169           0 :         JS_ReportErrorUTF8(cx, "couldn't open library %s: %s", pathCharsUTF8.ptr(), error);
     170             :     } else {
     171           0 :       JSAutoByteString pathCharsLatin1;
     172           0 :       if (pathCharsLatin1.encodeLatin1(cx, pathStr))
     173           0 :         JS_ReportErrorLatin1(cx, "couldn't open library %s: %s", pathCharsLatin1.ptr(), error);
     174             :     }
     175           0 :     return nullptr;
     176             :   }
     177             : 
     178             :   // stash the library
     179           0 :   JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PrivateValue(library));
     180             : 
     181           0 :   return libraryObj;
     182             : }
     183             : 
     184             : bool
     185           0 : Library::IsLibrary(JSObject* obj)
     186             : {
     187           0 :   return JS_GetClass(obj) == &sLibraryClass;
     188             : }
     189             : 
     190             : PRLibrary*
     191           0 : Library::GetLibrary(JSObject* obj)
     192             : {
     193           0 :   MOZ_ASSERT(IsLibrary(obj));
     194             : 
     195           0 :   Value slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
     196           0 :   return static_cast<PRLibrary*>(slot.toPrivate());
     197             : }
     198             : 
     199             : static void
     200           0 : UnloadLibrary(JSObject* obj)
     201             : {
     202           0 :   PRLibrary* library = Library::GetLibrary(obj);
     203           0 :   if (library)
     204           0 :     PR_UnloadLibrary(library);
     205           0 : }
     206             : 
     207             : void
     208           0 : Library::Finalize(JSFreeOp* fop, JSObject* obj)
     209             : {
     210           0 :   UnloadLibrary(obj);
     211           0 : }
     212             : 
     213             : bool
     214           0 : Library::Open(JSContext* cx, unsigned argc, Value* vp)
     215             : {
     216           0 :   CallArgs args = CallArgsFromVp(argc, vp);
     217           0 :   JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
     218           0 :   if (!ctypesObj)
     219           0 :     return false;
     220           0 :   if (!IsCTypesGlobal(ctypesObj)) {
     221           0 :     JS_ReportErrorASCII(cx, "not a ctypes object");
     222           0 :     return false;
     223             :   }
     224             : 
     225           0 :   if (args.length() != 1 || args[0].isUndefined()) {
     226           0 :     JS_ReportErrorASCII(cx, "open requires a single argument");
     227           0 :     return false;
     228             :   }
     229             : 
     230           0 :   JSObject* library = Create(cx, args[0], GetCallbacks(ctypesObj));
     231           0 :   if (!library)
     232           0 :     return false;
     233             : 
     234           0 :   args.rval().setObject(*library);
     235           0 :   return true;
     236             : }
     237             : 
     238             : bool
     239           0 : Library::Close(JSContext* cx, unsigned argc, Value* vp)
     240             : {
     241           0 :   CallArgs args = CallArgsFromVp(argc, vp);
     242           0 :   JSObject* obj = JS_THIS_OBJECT(cx, vp);
     243           0 :   if (!obj)
     244           0 :     return false;
     245           0 :   if (!IsLibrary(obj)) {
     246           0 :     JS_ReportErrorASCII(cx, "not a library");
     247           0 :     return false;
     248             :   }
     249             : 
     250           0 :   if (args.length() != 0) {
     251           0 :     JS_ReportErrorASCII(cx, "close doesn't take any arguments");
     252           0 :     return false;
     253             :   }
     254             : 
     255             :   // delete our internal objects
     256           0 :   UnloadLibrary(obj);
     257           0 :   JS_SetReservedSlot(obj, SLOT_LIBRARY, PrivateValue(nullptr));
     258             : 
     259           0 :   args.rval().setUndefined();
     260           0 :   return true;
     261             : }
     262             : 
     263             : bool
     264           0 : Library::Declare(JSContext* cx, unsigned argc, Value* vp)
     265             : {
     266           0 :   CallArgs args = CallArgsFromVp(argc, vp);
     267           0 :   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
     268           0 :   if (!obj)
     269           0 :     return false;
     270           0 :   if (!IsLibrary(obj)) {
     271           0 :     JS_ReportErrorASCII(cx, "not a library");
     272           0 :     return false;
     273             :   }
     274             : 
     275           0 :   PRLibrary* library = GetLibrary(obj);
     276           0 :   if (!library) {
     277           0 :     JS_ReportErrorASCII(cx, "library not open");
     278           0 :     return false;
     279             :   }
     280             : 
     281             :   // We allow two API variants:
     282             :   // 1) library.declare(name, abi, returnType, argType1, ...)
     283             :   //    declares a function with the given properties, and resolves the symbol
     284             :   //    address in the library.
     285             :   // 2) library.declare(name, type)
     286             :   //    declares a symbol of 'type', and resolves it. The object that comes
     287             :   //    back will be of type 'type', and will point into the symbol data.
     288             :   //    This data will be both readable and writable via the usual CData
     289             :   //    accessors. If 'type' is a PointerType to a FunctionType, the result will
     290             :   //    be a function pointer, as with 1).
     291           0 :   if (args.length() < 2) {
     292           0 :     JS_ReportErrorASCII(cx, "declare requires at least two arguments");
     293           0 :     return false;
     294             :   }
     295             : 
     296           0 :   if (!args[0].isString()) {
     297           0 :     JS_ReportErrorASCII(cx, "first argument must be a string");
     298           0 :     return false;
     299             :   }
     300             : 
     301           0 :   RootedObject fnObj(cx, nullptr);
     302           0 :   RootedObject typeObj(cx);
     303           0 :   bool isFunction = args.length() > 2;
     304           0 :   if (isFunction) {
     305             :     // Case 1).
     306             :     // Create a FunctionType representing the function.
     307           0 :     fnObj = FunctionType::CreateInternal(cx, args[1], args[2],
     308           0 :                                          HandleValueArray::subarray(args, 3, args.length() - 3));
     309           0 :     if (!fnObj)
     310           0 :       return false;
     311             : 
     312             :     // Make a function pointer type.
     313           0 :     typeObj = PointerType::CreateInternal(cx, fnObj);
     314           0 :     if (!typeObj)
     315           0 :       return false;
     316             :   } else {
     317             :     // Case 2).
     318           0 :     if (args[1].isPrimitive() ||
     319           0 :         !CType::IsCType(args[1].toObjectOrNull()) ||
     320           0 :         !CType::IsSizeDefined(args[1].toObjectOrNull())) {
     321           0 :       JS_ReportErrorASCII(cx, "second argument must be a type of defined size");
     322           0 :       return false;
     323             :     }
     324             : 
     325           0 :     typeObj = args[1].toObjectOrNull();
     326           0 :     if (CType::GetTypeCode(typeObj) == TYPE_pointer) {
     327           0 :       fnObj = PointerType::GetBaseType(typeObj);
     328           0 :       isFunction = fnObj && CType::GetTypeCode(fnObj) == TYPE_function;
     329             :     }
     330             :   }
     331             : 
     332             :   void* data;
     333             :   PRFuncPtr fnptr;
     334           0 :   RootedString nameStr(cx, args[0].toString());
     335           0 :   AutoCString symbol;
     336           0 :   if (isFunction) {
     337             :     // Build the symbol, with mangling if necessary.
     338           0 :     FunctionType::BuildSymbolName(nameStr, fnObj, symbol);
     339           0 :     AppendString(symbol, "\0");
     340             : 
     341             :     // Look up the function symbol.
     342           0 :     fnptr = PR_FindFunctionSymbol(library, symbol.begin());
     343           0 :     if (!fnptr) {
     344           0 :       JS_ReportErrorASCII(cx, "couldn't find function symbol in library");
     345           0 :       return false;
     346             :     }
     347           0 :     data = &fnptr;
     348             : 
     349             :   } else {
     350             :     // 'typeObj' is another data type. Look up the data symbol.
     351           0 :     AppendString(symbol, nameStr);
     352           0 :     AppendString(symbol, "\0");
     353             : 
     354           0 :     data = PR_FindSymbol(library, symbol.begin());
     355           0 :     if (!data) {
     356           0 :       JS_ReportErrorASCII(cx, "couldn't find symbol in library");
     357           0 :       return false;
     358             :     }
     359             :   }
     360             : 
     361           0 :   RootedObject result(cx, CData::Create(cx, typeObj, obj, data, isFunction));
     362           0 :   if (!result)
     363           0 :     return false;
     364             : 
     365           0 :   if (isFunction)
     366           0 :     JS_SetReservedSlot(result, SLOT_FUNNAME, StringValue(nameStr));
     367             : 
     368           0 :   args.rval().setObject(*result);
     369             : 
     370             :   // Seal the CData object, to prevent modification of the function pointer.
     371             :   // This permanently associates this object with the library, and avoids
     372             :   // having to do things like reset SLOT_REFERENT when someone tries to
     373             :   // change the pointer value.
     374             :   // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
     375             :   // could be called on a sealed object.
     376           0 :   if (isFunction && !JS_FreezeObject(cx, result))
     377           0 :     return false;
     378             : 
     379           0 :   return true;
     380             : }
     381             : 
     382             : } // namespace ctypes
     383             : } // namespace js
     384             : 

Generated by: LCOV version 1.13