1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is js-ctypes.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * The Mozilla Foundation <http://www.mozilla.org/>.
19 : * Portions created by the Initial Developer are Copyright (C) 2009
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
24 : * Fredrik Larsson <nossralf@gmail.com>
25 : * Dan Witte <dwitte@mozilla.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "jscntxt.h"
42 : #include "jsstr.h"
43 : #include "Library.h"
44 : #include "CTypes.h"
45 : #include "prlink.h"
46 :
47 : namespace js {
48 : namespace ctypes {
49 :
50 : /*******************************************************************************
51 : ** JSAPI function prototypes
52 : *******************************************************************************/
53 :
54 : namespace Library
55 : {
56 : static void Finalize(JSContext* cx, JSObject* obj);
57 :
58 : static JSBool Close(JSContext* cx, unsigned argc, jsval* vp);
59 : static JSBool Declare(JSContext* cx, unsigned argc, jsval* vp);
60 : }
61 :
62 : /*******************************************************************************
63 : ** JSObject implementation
64 : *******************************************************************************/
65 :
66 : static JSClass sLibraryClass = {
67 : "Library",
68 : JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
69 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
70 : JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, Library::Finalize,
71 : JSCLASS_NO_OPTIONAL_MEMBERS
72 : };
73 :
74 : #define CTYPESFN_FLAGS \
75 : (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
76 :
77 : static JSFunctionSpec sLibraryFunctions[] = {
78 : JS_FN("close", Library::Close, 0, CTYPESFN_FLAGS),
79 : JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
80 : JS_FS_END
81 : };
82 :
83 : JSBool
84 54 : Library::Name(JSContext* cx, unsigned argc, jsval *vp)
85 : {
86 54 : if (argc != 1) {
87 0 : JS_ReportError(cx, "libraryName takes one argument");
88 0 : return JS_FALSE;
89 : }
90 :
91 54 : jsval arg = JS_ARGV(cx, vp)[0];
92 54 : JSString* str = NULL;
93 54 : if (JSVAL_IS_STRING(arg)) {
94 54 : str = JSVAL_TO_STRING(arg);
95 : }
96 : else {
97 0 : JS_ReportError(cx, "name argument must be a string");
98 0 : return JS_FALSE;
99 : }
100 :
101 108 : AutoString resultString;
102 54 : AppendString(resultString, DLL_PREFIX);
103 54 : AppendString(resultString, str);
104 54 : AppendString(resultString, DLL_SUFFIX);
105 :
106 54 : JSString *result = JS_NewUCStringCopyN(cx, resultString.begin(),
107 108 : resultString.length());
108 54 : if (!result)
109 0 : return JS_FALSE;
110 :
111 54 : JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
112 54 : return JS_TRUE;
113 : }
114 :
115 : JSObject*
116 57 : Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks)
117 : {
118 57 : JSObject* libraryObj = JS_NewObject(cx, &sLibraryClass, NULL, NULL);
119 57 : if (!libraryObj)
120 0 : return NULL;
121 114 : js::AutoObjectRooter root(cx, libraryObj);
122 :
123 : // initialize the library
124 57 : JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
125 :
126 : // attach API functions
127 57 : if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
128 0 : return NULL;
129 :
130 57 : if (!JSVAL_IS_STRING(path)) {
131 0 : JS_ReportError(cx, "open takes a string argument");
132 0 : return NULL;
133 : }
134 :
135 : PRLibSpec libSpec;
136 57 : JSFlatString* pathStr = JS_FlattenString(cx, JSVAL_TO_STRING(path));
137 57 : if (!pathStr)
138 0 : return NULL;
139 : #ifdef XP_WIN
140 : // On Windows, converting to native charset may corrupt path string.
141 : // So, we have to use Unicode path directly.
142 : const PRUnichar* pathChars = JS_GetFlatStringChars(pathStr);
143 : if (!pathChars)
144 : return NULL;
145 :
146 : libSpec.value.pathname_u = pathChars;
147 : libSpec.type = PR_LibSpec_PathnameU;
148 : #else
149 : // Convert to platform native charset if the appropriate callback has been
150 : // provided.
151 : char* pathBytes;
152 57 : if (callbacks && callbacks->unicodeToNative) {
153 : pathBytes =
154 57 : callbacks->unicodeToNative(cx, pathStr->chars(), pathStr->length());
155 114 : if (!pathBytes)
156 0 : return NULL;
157 :
158 : } else {
159 : // Fallback: assume the platform native charset is UTF-8. This is true
160 : // for Mac OS X, Android, and probably Linux.
161 : size_t nbytes =
162 0 : GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length());
163 0 : if (nbytes == (size_t) -1)
164 0 : return NULL;
165 :
166 0 : pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
167 0 : if (!pathBytes)
168 0 : return NULL;
169 :
170 : ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStr->chars(),
171 0 : pathStr->length(), pathBytes, &nbytes));
172 0 : pathBytes[nbytes] = 0;
173 : }
174 :
175 57 : libSpec.value.pathname = pathBytes;
176 57 : libSpec.type = PR_LibSpec_Pathname;
177 : #endif
178 :
179 57 : PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0);
180 : #ifndef XP_WIN
181 57 : JS_free(cx, pathBytes);
182 : #endif
183 57 : if (!library) {
184 2 : JS_ReportError(cx, "couldn't open library");
185 2 : return NULL;
186 : }
187 :
188 : // stash the library
189 55 : JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library));
190 :
191 55 : return libraryObj;
192 : }
193 :
194 : bool
195 14984 : Library::IsLibrary(JSObject* obj)
196 : {
197 14984 : return JS_GetClass(obj) == &sLibraryClass;
198 : }
199 :
200 : PRLibrary*
201 7516 : Library::GetLibrary(JSObject* obj)
202 : {
203 7516 : JS_ASSERT(IsLibrary(obj));
204 :
205 7516 : jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
206 7516 : return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
207 : }
208 :
209 : void
210 60 : Library::Finalize(JSContext* cx, JSObject* obj)
211 : {
212 : // unload the library
213 60 : PRLibrary* library = GetLibrary(obj);
214 60 : if (library)
215 55 : PR_UnloadLibrary(library);
216 60 : }
217 :
218 : JSBool
219 57 : Library::Open(JSContext* cx, unsigned argc, jsval *vp)
220 : {
221 57 : JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
222 57 : if (!ctypesObj || !IsCTypesGlobal(ctypesObj)) {
223 0 : JS_ReportError(cx, "not a ctypes object");
224 0 : return JS_FALSE;
225 : }
226 :
227 57 : if (argc != 1 || JSVAL_IS_VOID(JS_ARGV(cx, vp)[0])) {
228 0 : JS_ReportError(cx, "open requires a single argument");
229 0 : return JS_FALSE;
230 : }
231 :
232 57 : JSObject* library = Create(cx, JS_ARGV(cx, vp)[0], GetCallbacks(ctypesObj));
233 57 : if (!library)
234 2 : return JS_FALSE;
235 :
236 55 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(library));
237 55 : return JS_TRUE;
238 : }
239 :
240 : JSBool
241 4 : Library::Close(JSContext* cx, unsigned argc, jsval* vp)
242 : {
243 4 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
244 4 : if (!obj || !IsLibrary(obj)) {
245 1 : JS_ReportError(cx, "not a library");
246 1 : return JS_FALSE;
247 : }
248 :
249 3 : if (argc != 0) {
250 0 : JS_ReportError(cx, "close doesn't take any arguments");
251 0 : return JS_FALSE;
252 : }
253 :
254 : // delete our internal objects
255 3 : Finalize(cx, obj);
256 3 : JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
257 :
258 3 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
259 3 : return JS_TRUE;
260 : }
261 :
262 : JSBool
263 1480 : Library::Declare(JSContext* cx, unsigned argc, jsval* vp)
264 : {
265 1480 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
266 1480 : if (!obj || !IsLibrary(obj)) {
267 1 : JS_ReportError(cx, "not a library");
268 1 : return JS_FALSE;
269 : }
270 :
271 1479 : PRLibrary* library = GetLibrary(obj);
272 1479 : if (!library) {
273 1 : JS_ReportError(cx, "library not open");
274 1 : return JS_FALSE;
275 : }
276 :
277 : // We allow two API variants:
278 : // 1) library.declare(name, abi, returnType, argType1, ...)
279 : // declares a function with the given properties, and resolves the symbol
280 : // address in the library.
281 : // 2) library.declare(name, type)
282 : // declares a symbol of 'type', and resolves it. The object that comes
283 : // back will be of type 'type', and will point into the symbol data.
284 : // This data will be both readable and writable via the usual CData
285 : // accessors. If 'type' is a PointerType to a FunctionType, the result will
286 : // be a function pointer, as with 1).
287 1478 : if (argc < 2) {
288 0 : JS_ReportError(cx, "declare requires at least two arguments");
289 0 : return JS_FALSE;
290 : }
291 :
292 1478 : jsval* argv = JS_ARGV(cx, vp);
293 1478 : if (!JSVAL_IS_STRING(argv[0])) {
294 0 : JS_ReportError(cx, "first argument must be a string");
295 0 : return JS_FALSE;
296 : }
297 :
298 1478 : JSObject* fnObj = NULL;
299 : JSObject* typeObj;
300 2956 : js::AutoObjectRooter root(cx);
301 1478 : bool isFunction = argc > 2;
302 1478 : if (isFunction) {
303 : // Case 1).
304 : // Create a FunctionType representing the function.
305 : fnObj = FunctionType::CreateInternal(cx,
306 1295 : argv[1], argv[2], &argv[3], argc - 3);
307 1295 : if (!fnObj)
308 2 : return JS_FALSE;
309 1293 : root.setObject(fnObj);
310 :
311 : // Make a function pointer type.
312 1293 : typeObj = PointerType::CreateInternal(cx, fnObj);
313 1293 : if (!typeObj)
314 0 : return JS_FALSE;
315 1293 : root.setObject(typeObj);
316 :
317 : } else {
318 : // Case 2).
319 549 : if (JSVAL_IS_PRIMITIVE(argv[1]) ||
320 183 : !CType::IsCType(JSVAL_TO_OBJECT(argv[1])) ||
321 183 : !CType::IsSizeDefined(JSVAL_TO_OBJECT(argv[1]))) {
322 0 : JS_ReportError(cx, "second argument must be a type of defined size");
323 0 : return JS_FALSE;
324 : }
325 :
326 183 : typeObj = JSVAL_TO_OBJECT(argv[1]);
327 183 : if (CType::GetTypeCode(typeObj) == TYPE_pointer) {
328 181 : fnObj = PointerType::GetBaseType(typeObj);
329 181 : isFunction = fnObj && CType::GetTypeCode(fnObj) == TYPE_function;
330 : }
331 : }
332 :
333 : void* data;
334 : PRFuncPtr fnptr;
335 1476 : JSString* nameStr = JSVAL_TO_STRING(argv[0]);
336 2952 : AutoCString symbol;
337 1476 : if (isFunction) {
338 : // Build the symbol, with mangling if necessary.
339 1474 : FunctionType::BuildSymbolName(nameStr, fnObj, symbol);
340 1474 : AppendString(symbol, "\0");
341 :
342 : // Look up the function symbol.
343 1474 : fnptr = PR_FindFunctionSymbol(library, symbol.begin());
344 1474 : if (!fnptr) {
345 0 : JS_ReportError(cx, "couldn't find function symbol in library");
346 0 : return JS_FALSE;
347 : }
348 1474 : data = &fnptr;
349 :
350 : } else {
351 : // 'typeObj' is another data type. Look up the data symbol.
352 2 : AppendString(symbol, nameStr);
353 2 : AppendString(symbol, "\0");
354 :
355 2 : data = PR_FindSymbol(library, symbol.begin());
356 2 : if (!data) {
357 0 : JS_ReportError(cx, "couldn't find symbol in library");
358 0 : return JS_FALSE;
359 : }
360 : }
361 :
362 1476 : JSObject* result = CData::Create(cx, typeObj, obj, data, isFunction);
363 1476 : if (!result)
364 0 : return JS_FALSE;
365 :
366 1476 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
367 :
368 : // Seal the CData object, to prevent modification of the function pointer.
369 : // This permanently associates this object with the library, and avoids
370 : // having to do things like reset SLOT_REFERENT when someone tries to
371 : // change the pointer value.
372 : // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
373 : // could be called on a sealed object.
374 1476 : if (isFunction && !JS_FreezeObject(cx, result))
375 0 : return JS_FALSE;
376 :
377 1476 : return JS_TRUE;
378 : }
379 :
380 : }
381 : }
382 :
|