js/src/ctypes/Library.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/ctypes/Library.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,367 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * vim: set ts=2 sw=2 et tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "ctypes/Library.h"
    1.11 +
    1.12 +#include "prlink.h"
    1.13 +
    1.14 +#include "ctypes/CTypes.h"
    1.15 +
    1.16 +namespace js {
    1.17 +namespace ctypes {
    1.18 +
    1.19 +/*******************************************************************************
    1.20 +** JSAPI function prototypes
    1.21 +*******************************************************************************/
    1.22 +
    1.23 +namespace Library
    1.24 +{
    1.25 +  static void Finalize(JSFreeOp *fop, JSObject* obj);
    1.26 +
    1.27 +  static bool Close(JSContext* cx, unsigned argc, jsval* vp);
    1.28 +  static bool Declare(JSContext* cx, unsigned argc, jsval* vp);
    1.29 +}
    1.30 +
    1.31 +/*******************************************************************************
    1.32 +** JSObject implementation
    1.33 +*******************************************************************************/
    1.34 +
    1.35 +typedef Rooted<JSFlatString*>    RootedFlatString;
    1.36 +
    1.37 +static const JSClass sLibraryClass = {
    1.38 +  "Library",
    1.39 +  JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
    1.40 +  JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
    1.41 +  JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, Library::Finalize
    1.42 +};
    1.43 +
    1.44 +#define CTYPESFN_FLAGS \
    1.45 +  (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
    1.46 +
    1.47 +static const JSFunctionSpec sLibraryFunctions[] = {
    1.48 +  JS_FN("close",   Library::Close,   0, CTYPESFN_FLAGS),
    1.49 +  JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
    1.50 +  JS_FS_END
    1.51 +};
    1.52 +
    1.53 +bool
    1.54 +Library::Name(JSContext* cx, unsigned argc, jsval *vp)
    1.55 +{
    1.56 +  CallArgs args = CallArgsFromVp(argc, vp);
    1.57 +  if (args.length() != 1) {
    1.58 +    JS_ReportError(cx, "libraryName takes one argument");
    1.59 +    return false;
    1.60 +  }
    1.61 +
    1.62 +  Value arg = args[0];
    1.63 +  JSString* str = nullptr;
    1.64 +  if (JSVAL_IS_STRING(arg)) {
    1.65 +    str = JSVAL_TO_STRING(arg);
    1.66 +  }
    1.67 +  else {
    1.68 +    JS_ReportError(cx, "name argument must be a string");
    1.69 +    return false;
    1.70 +  }
    1.71 +
    1.72 +  AutoString resultString;
    1.73 +  AppendString(resultString, DLL_PREFIX);
    1.74 +  AppendString(resultString, str);
    1.75 +  AppendString(resultString, DLL_SUFFIX);
    1.76 +
    1.77 +  JSString *result = JS_NewUCStringCopyN(cx, resultString.begin(),
    1.78 +                                         resultString.length());
    1.79 +  if (!result)
    1.80 +    return false;
    1.81 +
    1.82 +  args.rval().setString(result);
    1.83 +  return true;
    1.84 +}
    1.85 +
    1.86 +JSObject*
    1.87 +Library::Create(JSContext* cx, jsval path_, JSCTypesCallbacks* callbacks)
    1.88 +{
    1.89 +  RootedValue path(cx, path_);
    1.90 +  RootedObject libraryObj(cx,
    1.91 +                          JS_NewObject(cx, &sLibraryClass, NullPtr(), NullPtr()));
    1.92 +  if (!libraryObj)
    1.93 +    return nullptr;
    1.94 +
    1.95 +  // initialize the library
    1.96 +  JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(nullptr));
    1.97 +
    1.98 +  // attach API functions
    1.99 +  if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
   1.100 +    return nullptr;
   1.101 +
   1.102 +  if (!JSVAL_IS_STRING(path)) {
   1.103 +    JS_ReportError(cx, "open takes a string argument");
   1.104 +    return nullptr;
   1.105 +  }
   1.106 +
   1.107 +  PRLibSpec libSpec;
   1.108 +  RootedFlatString pathStr(cx, JS_FlattenString(cx, JSVAL_TO_STRING(path)));
   1.109 +  if (!pathStr)
   1.110 +    return nullptr;
   1.111 +#ifdef XP_WIN
   1.112 +  // On Windows, converting to native charset may corrupt path string.
   1.113 +  // So, we have to use Unicode path directly.
   1.114 +  char16ptr_t pathChars = JS_GetFlatStringChars(pathStr);
   1.115 +  if (!pathChars)
   1.116 +    return nullptr;
   1.117 +
   1.118 +  libSpec.value.pathname_u = pathChars;
   1.119 +  libSpec.type = PR_LibSpec_PathnameU;
   1.120 +#else
   1.121 +  // Convert to platform native charset if the appropriate callback has been
   1.122 +  // provided.
   1.123 +  char* pathBytes;
   1.124 +  if (callbacks && callbacks->unicodeToNative) {
   1.125 +    pathBytes =
   1.126 +      callbacks->unicodeToNative(cx, pathStr->chars(), pathStr->length());
   1.127 +    if (!pathBytes)
   1.128 +      return nullptr;
   1.129 +
   1.130 +  } else {
   1.131 +    // Fallback: assume the platform native charset is UTF-8. This is true
   1.132 +    // for Mac OS X, Android, and probably Linux.
   1.133 +    size_t nbytes =
   1.134 +      GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length());
   1.135 +    if (nbytes == (size_t) -1)
   1.136 +      return nullptr;
   1.137 +
   1.138 +    pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
   1.139 +    if (!pathBytes)
   1.140 +      return nullptr;
   1.141 +
   1.142 +    ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStr->chars(),
   1.143 +                pathStr->length(), pathBytes, &nbytes));
   1.144 +    pathBytes[nbytes] = 0;
   1.145 +  }
   1.146 +
   1.147 +  libSpec.value.pathname = pathBytes;
   1.148 +  libSpec.type = PR_LibSpec_Pathname;
   1.149 +#endif
   1.150 +
   1.151 +  PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0);
   1.152 +
   1.153 +  if (!library) {
   1.154 +#ifdef XP_WIN
   1.155 +    JS_ReportError(cx, "couldn't open library %hs", pathChars);
   1.156 +#else
   1.157 +    JS_ReportError(cx, "couldn't open library %s", pathBytes);
   1.158 +    JS_free(cx, pathBytes);
   1.159 +#endif
   1.160 +    return nullptr;
   1.161 +  }
   1.162 +
   1.163 +#ifndef XP_WIN
   1.164 +  JS_free(cx, pathBytes);
   1.165 +#endif
   1.166 +
   1.167 +  // stash the library
   1.168 +  JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library));
   1.169 +
   1.170 +  return libraryObj;
   1.171 +}
   1.172 +
   1.173 +bool
   1.174 +Library::IsLibrary(JSObject* obj)
   1.175 +{
   1.176 +  return JS_GetClass(obj) == &sLibraryClass;
   1.177 +}
   1.178 +
   1.179 +PRLibrary*
   1.180 +Library::GetLibrary(JSObject* obj)
   1.181 +{
   1.182 +  JS_ASSERT(IsLibrary(obj));
   1.183 +
   1.184 +  jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
   1.185 +  return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
   1.186 +}
   1.187 +
   1.188 +static void
   1.189 +UnloadLibrary(JSObject* obj)
   1.190 +{
   1.191 +  PRLibrary* library = Library::GetLibrary(obj);
   1.192 +  if (library)
   1.193 +    PR_UnloadLibrary(library);
   1.194 +}
   1.195 +
   1.196 +void
   1.197 +Library::Finalize(JSFreeOp *fop, JSObject* obj)
   1.198 +{
   1.199 +  UnloadLibrary(obj);
   1.200 +}
   1.201 +
   1.202 +bool
   1.203 +Library::Open(JSContext* cx, unsigned argc, jsval *vp)
   1.204 +{
   1.205 +  CallArgs args = CallArgsFromVp(argc, vp);
   1.206 +  JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
   1.207 +  if (!ctypesObj)
   1.208 +    return false;
   1.209 +  if (!IsCTypesGlobal(ctypesObj)) {
   1.210 +    JS_ReportError(cx, "not a ctypes object");
   1.211 +    return false;
   1.212 +  }
   1.213 +
   1.214 +  if (args.length() != 1 || args[0].isUndefined()) {
   1.215 +    JS_ReportError(cx, "open requires a single argument");
   1.216 +    return false;
   1.217 +  }
   1.218 +
   1.219 +  JSObject* library = Create(cx, args[0], GetCallbacks(ctypesObj));
   1.220 +  if (!library)
   1.221 +    return false;
   1.222 +
   1.223 +  args.rval().setObject(*library);
   1.224 +  return true;
   1.225 +}
   1.226 +
   1.227 +bool
   1.228 +Library::Close(JSContext* cx, unsigned argc, jsval* vp)
   1.229 +{
   1.230 +  CallArgs args = CallArgsFromVp(argc, vp);
   1.231 +  JSObject* obj = JS_THIS_OBJECT(cx, vp);
   1.232 +  if (!obj)
   1.233 +    return false;
   1.234 +  if (!IsLibrary(obj)) {
   1.235 +    JS_ReportError(cx, "not a library");
   1.236 +    return false;
   1.237 +  }
   1.238 +
   1.239 +  if (args.length() != 0) {
   1.240 +    JS_ReportError(cx, "close doesn't take any arguments");
   1.241 +    return false;
   1.242 +  }
   1.243 +
   1.244 +  // delete our internal objects
   1.245 +  UnloadLibrary(obj);
   1.246 +  JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(nullptr));
   1.247 +
   1.248 +  args.rval().setUndefined();
   1.249 +  return true;
   1.250 +}
   1.251 +
   1.252 +bool
   1.253 +Library::Declare(JSContext* cx, unsigned argc, jsval* vp)
   1.254 +{
   1.255 +  CallArgs args = CallArgsFromVp(argc, vp);
   1.256 +  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   1.257 +  if (!obj)
   1.258 +    return false;
   1.259 +  if (!IsLibrary(obj)) {
   1.260 +    JS_ReportError(cx, "not a library");
   1.261 +    return false;
   1.262 +  }
   1.263 +
   1.264 +  PRLibrary* library = GetLibrary(obj);
   1.265 +  if (!library) {
   1.266 +    JS_ReportError(cx, "library not open");
   1.267 +    return false;
   1.268 +  }
   1.269 +
   1.270 +  // We allow two API variants:
   1.271 +  // 1) library.declare(name, abi, returnType, argType1, ...)
   1.272 +  //    declares a function with the given properties, and resolves the symbol
   1.273 +  //    address in the library.
   1.274 +  // 2) library.declare(name, type)
   1.275 +  //    declares a symbol of 'type', and resolves it. The object that comes
   1.276 +  //    back will be of type 'type', and will point into the symbol data.
   1.277 +  //    This data will be both readable and writable via the usual CData
   1.278 +  //    accessors. If 'type' is a PointerType to a FunctionType, the result will
   1.279 +  //    be a function pointer, as with 1).
   1.280 +  if (args.length() < 2) {
   1.281 +    JS_ReportError(cx, "declare requires at least two arguments");
   1.282 +    return false;
   1.283 +  }
   1.284 +
   1.285 +  if (!args[0].isString()) {
   1.286 +    JS_ReportError(cx, "first argument must be a string");
   1.287 +    return false;
   1.288 +  }
   1.289 +
   1.290 +  RootedObject fnObj(cx, nullptr);
   1.291 +  RootedObject typeObj(cx);
   1.292 +  bool isFunction = args.length() > 2;
   1.293 +  if (isFunction) {
   1.294 +    // Case 1).
   1.295 +    // Create a FunctionType representing the function.
   1.296 +    fnObj = FunctionType::CreateInternal(cx,
   1.297 +              args[1], args[2], &args.array()[3], args.length() - 3);
   1.298 +    if (!fnObj)
   1.299 +      return false;
   1.300 +
   1.301 +    // Make a function pointer type.
   1.302 +    typeObj = PointerType::CreateInternal(cx, fnObj);
   1.303 +    if (!typeObj)
   1.304 +      return false;
   1.305 +  } else {
   1.306 +    // Case 2).
   1.307 +    if (args[1].isPrimitive() ||
   1.308 +        !CType::IsCType(args[1].toObjectOrNull()) ||
   1.309 +        !CType::IsSizeDefined(args[1].toObjectOrNull())) {
   1.310 +      JS_ReportError(cx, "second argument must be a type of defined size");
   1.311 +      return false;
   1.312 +    }
   1.313 +
   1.314 +    typeObj = args[1].toObjectOrNull();
   1.315 +    if (CType::GetTypeCode(typeObj) == TYPE_pointer) {
   1.316 +      fnObj = PointerType::GetBaseType(typeObj);
   1.317 +      isFunction = fnObj && CType::GetTypeCode(fnObj) == TYPE_function;
   1.318 +    }
   1.319 +  }
   1.320 +
   1.321 +  void* data;
   1.322 +  PRFuncPtr fnptr;
   1.323 +  JSString* nameStr = args[0].toString();
   1.324 +  AutoCString symbol;
   1.325 +  if (isFunction) {
   1.326 +    // Build the symbol, with mangling if necessary.
   1.327 +    FunctionType::BuildSymbolName(nameStr, fnObj, symbol);
   1.328 +    AppendString(symbol, "\0");
   1.329 +
   1.330 +    // Look up the function symbol.
   1.331 +    fnptr = PR_FindFunctionSymbol(library, symbol.begin());
   1.332 +    if (!fnptr) {
   1.333 +      JS_ReportError(cx, "couldn't find function symbol in library");
   1.334 +      return false;
   1.335 +    }
   1.336 +    data = &fnptr;
   1.337 +
   1.338 +  } else {
   1.339 +    // 'typeObj' is another data type. Look up the data symbol.
   1.340 +    AppendString(symbol, nameStr);
   1.341 +    AppendString(symbol, "\0");
   1.342 +
   1.343 +    data = PR_FindSymbol(library, symbol.begin());
   1.344 +    if (!data) {
   1.345 +      JS_ReportError(cx, "couldn't find symbol in library");
   1.346 +      return false;
   1.347 +    }
   1.348 +  }
   1.349 +
   1.350 +  RootedObject result(cx, CData::Create(cx, typeObj, obj, data, isFunction));
   1.351 +  if (!result)
   1.352 +    return false;
   1.353 +
   1.354 +  args.rval().setObject(*result);
   1.355 +
   1.356 +  // Seal the CData object, to prevent modification of the function pointer.
   1.357 +  // This permanently associates this object with the library, and avoids
   1.358 +  // having to do things like reset SLOT_REFERENT when someone tries to
   1.359 +  // change the pointer value.
   1.360 +  // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
   1.361 +  // could be called on a sealed object.
   1.362 +  if (isFunction && !JS_FreezeObject(cx, result))
   1.363 +    return false;
   1.364 +
   1.365 +  return true;
   1.366 +}
   1.367 +
   1.368 +}
   1.369 +}
   1.370 +

mercurial