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 +