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.

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

mercurial