/js/src/ctypes/Library.cpp
http://github.com/zpao/v8monkey · C++ · 382 lines · 246 code · 57 blank · 79 comment · 51 complexity · aa102e7b81cc261ce737fc81b942f8b5 MD5 · raw file
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
- /* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is js-ctypes.
- *
- * The Initial Developer of the Original Code is
- * The Mozilla Foundation <http://www.mozilla.org/>.
- * Portions created by the Initial Developer are Copyright (C) 2009
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
- * Fredrik Larsson <nossralf@gmail.com>
- * Dan Witte <dwitte@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
- #include "jscntxt.h"
- #include "jsstr.h"
- #include "Library.h"
- #include "CTypes.h"
- #include "prlink.h"
- namespace js {
- namespace ctypes {
- /*******************************************************************************
- ** JSAPI function prototypes
- *******************************************************************************/
- namespace Library
- {
- static void Finalize(JSContext* cx, JSObject* obj);
- static JSBool Close(JSContext* cx, uintN argc, jsval* vp);
- static JSBool Declare(JSContext* cx, uintN argc, jsval* vp);
- }
- /*******************************************************************************
- ** JSObject implementation
- *******************************************************************************/
- static JSClass sLibraryClass = {
- "Library",
- JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, Library::Finalize,
- JSCLASS_NO_OPTIONAL_MEMBERS
- };
- #define CTYPESFN_FLAGS \
- (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
- static JSFunctionSpec sLibraryFunctions[] = {
- JS_FN("close", Library::Close, 0, CTYPESFN_FLAGS),
- JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
- JS_FS_END
- };
- JSBool
- Library::Name(JSContext* cx, uintN argc, jsval *vp)
- {
- if (argc != 1) {
- JS_ReportError(cx, "libraryName takes one argument");
- return JS_FALSE;
- }
- jsval arg = JS_ARGV(cx, vp)[0];
- JSString* str = NULL;
- if (JSVAL_IS_STRING(arg)) {
- str = JSVAL_TO_STRING(arg);
- }
- else {
- JS_ReportError(cx, "name argument must be a string");
- return JS_FALSE;
- }
- AutoString resultString;
- AppendString(resultString, DLL_PREFIX);
- AppendString(resultString, str);
- AppendString(resultString, DLL_SUFFIX);
- JSString *result = JS_NewUCStringCopyN(cx, resultString.begin(),
- resultString.length());
- if (!result)
- return JS_FALSE;
- JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
- return JS_TRUE;
- }
- JSObject*
- Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks)
- {
- JSObject* libraryObj = JS_NewObject(cx, &sLibraryClass, NULL, NULL);
- if (!libraryObj)
- return NULL;
- js::AutoObjectRooter root(cx, libraryObj);
- // initialize the library
- JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
- // attach API functions
- if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
- return NULL;
- if (!JSVAL_IS_STRING(path)) {
- JS_ReportError(cx, "open takes a string argument");
- return NULL;
- }
- PRLibSpec libSpec;
- JSFlatString* pathStr = JS_FlattenString(cx, JSVAL_TO_STRING(path));
- if (!pathStr)
- return NULL;
- #ifdef XP_WIN
- // On Windows, converting to native charset may corrupt path string.
- // So, we have to use Unicode path directly.
- const PRUnichar* pathChars = JS_GetFlatStringChars(pathStr);
- if (!pathChars)
- return NULL;
- libSpec.value.pathname_u = pathChars;
- libSpec.type = PR_LibSpec_PathnameU;
- #else
- // Convert to platform native charset if the appropriate callback has been
- // provided.
- char* pathBytes;
- if (callbacks && callbacks->unicodeToNative) {
- pathBytes =
- callbacks->unicodeToNative(cx, pathStr->chars(), pathStr->length());
- if (!pathBytes)
- return NULL;
- } else {
- // Fallback: assume the platform native charset is UTF-8. This is true
- // for Mac OS X, Android, and probably Linux.
- size_t nbytes =
- GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length());
- if (nbytes == (size_t) -1)
- return NULL;
- pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
- if (!pathBytes)
- return NULL;
- ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStr->chars(),
- pathStr->length(), pathBytes, &nbytes));
- pathBytes[nbytes] = 0;
- }
- libSpec.value.pathname = pathBytes;
- libSpec.type = PR_LibSpec_Pathname;
- #endif
- PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0);
- #ifndef XP_WIN
- JS_free(cx, pathBytes);
- #endif
- if (!library) {
- JS_ReportError(cx, "couldn't open library");
- return NULL;
- }
- // stash the library
- JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library));
- return libraryObj;
- }
- bool
- Library::IsLibrary(JSObject* obj)
- {
- return JS_GetClass(obj) == &sLibraryClass;
- }
- PRLibrary*
- Library::GetLibrary(JSObject* obj)
- {
- JS_ASSERT(IsLibrary(obj));
- jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
- return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
- }
- void
- Library::Finalize(JSContext* cx, JSObject* obj)
- {
- // unload the library
- PRLibrary* library = GetLibrary(obj);
- if (library)
- PR_UnloadLibrary(library);
- }
- JSBool
- Library::Open(JSContext* cx, uintN argc, jsval *vp)
- {
- JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
- if (!ctypesObj || !IsCTypesGlobal(ctypesObj)) {
- JS_ReportError(cx, "not a ctypes object");
- return JS_FALSE;
- }
- if (argc != 1 || JSVAL_IS_VOID(JS_ARGV(cx, vp)[0])) {
- JS_ReportError(cx, "open requires a single argument");
- return JS_FALSE;
- }
- JSObject* library = Create(cx, JS_ARGV(cx, vp)[0], GetCallbacks(ctypesObj));
- if (!library)
- return JS_FALSE;
- JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(library));
- return JS_TRUE;
- }
- JSBool
- Library::Close(JSContext* cx, uintN argc, jsval* vp)
- {
- JSObject* obj = JS_THIS_OBJECT(cx, vp);
- if (!obj || !IsLibrary(obj)) {
- JS_ReportError(cx, "not a library");
- return JS_FALSE;
- }
- if (argc != 0) {
- JS_ReportError(cx, "close doesn't take any arguments");
- return JS_FALSE;
- }
- // delete our internal objects
- Finalize(cx, obj);
- JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
- JS_SET_RVAL(cx, vp, JSVAL_VOID);
- return JS_TRUE;
- }
- JSBool
- Library::Declare(JSContext* cx, uintN argc, jsval* vp)
- {
- JSObject* obj = JS_THIS_OBJECT(cx, vp);
- if (!obj || !IsLibrary(obj)) {
- JS_ReportError(cx, "not a library");
- return JS_FALSE;
- }
- PRLibrary* library = GetLibrary(obj);
- if (!library) {
- JS_ReportError(cx, "library not open");
- return JS_FALSE;
- }
- // We allow two API variants:
- // 1) library.declare(name, abi, returnType, argType1, ...)
- // declares a function with the given properties, and resolves the symbol
- // address in the library.
- // 2) library.declare(name, type)
- // declares a symbol of 'type', and resolves it. The object that comes
- // back will be of type 'type', and will point into the symbol data.
- // This data will be both readable and writable via the usual CData
- // accessors. If 'type' is a PointerType to a FunctionType, the result will
- // be a function pointer, as with 1).
- if (argc < 2) {
- JS_ReportError(cx, "declare requires at least two arguments");
- return JS_FALSE;
- }
- jsval* argv = JS_ARGV(cx, vp);
- if (!JSVAL_IS_STRING(argv[0])) {
- JS_ReportError(cx, "first argument must be a string");
- return JS_FALSE;
- }
- JSObject* fnObj = NULL;
- JSObject* typeObj;
- js::AutoObjectRooter root(cx);
- bool isFunction = argc > 2;
- if (isFunction) {
- // Case 1).
- // Create a FunctionType representing the function.
- fnObj = FunctionType::CreateInternal(cx,
- argv[1], argv[2], &argv[3], argc - 3);
- if (!fnObj)
- return JS_FALSE;
- root.setObject(fnObj);
- // Make a function pointer type.
- typeObj = PointerType::CreateInternal(cx, fnObj);
- if (!typeObj)
- return JS_FALSE;
- root.setObject(typeObj);
- } else {
- // Case 2).
- if (JSVAL_IS_PRIMITIVE(argv[1]) ||
- !CType::IsCType(JSVAL_TO_OBJECT(argv[1])) ||
- !CType::IsSizeDefined(JSVAL_TO_OBJECT(argv[1]))) {
- JS_ReportError(cx, "second argument must be a type of defined size");
- return JS_FALSE;
- }
- typeObj = JSVAL_TO_OBJECT(argv[1]);
- if (CType::GetTypeCode(typeObj) == TYPE_pointer) {
- fnObj = PointerType::GetBaseType(typeObj);
- isFunction = fnObj && CType::GetTypeCode(fnObj) == TYPE_function;
- }
- }
- void* data;
- PRFuncPtr fnptr;
- JSString* nameStr = JSVAL_TO_STRING(argv[0]);
- AutoCString symbol;
- if (isFunction) {
- // Build the symbol, with mangling if necessary.
- FunctionType::BuildSymbolName(nameStr, fnObj, symbol);
- AppendString(symbol, "\0");
- // Look up the function symbol.
- fnptr = PR_FindFunctionSymbol(library, symbol.begin());
- if (!fnptr) {
- JS_ReportError(cx, "couldn't find function symbol in library");
- return JS_FALSE;
- }
- data = &fnptr;
- } else {
- // 'typeObj' is another data type. Look up the data symbol.
- AppendString(symbol, nameStr);
- AppendString(symbol, "\0");
- data = PR_FindSymbol(library, symbol.begin());
- if (!data) {
- JS_ReportError(cx, "couldn't find symbol in library");
- return JS_FALSE;
- }
- }
- JSObject* result = CData::Create(cx, typeObj, obj, data, isFunction);
- if (!result)
- return JS_FALSE;
- JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
- // Seal the CData object, to prevent modification of the function pointer.
- // This permanently associates this object with the library, and avoids
- // having to do things like reset SLOT_REFERENT when someone tries to
- // change the pointer value.
- // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
- // could be called on a sealed object.
- if (isFunction && !JS_FreezeObject(cx, result))
- return JS_FALSE;
- return JS_TRUE;
- }
- }
- }