CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / js / src / ctypes / Library.cpp
blob7e14bbadbbe515deb56cdf0b2b51bef4fbb9989c
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
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/
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.
15 * The Original Code is js-ctypes.
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.
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>
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.
39 * ***** END LICENSE BLOCK ***** */
41 #include "jscntxt.h"
42 #include "Library.h"
43 #include "CTypes.h"
44 #include "prlink.h"
46 namespace js {
47 namespace ctypes {
49 /*******************************************************************************
50 ** JSAPI function prototypes
51 *******************************************************************************/
53 namespace Library
55 static void Finalize(JSContext* cx, JSObject* obj);
57 static JSBool Close(JSContext* cx, uintN argc, jsval* vp);
58 static JSBool Declare(JSContext* cx, uintN argc, jsval* vp);
61 /*******************************************************************************
62 ** JSObject implementation
63 *******************************************************************************/
65 static JSClass sLibraryClass = {
66 "Library",
67 JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS) | JSCLASS_MARK_IS_TRACE,
68 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
69 JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, Library::Finalize,
70 JSCLASS_NO_OPTIONAL_MEMBERS
73 #define CTYPESFN_FLAGS \
74 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
76 static JSFunctionSpec sLibraryFunctions[] = {
77 JS_FN("close", Library::Close, 0, CTYPESFN_FLAGS),
78 JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
79 JS_FS_END
82 JSBool
83 Library::Name(JSContext* cx, uintN argc, jsval *vp)
85 if (argc != 1) {
86 JS_ReportError(cx, "libraryName takes one argument");
87 return JS_FALSE;
90 jsval arg = JS_ARGV(cx, vp)[0];
91 JSString* str = NULL;
92 if (JSVAL_IS_STRING(arg)) {
93 str = JSVAL_TO_STRING(arg);
95 else {
96 JS_ReportError(cx, "name argument must be a string");
97 return JS_FALSE;
100 AutoString resultString;
101 AppendString(resultString, DLL_PREFIX);
102 AppendString(resultString, str);
103 AppendString(resultString, DLL_SUFFIX);
105 JSString *result = JS_NewUCStringCopyN(cx, resultString.begin(),
106 resultString.length());
107 if (!result)
108 return JS_FALSE;
110 JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
111 return JS_TRUE;
114 JSObject*
115 Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks)
117 JSObject* libraryObj = JS_NewObject(cx, &sLibraryClass, NULL, NULL);
118 if (!libraryObj)
119 return NULL;
120 js::AutoObjectRooter root(cx, libraryObj);
122 // initialize the library
123 if (!JS_SetReservedSlot(cx, libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL)))
124 return NULL;
126 // attach API functions
127 if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
128 return NULL;
130 if (!JSVAL_IS_STRING(path)) {
131 JS_ReportError(cx, "open takes a string argument");
132 return NULL;
135 PRLibSpec libSpec;
136 JSFlatString* pathStr = JS_FlattenString(cx, JSVAL_TO_STRING(path));
137 if (!pathStr)
138 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;
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 if (callbacks && callbacks->unicodeToNative) {
153 pathBytes =
154 callbacks->unicodeToNative(cx, pathStr->chars(), pathStr->length());
155 if (!pathBytes)
156 return NULL;
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 js_GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length());
163 if (nbytes == (size_t) -1)
164 return NULL;
166 pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
167 if (!pathBytes)
168 return NULL;
170 ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, pathStr->chars(),
171 pathStr->length(), pathBytes, &nbytes));
172 pathBytes[nbytes] = 0;
175 libSpec.value.pathname = pathBytes;
176 libSpec.type = PR_LibSpec_Pathname;
177 #endif
179 PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0);
180 #ifndef XP_WIN
181 JS_free(cx, pathBytes);
182 #endif
183 if (!library) {
184 JS_ReportError(cx, "couldn't open library");
185 return NULL;
188 // stash the library
189 if (!JS_SetReservedSlot(cx, libraryObj, SLOT_LIBRARY,
190 PRIVATE_TO_JSVAL(library)))
191 return NULL;
193 return libraryObj;
196 bool
197 Library::IsLibrary(JSContext* cx, JSObject* obj)
199 return JS_GET_CLASS(cx, obj) == &sLibraryClass;
202 PRLibrary*
203 Library::GetLibrary(JSContext* cx, JSObject* obj)
205 JS_ASSERT(IsLibrary(cx, obj));
207 jsval slot;
208 JS_GetReservedSlot(cx, obj, SLOT_LIBRARY, &slot);
209 return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
212 void
213 Library::Finalize(JSContext* cx, JSObject* obj)
215 // unload the library
216 PRLibrary* library = GetLibrary(cx, obj);
217 if (library)
218 PR_UnloadLibrary(library);
221 JSBool
222 Library::Open(JSContext* cx, uintN argc, jsval *vp)
224 JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
225 if (!ctypesObj || !IsCTypesGlobal(cx, ctypesObj)) {
226 JS_ReportError(cx, "not a ctypes object");
227 return JS_FALSE;
230 if (argc != 1 || JSVAL_IS_VOID(JS_ARGV(cx, vp)[0])) {
231 JS_ReportError(cx, "open requires a single argument");
232 return JS_FALSE;
235 JSObject* library = Create(cx, JS_ARGV(cx, vp)[0], GetCallbacks(cx, ctypesObj));
236 if (!library)
237 return JS_FALSE;
239 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(library));
240 return JS_TRUE;
243 JSBool
244 Library::Close(JSContext* cx, uintN argc, jsval* vp)
246 JSObject* obj = JS_THIS_OBJECT(cx, vp);
247 if (!obj || !IsLibrary(cx, obj)) {
248 JS_ReportError(cx, "not a library");
249 return JS_FALSE;
252 if (argc != 0) {
253 JS_ReportError(cx, "close doesn't take any arguments");
254 return JS_FALSE;
257 // delete our internal objects
258 Finalize(cx, obj);
259 JS_SetReservedSlot(cx, obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
261 JS_SET_RVAL(cx, vp, JSVAL_VOID);
262 return JS_TRUE;
265 JSBool
266 Library::Declare(JSContext* cx, uintN argc, jsval* vp)
268 JSObject* obj = JS_THIS_OBJECT(cx, vp);
269 if (!obj || !IsLibrary(cx, obj)) {
270 JS_ReportError(cx, "not a library");
271 return JS_FALSE;
274 PRLibrary* library = GetLibrary(cx, obj);
275 if (!library) {
276 JS_ReportError(cx, "library not open");
277 return JS_FALSE;
280 // We allow two API variants:
281 // 1) library.declare(name, abi, returnType, argType1, ...)
282 // declares a function with the given properties, and resolves the symbol
283 // address in the library.
284 // 2) library.declare(name, type)
285 // declares a symbol of 'type', and resolves it. The object that comes
286 // back will be of type 'type', and will point into the symbol data.
287 // This data will be both readable and writable via the usual CData
288 // accessors. If 'type' is a PointerType to a FunctionType, the result will
289 // be a function pointer, as with 1).
290 if (argc < 2) {
291 JS_ReportError(cx, "declare requires at least two arguments");
292 return JS_FALSE;
295 jsval* argv = JS_ARGV(cx, vp);
296 if (!JSVAL_IS_STRING(argv[0])) {
297 JS_ReportError(cx, "first argument must be a string");
298 return JS_FALSE;
301 JSObject* fnObj = NULL;
302 JSObject* typeObj;
303 js::AutoObjectRooter root(cx);
304 bool isFunction = argc > 2;
305 if (isFunction) {
306 // Case 1).
307 // Create a FunctionType representing the function.
308 fnObj = FunctionType::CreateInternal(cx,
309 argv[1], argv[2], &argv[3], argc - 3);
310 if (!fnObj)
311 return JS_FALSE;
312 root.setObject(fnObj);
314 // Make a function pointer type.
315 typeObj = PointerType::CreateInternal(cx, fnObj);
316 if (!typeObj)
317 return JS_FALSE;
318 root.setObject(typeObj);
320 } else {
321 // Case 2).
322 if (JSVAL_IS_PRIMITIVE(argv[1]) ||
323 !CType::IsCType(cx, JSVAL_TO_OBJECT(argv[1])) ||
324 !CType::IsSizeDefined(cx, JSVAL_TO_OBJECT(argv[1]))) {
325 JS_ReportError(cx, "second argument must be a type of defined size");
326 return JS_FALSE;
329 typeObj = JSVAL_TO_OBJECT(argv[1]);
330 if (CType::GetTypeCode(cx, typeObj) == TYPE_pointer) {
331 fnObj = PointerType::GetBaseType(cx, typeObj);
332 isFunction = fnObj && CType::GetTypeCode(cx, fnObj) == TYPE_function;
336 void* data;
337 PRFuncPtr fnptr;
338 JSString* nameStr = JSVAL_TO_STRING(argv[0]);
339 AutoCString symbol;
340 if (isFunction) {
341 // Build the symbol, with mangling if necessary.
342 FunctionType::BuildSymbolName(cx, nameStr, fnObj, symbol);
343 AppendString(symbol, "\0");
345 // Look up the function symbol.
346 fnptr = PR_FindFunctionSymbol(library, symbol.begin());
347 if (!fnptr) {
348 JS_ReportError(cx, "couldn't find function symbol in library");
349 return JS_FALSE;
351 data = &fnptr;
353 } else {
354 // 'typeObj' is another data type. Look up the data symbol.
355 AppendString(symbol, nameStr);
356 AppendString(symbol, "\0");
358 data = PR_FindSymbol(library, symbol.begin());
359 if (!data) {
360 JS_ReportError(cx, "couldn't find symbol in library");
361 return JS_FALSE;
365 JSObject* result = CData::Create(cx, typeObj, obj, data, isFunction);
366 if (!result)
367 return JS_FALSE;
369 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
371 // Seal the CData object, to prevent modification of the function pointer.
372 // This permanently associates this object with the library, and avoids
373 // having to do things like reset SLOT_REFERENT when someone tries to
374 // change the pointer value.
375 // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
376 // could be called on a sealed object.
377 if (isFunction && !JS_FreezeObject(cx, result))
378 return JS_FALSE;
380 return JS_TRUE;