PageRenderTime 34ms CodeModel.GetById 11ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/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
  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
  4 *
  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/
  9 *
 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.
 14 *
 15 * The Original Code is js-ctypes.
 16 *
 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.
 21 *
 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>
 26 *
 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.
 38 *
 39 * ***** END LICENSE BLOCK ***** */
 40
 41#include "jscntxt.h"
 42#include "jsstr.h"
 43#include "Library.h"
 44#include "CTypes.h"
 45#include "prlink.h"
 46
 47namespace js {
 48namespace ctypes {
 49
 50/*******************************************************************************
 51** JSAPI function prototypes
 52*******************************************************************************/
 53
 54namespace Library
 55{
 56  static void Finalize(JSContext* cx, JSObject* obj);
 57
 58  static JSBool Close(JSContext* cx, uintN argc, jsval* vp);
 59  static JSBool Declare(JSContext* cx, uintN argc, jsval* vp);
 60}
 61
 62/*******************************************************************************
 63** JSObject implementation
 64*******************************************************************************/
 65
 66static JSClass sLibraryClass = {
 67  "Library",
 68  JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
 69  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 70  JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, Library::Finalize,
 71  JSCLASS_NO_OPTIONAL_MEMBERS
 72};
 73
 74#define CTYPESFN_FLAGS \
 75  (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 76
 77static JSFunctionSpec sLibraryFunctions[] = {
 78  JS_FN("close",   Library::Close,   0, CTYPESFN_FLAGS),
 79  JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
 80  JS_FS_END
 81};
 82
 83JSBool
 84Library::Name(JSContext* cx, uintN argc, jsval *vp)
 85{
 86  if (argc != 1) {
 87    JS_ReportError(cx, "libraryName takes one argument");
 88    return JS_FALSE;
 89  }
 90
 91  jsval arg = JS_ARGV(cx, vp)[0];
 92  JSString* str = NULL;
 93  if (JSVAL_IS_STRING(arg)) {
 94    str = JSVAL_TO_STRING(arg);
 95  }
 96  else {
 97    JS_ReportError(cx, "name argument must be a string");
 98      return JS_FALSE;
 99  }
100
101  AutoString resultString;
102  AppendString(resultString, DLL_PREFIX);
103  AppendString(resultString, str);
104  AppendString(resultString, DLL_SUFFIX);
105
106  JSString *result = JS_NewUCStringCopyN(cx, resultString.begin(),
107                                         resultString.length());
108  if (!result)
109    return JS_FALSE;
110
111  JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
112  return JS_TRUE;
113}
114
115JSObject*
116Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks)
117{
118  JSObject* libraryObj = JS_NewObject(cx, &sLibraryClass, NULL, NULL);
119  if (!libraryObj)
120    return NULL;
121  js::AutoObjectRooter root(cx, libraryObj);
122
123  // initialize the library
124  JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
125
126  // attach API functions
127  if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions))
128    return NULL;
129
130  if (!JSVAL_IS_STRING(path)) {
131    JS_ReportError(cx, "open takes a string argument");
132    return NULL;
133  }
134
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;
145
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;
157
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      GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length());
163    if (nbytes == (size_t) -1)
164      return NULL;
165
166    pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
167    if (!pathBytes)
168      return NULL;
169
170    ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStr->chars(),
171                pathStr->length(), pathBytes, &nbytes));
172    pathBytes[nbytes] = 0;
173  }
174
175  libSpec.value.pathname = pathBytes;
176  libSpec.type = PR_LibSpec_Pathname;
177#endif
178
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;
186  }
187
188  // stash the library
189  JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library));
190
191  return libraryObj;
192}
193
194bool
195Library::IsLibrary(JSObject* obj)
196{
197  return JS_GetClass(obj) == &sLibraryClass;
198}
199
200PRLibrary*
201Library::GetLibrary(JSObject* obj)
202{
203  JS_ASSERT(IsLibrary(obj));
204
205  jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
206  return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
207}
208
209void
210Library::Finalize(JSContext* cx, JSObject* obj)
211{
212  // unload the library
213  PRLibrary* library = GetLibrary(obj);
214  if (library)
215    PR_UnloadLibrary(library);
216}
217
218JSBool
219Library::Open(JSContext* cx, uintN argc, jsval *vp)
220{
221  JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
222  if (!ctypesObj || !IsCTypesGlobal(ctypesObj)) {
223    JS_ReportError(cx, "not a ctypes object");
224    return JS_FALSE;
225  }
226
227  if (argc != 1 || JSVAL_IS_VOID(JS_ARGV(cx, vp)[0])) {
228    JS_ReportError(cx, "open requires a single argument");
229    return JS_FALSE;
230  }
231
232  JSObject* library = Create(cx, JS_ARGV(cx, vp)[0], GetCallbacks(ctypesObj));
233  if (!library)
234    return JS_FALSE;
235
236  JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(library));
237  return JS_TRUE;
238}
239
240JSBool
241Library::Close(JSContext* cx, uintN argc, jsval* vp)
242{
243  JSObject* obj = JS_THIS_OBJECT(cx, vp);
244  if (!obj || !IsLibrary(obj)) {
245    JS_ReportError(cx, "not a library");
246    return JS_FALSE;
247  }
248
249  if (argc != 0) {
250    JS_ReportError(cx, "close doesn't take any arguments");
251    return JS_FALSE;
252  }
253
254  // delete our internal objects
255  Finalize(cx, obj);
256  JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
257
258  JS_SET_RVAL(cx, vp, JSVAL_VOID);
259  return JS_TRUE;
260}
261
262JSBool
263Library::Declare(JSContext* cx, uintN argc, jsval* vp)
264{
265  JSObject* obj = JS_THIS_OBJECT(cx, vp);
266  if (!obj || !IsLibrary(obj)) {
267    JS_ReportError(cx, "not a library");
268    return JS_FALSE;
269  }
270
271  PRLibrary* library = GetLibrary(obj);
272  if (!library) {
273    JS_ReportError(cx, "library not open");
274    return JS_FALSE;
275  }
276
277  // We allow two API variants:
278  // 1) library.declare(name, abi, returnType, argType1, ...)
279  //    declares a function with the given properties, and resolves the symbol
280  //    address in the library.
281  // 2) library.declare(name, type)
282  //    declares a symbol of 'type', and resolves it. The object that comes
283  //    back will be of type 'type', and will point into the symbol data.
284  //    This data will be both readable and writable via the usual CData
285  //    accessors. If 'type' is a PointerType to a FunctionType, the result will
286  //    be a function pointer, as with 1). 
287  if (argc < 2) {
288    JS_ReportError(cx, "declare requires at least two arguments");
289    return JS_FALSE;
290  }
291
292  jsval* argv = JS_ARGV(cx, vp);
293  if (!JSVAL_IS_STRING(argv[0])) {
294    JS_ReportError(cx, "first argument must be a string");
295    return JS_FALSE;
296  }
297
298  JSObject* fnObj = NULL;
299  JSObject* typeObj;
300  js::AutoObjectRooter root(cx);
301  bool isFunction = argc > 2;
302  if (isFunction) {
303    // Case 1).
304    // Create a FunctionType representing the function.
305    fnObj = FunctionType::CreateInternal(cx,
306              argv[1], argv[2], &argv[3], argc - 3);
307    if (!fnObj)
308      return JS_FALSE;
309    root.setObject(fnObj);
310
311    // Make a function pointer type.
312    typeObj = PointerType::CreateInternal(cx, fnObj);
313    if (!typeObj)
314      return JS_FALSE;
315    root.setObject(typeObj);
316
317  } else {
318    // Case 2).
319    if (JSVAL_IS_PRIMITIVE(argv[1]) ||
320        !CType::IsCType(JSVAL_TO_OBJECT(argv[1])) ||
321        !CType::IsSizeDefined(JSVAL_TO_OBJECT(argv[1]))) {
322      JS_ReportError(cx, "second argument must be a type of defined size");
323      return JS_FALSE;
324    }
325
326    typeObj = JSVAL_TO_OBJECT(argv[1]);
327    if (CType::GetTypeCode(typeObj) == TYPE_pointer) {
328      fnObj = PointerType::GetBaseType(typeObj);
329      isFunction = fnObj && CType::GetTypeCode(fnObj) == TYPE_function;
330    }
331  }
332
333  void* data;
334  PRFuncPtr fnptr;
335  JSString* nameStr = JSVAL_TO_STRING(argv[0]);
336  AutoCString symbol;
337  if (isFunction) {
338    // Build the symbol, with mangling if necessary.
339    FunctionType::BuildSymbolName(nameStr, fnObj, symbol);
340    AppendString(symbol, "\0");
341
342    // Look up the function symbol.
343    fnptr = PR_FindFunctionSymbol(library, symbol.begin());
344    if (!fnptr) {
345      JS_ReportError(cx, "couldn't find function symbol in library");
346      return JS_FALSE;
347    }
348    data = &fnptr;
349
350  } else {
351    // 'typeObj' is another data type. Look up the data symbol.
352    AppendString(symbol, nameStr);
353    AppendString(symbol, "\0");
354
355    data = PR_FindSymbol(library, symbol.begin());
356    if (!data) {
357      JS_ReportError(cx, "couldn't find symbol in library");
358      return JS_FALSE;
359    }
360  }
361
362  JSObject* result = CData::Create(cx, typeObj, obj, data, isFunction);
363  if (!result)
364    return JS_FALSE;
365
366  JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
367
368  // Seal the CData object, to prevent modification of the function pointer.
369  // This permanently associates this object with the library, and avoids
370  // having to do things like reset SLOT_REFERENT when someone tries to
371  // change the pointer value.
372  // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
373  // could be called on a sealed object.
374  if (isFunction && !JS_FreezeObject(cx, result))
375    return JS_FALSE;
376
377  return JS_TRUE;
378}
379
380}
381}
382