summaryrefslogtreecommitdiffstats
path: root/js/src/ctypes/Library.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/ctypes/Library.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/ctypes/Library.cpp')
-rw-r--r--js/src/ctypes/Library.cpp383
1 files changed, 383 insertions, 0 deletions
diff --git a/js/src/ctypes/Library.cpp b/js/src/ctypes/Library.cpp
new file mode 100644
index 000000000..9f7225693
--- /dev/null
+++ b/js/src/ctypes/Library.cpp
@@ -0,0 +1,383 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=2 sw=2 et tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ctypes/Library.h"
+
+#include "prerror.h"
+#include "prlink.h"
+
+#include "ctypes/CTypes.h"
+
+namespace js {
+namespace ctypes {
+
+/*******************************************************************************
+** JSAPI function prototypes
+*******************************************************************************/
+
+namespace Library
+{
+ static void Finalize(JSFreeOp* fop, JSObject* obj);
+
+ static bool Close(JSContext* cx, unsigned argc, Value* vp);
+ static bool Declare(JSContext* cx, unsigned argc, Value* vp);
+} // namespace Library
+
+/*******************************************************************************
+** JSObject implementation
+*******************************************************************************/
+
+typedef Rooted<JSFlatString*> RootedFlatString;
+
+static const JSClassOps sLibraryClassOps = {
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, Library::Finalize
+};
+
+static const JSClass sLibraryClass = {
+ "Library",
+ JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS) |
+ JSCLASS_FOREGROUND_FINALIZE,
+ &sLibraryClassOps
+};
+
+#define CTYPESFN_FLAGS \
+ (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
+
+static const JSFunctionSpec sLibraryFunctions[] = {
+ JS_FN("close", Library::Close, 0, CTYPESFN_FLAGS),
+ JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
+ JS_FS_END
+};
+
+bool
+Library::Name(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 1) {
+ JS_ReportErrorASCII(cx, "libraryName takes one argument");
+ return false;
+ }
+
+ Value arg = args[0];
+ JSString* str = nullptr;
+ if (arg.isString()) {
+ str = arg.toString();
+ } else {
+ JS_ReportErrorASCII(cx, "name argument must be a string");
+ return 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 false;
+
+ args.rval().setString(result);
+ return true;
+}
+
+JSObject*
+Library::Create(JSContext* cx, HandleValue path, const JSCTypesCallbacks* callbacks)
+{
+ RootedObject libraryObj(cx, JS_NewObject(cx, &sLibraryClass));
+ if (!libraryObj)
+ return nullptr;
+
+ // initialize the library
+ JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PrivateValue(nullptr));
+
+ // attach API functions
+ if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
+ return nullptr;
+
+ if (!path.isString()) {
+ JS_ReportErrorASCII(cx, "open takes a string argument");
+ return nullptr;
+ }
+
+ PRLibSpec libSpec;
+ RootedFlatString pathStr(cx, JS_FlattenString(cx, path.toString()));
+ if (!pathStr)
+ return nullptr;
+ AutoStableStringChars pathStrChars(cx);
+ if (!pathStrChars.initTwoByte(cx, pathStr))
+ return nullptr;
+#ifdef XP_WIN
+ // On Windows, converting to native charset may corrupt path string.
+ // So, we have to use Unicode path directly.
+ char16ptr_t pathChars = pathStrChars.twoByteChars();
+ 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, pathStrChars.twoByteChars(), pathStr->length());
+ if (!pathBytes)
+ return nullptr;
+
+ } 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, pathStrChars.twoByteChars(), pathStr->length());
+ if (nbytes == (size_t) -1)
+ return nullptr;
+
+ pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
+ if (!pathBytes)
+ return nullptr;
+
+ ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStrChars.twoByteChars(),
+ 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) {
+#define MAX_ERROR_LEN 1024
+ char error[MAX_ERROR_LEN] = "Cannot get error from NSPR.";
+ uint32_t errorLen = PR_GetErrorTextLength();
+ if (errorLen && errorLen < MAX_ERROR_LEN)
+ PR_GetErrorText(error);
+#undef MAX_ERROR_LEN
+
+ if (JS::StringIsASCII(error)) {
+ JSAutoByteString pathCharsUTF8;
+ if (pathCharsUTF8.encodeUtf8(cx, pathStr))
+ JS_ReportErrorUTF8(cx, "couldn't open library %s: %s", pathCharsUTF8.ptr(), error);
+ } else {
+ JSAutoByteString pathCharsLatin1;
+ if (pathCharsLatin1.encodeLatin1(cx, pathStr))
+ JS_ReportErrorLatin1(cx, "couldn't open library %s: %s", pathCharsLatin1.ptr(), error);
+ }
+ return nullptr;
+ }
+
+ // stash the library
+ JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PrivateValue(library));
+
+ return libraryObj;
+}
+
+bool
+Library::IsLibrary(JSObject* obj)
+{
+ return JS_GetClass(obj) == &sLibraryClass;
+}
+
+PRLibrary*
+Library::GetLibrary(JSObject* obj)
+{
+ MOZ_ASSERT(IsLibrary(obj));
+
+ Value slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
+ return static_cast<PRLibrary*>(slot.toPrivate());
+}
+
+static void
+UnloadLibrary(JSObject* obj)
+{
+ PRLibrary* library = Library::GetLibrary(obj);
+ if (library)
+ PR_UnloadLibrary(library);
+}
+
+void
+Library::Finalize(JSFreeOp* fop, JSObject* obj)
+{
+ UnloadLibrary(obj);
+}
+
+bool
+Library::Open(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
+ if (!ctypesObj)
+ return false;
+ if (!IsCTypesGlobal(ctypesObj)) {
+ JS_ReportErrorASCII(cx, "not a ctypes object");
+ return false;
+ }
+
+ if (args.length() != 1 || args[0].isUndefined()) {
+ JS_ReportErrorASCII(cx, "open requires a single argument");
+ return false;
+ }
+
+ JSObject* library = Create(cx, args[0], GetCallbacks(ctypesObj));
+ if (!library)
+ return false;
+
+ args.rval().setObject(*library);
+ return true;
+}
+
+bool
+Library::Close(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ if (!obj)
+ return false;
+ if (!IsLibrary(obj)) {
+ JS_ReportErrorASCII(cx, "not a library");
+ return false;
+ }
+
+ if (args.length() != 0) {
+ JS_ReportErrorASCII(cx, "close doesn't take any arguments");
+ return false;
+ }
+
+ // delete our internal objects
+ UnloadLibrary(obj);
+ JS_SetReservedSlot(obj, SLOT_LIBRARY, PrivateValue(nullptr));
+
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+Library::Declare(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+ if (!obj)
+ return false;
+ if (!IsLibrary(obj)) {
+ JS_ReportErrorASCII(cx, "not a library");
+ return false;
+ }
+
+ PRLibrary* library = GetLibrary(obj);
+ if (!library) {
+ JS_ReportErrorASCII(cx, "library not open");
+ return 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 (args.length() < 2) {
+ JS_ReportErrorASCII(cx, "declare requires at least two arguments");
+ return false;
+ }
+
+ if (!args[0].isString()) {
+ JS_ReportErrorASCII(cx, "first argument must be a string");
+ return false;
+ }
+
+ RootedObject fnObj(cx, nullptr);
+ RootedObject typeObj(cx);
+ bool isFunction = args.length() > 2;
+ if (isFunction) {
+ // Case 1).
+ // Create a FunctionType representing the function.
+ fnObj = FunctionType::CreateInternal(cx, args[1], args[2],
+ HandleValueArray::subarray(args, 3, args.length() - 3));
+ if (!fnObj)
+ return false;
+
+ // Make a function pointer type.
+ typeObj = PointerType::CreateInternal(cx, fnObj);
+ if (!typeObj)
+ return false;
+ } else {
+ // Case 2).
+ if (args[1].isPrimitive() ||
+ !CType::IsCType(args[1].toObjectOrNull()) ||
+ !CType::IsSizeDefined(args[1].toObjectOrNull())) {
+ JS_ReportErrorASCII(cx, "second argument must be a type of defined size");
+ return false;
+ }
+
+ typeObj = args[1].toObjectOrNull();
+ if (CType::GetTypeCode(typeObj) == TYPE_pointer) {
+ fnObj = PointerType::GetBaseType(typeObj);
+ isFunction = fnObj && CType::GetTypeCode(fnObj) == TYPE_function;
+ }
+ }
+
+ void* data;
+ PRFuncPtr fnptr;
+ RootedString nameStr(cx, args[0].toString());
+ 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_ReportErrorASCII(cx, "couldn't find function symbol in library");
+ return 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_ReportErrorASCII(cx, "couldn't find symbol in library");
+ return false;
+ }
+ }
+
+ RootedObject result(cx, CData::Create(cx, typeObj, obj, data, isFunction));
+ if (!result)
+ return false;
+
+ if (isFunction)
+ JS_SetReservedSlot(result, SLOT_FUNNAME, StringValue(nameStr));
+
+ args.rval().setObject(*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 false;
+
+ return true;
+}
+
+} // namespace ctypes
+} // namespace js
+