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"
11 #include "ctypes/CTypes.h"
16 /*******************************************************************************
17 ** JSAPI function prototypes
18 *******************************************************************************/
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
= {
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
),
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");
60 JSString
* str
= nullptr;
64 JS_ReportError(cx
, "name argument must be a string");
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());
78 args
.rval().setString(result
);
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()));
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
))
98 if (!path
.isString()) {
99 JS_ReportError(cx
, "open takes a string argument");
104 RootedFlatString
pathStr(cx
, JS_FlattenString(cx
, path
.toString()));
107 AutoStableStringChars
pathStrChars(cx
);
108 if (!pathStrChars
.initTwoByte(cx
, pathStr
))
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
;
117 // Convert to platform native charset if the appropriate callback has been
120 if (callbacks
&& callbacks
->unicodeToNative
) {
122 callbacks
->unicodeToNative(cx
, pathStrChars
.twoByteChars(), pathStr
->length());
127 // Fallback: assume the platform native charset is UTF-8. This is true
128 // for Mac OS X, Android, and probably Linux.
130 GetDeflatedUTF8StringLength(cx
, pathStrChars
.twoByteChars(), pathStr
->length());
131 if (nbytes
== (size_t) -1)
134 pathBytes
= static_cast<char*>(JS_malloc(cx
, nbytes
+ 1));
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
;
147 PRLibrary
* library
= PR_LoadLibraryWithFlags(libSpec
, 0);
151 JS_ReportError(cx
, "couldn't open library %hs", pathChars
);
153 JS_ReportError(cx
, "couldn't open library %s", pathBytes
);
154 JS_free(cx
, pathBytes
);
160 JS_free(cx
, pathBytes
);
164 JS_SetReservedSlot(libraryObj
, SLOT_LIBRARY
, PRIVATE_TO_JSVAL(library
));
170 Library::IsLibrary(JSObject
* obj
)
172 return JS_GetClass(obj
) == &sLibraryClass
;
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());
185 UnloadLibrary(JSObject
* obj
)
187 PRLibrary
* library
= Library::GetLibrary(obj
);
189 PR_UnloadLibrary(library
);
193 Library::Finalize(JSFreeOp
* fop
, JSObject
* obj
)
199 Library::Open(JSContext
* cx
, unsigned argc
, jsval
* vp
)
201 CallArgs args
= CallArgsFromVp(argc
, vp
);
202 JSObject
* ctypesObj
= JS_THIS_OBJECT(cx
, vp
);
205 if (!IsCTypesGlobal(ctypesObj
)) {
206 JS_ReportError(cx
, "not a ctypes object");
210 if (args
.length() != 1 || args
[0].isUndefined()) {
211 JS_ReportError(cx
, "open requires a single argument");
215 JSObject
* library
= Create(cx
, args
[0], GetCallbacks(ctypesObj
));
219 args
.rval().setObject(*library
);
224 Library::Close(JSContext
* cx
, unsigned argc
, jsval
* vp
)
226 CallArgs args
= CallArgsFromVp(argc
, vp
);
227 JSObject
* obj
= JS_THIS_OBJECT(cx
, vp
);
230 if (!IsLibrary(obj
)) {
231 JS_ReportError(cx
, "not a library");
235 if (args
.length() != 0) {
236 JS_ReportError(cx
, "close doesn't take any arguments");
240 // delete our internal objects
242 JS_SetReservedSlot(obj
, SLOT_LIBRARY
, PRIVATE_TO_JSVAL(nullptr));
244 args
.rval().setUndefined();
249 Library::Declare(JSContext
* cx
, unsigned argc
, jsval
* vp
)
251 CallArgs args
= CallArgsFromVp(argc
, vp
);
252 RootedObject
obj(cx
, JS_THIS_OBJECT(cx
, vp
));
255 if (!IsLibrary(obj
)) {
256 JS_ReportError(cx
, "not a library");
260 PRLibrary
* library
= GetLibrary(obj
);
262 JS_ReportError(cx
, "library not open");
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");
281 if (!args
[0].isString()) {
282 JS_ReportError(cx
, "first argument must be a string");
286 RootedObject
fnObj(cx
, nullptr);
287 RootedObject
typeObj(cx
);
288 bool isFunction
= args
.length() > 2;
291 // Create a FunctionType representing the function.
292 fnObj
= FunctionType::CreateInternal(cx
,
293 args
[1], args
[2], &args
.array()[3], args
.length() - 3);
297 // Make a function pointer type.
298 typeObj
= PointerType::CreateInternal(cx
, fnObj
);
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");
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
;
319 JSString
* nameStr
= args
[0].toString();
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());
329 JS_ReportError(cx
, "couldn't find function symbol in library");
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());
341 JS_ReportError(cx
, "couldn't find symbol in library");
346 RootedObject
result(cx
, CData::Create(cx
, typeObj
, obj
, data
, isFunction
));
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
))