js/src/ctypes/Library.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:88e09f0324f0
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/. */
6
7 #include "ctypes/Library.h"
8
9 #include "prlink.h"
10
11 #include "ctypes/CTypes.h"
12
13 namespace js {
14 namespace ctypes {
15
16 /*******************************************************************************
17 ** JSAPI function prototypes
18 *******************************************************************************/
19
20 namespace Library
21 {
22 static void Finalize(JSFreeOp *fop, JSObject* obj);
23
24 static bool Close(JSContext* cx, unsigned argc, jsval* vp);
25 static bool Declare(JSContext* cx, unsigned argc, jsval* vp);
26 }
27
28 /*******************************************************************************
29 ** JSObject implementation
30 *******************************************************************************/
31
32 typedef Rooted<JSFlatString*> RootedFlatString;
33
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 };
40
41 #define CTYPESFN_FLAGS \
42 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
43
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 };
49
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 }
58
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 }
68
69 AutoString resultString;
70 AppendString(resultString, DLL_PREFIX);
71 AppendString(resultString, str);
72 AppendString(resultString, DLL_SUFFIX);
73
74 JSString *result = JS_NewUCStringCopyN(cx, resultString.begin(),
75 resultString.length());
76 if (!result)
77 return false;
78
79 args.rval().setString(result);
80 return true;
81 }
82
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;
91
92 // initialize the library
93 JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(nullptr));
94
95 // attach API functions
96 if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
97 return nullptr;
98
99 if (!JSVAL_IS_STRING(path)) {
100 JS_ReportError(cx, "open takes a string argument");
101 return nullptr;
102 }
103
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;
114
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;
126
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;
134
135 pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
136 if (!pathBytes)
137 return nullptr;
138
139 ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStr->chars(),
140 pathStr->length(), pathBytes, &nbytes));
141 pathBytes[nbytes] = 0;
142 }
143
144 libSpec.value.pathname = pathBytes;
145 libSpec.type = PR_LibSpec_Pathname;
146 #endif
147
148 PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0);
149
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 }
159
160 #ifndef XP_WIN
161 JS_free(cx, pathBytes);
162 #endif
163
164 // stash the library
165 JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library));
166
167 return libraryObj;
168 }
169
170 bool
171 Library::IsLibrary(JSObject* obj)
172 {
173 return JS_GetClass(obj) == &sLibraryClass;
174 }
175
176 PRLibrary*
177 Library::GetLibrary(JSObject* obj)
178 {
179 JS_ASSERT(IsLibrary(obj));
180
181 jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
182 return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
183 }
184
185 static void
186 UnloadLibrary(JSObject* obj)
187 {
188 PRLibrary* library = Library::GetLibrary(obj);
189 if (library)
190 PR_UnloadLibrary(library);
191 }
192
193 void
194 Library::Finalize(JSFreeOp *fop, JSObject* obj)
195 {
196 UnloadLibrary(obj);
197 }
198
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 }
210
211 if (args.length() != 1 || args[0].isUndefined()) {
212 JS_ReportError(cx, "open requires a single argument");
213 return false;
214 }
215
216 JSObject* library = Create(cx, args[0], GetCallbacks(ctypesObj));
217 if (!library)
218 return false;
219
220 args.rval().setObject(*library);
221 return true;
222 }
223
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 }
235
236 if (args.length() != 0) {
237 JS_ReportError(cx, "close doesn't take any arguments");
238 return false;
239 }
240
241 // delete our internal objects
242 UnloadLibrary(obj);
243 JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(nullptr));
244
245 args.rval().setUndefined();
246 return true;
247 }
248
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 }
260
261 PRLibrary* library = GetLibrary(obj);
262 if (!library) {
263 JS_ReportError(cx, "library not open");
264 return false;
265 }
266
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 }
281
282 if (!args[0].isString()) {
283 JS_ReportError(cx, "first argument must be a string");
284 return false;
285 }
286
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;
297
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 }
310
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 }
317
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");
326
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;
334
335 } else {
336 // 'typeObj' is another data type. Look up the data symbol.
337 AppendString(symbol, nameStr);
338 AppendString(symbol, "\0");
339
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 }
346
347 RootedObject result(cx, CData::Create(cx, typeObj, obj, data, isFunction));
348 if (!result)
349 return false;
350
351 args.rval().setObject(*result);
352
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;
361
362 return true;
363 }
364
365 }
366 }
367

mercurial