PageRenderTime 156ms CodeModel.GetById 49ms app.highlight 88ms RepoModel.GetById 1ms app.codeStats 2ms

/js/src/ctypes/CTypes.cpp

http://github.com/zpao/v8monkey
C++ | 6444 lines | 4660 code | 932 blank | 852 comment | 959 complexity | 829baca86e5d654eabff0f1dc74200a4 MD5 | raw file

Large files files are truncated, but you can click here to view the full 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 *  Dan Witte <dwitte@mozilla.com>
  24 *
  25 * Alternatively, the contents of this file may be used under the terms of
  26 * either the GNU General Public License Version 2 or later (the "GPL"), or
  27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28 * in which case the provisions of the GPL or the LGPL are applicable instead
  29 * of those above. If you wish to allow use of your version of this file only
  30 * under the terms of either the GPL or the LGPL, and not to allow others to
  31 * use your version of this file under the terms of the MPL, indicate your
  32 * decision by deleting the provisions above and replace them with the notice
  33 * and other provisions required by the GPL or the LGPL. If you do not delete
  34 * the provisions above, a recipient may use your version of this file under
  35 * the terms of any one of the MPL, the GPL or the LGPL.
  36 *
  37 * ***** END LICENSE BLOCK ***** */
  38
  39#include "CTypes.h"
  40#include "Library.h"
  41#include "jsnum.h"
  42#include "jscompartment.h"
  43#include "jsobjinlines.h"
  44#include <limits>
  45
  46#include <math.h>
  47#if defined(XP_WIN) || defined(XP_OS2)
  48#include <float.h>
  49#endif
  50
  51#if defined(SOLARIS)
  52#include <ieeefp.h>
  53#endif
  54
  55#ifdef HAVE_SSIZE_T
  56#include <sys/types.h>
  57#endif
  58
  59using namespace std;
  60
  61namespace js {
  62namespace ctypes {
  63
  64/*******************************************************************************
  65** JSAPI function prototypes
  66*******************************************************************************/
  67
  68static JSBool ConstructAbstract(JSContext* cx, uintN argc, jsval* vp);
  69
  70namespace CType {
  71  static JSBool ConstructData(JSContext* cx, uintN argc, jsval* vp);
  72  static JSBool ConstructBasic(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
  73
  74  static void Trace(JSTracer* trc, JSObject* obj);
  75  static void Finalize(JSContext* cx, JSObject* obj);
  76  static void FinalizeProtoClass(JSContext* cx, JSObject* obj);
  77
  78  static JSBool PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval,
  79    jsval* vp);
  80  static JSBool NameGetter(JSContext* cx, JSObject* obj, jsid idval,
  81    jsval* vp);
  82  static JSBool SizeGetter(JSContext* cx, JSObject* obj, jsid idval,
  83    jsval* vp);
  84  static JSBool PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
  85  static JSBool CreateArray(JSContext* cx, uintN argc, jsval* vp);
  86  static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
  87  static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
  88  static JSBool HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp);
  89}
  90
  91namespace PointerType {
  92  static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
  93  static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
  94
  95  static JSBool TargetTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
  96    jsval* vp);
  97  static JSBool ContentsGetter(JSContext* cx, JSObject* obj, jsid idval,
  98    jsval* vp);
  99  static JSBool ContentsSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict,
 100    jsval* vp);
 101  static JSBool IsNull(JSContext* cx, uintN argc, jsval* vp);
 102  static JSBool Increment(JSContext* cx, uintN argc, jsval* vp);
 103  static JSBool Decrement(JSContext* cx, uintN argc, jsval* vp);
 104  // The following is not an instance function, since we don't want to expose arbitrary
 105  // pointer arithmetic at this moment.
 106  static JSBool OffsetBy(JSContext* cx, intN offset, jsval* vp);
 107}
 108
 109namespace ArrayType {
 110  static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
 111  static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
 112
 113  static JSBool ElementTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
 114    jsval* vp);
 115  static JSBool LengthGetter(JSContext* cx, JSObject* obj, jsid idval,
 116    jsval* vp);
 117  static JSBool Getter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
 118  static JSBool Setter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp);
 119  static JSBool AddressOfElement(JSContext* cx, uintN argc, jsval* vp);
 120}
 121
 122namespace StructType {
 123  static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
 124  static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
 125
 126  static JSBool FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval,
 127    jsval* vp);
 128  static JSBool FieldGetter(JSContext* cx, JSObject* obj, jsid idval,
 129    jsval* vp);
 130  static JSBool FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict,
 131                            jsval* vp);
 132  static JSBool AddressOfField(JSContext* cx, uintN argc, jsval* vp);
 133  static JSBool Define(JSContext* cx, uintN argc, jsval* vp);
 134}
 135
 136namespace FunctionType {
 137  static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
 138  static JSBool ConstructData(JSContext* cx, JSObject* typeObj,
 139    JSObject* dataObj, JSObject* fnObj, JSObject* thisObj, jsval errVal);
 140
 141  static JSBool Call(JSContext* cx, uintN argc, jsval* vp);
 142
 143  static JSBool ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval,
 144    jsval* vp);
 145  static JSBool ReturnTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
 146    jsval* vp);
 147  static JSBool ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
 148  static JSBool IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval,
 149    jsval* vp);
 150}
 151
 152namespace CClosure {
 153  static void Trace(JSTracer* trc, JSObject* obj);
 154  static void Finalize(JSContext* cx, JSObject* obj);
 155
 156  // libffi callback
 157  static void ClosureStub(ffi_cif* cif, void* result, void** args,
 158    void* userData);
 159}
 160
 161namespace CData {
 162  static void Finalize(JSContext* cx, JSObject* obj);
 163
 164  static JSBool ValueGetter(JSContext* cx, JSObject* obj, jsid idval,
 165                            jsval* vp);
 166  static JSBool ValueSetter(JSContext* cx, JSObject* obj, jsid idval,
 167                            JSBool strict, jsval* vp);
 168  static JSBool Address(JSContext* cx, uintN argc, jsval* vp);
 169  static JSBool ReadString(JSContext* cx, uintN argc, jsval* vp);
 170  static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
 171}
 172
 173// Int64Base provides functions common to Int64 and UInt64.
 174namespace Int64Base {
 175  JSObject* Construct(JSContext* cx, JSObject* proto, uint64_t data,
 176    bool isUnsigned);
 177
 178  uint64_t GetInt(JSObject* obj);
 179
 180  JSBool ToString(JSContext* cx, JSObject* obj, uintN argc, jsval* vp,
 181    bool isUnsigned);
 182
 183  JSBool ToSource(JSContext* cx, JSObject* obj, uintN argc, jsval* vp,
 184    bool isUnsigned);
 185
 186  static void Finalize(JSContext* cx, JSObject* obj);
 187}
 188
 189namespace Int64 {
 190  static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
 191
 192  static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
 193  static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
 194
 195  static JSBool Compare(JSContext* cx, uintN argc, jsval* vp);
 196  static JSBool Lo(JSContext* cx, uintN argc, jsval* vp);
 197  static JSBool Hi(JSContext* cx, uintN argc, jsval* vp);
 198  static JSBool Join(JSContext* cx, uintN argc, jsval* vp);
 199}
 200
 201namespace UInt64 {
 202  static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
 203
 204  static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
 205  static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
 206
 207  static JSBool Compare(JSContext* cx, uintN argc, jsval* vp);
 208  static JSBool Lo(JSContext* cx, uintN argc, jsval* vp);
 209  static JSBool Hi(JSContext* cx, uintN argc, jsval* vp);
 210  static JSBool Join(JSContext* cx, uintN argc, jsval* vp);
 211}
 212
 213/*******************************************************************************
 214** JSClass definitions and initialization functions
 215*******************************************************************************/
 216
 217// Class representing the 'ctypes' object itself. This exists to contain the
 218// JSCTypesCallbacks set of function pointers.
 219static JSClass sCTypesGlobalClass = {
 220  "ctypes",
 221  JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS),
 222  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 223  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
 224  JSCLASS_NO_OPTIONAL_MEMBERS
 225};
 226
 227static JSClass sCABIClass = {
 228  "CABI",
 229  JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS),
 230  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 231  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
 232  JSCLASS_NO_OPTIONAL_MEMBERS
 233};
 234
 235// Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
 236// This exists to give said prototypes a class of "CType", and to provide
 237// reserved slots for stashing various other prototype objects.
 238static JSClass sCTypeProtoClass = {
 239  "CType",
 240  JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
 241  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 242  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::FinalizeProtoClass,
 243  NULL, NULL, ConstructAbstract, ConstructAbstract, NULL, NULL, NULL, NULL
 244};
 245
 246// Class representing ctypes.CData.prototype and the 'prototype' properties
 247// of CTypes. This exists to give said prototypes a class of "CData".
 248static JSClass sCDataProtoClass = {
 249  "CData",
 250  0,
 251  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 252  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
 253  JSCLASS_NO_OPTIONAL_MEMBERS
 254};
 255
 256static JSClass sCTypeClass = {
 257  "CType",
 258  JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
 259  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 260  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize,
 261  NULL, NULL, CType::ConstructData, CType::ConstructData, NULL,
 262  CType::HasInstance, CType::Trace, NULL
 263};
 264
 265static JSClass sCDataClass = {
 266  "CData",
 267  JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
 268  JS_PropertyStub, JS_PropertyStub, ArrayType::Getter, ArrayType::Setter,
 269  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CData::Finalize,
 270  NULL, NULL, FunctionType::Call, FunctionType::Call, NULL, NULL, NULL, NULL
 271};
 272
 273static JSClass sCClosureClass = {
 274  "CClosure",
 275  JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
 276  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 277  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize,
 278  NULL, NULL, NULL, NULL, NULL, NULL, CClosure::Trace, NULL
 279};
 280
 281#define CTYPESFN_FLAGS \
 282  (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 283
 284#define CTYPESCTOR_FLAGS \
 285  (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
 286
 287#define CTYPESPROP_FLAGS \
 288  (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 289
 290#define CDATAFN_FLAGS \
 291  (JSPROP_READONLY | JSPROP_PERMANENT)
 292
 293static JSPropertySpec sCTypeProps[] = {
 294  { "name", 0, CTYPESPROP_FLAGS, CType::NameGetter, NULL },
 295  { "size", 0, CTYPESPROP_FLAGS, CType::SizeGetter, NULL },
 296  { "ptr", 0, CTYPESPROP_FLAGS, CType::PtrGetter, NULL },
 297  { "prototype", 0, CTYPESPROP_FLAGS, CType::PrototypeGetter, NULL },
 298  { 0, 0, 0, NULL, NULL }
 299};
 300
 301static JSFunctionSpec sCTypeFunctions[] = {
 302  JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
 303  JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
 304  JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
 305  JS_FS_END
 306};
 307
 308static JSPropertySpec sCDataProps[] = {
 309  { "value", 0, JSPROP_SHARED | JSPROP_PERMANENT,
 310    CData::ValueGetter, CData::ValueSetter },
 311  { 0, 0, 0, NULL, NULL }
 312};
 313
 314static JSFunctionSpec sCDataFunctions[] = {
 315  JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
 316  JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
 317  JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
 318  JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
 319  JS_FS_END
 320};
 321
 322static JSFunctionSpec sPointerFunction =
 323  JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
 324
 325static JSPropertySpec sPointerProps[] = {
 326  { "targetType", 0, CTYPESPROP_FLAGS, PointerType::TargetTypeGetter, NULL },
 327  { 0, 0, 0, NULL, NULL }
 328};
 329
 330static JSFunctionSpec sPointerInstanceFunctions[] = {
 331  JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
 332  JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS),
 333  JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS),
 334  JS_FS_END
 335};
 336  
 337static JSPropertySpec sPointerInstanceProps[] = {
 338  { "contents", 0, JSPROP_SHARED | JSPROP_PERMANENT,
 339    PointerType::ContentsGetter, PointerType::ContentsSetter },
 340  { 0, 0, 0, NULL, NULL }
 341};
 342
 343static JSFunctionSpec sArrayFunction =
 344  JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
 345
 346static JSPropertySpec sArrayProps[] = {
 347  { "elementType", 0, CTYPESPROP_FLAGS, ArrayType::ElementTypeGetter, NULL },
 348  { "length", 0, CTYPESPROP_FLAGS, ArrayType::LengthGetter, NULL },
 349  { 0, 0, 0, NULL, NULL }
 350};
 351
 352static JSFunctionSpec sArrayInstanceFunctions[] = {
 353  JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
 354  JS_FS_END
 355};
 356
 357static JSPropertySpec sArrayInstanceProps[] = {
 358  { "length", 0, JSPROP_SHARED | JSPROP_READONLY | JSPROP_PERMANENT,
 359    ArrayType::LengthGetter, NULL },
 360  { 0, 0, 0, NULL, NULL }
 361};
 362
 363static JSFunctionSpec sStructFunction =
 364  JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
 365
 366static JSPropertySpec sStructProps[] = {
 367  { "fields", 0, CTYPESPROP_FLAGS, StructType::FieldsArrayGetter, NULL },
 368  { 0, 0, 0, NULL, NULL }
 369};
 370
 371static JSFunctionSpec sStructFunctions[] = {
 372  JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
 373  JS_FS_END
 374};
 375
 376static JSFunctionSpec sStructInstanceFunctions[] = {
 377  JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
 378  JS_FS_END
 379};
 380
 381static JSFunctionSpec sFunctionFunction =
 382  JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
 383
 384static JSPropertySpec sFunctionProps[] = {
 385  { "argTypes", 0, CTYPESPROP_FLAGS, FunctionType::ArgTypesGetter, NULL },
 386  { "returnType", 0, CTYPESPROP_FLAGS, FunctionType::ReturnTypeGetter, NULL },
 387  { "abi", 0, CTYPESPROP_FLAGS, FunctionType::ABIGetter, NULL },
 388  { "isVariadic", 0, CTYPESPROP_FLAGS, FunctionType::IsVariadicGetter, NULL },
 389  { 0, 0, 0, NULL, NULL }
 390};
 391
 392static JSClass sInt64ProtoClass = {
 393  "Int64",
 394  0,
 395  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 396  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
 397  JSCLASS_NO_OPTIONAL_MEMBERS
 398};
 399
 400static JSClass sUInt64ProtoClass = {
 401  "UInt64",
 402  0,
 403  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 404  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
 405  JSCLASS_NO_OPTIONAL_MEMBERS
 406};
 407
 408static JSClass sInt64Class = {
 409  "Int64",
 410  JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
 411  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 412  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize,
 413  JSCLASS_NO_OPTIONAL_MEMBERS
 414};
 415
 416static JSClass sUInt64Class = {
 417  "UInt64",
 418  JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
 419  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
 420  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize,
 421  JSCLASS_NO_OPTIONAL_MEMBERS
 422};
 423
 424static JSFunctionSpec sInt64StaticFunctions[] = {
 425  JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
 426  JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
 427  JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
 428  JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS),
 429  JS_FS_END
 430};
 431
 432static JSFunctionSpec sUInt64StaticFunctions[] = {
 433  JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
 434  JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
 435  JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
 436  JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS),
 437  JS_FS_END
 438};
 439
 440static JSFunctionSpec sInt64Functions[] = {
 441  JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
 442  JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
 443  JS_FS_END
 444};
 445
 446static JSFunctionSpec sUInt64Functions[] = {
 447  JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
 448  JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
 449  JS_FS_END
 450};
 451
 452static JSFunctionSpec sModuleFunctions[] = {
 453  JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
 454  JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
 455  JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
 456  JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
 457  JS_FS_END
 458};
 459
 460static inline bool FloatIsFinite(jsdouble f) {
 461#ifdef WIN32
 462  return _finite(f) != 0;
 463#else
 464  return finite(f);
 465#endif
 466}
 467
 468JS_ALWAYS_INLINE JSString*
 469NewUCString(JSContext* cx, const AutoString& from)
 470{
 471  return JS_NewUCStringCopyN(cx, from.begin(), from.length());
 472}
 473
 474JS_ALWAYS_INLINE size_t
 475Align(size_t val, size_t align)
 476{
 477  return ((val - 1) | (align - 1)) + 1;
 478}
 479
 480static ABICode
 481GetABICode(JSObject* obj)
 482{
 483  // make sure we have an object representing a CABI class,
 484  // and extract the enumerated class type from the reserved slot.
 485  if (JS_GetClass(obj) != &sCABIClass)
 486    return INVALID_ABI;
 487
 488  jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE);
 489  return ABICode(JSVAL_TO_INT(result));
 490}
 491
 492JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
 493#define MSG_DEF(name, number, count, exception, format) \
 494  { format, count, exception } ,
 495#include "ctypes.msg"
 496#undef MSG_DEF
 497};
 498
 499const JSErrorFormatString*
 500GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber)
 501{
 502  if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
 503    return &ErrorFormatString[errorNumber];
 504  return NULL;
 505}
 506
 507JSBool
 508TypeError(JSContext* cx, const char* expected, jsval actual)
 509{
 510  JSString* str = JS_ValueToSource(cx, actual);
 511  JSAutoByteString bytes;
 512  
 513  const char* src;
 514  if (str) {
 515    src = bytes.encode(cx, str);
 516    if (!src)
 517      return false;
 518  } else {
 519    JS_ClearPendingException(cx);
 520    src = "<<error converting value to string>>";
 521  }
 522  JS_ReportErrorNumber(cx, GetErrorMessage, NULL,
 523                       CTYPESMSG_TYPE_ERROR, expected, src);
 524  return false;
 525}
 526
 527static JSObject*
 528InitCTypeClass(JSContext* cx, JSObject* parent)
 529{
 530  JSFunction* fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0,
 531                      CTYPESCTOR_FLAGS);
 532  if (!fun)
 533    return NULL;
 534
 535  JSObject* ctor = JS_GetFunctionObject(fun);
 536  JSObject* fnproto = JS_GetPrototype(ctor);
 537  JS_ASSERT(ctor);
 538  JS_ASSERT(fnproto);
 539
 540  // Set up ctypes.CType.prototype.
 541  JSObject* prototype = JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent);
 542  if (!prototype)
 543    return NULL;
 544
 545  if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
 546         NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
 547    return NULL;
 548
 549  if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
 550         NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
 551    return NULL;
 552
 553  // Define properties and functions common to all CTypes.
 554  if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
 555      !JS_DefineFunctions(cx, prototype, sCTypeFunctions))
 556    return NULL;
 557
 558  if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype))
 559    return NULL;
 560
 561  return prototype;
 562}
 563
 564static JSObject*
 565InitCDataClass(JSContext* cx, JSObject* parent, JSObject* CTypeProto)
 566{
 567  JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
 568                      CTYPESCTOR_FLAGS);
 569  if (!fun)
 570    return NULL;
 571
 572  JSObject* ctor = JS_GetFunctionObject(fun);
 573  JS_ASSERT(ctor);
 574
 575  // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
 576  // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
 577  // prototype chain.)
 578  if (!JS_SetPrototype(cx, ctor, CTypeProto))
 579    return NULL;
 580
 581  // Set up ctypes.CData.prototype.
 582  JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, NULL, parent);
 583  if (!prototype)
 584    return NULL;
 585
 586  if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
 587         NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
 588    return NULL;
 589
 590  if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
 591         NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
 592    return NULL;
 593
 594  // Define properties and functions common to all CDatas.
 595  if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
 596      !JS_DefineFunctions(cx, prototype, sCDataFunctions))
 597    return NULL;
 598
 599  if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
 600      !JS_FreezeObject(cx, ctor))
 601    return NULL;
 602
 603  return prototype;
 604}
 605
 606static JSBool
 607DefineABIConstant(JSContext* cx,
 608                  JSObject* parent,
 609                  const char* name,
 610                  ABICode code)
 611{
 612  JSObject* obj = JS_DefineObject(cx, parent, name, &sCABIClass, NULL,
 613                    JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
 614  if (!obj)
 615    return false;
 616  JS_SetReservedSlot(obj, SLOT_ABICODE, INT_TO_JSVAL(code));
 617  return JS_FreezeObject(cx, obj);
 618}
 619
 620// Set up a single type constructor for
 621// ctypes.{Pointer,Array,Struct,Function}Type.
 622static JSBool
 623InitTypeConstructor(JSContext* cx,
 624                    JSObject* parent,
 625                    JSObject* CTypeProto,
 626                    JSObject* CDataProto,
 627                    JSFunctionSpec spec,
 628                    JSFunctionSpec* fns,
 629                    JSPropertySpec* props,
 630                    JSFunctionSpec* instanceFns,
 631                    JSPropertySpec* instanceProps,
 632                    JSObject*& typeProto,
 633                    JSObject*& dataProto)
 634{
 635  JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call, 
 636                      spec.nargs, spec.flags);
 637  if (!fun)
 638    return false;
 639
 640  JSObject* obj = JS_GetFunctionObject(fun);
 641  if (!obj)
 642    return false;
 643
 644  // Set up the .prototype and .prototype.constructor properties.
 645  typeProto = JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent);
 646  if (!typeProto)
 647    return false;
 648
 649  // Define property before proceeding, for GC safety.
 650  if (!JS_DefineProperty(cx, obj, "prototype", OBJECT_TO_JSVAL(typeProto),
 651         NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
 652    return false;
 653
 654  if (fns && !JS_DefineFunctions(cx, typeProto, fns))
 655    return false;
 656
 657  if (!JS_DefineProperties(cx, typeProto, props))
 658    return false;
 659
 660  if (!JS_DefineProperty(cx, typeProto, "constructor", OBJECT_TO_JSVAL(obj),
 661         NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
 662    return false;
 663
 664  // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
 665  // the type constructor, for faster lookup.
 666  js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto));
 667
 668  // Create an object to serve as the common ancestor for all CData objects
 669  // created from the given type constructor. This has ctypes.CData.prototype
 670  // as its prototype, such that it inherits the properties and functions
 671  // common to all CDatas.
 672  dataProto = JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent);
 673  if (!dataProto)
 674    return false;
 675  js::AutoObjectRooter protoroot(cx, dataProto);
 676
 677  // Define functions and properties on the 'dataProto' object that are common
 678  // to all CData objects created from this type constructor. (These will
 679  // become functions and properties on CData objects created from this type.)
 680  if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns))
 681    return false;
 682
 683  if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps))
 684    return false;
 685
 686  // Link the type prototype to the data prototype.
 687  JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto));
 688
 689  if (!JS_FreezeObject(cx, obj) ||
 690      //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
 691      !JS_FreezeObject(cx, typeProto))
 692    return false;
 693
 694  return true;
 695}
 696
 697JSObject*
 698InitInt64Class(JSContext* cx,
 699               JSObject* parent,
 700               JSClass* clasp,
 701               JSNative construct,
 702               JSFunctionSpec* fs,
 703               JSFunctionSpec* static_fs)
 704{
 705  // Init type class and constructor
 706  JSObject* prototype = JS_InitClass(cx, parent, NULL, clasp, construct,
 707    0, NULL, fs, NULL, static_fs);
 708  if (!prototype)
 709    return NULL;
 710
 711  JSObject* ctor = JS_GetConstructor(cx, prototype);
 712  if (!ctor)
 713    return NULL;
 714  if (!JS_FreezeObject(cx, ctor))
 715    return NULL;
 716
 717  // Redefine the 'join' function as an extended native and stash
 718  // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
 719  JS_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass);
 720  JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join;
 721  JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native,
 722                      2, CTYPESFN_FLAGS);
 723  if (!fun)
 724    return NULL;
 725
 726  js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
 727    OBJECT_TO_JSVAL(prototype));
 728
 729  if (!JS_FreezeObject(cx, prototype))
 730    return NULL;
 731
 732  return prototype;
 733}
 734
 735static void
 736AttachProtos(JSObject* proto, JSObject** protos)
 737{
 738  // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
 739  // to the appropriate CTypeProtoSlot. (SLOT_UINT64PROTO is the last slot
 740  // of [[Class]] "CTypeProto" that we fill in this automated manner.)
 741  for (uint32_t i = 0; i <= SLOT_UINT64PROTO; ++i)
 742    JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i]));
 743}
 744
 745JSBool
 746InitTypeClasses(JSContext* cx, JSObject* parent)
 747{
 748  // Initialize the ctypes.CType class. This acts as an abstract base class for
 749  // the various types, and provides the common API functions. It has:
 750  //   * [[Class]] "Function"
 751  //   * __proto__ === Function.prototype
 752  //   * A constructor that throws a TypeError. (You can't construct an
 753  //     abstract type!)
 754  //   * 'prototype' property:
 755  //     * [[Class]] "CTypeProto"
 756  //     * __proto__ === Function.prototype
 757  //     * A constructor that throws a TypeError. (You can't construct an
 758  //       abstract type instance!)
 759  //     * 'constructor' property === ctypes.CType
 760  //     * Provides properties and functions common to all CTypes.
 761  JSObject* CTypeProto = InitCTypeClass(cx, parent);
 762  if (!CTypeProto)
 763    return false;
 764
 765  // Initialize the ctypes.CData class. This acts as an abstract base class for
 766  // instances of the various types, and provides the common API functions.
 767  // It has:
 768  //   * [[Class]] "Function"
 769  //   * __proto__ === Function.prototype
 770  //   * A constructor that throws a TypeError. (You can't construct an
 771  //     abstract type instance!)
 772  //   * 'prototype' property:
 773  //     * [[Class]] "CDataProto"
 774  //     * 'constructor' property === ctypes.CData
 775  //     * Provides properties and functions common to all CDatas.
 776  JSObject* CDataProto = InitCDataClass(cx, parent, CTypeProto);
 777  if (!CDataProto)
 778    return false;
 779
 780  // Link CTypeProto to CDataProto.
 781  JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO,
 782                     OBJECT_TO_JSVAL(CDataProto));
 783
 784  // Create and attach the special class constructors: ctypes.PointerType,
 785  // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType.
 786  // Each of these constructors 'c' has, respectively:
 787  //   * [[Class]] "Function"
 788  //   * __proto__ === Function.prototype
 789  //   * A constructor that creates a user-defined type.
 790  //   * 'prototype' property:
 791  //     * [[Class]] "CTypeProto"
 792  //     * __proto__ === ctypes.CType.prototype
 793  //     * 'constructor' property === 'c'
 794  // We also construct an object 'p' to serve, given a type object 't'
 795  // constructed from one of these type constructors, as
 796  // 't.prototype.__proto__'. This object has:
 797  //   * [[Class]] "CDataProto"
 798  //   * __proto__ === ctypes.CData.prototype
 799  //   * Properties and functions common to all CDatas.
 800  // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type
 801  // will have, resp.:
 802  //   * [[Class]] "CType"
 803  //   * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype
 804  //   * A constructor which creates and returns a CData object, containing
 805  //     binary data of the given type.
 806  //   * 'prototype' property:
 807  //     * [[Class]] "CDataProto"
 808  //     * __proto__ === 'p', the prototype object from above
 809  //     * 'constructor' property === 't'
 810  JSObject* protos[CTYPEPROTO_SLOTS];
 811  if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
 812         sPointerFunction, NULL, sPointerProps,
 813         sPointerInstanceFunctions, sPointerInstanceProps,
 814         protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
 815    return false;
 816  js::AutoObjectRooter proot(cx, protos[SLOT_POINTERDATAPROTO]);
 817
 818  if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
 819         sArrayFunction, NULL, sArrayProps,
 820         sArrayInstanceFunctions, sArrayInstanceProps,
 821         protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
 822    return false;
 823  js::AutoObjectRooter aroot(cx, protos[SLOT_ARRAYDATAPROTO]);
 824
 825  if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
 826         sStructFunction, sStructFunctions, sStructProps,
 827         sStructInstanceFunctions, NULL,
 828         protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
 829    return false;
 830  js::AutoObjectRooter sroot(cx, protos[SLOT_STRUCTDATAPROTO]);
 831
 832  if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
 833         sFunctionFunction, NULL, sFunctionProps, NULL, NULL,
 834         protos[SLOT_FUNCTIONPROTO], protos[SLOT_FUNCTIONDATAPROTO]))
 835    return false;
 836  js::AutoObjectRooter froot(cx, protos[SLOT_FUNCTIONDATAPROTO]);
 837
 838  protos[SLOT_CDATAPROTO] = CDataProto;
 839
 840  // Create and attach the ctypes.{Int64,UInt64} constructors.
 841  // Each of these has, respectively:
 842  //   * [[Class]] "Function"
 843  //   * __proto__ === Function.prototype
 844  //   * A constructor that creates a ctypes.{Int64,UInt64} object, respectively.
 845  //   * 'prototype' property:
 846  //     * [[Class]] {"Int64Proto","UInt64Proto"}
 847  //     * 'constructor' property === ctypes.{Int64,UInt64}
 848  protos[SLOT_INT64PROTO] = InitInt64Class(cx, parent, &sInt64ProtoClass,
 849    Int64::Construct, sInt64Functions, sInt64StaticFunctions);
 850  if (!protos[SLOT_INT64PROTO])
 851    return false;
 852  protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass,
 853    UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions);
 854  if (!protos[SLOT_UINT64PROTO])
 855    return false;
 856
 857  // Attach the prototypes just created to each of ctypes.CType.prototype,
 858  // and the special type constructors, so we can access them when constructing
 859  // instances of those types. 
 860  AttachProtos(CTypeProto, protos);
 861  AttachProtos(protos[SLOT_POINTERPROTO], protos);
 862  AttachProtos(protos[SLOT_ARRAYPROTO], protos);
 863  AttachProtos(protos[SLOT_STRUCTPROTO], protos);
 864  AttachProtos(protos[SLOT_FUNCTIONPROTO], protos);
 865
 866  // Attach objects representing ABI constants.
 867  if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT) ||
 868      !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL) ||
 869      !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI))
 870    return false;
 871
 872  // Create objects representing the builtin types, and attach them to the
 873  // ctypes object. Each type object 't' has:
 874  //   * [[Class]] "CType"
 875  //   * __proto__ === ctypes.CType.prototype
 876  //   * A constructor which creates and returns a CData object, containing
 877  //     binary data of the given type.
 878  //   * 'prototype' property:
 879  //     * [[Class]] "CDataProto"
 880  //     * __proto__ === ctypes.CData.prototype
 881  //     * 'constructor' property === 't'
 882#define DEFINE_TYPE(name, type, ffiType)                                       \
 883  JSObject* typeObj_##name =                                                   \
 884    CType::DefineBuiltin(cx, parent, #name, CTypeProto, CDataProto, #name,     \
 885      TYPE_##name, INT_TO_JSVAL(sizeof(type)),                                 \
 886      INT_TO_JSVAL(ffiType.alignment), &ffiType);                              \
 887  if (!typeObj_##name)                                                         \
 888    return false;
 889#include "typedefs.h"
 890
 891  // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
 892  // the same type in C.
 893  if (!JS_DefineProperty(cx, parent, "unsigned",
 894         OBJECT_TO_JSVAL(typeObj_unsigned_int), NULL, NULL,
 895         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
 896    return false;
 897
 898  // Create objects representing the special types void_t and voidptr_t.
 899  JSObject* typeObj =
 900    CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void",
 901      TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void);
 902  if (!typeObj)
 903    return false;
 904
 905  typeObj = PointerType::CreateInternal(cx, typeObj);
 906  if (!typeObj)
 907    return false;
 908  if (!JS_DefineProperty(cx, parent, "voidptr_t", OBJECT_TO_JSVAL(typeObj),
 909         NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
 910    return false;
 911
 912  return true;
 913}
 914
 915bool
 916IsCTypesGlobal(JSObject* obj)
 917{
 918  return JS_GetClass(obj) == &sCTypesGlobalClass;
 919}
 920
 921// Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'.
 922JSCTypesCallbacks*
 923GetCallbacks(JSObject* obj)
 924{
 925  JS_ASSERT(IsCTypesGlobal(obj));
 926
 927  jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS);
 928  if (JSVAL_IS_VOID(result))
 929    return NULL;
 930
 931  return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result));
 932}
 933
 934JS_BEGIN_EXTERN_C
 935
 936JS_PUBLIC_API(JSBool)
 937JS_InitCTypesClass(JSContext* cx, JSObject* global)
 938{
 939  // attach ctypes property to global object
 940  JSObject* ctypes = JS_NewObject(cx, &sCTypesGlobalClass, NULL, NULL);
 941  if (!ctypes)
 942    return false;
 943
 944  if (!JS_DefineProperty(cx, global, "ctypes", OBJECT_TO_JSVAL(ctypes),
 945         JS_PropertyStub, JS_StrictPropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) {
 946    return false;
 947  }
 948
 949  if (!InitTypeClasses(cx, ctypes))
 950    return false;
 951
 952  // attach API functions
 953  if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions))
 954    return false;
 955
 956  // Seal the ctypes object, to prevent modification.
 957  return JS_FreezeObject(cx, ctypes);
 958}
 959
 960JS_PUBLIC_API(void)
 961JS_SetCTypesCallbacks(JSObject* ctypesObj,
 962                      JSCTypesCallbacks* callbacks)
 963{
 964  JS_ASSERT(callbacks);
 965  JS_ASSERT(IsCTypesGlobal(ctypesObj));
 966
 967  // Set the callbacks on a reserved slot.
 968  JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, PRIVATE_TO_JSVAL(callbacks));
 969}
 970
 971JS_END_EXTERN_C
 972
 973/*******************************************************************************
 974** Type conversion functions
 975*******************************************************************************/
 976
 977// Enforce some sanity checks on type widths and properties.
 978// Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
 979// autoconverts to a primitive JS number; to support ILP64 architectures, it
 980// would need to autoconvert to an Int64 object instead. Therefore we enforce
 981// this invariant here.)
 982JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4);
 983JS_STATIC_ASSERT(sizeof(char) == 1);
 984JS_STATIC_ASSERT(sizeof(short) == 2);
 985JS_STATIC_ASSERT(sizeof(int) == 4);
 986JS_STATIC_ASSERT(sizeof(unsigned) == 4);
 987JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8);
 988JS_STATIC_ASSERT(sizeof(long long) == 8);
 989JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t));
 990JS_STATIC_ASSERT(sizeof(float) == 4);
 991JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*));
 992JS_STATIC_ASSERT(numeric_limits<double>::is_signed);
 993
 994// Templated helper to convert FromType to TargetType, for the default case
 995// where the trivial POD constructor will do.
 996template<class TargetType, class FromType>
 997struct ConvertImpl {
 998  static JS_ALWAYS_INLINE TargetType Convert(FromType d) {
 999    return TargetType(d);
1000  }
1001};
1002
1003#ifdef _MSC_VER
1004// MSVC can't perform double to unsigned __int64 conversion when the
1005// double is greater than 2^63 - 1. Help it along a little.
1006template<>
1007struct ConvertImpl<uint64_t, jsdouble> {
1008  static JS_ALWAYS_INLINE uint64_t Convert(jsdouble d) {
1009    return d > 0x7fffffffffffffffui64 ?
1010           uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
1011           uint64_t(d);
1012  }
1013};
1014#endif
1015
1016// C++ doesn't guarantee that exact values are the only ones that will
1017// round-trip. In fact, on some platforms, including SPARC, there are pairs of
1018// values, a uint64_t and a double, such that neither value is exactly
1019// representable in the other type, but they cast to each other.
1020#ifdef SPARC
1021// Simulate x86 overflow behavior
1022template<>
1023struct ConvertImpl<uint64_t, jsdouble> {
1024  static JS_ALWAYS_INLINE uint64_t Convert(jsdouble d) {
1025    return d >= 0xffffffffffffffff ?
1026           0x8000000000000000 : uint64_t(d);
1027  }
1028};
1029
1030template<>
1031struct ConvertImpl<int64_t, jsdouble> {
1032  static JS_ALWAYS_INLINE int64_t Convert(jsdouble d) {
1033    return d >= 0x7fffffffffffffff ?
1034           0x8000000000000000 : int64_t(d);
1035  }
1036};
1037#endif
1038
1039template<class TargetType, class FromType>
1040static JS_ALWAYS_INLINE TargetType Convert(FromType d)
1041{
1042  return ConvertImpl<TargetType, FromType>::Convert(d);
1043}
1044
1045template<class TargetType, class FromType>
1046static JS_ALWAYS_INLINE bool IsAlwaysExact()
1047{
1048  // Return 'true' if TargetType can always exactly represent FromType.
1049  // This means that:
1050  // 1) TargetType must be the same or more bits wide as FromType. For integers
1051  //    represented in 'n' bits, unsigned variants will have 'n' digits while
1052  //    signed will have 'n - 1'. For floating point types, 'digits' is the
1053  //    mantissa width.
1054  // 2) If FromType is signed, TargetType must also be signed. (Floating point
1055  //    types are always signed.)
1056  // 3) If TargetType is an exact integral type, FromType must be also.
1057  if (numeric_limits<TargetType>::digits < numeric_limits<FromType>::digits)
1058    return false;
1059
1060  if (numeric_limits<FromType>::is_signed &&
1061      !numeric_limits<TargetType>::is_signed)
1062    return false;
1063
1064  if (!numeric_limits<FromType>::is_exact &&
1065      numeric_limits<TargetType>::is_exact)
1066    return false;
1067
1068  return true;
1069}
1070
1071// Templated helper to determine if FromType 'i' converts losslessly to
1072// TargetType 'j'. Default case where both types are the same signedness.
1073template<class TargetType, class FromType, bool TargetSigned, bool FromSigned>
1074struct IsExactImpl {
1075  static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1076    JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1077    return FromType(j) == i;
1078  }
1079};
1080
1081// Specialization where TargetType is unsigned, FromType is signed.
1082template<class TargetType, class FromType>
1083struct IsExactImpl<TargetType, FromType, false, true> {
1084  static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1085    JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1086    return i >= 0 && FromType(j) == i;
1087  }
1088};
1089
1090// Specialization where TargetType is signed, FromType is unsigned.
1091template<class TargetType, class FromType>
1092struct IsExactImpl<TargetType, FromType, true, false> {
1093  static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1094    JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1095    return TargetType(i) >= 0 && FromType(j) == i;
1096  }
1097};
1098
1099// Convert FromType 'i' to TargetType 'result', returning true iff 'result'
1100// is an exact representation of 'i'.
1101template<class TargetType, class FromType>
1102static JS_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result)
1103{
1104  // Require that TargetType is integral, to simplify conversion.
1105  JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1106
1107  *result = Convert<TargetType>(i);
1108
1109  // See if we can avoid a dynamic check.
1110  if (IsAlwaysExact<TargetType, FromType>())
1111    return true;
1112
1113  // Return 'true' if 'i' is exactly representable in 'TargetType'.
1114  return IsExactImpl<TargetType,
1115                     FromType,
1116                     numeric_limits<TargetType>::is_signed,
1117                     numeric_limits<FromType>::is_signed>::Test(i, *result);
1118}
1119
1120// Templated helper to determine if Type 'i' is negative. Default case
1121// where IntegerType is unsigned.
1122template<class Type, bool IsSigned>
1123struct IsNegativeImpl {
1124  static JS_ALWAYS_INLINE bool Test(Type i) {
1125    return false;
1126  }
1127};
1128
1129// Specialization where Type is signed.
1130template<class Type>
1131struct IsNegativeImpl<Type, true> {
1132  static JS_ALWAYS_INLINE bool Test(Type i) {
1133    return i < 0;
1134  }
1135};
1136
1137// Determine whether Type 'i' is negative.
1138template<class Type>
1139static JS_ALWAYS_INLINE bool IsNegative(Type i)
1140{
1141  return IsNegativeImpl<Type, numeric_limits<Type>::is_signed>::Test(i);
1142}
1143
1144// Implicitly convert val to bool, allowing JSBool, jsint, and jsdouble
1145// arguments numerically equal to 0 or 1.
1146static bool
1147jsvalToBool(JSContext* cx, jsval val, bool* result)
1148{
1149  if (JSVAL_IS_BOOLEAN(val)) {
1150    *result = JSVAL_TO_BOOLEAN(val) != JS_FALSE;
1151    return true;
1152  }
1153  if (JSVAL_IS_INT(val)) {
1154    jsint i = JSVAL_TO_INT(val);
1155    *result = i != 0;
1156    return i == 0 || i == 1;
1157  }
1158  if (JSVAL_IS_DOUBLE(val)) {
1159    jsdouble d = JSVAL_TO_DOUBLE(val);
1160    *result = d != 0;
1161    // Allow -0.
1162    return d == 1 || d == 0;
1163  }
1164  // Don't silently convert null to bool. It's probably a mistake.
1165  return false;
1166}
1167
1168// Implicitly convert val to IntegerType, allowing JSBool, jsint, jsdouble,
1169// Int64, UInt64, and CData integer types 't' where all values of 't' are
1170// representable by IntegerType.
1171template<class IntegerType>
1172static bool
1173jsvalToInteger(JSContext* cx, jsval val, IntegerType* result)
1174{
1175  JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1176
1177  if (JSVAL_IS_INT(val)) {
1178    // Make sure the integer fits in the alotted precision, and has the right
1179    // sign.
1180    jsint i = JSVAL_TO_INT(val);
1181    return ConvertExact(i, result);
1182  }
1183  if (JSVAL_IS_DOUBLE(val)) {
1184    // Don't silently lose bits here -- check that val really is an
1185    // integer value, and has the right sign.
1186    jsdouble d = JSVAL_TO_DOUBLE(val);
1187    return ConvertExact(d, result);
1188  }
1189  if (!JSVAL_IS_PRIMITIVE(val)) {
1190    JSObject* obj = JSVAL_TO_OBJECT(val);
1191    if (CData::IsCData(obj)) {
1192      JSObject* typeObj = CData::GetCType(obj);
1193      void* data = CData::GetData(obj);
1194
1195      // Check whether the source type is always representable, with exact
1196      // precision, by the target type. If it is, convert the value.
1197      switch (CType::GetTypeCode(typeObj)) {
1198#define DEFINE_INT_TYPE(name, fromType, ffiType)                               \
1199      case TYPE_##name:                                                        \
1200        if (!IsAlwaysExact<IntegerType, fromType>())                           \
1201          return false;                                                        \
1202        *result = IntegerType(*static_cast<fromType*>(data));                  \
1203        return true;
1204#define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1205#include "typedefs.h"
1206      case TYPE_void_t:
1207      case TYPE_bool:
1208      case TYPE_float:
1209      case TYPE_double:
1210      case TYPE_float32_t:
1211      case TYPE_float64_t:
1212      case TYPE_char:
1213      case TYPE_signed_char:
1214      case TYPE_unsigned_char:
1215      case TYPE_jschar:
1216      case TYPE_pointer:
1217      case TYPE_function:
1218      case TYPE_array:
1219      case TYPE_struct:
1220        // Not a compatible number type.
1221        return false;
1222      }
1223    }
1224
1225    if (Int64::IsInt64(obj)) {
1226      // Make sure the integer fits in IntegerType.
1227      int64_t i = Int64Base::GetInt(obj);
1228      return ConvertExact(i, result);
1229    }
1230
1231    if (UInt64::IsUInt64(obj)) {
1232      // Make sure the integer fits in IntegerType.
1233      uint64_t i = Int64Base::GetInt(obj);
1234      return ConvertExact(i, result);
1235    }
1236
1237    return false; 
1238  }
1239  if (JSVAL_IS_BOOLEAN(val)) {
1240    // Implicitly promote boolean values to 0 or 1, like C.
1241    *result = JSVAL_TO_BOOLEAN(val);
1242    JS_ASSERT(*result == 0 || *result == 1);
1243    return true;
1244  }
1245  // Don't silently convert null to an integer. It's probably a mistake.
1246  return false;
1247}
1248
1249// Implicitly convert val to FloatType, allowing jsint, jsdouble,
1250// Int64, UInt64, and CData numeric types 't' where all values of 't' are
1251// representable by FloatType.
1252template<class FloatType>
1253static bool
1254jsvalToFloat(JSContext *cx, jsval val, FloatType* result)
1255{
1256  JS_STATIC_ASSERT(!numeric_limits<FloatType>::is_exact);
1257
1258  // The following casts may silently throw away some bits, but there's
1259  // no good way around it. Sternly requiring that the 64-bit double
1260  // argument be exactly representable as a 32-bit float is
1261  // unrealistic: it would allow 1/2 to pass but not 1/3.
1262  if (JSVAL_IS_INT(val)) {
1263    *result = FloatType(JSVAL_TO_INT(val));
1264    return true;
1265  }
1266  if (JSVAL_IS_DOUBLE(val)) {
1267    *result = FloatType(JSVAL_TO_DOUBLE(val));
1268    return true;
1269  }
1270  if (!JSVAL_IS_PRIMITIVE(val)) {
1271    JSObject* obj = JSVAL_TO_OBJECT(val);
1272    if (CData::IsCData(obj)) {
1273      JSObject* typeObj = CData::GetCType(obj);
1274      void* data = CData::GetData(obj);
1275
1276      // Check whether the source type is always representable, with exact
1277      // precision, by the target type. If it is, convert the value.
1278      switch (CType::GetTypeCode(typeObj)) {
1279#define DEFINE_FLOAT_TYPE(name, fromType, ffiType)                             \
1280      case TYPE_##name:                                                        \
1281        if (!IsAlwaysExact<FloatType, fromType>())                             \
1282          return false;                                                        \
1283        *result = FloatType(*static_cast<fromType*>(data));                    \
1284        return true;
1285#define DEFINE_INT_TYPE(x, y, z) DEFINE_FLOAT_TYPE(x, y, z)
1286#define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1287#include "typedefs.h"
1288      case TYPE_void_t:
1289      case TYPE_bool:
1290      case TYPE_char:
1291      case TYPE_signed_char:
1292      case TYPE_unsigned_char:
1293      case TYPE_jschar:
1294      case TYPE_pointer:
1295      case TYPE_function:
1296      case TYPE_array:
1297      case TYPE_struct:
1298        // Not a compatible number type.
1299        return false;
1300      }
1301    }
1302  }
1303  // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
1304  // does it. It's likely to be a mistake.
1305  return false;
1306}
1307
1308template<class IntegerType>
1309static bool
1310StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
1311{
1312  JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1313
1314  const jschar* cp = string->getChars(NULL);
1315  if (!cp)
1316    return false;
1317
1318  const jschar* end = cp + string->length();
1319  if (cp == end)
1320    return false;
1321
1322  IntegerType sign = 1;
1323  if (cp[0] == '-') {
1324    if (!numeric_limits<IntegerType>::is_signed)
1325      return false;
1326
1327    sign = -1;
1328    ++cp;
1329  }
1330
1331  // Assume base-10, unless the string begins with '0x' or '0X'.
1332  IntegerType base = 10;
1333  if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) {
1334    cp += 2;
1335    base = 16;
1336  }
1337
1338  // Scan the string left to right and build the number,
1339  // checking for valid characters 0 - 9, a - f, A - F and overflow.
1340  IntegerType i = 0;
1341  while (cp != end) {
1342    jschar c = *cp++;
1343    if (c >= '0' && c <= '9')
1344      c -= '0';
1345    else if (base == 16 && c >= 'a' && c <= 'f')
1346      c = c - 'a' + 10;
1347    else if (base == 16 && c >= 'A' && c <= 'F')
1348      c = c - 'A' + 10;
1349    else
1350      return false;
1351
1352    IntegerType ii = i;
1353    i = ii * base + sign * c;
1354    if (i / base != ii) // overflow
1355      return false;
1356  }
1357
1358  *result = i;
1359  return true;
1360}
1361
1362// Implicitly convert val to IntegerType, allowing jsint, jsdouble,
1363// Int64, UInt64, and optionally a decimal or hexadecimal string argument.
1364// (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
1365template<class IntegerType>
1366static bool
1367jsvalToBigInteger(JSContext* cx,
1368                  jsval val,
1369                  bool allowString,
1370                  IntegerType* result)
1371{
1372  JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1373
1374  if (JSVAL_IS_INT(val)) {
1375    // Make sure the integer fits in the alotted precision, and has the right
1376    // sign.
1377    jsint i = JSVAL_TO_INT(val);
1378    return ConvertExact(i, result);
1379  }
1380  if (JSVAL_IS_DOUBLE(val)) {
1381    // Don't silently lose bits here -- check that val really is an
1382    // integer value, and has the right sign.
1383    jsdouble d = JSVAL_TO_DOUBLE(val);
1384    return ConvertExact(d, result);
1385  }
1386  if (allowString && JSVAL_IS_STRING(val)) {
1387    // Allow conversion from base-10 or base-16 strings, provided the result
1388    // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
1389    // to the JS array element operator, which will automatically call
1390    // toString() on the object for us.)
1391    return StringToInteger(cx, JSVAL_TO_STRING(val), result);
1392  }
1393  if (!JSVAL_IS_PRIMITIVE(val)) {
1394    // Allow conversion from an Int64 or UInt64 object directly.
1395    JSObject* obj = JSVAL_TO_OBJECT(val);
1396
1397    if (UInt64::IsUInt64(obj)) {
1398      // Make sure the integer fits in IntegerType.
1399      uint64_t i = Int64Base::GetInt(obj);
1400      return ConvertExact(i, result);
1401    }
1402
1403    if (Int64::IsInt64(obj)) {
1404      // Make sure the integer fits in IntegerType.
1405      int64_t i = Int64Base::GetInt(obj);
1406      return ConvertExact(i, result);
1407    }
1408  }
1409  return false;
1410}
1411
1412// Implicitly convert val to a size value, where the size value is represented
1413// by size_t but must also fit in a jsdouble.
1414static bool
1415jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result)
1416{
1417  if (!jsvalToBigInteger(cx, val, allowString, result))
1418    return false;
1419
1420  // Also check that the result fits in a jsdouble.
1421  return Convert<size_t>(jsdouble(*result)) == *result;
1422}
1423
1424// Implicitly convert val to IntegerType, allowing jsint, jsdouble,
1425// Int64, UInt64, and optionally a decimal or hexadecimal string argument.
1426// (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
1427template<class IntegerType>
1428static bool
1429jsidToBigInteger(JSContext* cx,
1430                  jsid val,
1431                  bool allowSt

Large files files are truncated, but you can click here to view the full file