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