Bumping manifests a=b2g-bump
[gecko.git] / js / src / ctypes / Library.cpp
blob4be0d82aaafff5b5747fd9c48231c8000f65488a
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
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);
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 nullptr, nullptr, nullptr, nullptr,
38 nullptr, nullptr, nullptr, Library::Finalize
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
50 bool
51 Library::Name(JSContext* cx, unsigned argc, jsval* vp)
53 CallArgs args = CallArgsFromVp(argc, vp);
54 if (args.length() != 1) {
55 JS_ReportError(cx, "libraryName takes one argument");
56 return false;
59 Value arg = args[0];
60 JSString* str = nullptr;
61 if (arg.isString()) {
62 str = arg.toString();
63 } else {
64 JS_ReportError(cx, "name argument must be a string");
65 return false;
68 AutoString resultString;
69 AppendString(resultString, DLL_PREFIX);
70 AppendString(resultString, str);
71 AppendString(resultString, DLL_SUFFIX);
73 JSString* result = JS_NewUCStringCopyN(cx, resultString.begin(),
74 resultString.length());
75 if (!result)
76 return false;
78 args.rval().setString(result);
79 return true;
82 JSObject*
83 Library::Create(JSContext* cx, jsval path_, const JSCTypesCallbacks* callbacks)
85 RootedValue path(cx, path_);
86 RootedObject libraryObj(cx,
87 JS_NewObject(cx, &sLibraryClass, NullPtr(), NullPtr()));
88 if (!libraryObj)
89 return nullptr;
91 // initialize the library
92 JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(nullptr));
94 // attach API functions
95 if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
96 return nullptr;
98 if (!path.isString()) {
99 JS_ReportError(cx, "open takes a string argument");
100 return nullptr;
103 PRLibSpec libSpec;
104 RootedFlatString pathStr(cx, JS_FlattenString(cx, path.toString()));
105 if (!pathStr)
106 return nullptr;
107 AutoStableStringChars pathStrChars(cx);
108 if (!pathStrChars.initTwoByte(cx, pathStr))
109 return nullptr;
110 #ifdef XP_WIN
111 // On Windows, converting to native charset may corrupt path string.
112 // So, we have to use Unicode path directly.
113 char16ptr_t pathChars = pathStrChars.twoByteChars();
114 libSpec.value.pathname_u = pathChars;
115 libSpec.type = PR_LibSpec_PathnameU;
116 #else
117 // Convert to platform native charset if the appropriate callback has been
118 // provided.
119 char* pathBytes;
120 if (callbacks && callbacks->unicodeToNative) {
121 pathBytes =
122 callbacks->unicodeToNative(cx, pathStrChars.twoByteChars(), pathStr->length());
123 if (!pathBytes)
124 return nullptr;
126 } else {
127 // Fallback: assume the platform native charset is UTF-8. This is true
128 // for Mac OS X, Android, and probably Linux.
129 size_t nbytes =
130 GetDeflatedUTF8StringLength(cx, pathStrChars.twoByteChars(), pathStr->length());
131 if (nbytes == (size_t) -1)
132 return nullptr;
134 pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
135 if (!pathBytes)
136 return nullptr;
138 ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStrChars.twoByteChars(),
139 pathStr->length(), pathBytes, &nbytes));
140 pathBytes[nbytes] = 0;
143 libSpec.value.pathname = pathBytes;
144 libSpec.type = PR_LibSpec_Pathname;
145 #endif
147 PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0);
149 if (!library) {
150 #ifdef XP_WIN
151 JS_ReportError(cx, "couldn't open library %hs", pathChars);
152 #else
153 JS_ReportError(cx, "couldn't open library %s", pathBytes);
154 JS_free(cx, pathBytes);
155 #endif
156 return nullptr;
159 #ifndef XP_WIN
160 JS_free(cx, pathBytes);
161 #endif
163 // stash the library
164 JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library));
166 return libraryObj;
169 bool
170 Library::IsLibrary(JSObject* obj)
172 return JS_GetClass(obj) == &sLibraryClass;
175 PRLibrary*
176 Library::GetLibrary(JSObject* obj)
178 MOZ_ASSERT(IsLibrary(obj));
180 jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
181 return static_cast<PRLibrary*>(slot.toPrivate());
184 static void
185 UnloadLibrary(JSObject* obj)
187 PRLibrary* library = Library::GetLibrary(obj);
188 if (library)
189 PR_UnloadLibrary(library);
192 void
193 Library::Finalize(JSFreeOp* fop, JSObject* obj)
195 UnloadLibrary(obj);
198 bool
199 Library::Open(JSContext* cx, unsigned argc, jsval* vp)
201 CallArgs args = CallArgsFromVp(argc, vp);
202 JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
203 if (!ctypesObj)
204 return false;
205 if (!IsCTypesGlobal(ctypesObj)) {
206 JS_ReportError(cx, "not a ctypes object");
207 return false;
210 if (args.length() != 1 || args[0].isUndefined()) {
211 JS_ReportError(cx, "open requires a single argument");
212 return false;
215 JSObject* library = Create(cx, args[0], GetCallbacks(ctypesObj));
216 if (!library)
217 return false;
219 args.rval().setObject(*library);
220 return true;
223 bool
224 Library::Close(JSContext* cx, unsigned argc, jsval* vp)
226 CallArgs args = CallArgsFromVp(argc, vp);
227 JSObject* obj = JS_THIS_OBJECT(cx, vp);
228 if (!obj)
229 return false;
230 if (!IsLibrary(obj)) {
231 JS_ReportError(cx, "not a library");
232 return false;
235 if (args.length() != 0) {
236 JS_ReportError(cx, "close doesn't take any arguments");
237 return false;
240 // delete our internal objects
241 UnloadLibrary(obj);
242 JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(nullptr));
244 args.rval().setUndefined();
245 return true;
248 bool
249 Library::Declare(JSContext* cx, unsigned argc, jsval* vp)
251 CallArgs args = CallArgsFromVp(argc, vp);
252 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
253 if (!obj)
254 return false;
255 if (!IsLibrary(obj)) {
256 JS_ReportError(cx, "not a library");
257 return false;
260 PRLibrary* library = GetLibrary(obj);
261 if (!library) {
262 JS_ReportError(cx, "library not open");
263 return false;
266 // We allow two API variants:
267 // 1) library.declare(name, abi, returnType, argType1, ...)
268 // declares a function with the given properties, and resolves the symbol
269 // address in the library.
270 // 2) library.declare(name, type)
271 // declares a symbol of 'type', and resolves it. The object that comes
272 // back will be of type 'type', and will point into the symbol data.
273 // This data will be both readable and writable via the usual CData
274 // accessors. If 'type' is a PointerType to a FunctionType, the result will
275 // be a function pointer, as with 1).
276 if (args.length() < 2) {
277 JS_ReportError(cx, "declare requires at least two arguments");
278 return false;
281 if (!args[0].isString()) {
282 JS_ReportError(cx, "first argument must be a string");
283 return false;
286 RootedObject fnObj(cx, nullptr);
287 RootedObject typeObj(cx);
288 bool isFunction = args.length() > 2;
289 if (isFunction) {
290 // Case 1).
291 // Create a FunctionType representing the function.
292 fnObj = FunctionType::CreateInternal(cx,
293 args[1], args[2], &args.array()[3], args.length() - 3);
294 if (!fnObj)
295 return false;
297 // Make a function pointer type.
298 typeObj = PointerType::CreateInternal(cx, fnObj);
299 if (!typeObj)
300 return false;
301 } else {
302 // Case 2).
303 if (args[1].isPrimitive() ||
304 !CType::IsCType(args[1].toObjectOrNull()) ||
305 !CType::IsSizeDefined(args[1].toObjectOrNull())) {
306 JS_ReportError(cx, "second argument must be a type of defined size");
307 return false;
310 typeObj = args[1].toObjectOrNull();
311 if (CType::GetTypeCode(typeObj) == TYPE_pointer) {
312 fnObj = PointerType::GetBaseType(typeObj);
313 isFunction = fnObj && CType::GetTypeCode(fnObj) == TYPE_function;
317 void* data;
318 PRFuncPtr fnptr;
319 JSString* nameStr = args[0].toString();
320 AutoCString symbol;
321 if (isFunction) {
322 // Build the symbol, with mangling if necessary.
323 FunctionType::BuildSymbolName(nameStr, fnObj, symbol);
324 AppendString(symbol, "\0");
326 // Look up the function symbol.
327 fnptr = PR_FindFunctionSymbol(library, symbol.begin());
328 if (!fnptr) {
329 JS_ReportError(cx, "couldn't find function symbol in library");
330 return false;
332 data = &fnptr;
334 } else {
335 // 'typeObj' is another data type. Look up the data symbol.
336 AppendString(symbol, nameStr);
337 AppendString(symbol, "\0");
339 data = PR_FindSymbol(library, symbol.begin());
340 if (!data) {
341 JS_ReportError(cx, "couldn't find symbol in library");
342 return false;
346 RootedObject result(cx, CData::Create(cx, typeObj, obj, data, isFunction));
347 if (!result)
348 return false;
350 args.rval().setObject(*result);
352 // Seal the CData object, to prevent modification of the function pointer.
353 // This permanently associates this object with the library, and avoids
354 // having to do things like reset SLOT_REFERENT when someone tries to
355 // change the pointer value.
356 // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
357 // could be called on a sealed object.
358 if (isFunction && !JS_FreezeObject(cx, result))
359 return false;
361 return true;