js/src/ctypes/Library.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     7 #include "ctypes/Library.h"
     9 #include "prlink.h"
    11 #include "ctypes/CTypes.h"
    13 namespace js {
    14 namespace ctypes {
    16 /*******************************************************************************
    17 ** JSAPI function prototypes
    18 *******************************************************************************/
    20 namespace Library
    21 {
    22   static void Finalize(JSFreeOp *fop, JSObject* obj);
    24   static bool Close(JSContext* cx, unsigned argc, jsval* vp);
    25   static bool Declare(JSContext* cx, unsigned argc, jsval* vp);
    26 }
    28 /*******************************************************************************
    29 ** JSObject implementation
    30 *******************************************************************************/
    32 typedef Rooted<JSFlatString*>    RootedFlatString;
    34 static const JSClass sLibraryClass = {
    35   "Library",
    36   JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
    37   JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
    38   JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, Library::Finalize
    39 };
    41 #define CTYPESFN_FLAGS \
    42   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
    44 static const JSFunctionSpec sLibraryFunctions[] = {
    45   JS_FN("close",   Library::Close,   0, CTYPESFN_FLAGS),
    46   JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
    47   JS_FS_END
    48 };
    50 bool
    51 Library::Name(JSContext* cx, unsigned argc, jsval *vp)
    52 {
    53   CallArgs args = CallArgsFromVp(argc, vp);
    54   if (args.length() != 1) {
    55     JS_ReportError(cx, "libraryName takes one argument");
    56     return false;
    57   }
    59   Value arg = args[0];
    60   JSString* str = nullptr;
    61   if (JSVAL_IS_STRING(arg)) {
    62     str = JSVAL_TO_STRING(arg);
    63   }
    64   else {
    65     JS_ReportError(cx, "name argument must be a string");
    66     return false;
    67   }
    69   AutoString resultString;
    70   AppendString(resultString, DLL_PREFIX);
    71   AppendString(resultString, str);
    72   AppendString(resultString, DLL_SUFFIX);
    74   JSString *result = JS_NewUCStringCopyN(cx, resultString.begin(),
    75                                          resultString.length());
    76   if (!result)
    77     return false;
    79   args.rval().setString(result);
    80   return true;
    81 }
    83 JSObject*
    84 Library::Create(JSContext* cx, jsval path_, JSCTypesCallbacks* callbacks)
    85 {
    86   RootedValue path(cx, path_);
    87   RootedObject libraryObj(cx,
    88                           JS_NewObject(cx, &sLibraryClass, NullPtr(), NullPtr()));
    89   if (!libraryObj)
    90     return nullptr;
    92   // initialize the library
    93   JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(nullptr));
    95   // attach API functions
    96   if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
    97     return nullptr;
    99   if (!JSVAL_IS_STRING(path)) {
   100     JS_ReportError(cx, "open takes a string argument");
   101     return nullptr;
   102   }
   104   PRLibSpec libSpec;
   105   RootedFlatString pathStr(cx, JS_FlattenString(cx, JSVAL_TO_STRING(path)));
   106   if (!pathStr)
   107     return nullptr;
   108 #ifdef XP_WIN
   109   // On Windows, converting to native charset may corrupt path string.
   110   // So, we have to use Unicode path directly.
   111   char16ptr_t pathChars = JS_GetFlatStringChars(pathStr);
   112   if (!pathChars)
   113     return nullptr;
   115   libSpec.value.pathname_u = pathChars;
   116   libSpec.type = PR_LibSpec_PathnameU;
   117 #else
   118   // Convert to platform native charset if the appropriate callback has been
   119   // provided.
   120   char* pathBytes;
   121   if (callbacks && callbacks->unicodeToNative) {
   122     pathBytes =
   123       callbacks->unicodeToNative(cx, pathStr->chars(), pathStr->length());
   124     if (!pathBytes)
   125       return nullptr;
   127   } else {
   128     // Fallback: assume the platform native charset is UTF-8. This is true
   129     // for Mac OS X, Android, and probably Linux.
   130     size_t nbytes =
   131       GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length());
   132     if (nbytes == (size_t) -1)
   133       return nullptr;
   135     pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
   136     if (!pathBytes)
   137       return nullptr;
   139     ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStr->chars(),
   140                 pathStr->length(), pathBytes, &nbytes));
   141     pathBytes[nbytes] = 0;
   142   }
   144   libSpec.value.pathname = pathBytes;
   145   libSpec.type = PR_LibSpec_Pathname;
   146 #endif
   148   PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0);
   150   if (!library) {
   151 #ifdef XP_WIN
   152     JS_ReportError(cx, "couldn't open library %hs", pathChars);
   153 #else
   154     JS_ReportError(cx, "couldn't open library %s", pathBytes);
   155     JS_free(cx, pathBytes);
   156 #endif
   157     return nullptr;
   158   }
   160 #ifndef XP_WIN
   161   JS_free(cx, pathBytes);
   162 #endif
   164   // stash the library
   165   JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library));
   167   return libraryObj;
   168 }
   170 bool
   171 Library::IsLibrary(JSObject* obj)
   172 {
   173   return JS_GetClass(obj) == &sLibraryClass;
   174 }
   176 PRLibrary*
   177 Library::GetLibrary(JSObject* obj)
   178 {
   179   JS_ASSERT(IsLibrary(obj));
   181   jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
   182   return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
   183 }
   185 static void
   186 UnloadLibrary(JSObject* obj)
   187 {
   188   PRLibrary* library = Library::GetLibrary(obj);
   189   if (library)
   190     PR_UnloadLibrary(library);
   191 }
   193 void
   194 Library::Finalize(JSFreeOp *fop, JSObject* obj)
   195 {
   196   UnloadLibrary(obj);
   197 }
   199 bool
   200 Library::Open(JSContext* cx, unsigned argc, jsval *vp)
   201 {
   202   CallArgs args = CallArgsFromVp(argc, vp);
   203   JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
   204   if (!ctypesObj)
   205     return false;
   206   if (!IsCTypesGlobal(ctypesObj)) {
   207     JS_ReportError(cx, "not a ctypes object");
   208     return false;
   209   }
   211   if (args.length() != 1 || args[0].isUndefined()) {
   212     JS_ReportError(cx, "open requires a single argument");
   213     return false;
   214   }
   216   JSObject* library = Create(cx, args[0], GetCallbacks(ctypesObj));
   217   if (!library)
   218     return false;
   220   args.rval().setObject(*library);
   221   return true;
   222 }
   224 bool
   225 Library::Close(JSContext* cx, unsigned argc, jsval* vp)
   226 {
   227   CallArgs args = CallArgsFromVp(argc, vp);
   228   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   229   if (!obj)
   230     return false;
   231   if (!IsLibrary(obj)) {
   232     JS_ReportError(cx, "not a library");
   233     return false;
   234   }
   236   if (args.length() != 0) {
   237     JS_ReportError(cx, "close doesn't take any arguments");
   238     return false;
   239   }
   241   // delete our internal objects
   242   UnloadLibrary(obj);
   243   JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(nullptr));
   245   args.rval().setUndefined();
   246   return true;
   247 }
   249 bool
   250 Library::Declare(JSContext* cx, unsigned argc, jsval* vp)
   251 {
   252   CallArgs args = CallArgsFromVp(argc, vp);
   253   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   254   if (!obj)
   255     return false;
   256   if (!IsLibrary(obj)) {
   257     JS_ReportError(cx, "not a library");
   258     return false;
   259   }
   261   PRLibrary* library = GetLibrary(obj);
   262   if (!library) {
   263     JS_ReportError(cx, "library not open");
   264     return false;
   265   }
   267   // We allow two API variants:
   268   // 1) library.declare(name, abi, returnType, argType1, ...)
   269   //    declares a function with the given properties, and resolves the symbol
   270   //    address in the library.
   271   // 2) library.declare(name, type)
   272   //    declares a symbol of 'type', and resolves it. The object that comes
   273   //    back will be of type 'type', and will point into the symbol data.
   274   //    This data will be both readable and writable via the usual CData
   275   //    accessors. If 'type' is a PointerType to a FunctionType, the result will
   276   //    be a function pointer, as with 1).
   277   if (args.length() < 2) {
   278     JS_ReportError(cx, "declare requires at least two arguments");
   279     return false;
   280   }
   282   if (!args[0].isString()) {
   283     JS_ReportError(cx, "first argument must be a string");
   284     return false;
   285   }
   287   RootedObject fnObj(cx, nullptr);
   288   RootedObject typeObj(cx);
   289   bool isFunction = args.length() > 2;
   290   if (isFunction) {
   291     // Case 1).
   292     // Create a FunctionType representing the function.
   293     fnObj = FunctionType::CreateInternal(cx,
   294               args[1], args[2], &args.array()[3], args.length() - 3);
   295     if (!fnObj)
   296       return false;
   298     // Make a function pointer type.
   299     typeObj = PointerType::CreateInternal(cx, fnObj);
   300     if (!typeObj)
   301       return false;
   302   } else {
   303     // Case 2).
   304     if (args[1].isPrimitive() ||
   305         !CType::IsCType(args[1].toObjectOrNull()) ||
   306         !CType::IsSizeDefined(args[1].toObjectOrNull())) {
   307       JS_ReportError(cx, "second argument must be a type of defined size");
   308       return false;
   309     }
   311     typeObj = args[1].toObjectOrNull();
   312     if (CType::GetTypeCode(typeObj) == TYPE_pointer) {
   313       fnObj = PointerType::GetBaseType(typeObj);
   314       isFunction = fnObj && CType::GetTypeCode(fnObj) == TYPE_function;
   315     }
   316   }
   318   void* data;
   319   PRFuncPtr fnptr;
   320   JSString* nameStr = args[0].toString();
   321   AutoCString symbol;
   322   if (isFunction) {
   323     // Build the symbol, with mangling if necessary.
   324     FunctionType::BuildSymbolName(nameStr, fnObj, symbol);
   325     AppendString(symbol, "\0");
   327     // Look up the function symbol.
   328     fnptr = PR_FindFunctionSymbol(library, symbol.begin());
   329     if (!fnptr) {
   330       JS_ReportError(cx, "couldn't find function symbol in library");
   331       return false;
   332     }
   333     data = &fnptr;
   335   } else {
   336     // 'typeObj' is another data type. Look up the data symbol.
   337     AppendString(symbol, nameStr);
   338     AppendString(symbol, "\0");
   340     data = PR_FindSymbol(library, symbol.begin());
   341     if (!data) {
   342       JS_ReportError(cx, "couldn't find symbol in library");
   343       return false;
   344     }
   345   }
   347   RootedObject result(cx, CData::Create(cx, typeObj, obj, data, isFunction));
   348   if (!result)
   349     return false;
   351   args.rval().setObject(*result);
   353   // Seal the CData object, to prevent modification of the function pointer.
   354   // This permanently associates this object with the library, and avoids
   355   // having to do things like reset SLOT_REFERENT when someone tries to
   356   // change the pointer value.
   357   // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
   358   // could be called on a sealed object.
   359   if (isFunction && !JS_FreezeObject(cx, result))
   360     return false;
   362   return true;
   363 }
   365 }
   366 }

mercurial