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
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.
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 ***** */
49 /*******************************************************************************
50 ** JSAPI function prototypes
51 *******************************************************************************/
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
= {
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
),
83 Library::Name(JSContext
* cx
, uintN argc
, jsval
*vp
)
86 JS_ReportError(cx
, "libraryName takes one argument");
90 jsval arg
= JS_ARGV(cx
, vp
)[0];
92 if (JSVAL_IS_STRING(arg
)) {
93 str
= JSVAL_TO_STRING(arg
);
96 JS_ReportError(cx
, "name argument must be a string");
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());
110 JS_SET_RVAL(cx
, vp
, STRING_TO_JSVAL(result
));
115 Library::Create(JSContext
* cx
, jsval path
, JSCTypesCallbacks
* callbacks
)
117 JSObject
* libraryObj
= JS_NewObject(cx
, &sLibraryClass
, NULL
, NULL
);
120 js::AutoObjectRooter
root(cx
, libraryObj
);
122 // initialize the library
123 if (!JS_SetReservedSlot(cx
, libraryObj
, SLOT_LIBRARY
, PRIVATE_TO_JSVAL(NULL
)))
126 // attach API functions
127 if (!JS_DefineFunctions(cx
, libraryObj
, sLibraryFunctions
))
130 if (!JSVAL_IS_STRING(path
)) {
131 JS_ReportError(cx
, "open takes a string argument");
136 JSFlatString
* pathStr
= JS_FlattenString(cx
, JSVAL_TO_STRING(path
));
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
);
146 libSpec
.value
.pathname_u
= pathChars
;
147 libSpec
.type
= PR_LibSpec_PathnameU
;
149 // Convert to platform native charset if the appropriate callback has been
152 if (callbacks
&& callbacks
->unicodeToNative
) {
154 callbacks
->unicodeToNative(cx
, pathStr
->chars(), pathStr
->length());
159 // Fallback: assume the platform native charset is UTF-8. This is true
160 // for Mac OS X, Android, and probably Linux.
162 js_GetDeflatedUTF8StringLength(cx
, pathStr
->chars(), pathStr
->length());
163 if (nbytes
== (size_t) -1)
166 pathBytes
= static_cast<char*>(JS_malloc(cx
, nbytes
+ 1));
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
;
179 PRLibrary
* library
= PR_LoadLibraryWithFlags(libSpec
, 0);
181 JS_free(cx
, pathBytes
);
184 JS_ReportError(cx
, "couldn't open library");
189 if (!JS_SetReservedSlot(cx
, libraryObj
, SLOT_LIBRARY
,
190 PRIVATE_TO_JSVAL(library
)))
197 Library::IsLibrary(JSContext
* cx
, JSObject
* obj
)
199 return JS_GET_CLASS(cx
, obj
) == &sLibraryClass
;
203 Library::GetLibrary(JSContext
* cx
, JSObject
* obj
)
205 JS_ASSERT(IsLibrary(cx
, obj
));
208 JS_GetReservedSlot(cx
, obj
, SLOT_LIBRARY
, &slot
);
209 return static_cast<PRLibrary
*>(JSVAL_TO_PRIVATE(slot
));
213 Library::Finalize(JSContext
* cx
, JSObject
* obj
)
215 // unload the library
216 PRLibrary
* library
= GetLibrary(cx
, obj
);
218 PR_UnloadLibrary(library
);
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");
230 if (argc
!= 1 || JSVAL_IS_VOID(JS_ARGV(cx
, vp
)[0])) {
231 JS_ReportError(cx
, "open requires a single argument");
235 JSObject
* library
= Create(cx
, JS_ARGV(cx
, vp
)[0], GetCallbacks(cx
, ctypesObj
));
239 JS_SET_RVAL(cx
, vp
, OBJECT_TO_JSVAL(library
));
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");
253 JS_ReportError(cx
, "close doesn't take any arguments");
257 // delete our internal objects
259 JS_SetReservedSlot(cx
, obj
, SLOT_LIBRARY
, PRIVATE_TO_JSVAL(NULL
));
261 JS_SET_RVAL(cx
, vp
, JSVAL_VOID
);
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");
274 PRLibrary
* library
= GetLibrary(cx
, obj
);
276 JS_ReportError(cx
, "library not open");
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).
291 JS_ReportError(cx
, "declare requires at least two arguments");
295 jsval
* argv
= JS_ARGV(cx
, vp
);
296 if (!JSVAL_IS_STRING(argv
[0])) {
297 JS_ReportError(cx
, "first argument must be a string");
301 JSObject
* fnObj
= NULL
;
303 js::AutoObjectRooter
root(cx
);
304 bool isFunction
= argc
> 2;
307 // Create a FunctionType representing the function.
308 fnObj
= FunctionType::CreateInternal(cx
,
309 argv
[1], argv
[2], &argv
[3], argc
- 3);
312 root
.setObject(fnObj
);
314 // Make a function pointer type.
315 typeObj
= PointerType::CreateInternal(cx
, fnObj
);
318 root
.setObject(typeObj
);
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");
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
;
338 JSString
* nameStr
= JSVAL_TO_STRING(argv
[0]);
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());
348 JS_ReportError(cx
, "couldn't find function symbol in library");
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());
360 JS_ReportError(cx
, "couldn't find symbol in library");
365 JSObject
* result
= CData::Create(cx
, typeObj
, obj
, data
, isFunction
);
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
))