PageRenderTime 82ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/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
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, AGPL-1.0, LGPL-2.1, BSD-3-Clause, GPL-2.0, JSON, Apache-2.0, 0BSD
  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 allowString,
  1234. IntegerType* result)
  1235. {
  1236. JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
  1237. if (JSID_IS_INT(val)) {
  1238. // Make sure the integer fits in the alotted precision, and has the right
  1239. // sign.
  1240. jsint i = JSID_TO_INT(val);
  1241. return ConvertExact(i, result);
  1242. }
  1243. if (allowString && JSID_IS_STRING(val)) {
  1244. // Allow conversion from base-10 or base-16 strings, provided the result
  1245. // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
  1246. // to the JS array element operator, which will automatically call
  1247. // toString() on the object for us.)
  1248. return StringToInteger(cx, JSID_TO_STRING(val), result);
  1249. }
  1250. if (JSID_IS_OBJECT(val)) {
  1251. // Allow conversion from an Int64 or UInt64 object directly.
  1252. JSObject* obj = JSID_TO_OBJECT(val);
  1253. if (UInt64::IsUInt64(obj)) {
  1254. // Make sure the integer fits in IntegerType.
  1255. uint64_t i = Int64Base::GetInt(obj);
  1256. return ConvertExact(i, result);
  1257. }
  1258. if (Int64::IsInt64(obj)) {
  1259. // Make sure the integer fits in IntegerType.
  1260. int64_t i = Int64Base::GetInt(obj);
  1261. return ConvertExact(i, result);
  1262. }
  1263. }
  1264. return false;
  1265. }
  1266. // Implicitly convert val to a size value, where the size value is represented
  1267. // by size_t but must also fit in a jsdouble.
  1268. static bool
  1269. jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result)
  1270. {
  1271. if (!jsidToBigInteger(cx, val, allowString, result))
  1272. return false;
  1273. // Also check that the result fits in a jsdouble.
  1274. return Convert<size_t>(jsdouble(*result)) == *result;
  1275. }
  1276. // Implicitly convert a size value to a jsval, ensuring that the size_t value
  1277. // fits in a jsdouble.
  1278. static JSBool
  1279. SizeTojsval(JSContext* cx, size_t size, jsval* result)
  1280. {
  1281. if (Convert<size_t>(jsdouble(size)) != size) {
  1282. JS_ReportError(cx, "size overflow");
  1283. return false;
  1284. }
  1285. return JS_NewNumberValue(cx, jsdouble(size), result);
  1286. }
  1287. // Forcefully convert val to IntegerType when explicitly requested.
  1288. template<class IntegerType>
  1289. static bool
  1290. jsvalToIntegerExplicit(jsval val, IntegerType* result)
  1291. {
  1292. JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
  1293. if (JSVAL_IS_DOUBLE(val)) {
  1294. // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
  1295. jsdouble d = JSVAL_TO_DOUBLE(val);
  1296. *result = FloatIsFinite(d) ? IntegerType(d) : 0;
  1297. return true;
  1298. }
  1299. if (!JSVAL_IS_PRIMITIVE(val)) {
  1300. // Convert Int64 and UInt64 values by C-style cast.
  1301. JSObject* obj = JSVAL_TO_OBJECT(val);
  1302. if (Int64::IsInt64(obj)) {
  1303. int64_t i = Int64Base::GetInt(obj);
  1304. *result = IntegerType(i);
  1305. return true;
  1306. }
  1307. if (UInt64::IsUInt64(obj)) {
  1308. uint64_t i = Int64Base::GetInt(obj);
  1309. *result = IntegerType(i);
  1310. return true;
  1311. }
  1312. }
  1313. return false;
  1314. }
  1315. // Forcefully convert val to a pointer value when explicitly requested.
  1316. static bool
  1317. jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result)
  1318. {
  1319. if (JSVAL_IS_INT(val)) {
  1320. // jsint always fits in intptr_t. If the integer is negative, cast through
  1321. // an intptr_t intermediate to sign-extend.
  1322. jsint i = JSVAL_TO_INT(val);
  1323. *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i);
  1324. return true;
  1325. }
  1326. if (JSVAL_IS_DOUBLE(val)) {
  1327. jsdouble d = JSVAL_TO_DOUBLE(val);
  1328. if (d < 0) {
  1329. // Cast through an intptr_t intermediate to sign-extend.
  1330. intptr_t i = Convert<intptr_t>(d);
  1331. if (jsdouble(i) != d)
  1332. return false;
  1333. *result = uintptr_t(i);
  1334. return true;
  1335. }
  1336. // Don't silently lose bits here -- check that val really is an
  1337. // integer value, and has the right sign.
  1338. *result = Convert<uintptr_t>(d);
  1339. return jsdouble(*result) == d;
  1340. }
  1341. if (!JSVAL_IS_PRIMITIVE(val)) {
  1342. JSObject* obj = JSVAL_TO_OBJECT(val);
  1343. if (Int64::IsInt64(obj)) {
  1344. int64_t i = Int64Base::GetInt(obj);
  1345. intptr_t p = intptr_t(i);
  1346. // Make sure the integer fits in the alotted precision.
  1347. if (int64_t(p) != i)
  1348. return false;
  1349. *result = uintptr_t(p);
  1350. return true;
  1351. }
  1352. if (UInt64::IsUInt64(obj)) {
  1353. uint64_t i = Int64Base::GetInt(obj);
  1354. // Make sure the integer fits in the alotted precision.
  1355. *result = uintptr_t(i);
  1356. return uint64_t(*result) == i;
  1357. }
  1358. }
  1359. return false;
  1360. }
  1361. template<class IntegerType, class CharType, size_t N, class AP>
  1362. void
  1363. IntegerToString(IntegerType i, jsuint radix, Vector<CharType, N, AP>& result)
  1364. {
  1365. JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
  1366. // The buffer must be big enough for all the bits of IntegerType to fit,
  1367. // in base-2, including '-'.
  1368. CharType buffer[sizeof(IntegerType) * 8 + 1];
  1369. CharType* end = buffer + sizeof(buffer) / sizeof(CharType);
  1370. CharType* cp = end;
  1371. // Build the string in reverse. We use multiplication and subtraction
  1372. // instead of modulus because that's much faster.
  1373. const bool isNegative = IsNegative(i);
  1374. size_t sign = isNegative ? -1 : 1;
  1375. do {
  1376. IntegerType ii = i / IntegerType(radix);
  1377. size_t index = sign * size_t(i - ii * IntegerType(radix));
  1378. *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index];
  1379. i = ii;
  1380. } while (i != 0);
  1381. if (isNegative)
  1382. *--cp = '-';
  1383. JS_ASSERT(cp >= buffer);
  1384. result.append(cp, end);
  1385. }
  1386. template<class CharType>
  1387. static size_t
  1388. strnlen(const CharType* begin, size_t max)
  1389. {
  1390. for (const CharType* s = begin; s != begin + max; ++s)
  1391. if (*s == 0)
  1392. return s - begin;
  1393. return max;
  1394. }
  1395. // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where
  1396. // possible; otherwise, construct and return a CData object. The following
  1397. // semantics apply when constructing a CData object for return:
  1398. // * If 'wantPrimitive' is true, the caller indicates that 'result' must be
  1399. // a JS primitive, and ConvertToJS will fail if 'result' would be a CData
  1400. // object. Otherwise:
  1401. // * If a CData object 'parentObj' is supplied, the new CData object is
  1402. // dependent on the given parent and its buffer refers to a slice of the
  1403. // parent's buffer.
  1404. // * If 'parentObj' is null, the new CData object may or may not own its
  1405. // resulting buffer depending on the 'ownResult' argument.
  1406. JSBool
  1407. ConvertToJS(JSContext* cx,
  1408. JSObject* typeObj,
  1409. JSObject* parentObj,
  1410. void* data,
  1411. bool wantPrimitive,
  1412. bool ownResult,
  1413. jsval* result)
  1414. {
  1415. JS_ASSERT(!parentObj || CData::IsCData(parentObj));
  1416. JS_ASSERT(!parentObj || !ownResult);
  1417. JS_ASSERT(!wantPrimitive || !ownResult);
  1418. TypeCode typeCode = CType::GetTypeCode(typeObj);
  1419. switch (typeCode) {
  1420. case TYPE_void_t:
  1421. *result = JSVAL_VOID;
  1422. break;
  1423. case TYPE_bool:
  1424. *result = *static_cast<bool*>(data) ? JSVAL_TRUE : JSVAL_FALSE;
  1425. break;
  1426. #define DEFINE_INT_TYPE(name, type, ffiType) \
  1427. case TYPE_##name: { \
  1428. type value = *static_cast<type*>(data); \
  1429. if (sizeof(type) < 4) \
  1430. *result = INT_TO_JSVAL(jsint(value)); \
  1431. else if (!JS_NewNumberValue(cx, jsdouble(value), result)) \
  1432. return false; \
  1433. break; \
  1434. }
  1435. #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \
  1436. case TYPE_##name: { \
  1437. /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \
  1438. uint64_t value; \
  1439. JSObject* proto; \
  1440. if (!numeric_limits<type>::is_signed) { \
  1441. value = *static_cast<type*>(data); \
  1442. /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \
  1443. proto = CType::GetProtoFromType(typeObj, SLOT_UINT64PROTO); \
  1444. } else { \
  1445. value = int64_t(*static_cast<type*>(data)); \
  1446. /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \
  1447. proto = CType::GetProtoFromType(typeObj, SLOT_INT64PROTO); \
  1448. } \
  1449. \
  1450. JSObject* obj = Int64Base::Construct(cx, proto, value, \
  1451. !numeric_limits<type>::is_signed); \
  1452. if (!obj) \
  1453. return false; \
  1454. *result = OBJECT_TO_JSVAL(obj); \
  1455. break; \
  1456. }
  1457. #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
  1458. case TYPE_##name: { \
  1459. type value = *static_cast<type*>(data); \
  1460. if (!JS_NewNumberValue(cx, jsdouble(value), result)) \
  1461. return false; \
  1462. break; \
  1463. }
  1464. #define DEFINE_CHAR_TYPE(name, type, ffiType) \
  1465. case TYPE_##name: \
  1466. /* Convert to an integer. We have no idea what character encoding to */ \
  1467. /* use, if any. */ \
  1468. *result = INT_TO_JSVAL(*static_cast<type*>(data)); \
  1469. break;
  1470. #include "typedefs.h"
  1471. case TYPE_jschar: {
  1472. // Convert the jschar to a 1-character string.
  1473. JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1);
  1474. if (!str)
  1475. return false;
  1476. *result = STRING_TO_JSVAL(str);
  1477. break;
  1478. }
  1479. case TYPE_pointer:
  1480. case TYPE_array:
  1481. case TYPE_struct: {
  1482. // We're about to create a new CData object to return. If the caller doesn't
  1483. // want this, return early.
  1484. if (wantPrimitive) {
  1485. JS_ReportError(cx, "cannot convert to primitive value");
  1486. return false;
  1487. }
  1488. JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult);
  1489. if (!obj)
  1490. return false;
  1491. *result = OBJECT_TO_JSVAL(obj);
  1492. break;
  1493. }
  1494. case TYPE_function:
  1495. JS_NOT_REACHED("cannot return a FunctionType");
  1496. }
  1497. return true;
  1498. }
  1499. // Implicitly convert jsval 'val' to a C binary representation of CType
  1500. // 'targetType', storing the result in 'buffer'. Adequate space must be
  1501. // provided in 'buffer' by the caller. This function generally does minimal
  1502. // coercion between types. There are two cases in which this function is used:
  1503. // 1) The target buffer is internal to a CData object; we simply write data
  1504. // into it.
  1505. // 2) We are converting an argument for an ffi call, in which case 'isArgument'
  1506. // will be true. This allows us to handle a special case: if necessary,
  1507. // we can autoconvert a JS string primitive to a pointer-to-character type.
  1508. // In this case, ownership of the allocated string is handed off to the
  1509. // caller; 'freePointer' will be set to indicate this.
  1510. JSBool
  1511. ImplicitConvert(JSContext* cx,
  1512. jsval val,
  1513. JSObject* targetType,
  1514. void* buffer,
  1515. bool isArgument,
  1516. bool* freePointer)
  1517. {
  1518. JS_ASSERT(CType::IsSizeDefined(targetType));
  1519. // First, check if val is a CData object of type targetType.
  1520. JSObject* sourceData = NULL;
  1521. JSObject* sourceType = NULL;
  1522. if (!JSVAL_IS_PRIMITIVE(val) &&
  1523. CData::IsCData(JSVAL_TO_OBJECT(val))) {
  1524. sourceData = JSVAL_TO_OBJECT(val);
  1525. sourceType = CData::GetCType(sourceData);
  1526. // If the types are equal, copy the buffer contained within the CData.
  1527. // (Note that the buffers may overlap partially or completely.)
  1528. if (CType::TypesEqual(sourceType, targetType)) {
  1529. size_t size = CType::GetSize(sourceType);
  1530. memmove(buffer, CData::GetData(sourceData), size);
  1531. return true;
  1532. }
  1533. }
  1534. TypeCode targetCode = CType::GetTypeCode(targetType);
  1535. switch (targetCode) {
  1536. case TYPE_bool: {
  1537. // Do not implicitly lose bits, but allow the values 0, 1, and -0.
  1538. // Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`.
  1539. bool result;
  1540. if (!jsvalToBool(cx, val, &result))
  1541. return TypeError(cx, "boolean", val);
  1542. *static_cast<bool*>(buffer) = result;
  1543. break;
  1544. }
  1545. #define DEFINE_INT_TYPE(name, type, ffiType) \
  1546. case TYPE_##name: { \
  1547. /* Do not implicitly lose bits. */ \
  1548. type result; \
  1549. if (!jsvalToInteger(cx, val, &result)) \
  1550. return TypeError(cx, #name, val); \
  1551. *static_cast<type*>(buffer) = result; \
  1552. break; \
  1553. }
  1554. #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  1555. #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
  1556. case TYPE_##name: { \
  1557. type result; \
  1558. if (!jsvalToFloat(cx, val, &result)) \
  1559. return TypeError(cx, #name, val); \
  1560. *static_cast<type*>(buffer) = result; \
  1561. break; \
  1562. }
  1563. #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  1564. #define DEFINE_JSCHAR_TYPE(name, type, ffiType) \
  1565. case TYPE_##name: { \
  1566. /* Convert from a 1-character string, regardless of encoding, */ \
  1567. /* or from an integer, provided the result fits in 'type'. */ \
  1568. type result; \
  1569. if (JSVAL_IS_STRING(val)) { \
  1570. JSString* str = JSVAL_TO_STRING(val); \
  1571. if (str->length() != 1) \
  1572. return TypeError(cx, #name, val); \
  1573. const jschar *chars = str->getChars(cx); \
  1574. if (!chars) \
  1575. return false; \
  1576. result = chars[0]; \
  1577. } else if (!jsvalToInteger(cx, val, &result)) { \
  1578. return TypeError(cx, #name, val); \
  1579. } \
  1580. *static_cast<type*>(buffer) = result; \
  1581. break; \
  1582. }
  1583. #include "typedefs.h"
  1584. case TYPE_pointer: {
  1585. if (JSVAL_IS_NULL(val)) {
  1586. // Convert to a null pointer.
  1587. *static_cast<void**>(buffer) = NULL;
  1588. break;
  1589. }
  1590. JSObject* baseType = PointerType::GetBaseType(targetType);
  1591. if (sourceData) {
  1592. // First, determine if the targetType is ctypes.void_t.ptr.
  1593. TypeCode sourceCode = CType::GetTypeCode(sourceType);
  1594. void* sourceBuffer = CData::GetData(sourceData);
  1595. bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t;
  1596. if (sourceCode == TYPE_pointer && voidptrTarget) {
  1597. // Autoconvert if targetType is ctypes.voidptr_t.
  1598. *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer);
  1599. break;
  1600. }
  1601. if (sourceCode == TYPE_array) {
  1602. // Autoconvert an array to a ctypes.void_t.ptr or to
  1603. // sourceType.elementType.ptr, just like C.
  1604. JSObject* elementType = ArrayType::GetBaseType(sourceType);
  1605. if (voidptrTarget || CType::TypesEqual(baseType, elementType)) {
  1606. *static_cast<void**>(buffer) = sourceBuffer;
  1607. break;
  1608. }
  1609. }
  1610. } else if (isArgument && JSVAL_IS_STRING(val)) {
  1611. // Convert the string for the ffi call. This requires allocating space
  1612. // which the caller assumes ownership of.
  1613. // TODO: Extend this so we can safely convert strings at other times also.
  1614. JSString* sourceString = JSVAL_TO_STRING(val);
  1615. size_t sourceLength = sourceString->length();
  1616. const jschar* sourceChars = sourceString->getChars(cx);
  1617. if (!sourceChars)
  1618. return false;
  1619. switch (CType::GetTypeCode(baseType)) {
  1620. case TYPE_char:
  1621. case TYPE_signed_char:
  1622. case TYPE_unsigned_char: {
  1623. // Convert from UTF-16 to UTF-8.
  1624. size_t nbytes =
  1625. GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
  1626. if (nbytes == (size_t) -1)
  1627. return false;
  1628. char** charBuffer = static_cast<char**>(buffer);
  1629. *charBuffer = cx->array_new<char>(nbytes + 1);
  1630. if (!*charBuffer) {
  1631. JS_ReportAllocationOverflow(cx);
  1632. return false;
  1633. }
  1634. ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
  1635. *charBuffer, &nbytes));
  1636. (*charBuffer)[nbytes] = 0;
  1637. *freePointer = true;
  1638. break;
  1639. }
  1640. case TYPE_jschar: {
  1641. // Copy the jschar string data. (We could provide direct access to the
  1642. // JSString's buffer, but this approach is safer if the caller happens
  1643. // to modify the string.)
  1644. jschar** jscharBuffer = static_cast<jschar**>(buffer);
  1645. *jscharBuffer = cx->array_new<jschar>(sourceLength + 1);
  1646. if (!*jscharBuffer) {
  1647. JS_ReportAllocationOverflow(cx);
  1648. return false;
  1649. }
  1650. *freePointer = true;
  1651. memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar));
  1652. (*jscharBuffer)[sourceLength] = 0;
  1653. break;
  1654. }
  1655. default:
  1656. return TypeError(cx, "pointer", val);
  1657. }
  1658. break;
  1659. }
  1660. return TypeError(cx, "pointer", val);
  1661. }
  1662. case TYPE_array: {
  1663. JSObject* baseType = ArrayType::GetBaseType(targetType);
  1664. size_t targetLength = ArrayType::GetLength(targetType);
  1665. if (JSVAL_IS_STRING(val)) {
  1666. JSString* sourceString = JSVAL_TO_STRING(val);
  1667. size_t sourceLength = sourceString->length();
  1668. const jschar* sourceChars = sourceString->getChars(cx);
  1669. if (!sourceChars)
  1670. return false;
  1671. switch (CType::GetTypeCode(baseType)) {
  1672. case TYPE_char:
  1673. case TYPE_signed_char:
  1674. case TYPE_unsigned_char: {
  1675. // Convert from UTF-16 to UTF-8.
  1676. size_t nbytes =
  1677. GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
  1678. if (nbytes == (size_t) -1)
  1679. return false;
  1680. if (targetLength < nbytes) {
  1681. JS_ReportError(cx, "ArrayType has insufficient length");
  1682. return false;
  1683. }
  1684. char* charBuffer = static_cast<char*>(buffer);
  1685. ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
  1686. charBuffer, &nbytes));
  1687. if (targetLength > nbytes)
  1688. charBuffer[nbytes] = 0;
  1689. break;
  1690. }
  1691. case TYPE_jschar: {
  1692. // Copy the string data, jschar for jschar, including the terminator
  1693. // if there's space.
  1694. if (targetLength < sourceLength) {
  1695. JS_ReportError(cx, "ArrayType has insufficient length");
  1696. return false;
  1697. }
  1698. memcpy(buffer, sourceChars, sourceLength * sizeof(jschar));
  1699. if (targetLength > sourceLength)
  1700. static_cast<jschar*>(buffer)[sourceLength] = 0;
  1701. break;
  1702. }
  1703. default:
  1704. return TypeError(cx, "array", val);
  1705. }
  1706. } else if (!JSVAL_IS_PRIMITIVE(val) &&
  1707. JS_IsArrayObject(cx, JSVAL_TO_OBJECT(val))) {
  1708. // Convert each element of the array by calling ImplicitConvert.
  1709. JSObject* sourceArray = JSVAL_TO_OBJECT(val);
  1710. jsuint sourceLength;
  1711. if (!JS_GetArrayLength(cx, sourceArray, &sourceLength) ||
  1712. targetLength != size_t(sourceLength)) {
  1713. JS_ReportError(cx, "ArrayType length does not match source array length");
  1714. return false;
  1715. }
  1716. // Convert into an intermediate, in case of failure.
  1717. size_t elementSize = CType::GetSize(baseType);
  1718. size_t arraySize = elementSize * targetLength;
  1719. AutoPtr<char>::Array intermediate(cx->array_new<char>(arraySize));
  1720. if (!intermediate) {
  1721. JS_ReportAllocationOverflow(cx);
  1722. return false;
  1723. }
  1724. for (jsuint i = 0; i < sourceLength; ++i) {
  1725. js::AutoValueRooter item(cx);
  1726. if (!JS_GetElement(cx, sourceArray, i, item.jsval_addr()))
  1727. return false;
  1728. char* data = intermediate.get() + elementSize * i;
  1729. if (!ImplicitConvert(cx, item.jsval_value(), baseType, data, false, NULL))
  1730. return false;
  1731. }
  1732. memcpy(buffer, intermediate.get(), arraySize);
  1733. } else {
  1734. // Don't implicitly convert to string. Users can implicitly convert
  1735. // with `String(x)` or `""+x`.
  1736. return TypeError(cx, "array", val);
  1737. }
  1738. break;
  1739. }
  1740. case TYPE_struct: {
  1741. if (!JSVAL_IS_PRIMITIVE(val) && !sourceData) {
  1742. // Enumerate the properties of the object; if they match the struct
  1743. // specification, convert the fields.
  1744. JSObject* obj = JSVAL_TO_OBJECT(val);
  1745. JSObject* iter = JS_NewPropertyIterator(cx, obj);
  1746. if (!iter)
  1747. return false;
  1748. js::AutoObjectRooter iterroot(cx, iter);
  1749. // Convert into an intermediate, in case of failure.
  1750. size_t structSize = CType::GetSize(targetType);
  1751. AutoPtr<char>::Array intermediate(cx->array_new<char>(structSize));
  1752. if (!intermediate) {
  1753. JS_ReportAllocationOverflow(cx);
  1754. return false;
  1755. }
  1756. jsid id;
  1757. size_t i = 0;
  1758. while (1) {
  1759. if (!JS_NextProperty(cx, iter, &id))
  1760. return false;
  1761. if (JSID_IS_VOID(id))
  1762. break;
  1763. if (!JSID_IS_STRING(id)) {
  1764. JS_ReportError(cx, "property name is not a string");
  1765. return false;
  1766. }
  1767. JSFlatString *name = JSID_TO_FLAT_STRING(id);
  1768. const FieldInfo* field = StructType::LookupField(cx, targetType, name);
  1769. if (!field)
  1770. return false;
  1771. js::AutoValueRooter prop(cx);
  1772. if (!JS_GetPropertyById(cx, obj, id, prop.jsval_addr()))
  1773. return false;
  1774. // Convert the field via ImplicitConvert().
  1775. char* fieldData = intermediate.get() + field->mOffset;
  1776. if (!ImplicitConvert(cx, prop.jsval_value(), field->mType, fieldData, false, NULL))
  1777. return false;
  1778. ++i;
  1779. }
  1780. const FieldInfoHash* fields = StructType::GetFieldInfo(targetType);
  1781. if (i != fields->count()) {
  1782. JS_ReportError(cx, "missing fields");
  1783. return false;
  1784. }
  1785. memcpy(buffer, intermediate.get(), structSize);
  1786. break;
  1787. }
  1788. return TypeError(cx, "struct", val);
  1789. }
  1790. case TYPE_void_t:
  1791. case TYPE_function:
  1792. JS_NOT_REACHED("invalid type");
  1793. return false;
  1794. }
  1795. return true;
  1796. }
  1797. // Convert jsval 'val' to a C binary representation of CType 'targetType',
  1798. // storing the result in 'buffer'. This function is more forceful than
  1799. // ImplicitConvert.
  1800. JSBool
  1801. ExplicitConvert(JSContext* cx, jsval val, JSObject* targetType, void* buffer)
  1802. {
  1803. // If ImplicitConvert succeeds, use that result.
  1804. if (ImplicitConvert(cx, val, targetType, buffer, false, NULL))
  1805. return true;
  1806. // If ImplicitConvert failed, and there is no pending exception, then assume
  1807. // hard failure (out of memory, or some other similarly serious condition).
  1808. // We store any pending exception in case we need to re-throw it.
  1809. js::AutoValueRooter ex(cx);
  1810. if (!JS_GetPendingException(cx, ex.jsval_addr()))
  1811. return false;
  1812. // Otherwise, assume soft failure. Clear the pending exception so that we
  1813. // can throw a different one as required.
  1814. JS_ClearPendingException(cx);
  1815. TypeCode type = CType::GetTypeCode(targetType);
  1816. switch (type) {
  1817. case TYPE_bool: {
  1818. // Convert according to the ECMAScript ToBoolean() function.
  1819. JSBool result;
  1820. ASSERT_OK(JS_ValueToBoolean(cx, val, &result));
  1821. *static_cast<bool*>(buffer) = result != JS_FALSE;
  1822. break;
  1823. }
  1824. #define DEFINE_INT_TYPE(name, type, ffiType) \
  1825. case TYPE_##name: { \
  1826. /* Convert numeric values with a C-style cast, and */ \
  1827. /* allow conversion from a base-10 or base-16 string. */ \
  1828. type result; \
  1829. if (!jsvalToIntegerExplicit(val, &result) && \
  1830. (!JSVAL_IS_STRING(val) || \
  1831. !StringToInteger(cx, JSVAL_TO_STRING(val), &result))) \
  1832. return TypeError(cx, #name, val); \
  1833. *static_cast<type*>(buffer) = result; \
  1834. break; \
  1835. }
  1836. #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  1837. #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  1838. #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_CHAR_TYPE(x, y, z)
  1839. #include "typedefs.h"
  1840. case TYPE_pointer: {
  1841. // Convert a number, Int64 object, or UInt64 object to a pointer.
  1842. uintptr_t result;
  1843. if (!jsvalToPtrExplicit(cx, val, &result))
  1844. return TypeError(cx, "pointer", val);
  1845. *static_cast<uintptr_t*>(buffer) = result;
  1846. break;
  1847. }
  1848. case TYPE_float32_t:
  1849. case TYPE_float64_t:
  1850. case TYPE_float:
  1851. case TYPE_double:
  1852. case TYPE_array:
  1853. case TYPE_struct:
  1854. // ImplicitConvert is sufficient. Re-throw the exception it generated.
  1855. JS_SetPendingException(cx, ex.jsval_value());
  1856. return false;
  1857. case TYPE_void_t:
  1858. case TYPE_function:
  1859. JS_NOT_REACHED("invalid type");
  1860. return false;
  1861. }
  1862. return true;
  1863. }
  1864. // Given a CType 'typeObj', generate a string describing the C type declaration
  1865. // corresponding to 'typeObj'. For instance, the CType constructed from
  1866. // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string
  1867. // 'int32_t*(**)[4]'.
  1868. static JSString*
  1869. BuildTypeName(JSContext* cx, JSObject* typeObj)
  1870. {
  1871. AutoString result;
  1872. // Walk the hierarchy of types, outermost to innermost, building up the type
  1873. // string. This consists of the base type, which goes on the left.
  1874. // Derived type modifiers (* and []) build from the inside outward, with
  1875. // pointers on the left and arrays on the right. An excellent description
  1876. // of the rules for building C type declarations can be found at:
  1877. // http://unixwiz.net/techtips/reading-cdecl.html
  1878. TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping;
  1879. while (1) {
  1880. currentGrouping = CType::GetTypeCode(typeObj);
  1881. switch (currentGrouping) {
  1882. case TYPE_pointer: {
  1883. // Pointer types go on the left.
  1884. PrependString(result, "*");
  1885. typeObj = PointerType::GetBaseType(typeObj);
  1886. prevGrouping = currentGrouping;
  1887. continue;
  1888. }
  1889. case TYPE_array: {
  1890. if (prevGrouping == TYPE_pointer) {
  1891. // Outer type is pointer, inner type is array. Grouping is required.
  1892. PrependString(result, "(");
  1893. AppendString(result, ")");
  1894. }
  1895. // Array types go on the right.
  1896. AppendString(result, "[");
  1897. size_t length;
  1898. if (ArrayType::GetSafeLength(typeObj, &length))
  1899. IntegerToString(length, 10, result);
  1900. AppendString(result, "]");
  1901. typeObj = ArrayType::GetBaseType(typeObj);
  1902. prevGrouping = currentGrouping;
  1903. continue;
  1904. }
  1905. case TYPE_function: {
  1906. FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
  1907. // Add in the calling convention, if it's not cdecl.
  1908. // There's no trailing or leading space needed here, as none of the
  1909. // modifiers can produce a string beginning with an identifier ---
  1910. // except for TYPE_function itself, which is fine because functions
  1911. // can't return functions.
  1912. ABICode abi = GetABICode(fninfo->mABI);
  1913. if (abi == ABI_STDCALL)
  1914. PrependString(result, "__stdcall");
  1915. else if (abi == ABI_WINAPI)
  1916. PrependString(result, "WINAPI");
  1917. // Function application binds more tightly than dereferencing, so
  1918. // wrap pointer types in parens. Functions can't return functions
  1919. // (only pointers to them), and arrays can't hold functions
  1920. // (similarly), so we don't need to address those cases.
  1921. if (prevGrouping == TYPE_pointer) {
  1922. PrependString(result, "(");
  1923. AppendString(result, ")");
  1924. }
  1925. // Argument list goes on the right.
  1926. AppendString(result, "(");
  1927. for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
  1928. JSString* argName = CType::GetName(cx, fninfo->mArgTypes[i]);
  1929. AppendString(result, argName);
  1930. if (i != fninfo->mArgTypes.length() - 1 ||
  1931. fninfo->mIsVariadic)
  1932. AppendString(result, ", ");
  1933. }
  1934. if (fninfo->mIsVariadic)
  1935. AppendString(result, "...");
  1936. AppendString(result, ")");
  1937. // Set 'typeObj' to the return type, and let the loop process it.
  1938. // 'prevGrouping' doesn't matter here, because functions cannot return
  1939. // arrays -- thus the parenthetical rules don't get tickled.
  1940. typeObj = fninfo->mReturnType;
  1941. continue;
  1942. }
  1943. default:
  1944. // Either a basic or struct type. Use the type's name as the base type.
  1945. break;
  1946. }
  1947. break;
  1948. }
  1949. // If prepending the base type name directly would splice two
  1950. // identifiers, insert a space.
  1951. if (('a' <= result[0] && result[0] <= 'z') ||
  1952. ('A' <= result[0] && result[0] <= 'Z') ||
  1953. (result[0] == '_'))
  1954. PrependString(result, " ");
  1955. // Stick the base type and derived type parts together.
  1956. JSString* baseName = CType::GetName(cx, typeObj);
  1957. PrependString(result, baseName);
  1958. return NewUCString(cx, result);
  1959. }
  1960. // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)'
  1961. // would construct the same CType. If 'makeShort' is true, assume that any
  1962. // StructType 't' is bound to an in-scope variable of name 't.name', and use
  1963. // that variable in place of generating a string to construct the type 't'.
  1964. // (This means the type comparison function CType::TypesEqual will return true
  1965. // when comparing the input and output of BuildTypeSource, since struct
  1966. // equality is determined by strict JSObject pointer equality.)
  1967. static void
  1968. BuildTypeSource(JSContext* cx,
  1969. JSObject* typeObj,
  1970. bool makeShort,
  1971. AutoString& result)
  1972. {
  1973. // Walk the types, building up the toSource() string.
  1974. switch (CType::GetTypeCode(typeObj)) {
  1975. case TYPE_void_t:
  1976. #define DEFINE_TYPE(name, type, ffiType) \
  1977. case TYPE_##name:
  1978. #include "typedefs.h"
  1979. {
  1980. AppendString(result, "ctypes.");
  1981. JSString* nameStr = CType::GetName(cx, typeObj);
  1982. AppendString(result, nameStr);
  1983. break;
  1984. }
  1985. case TYPE_pointer: {
  1986. JSObject* baseType = PointerType::GetBaseType(typeObj);
  1987. // Specialcase ctypes.voidptr_t.
  1988. if (CType::GetTypeCode(baseType) == TYPE_void_t) {
  1989. AppendString(result, "ctypes.voidptr_t");
  1990. break;
  1991. }
  1992. // Recursively build the source string, and append '.ptr'.
  1993. BuildTypeSource(cx, baseType, makeShort, result);
  1994. AppendString(result, ".ptr");
  1995. break;
  1996. }
  1997. case TYPE_function: {
  1998. FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
  1999. AppendString(result, "ctypes.FunctionType(");
  2000. switch (GetABICode(fninfo->mABI)) {
  2001. case ABI_DEFAULT:
  2002. AppendString(result, "ctypes.default_abi, ");
  2003. break;
  2004. case ABI_STDCALL:
  2005. AppendString(result, "ctypes.stdcall_abi, ");
  2006. break;
  2007. case ABI_WINAPI:
  2008. AppendString(result, "ctypes.winapi_abi, ");
  2009. break;
  2010. case INVALID_ABI:
  2011. JS_NOT_REACHED("invalid abi");
  2012. break;
  2013. }
  2014. // Recursively build the source string describing the function return and
  2015. // argument types.
  2016. BuildTypeSource(cx, fninfo->mReturnType, true, result);
  2017. if (fninfo->mArgTypes.length() > 0) {
  2018. AppendString(result, ", [");
  2019. for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
  2020. BuildTypeSource(cx, fninfo->mArgTypes[i], true, result);
  2021. if (i != fninfo->mArgTypes.length() - 1 ||
  2022. fninfo->mIsVariadic)
  2023. AppendString(result, ", ");
  2024. }
  2025. if (fninfo->mIsVariadic)
  2026. AppendString(result, "\"...\"");
  2027. AppendString(result, "]");
  2028. }
  2029. AppendString(result, ")");
  2030. break;
  2031. }
  2032. case TYPE_array: {
  2033. // Recursively build the source string, and append '.array(n)',
  2034. // where n is the array length, or the empty string if the array length
  2035. // is undefined.
  2036. JSObject* baseType = ArrayType::GetBaseType(typeObj);
  2037. BuildTypeSource(cx, baseType, makeShort, result);
  2038. AppendString(result, ".array(");
  2039. size_t length;
  2040. if (ArrayType::GetSafeLength(typeObj, &length))
  2041. IntegerToString(length, 10, result);
  2042. AppendString(result, ")");
  2043. break;
  2044. }
  2045. case TYPE_struct: {
  2046. JSString* name = CType::GetName(cx, typeObj);
  2047. if (makeShort) {
  2048. // Shorten the type declaration by assuming that StructType 't' is bound
  2049. // to an in-scope variable of name 't.name'.
  2050. AppendString(result, name);
  2051. break;
  2052. }
  2053. // Write the full struct declaration.
  2054. AppendString(result, "ctypes.StructType(\"");
  2055. AppendString(result, name);
  2056. AppendString(result, "\"");
  2057. // If it's an opaque struct, we're done.
  2058. if (!CType::IsSizeDefined(typeObj)) {
  2059. AppendString(result, ")");
  2060. break;
  2061. }
  2062. AppendString(result, ", [");
  2063. const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
  2064. size_t length = fields->count();
  2065. Array<const FieldInfoHash::Entry*, 64> fieldsArray;
  2066. if (!fieldsArray.resize(length))
  2067. break;
  2068. for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
  2069. fieldsArray[r.front().value.mIndex] = &r.front();
  2070. for (size_t i = 0; i < length; ++i) {
  2071. const FieldInfoHash::Entry* entry = fieldsArray[i];
  2072. AppendString(result, "{ \"");
  2073. AppendString(result, entry->key);
  2074. AppendString(result, "\": ");
  2075. BuildTypeSource(cx, entry->value.mType, true, result);
  2076. AppendString(result, " }");
  2077. if (i != length - 1)
  2078. AppendString(result, ", ");
  2079. }
  2080. AppendString(result, "])");
  2081. break;
  2082. }
  2083. }
  2084. }
  2085. // Given a CData object of CType 'typeObj' with binary value 'data', generate a
  2086. // string 'result' such that 'eval(result)' would construct a CData object with
  2087. // the same CType and containing the same binary value. This assumes that any
  2088. // StructType 't' is bound to an in-scope variable of name 't.name'. (This means
  2089. // the type comparison function CType::TypesEqual will return true when
  2090. // comparing the types, since struct equality is determined by strict JSObject
  2091. // pointer equality.) Further, if 'isImplicit' is true, ensure that the
  2092. // resulting string can ImplicitConvert successfully if passed to another data
  2093. // constructor. (This is important when called recursively, since fields of
  2094. // structs and arrays are converted with ImplicitConvert.)
  2095. static JSBool
  2096. BuildDataSource(JSContext* cx,
  2097. JSObject* typeObj,
  2098. void* data,
  2099. bool isImplicit,
  2100. AutoString& result)
  2101. {
  2102. TypeCode type = CType::GetTypeCode(typeObj);
  2103. switch (type) {
  2104. case TYPE_bool:
  2105. if (*static_cast<bool*>(data))
  2106. AppendString(result, "true");
  2107. else
  2108. AppendString(result, "false");
  2109. break;
  2110. #define DEFINE_INT_TYPE(name, type, ffiType) \
  2111. case TYPE_##name: \
  2112. /* Serialize as a primitive decimal integer. */ \
  2113. IntegerToString(*static_cast<type*>(data), 10, result); \
  2114. break;
  2115. #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \
  2116. case TYPE_##name: \
  2117. /* Serialize as a wrapped decimal integer. */ \
  2118. if (!numeric_limits<type>::is_signed) \
  2119. AppendString(result, "ctypes.UInt64(\""); \
  2120. else \
  2121. AppendString(result, "ctypes.Int64(\""); \
  2122. \
  2123. IntegerToString(*static_cast<type*>(data), 10, result); \
  2124. AppendString(result, "\")"); \
  2125. break;
  2126. #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
  2127. case TYPE_##name: { \
  2128. /* Serialize as a primitive double. */ \
  2129. double fp = *static_cast<type*>(data); \
  2130. ToCStringBuf cbuf; \
  2131. char* str = NumberToCString(cx, &cbuf, fp); \
  2132. if (!str) { \
  2133. JS_ReportOutOfMemory(cx); \
  2134. return false; \
  2135. } \
  2136. \
  2137. result.append(str, strlen(str)); \
  2138. break; \
  2139. }
  2140. #define DEFINE_CHAR_TYPE(name, type, ffiType) \
  2141. case TYPE_##name: \
  2142. /* Serialize as an integer. */ \
  2143. IntegerToString(*static_cast<type*>(data), 10, result); \
  2144. break;
  2145. #include "typedefs.h"
  2146. case TYPE_jschar: {
  2147. // Serialize as a 1-character JS string.
  2148. JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1);
  2149. if (!str)
  2150. return false;
  2151. // Escape characters, and quote as necessary.
  2152. JSString* src = JS_ValueToSource(cx, STRING_TO_JSVAL(str));
  2153. if (!src)
  2154. return false;
  2155. AppendString(result, src);
  2156. break;
  2157. }
  2158. case TYPE_pointer:
  2159. case TYPE_function: {
  2160. if (isImplicit) {
  2161. // The result must be able to ImplicitConvert successfully.
  2162. // Wrap in a type constructor, then serialize for ExplicitConvert.
  2163. BuildTypeSource(cx, typeObj, true, result);
  2164. AppendString(result, "(");
  2165. }
  2166. // Serialize the pointer value as a wrapped hexadecimal integer.
  2167. uintptr_t ptr = *static_cast<uintptr_t*>(data);
  2168. AppendString(result, "ctypes.UInt64(\"0x");
  2169. IntegerToString(ptr, 16, result);
  2170. AppendString(result, "\")");
  2171. if (isImplicit)
  2172. AppendString(result, ")");
  2173. break;
  2174. }
  2175. case TYPE_array: {
  2176. // Serialize each element of the array recursively. Each element must
  2177. // be able to ImplicitConvert successfully.
  2178. JSObject* baseType = ArrayType::GetBaseType(typeObj);
  2179. AppendString(result, "[");
  2180. size_t length = ArrayType::GetLength(typeObj);
  2181. size_t elementSize = CType::GetSize(baseType);
  2182. for (size_t i = 0; i < length; ++i) {
  2183. char* element = static_cast<char*>(data) + elementSize * i;
  2184. if (!BuildDataSource(cx, baseType, element, true, result))
  2185. return false;
  2186. if (i + 1 < length)
  2187. AppendString(result, ", ");
  2188. }
  2189. AppendString(result, "]");
  2190. break;
  2191. }
  2192. case TYPE_struct: {
  2193. if (isImplicit) {
  2194. // The result must be able to ImplicitConvert successfully.
  2195. // Serialize the data as an object with properties, rather than
  2196. // a sequence of arguments to the StructType constructor.
  2197. AppendString(result, "{");
  2198. }
  2199. // Serialize each field of the struct recursively. Each field must
  2200. // be able to ImplicitConvert successfully.
  2201. const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
  2202. size_t length = fields->count();
  2203. Array<const FieldInfoHash::Entry*, 64> fieldsArray;
  2204. if (!fieldsArray.resize(length))
  2205. return false;
  2206. for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
  2207. fieldsArray[r.front().value.mIndex] = &r.front();
  2208. for (size_t i = 0; i < length; ++i) {
  2209. const FieldInfoHash::Entry* entry = fieldsArray[i];
  2210. if (isImplicit) {
  2211. AppendString(result, "\"");
  2212. AppendString(result, entry->key);
  2213. AppendString(result, "\": ");
  2214. }
  2215. char* fieldData = static_cast<char*>(data) + entry->value.mOffset;
  2216. if (!BuildDataSource(cx, entry->value.mType, fieldData, true, result))
  2217. return false;
  2218. if (i + 1 != length)
  2219. AppendString(result, ", ");
  2220. }
  2221. if (isImplicit)
  2222. AppendString(result, "}");
  2223. break;
  2224. }
  2225. case TYPE_void_t:
  2226. JS_NOT_REACHED("invalid type");
  2227. break;
  2228. }
  2229. return true;
  2230. }
  2231. /*******************************************************************************
  2232. ** JSAPI callback function implementations
  2233. *******************************************************************************/
  2234. JSBool
  2235. ConstructAbstract(JSContext* cx,
  2236. uintN argc,
  2237. jsval* vp)
  2238. {
  2239. // Calling an abstract base class constructor is disallowed.
  2240. JS_ReportError(cx, "cannot construct from abstract type");
  2241. return JS_FALSE;
  2242. }
  2243. /*******************************************************************************
  2244. ** CType implementation
  2245. *******************************************************************************/
  2246. JSBool
  2247. CType::ConstructData(JSContext* cx,
  2248. uintN argc,
  2249. jsval* vp)
  2250. {
  2251. // get the callee object...
  2252. JSObject* obj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
  2253. if (!CType::IsCType(obj)) {
  2254. JS_ReportError(cx, "not a CType");
  2255. return JS_FALSE;
  2256. }
  2257. // How we construct the CData object depends on what type we represent.
  2258. // An instance 'd' of a CData object of type 't' has:
  2259. // * [[Class]] "CData"
  2260. // * __proto__ === t.prototype
  2261. switch (GetTypeCode(obj)) {
  2262. case TYPE_void_t:
  2263. JS_ReportError(cx, "cannot construct from void_t");
  2264. return JS_FALSE;
  2265. case TYPE_function:
  2266. JS_ReportError(cx, "cannot construct from FunctionType; use FunctionType.ptr instead");
  2267. return JS_FALSE;
  2268. case TYPE_pointer:
  2269. return PointerType::ConstructData(cx, obj, argc, vp);
  2270. case TYPE_array:
  2271. return ArrayType::ConstructData(cx, obj, argc, vp);
  2272. case TYPE_struct:
  2273. return StructType::ConstructData(cx, obj, argc, vp);
  2274. default:
  2275. return ConstructBasic(cx, obj, argc, vp);
  2276. }
  2277. }
  2278. JSBool
  2279. CType::ConstructBasic(JSContext* cx,
  2280. JSObject* obj,
  2281. uintN argc,
  2282. jsval* vp)
  2283. {
  2284. if (argc > 1) {
  2285. JS_ReportError(cx, "CType constructor takes zero or one argument");
  2286. return JS_FALSE;
  2287. }
  2288. // construct a CData object
  2289. JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
  2290. if (!result)
  2291. return JS_FALSE;
  2292. if (argc == 1) {
  2293. if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(result)))
  2294. return JS_FALSE;
  2295. }
  2296. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  2297. return JS_TRUE;
  2298. }
  2299. JSObject*
  2300. CType::Create(JSContext* cx,
  2301. JSObject* typeProto,
  2302. JSObject* dataProto,
  2303. TypeCode type,
  2304. JSString* name,
  2305. jsval size,
  2306. jsval align,
  2307. ffi_type* ffiType)
  2308. {
  2309. JSObject* parent = JS_GetParent(typeProto);
  2310. JS_ASSERT(parent);
  2311. // Create a CType object with the properties and slots common to all CTypes.
  2312. // Each type object 't' has:
  2313. // * [[Class]] "CType"
  2314. // * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType,
  2315. // StructType}.prototype
  2316. // * A constructor which creates and returns a CData object, containing
  2317. // binary data of the given type.
  2318. // * 'prototype' property:
  2319. // * [[Class]] "CDataProto"
  2320. // * __proto__ === 'dataProto'; an object containing properties and
  2321. // functions common to all CData objects of types derived from
  2322. // 'typeProto'. (For instance, this could be ctypes.CData.prototype
  2323. // for simple types, or something representing structs for StructTypes.)
  2324. // * 'constructor' property === 't'
  2325. // * Additional properties specified by 'ps', as appropriate for the
  2326. // specific type instance 't'.
  2327. JSObject* typeObj = JS_NewObject(cx, &sCTypeClass, typeProto, parent);
  2328. if (!typeObj)
  2329. return NULL;
  2330. js::AutoObjectRooter root(cx, typeObj);
  2331. // Set up the reserved slots.
  2332. JS_SetReservedSlot(typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type));
  2333. if (ffiType)
  2334. JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType));
  2335. if (name)
  2336. JS_SetReservedSlot(typeObj, SLOT_NAME, STRING_TO_JSVAL(name));
  2337. JS_SetReservedSlot(typeObj, SLOT_SIZE, size);
  2338. JS_SetReservedSlot(typeObj, SLOT_ALIGN, align);
  2339. if (dataProto) {
  2340. // Set up the 'prototype' and 'prototype.constructor' properties.
  2341. JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, dataProto, parent);
  2342. if (!prototype)
  2343. return NULL;
  2344. js::AutoObjectRooter protoroot(cx, prototype);
  2345. if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
  2346. NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
  2347. return NULL;
  2348. // Set the 'prototype' object.
  2349. //if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212!
  2350. // return NULL;
  2351. JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype));
  2352. }
  2353. if (!JS_FreezeObject(cx, typeObj))
  2354. return NULL;
  2355. // Assert a sanity check on size and alignment: size % alignment should always
  2356. // be zero.
  2357. JS_ASSERT_IF(IsSizeDefined(typeObj),
  2358. GetSize(typeObj) % GetAlignment(typeObj) == 0);
  2359. return typeObj;
  2360. }
  2361. JSObject*
  2362. CType::DefineBuiltin(JSContext* cx,
  2363. JSObject* parent,
  2364. const char* propName,
  2365. JSObject* typeProto,
  2366. JSObject* dataProto,
  2367. const char* name,
  2368. TypeCode type,
  2369. jsval size,
  2370. jsval align,
  2371. ffi_type* ffiType)
  2372. {
  2373. JSString* nameStr = JS_NewStringCopyZ(cx, name);
  2374. if (!nameStr)
  2375. return NULL;
  2376. js::AutoStringRooter nameRoot(cx, nameStr);
  2377. // Create a new CType object with the common properties and slots.
  2378. JSObject* typeObj = Create(cx, typeProto, dataProto, type, nameStr, size,
  2379. align, ffiType);
  2380. if (!typeObj)
  2381. return NULL;
  2382. // Define the CType as a 'propName' property on 'parent'.
  2383. if (!JS_DefineProperty(cx, parent, propName, OBJECT_TO_JSVAL(typeObj),
  2384. NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
  2385. return NULL;
  2386. return typeObj;
  2387. }
  2388. void
  2389. CType::Finalize(JSContext* cx, JSObject* obj)
  2390. {
  2391. // Make sure our TypeCode slot is legit. If it's not, bail.
  2392. jsval slot = JS_GetReservedSlot(obj, SLOT_TYPECODE);
  2393. if (JSVAL_IS_VOID(slot))
  2394. return;
  2395. // The contents of our slots depends on what kind of type we are.
  2396. switch (TypeCode(JSVAL_TO_INT(slot))) {
  2397. case TYPE_function: {
  2398. // Free the FunctionInfo.
  2399. slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
  2400. if (!JSVAL_IS_VOID(slot))
  2401. cx->delete_(static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)));
  2402. break;
  2403. }
  2404. case TYPE_struct: {
  2405. // Free the FieldInfoHash table.
  2406. slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
  2407. if (!JSVAL_IS_VOID(slot)) {
  2408. void* info = JSVAL_TO_PRIVATE(slot);
  2409. cx->delete_(static_cast<FieldInfoHash*>(info));
  2410. }
  2411. }
  2412. // Fall through.
  2413. case TYPE_array: {
  2414. // Free the ffi_type info.
  2415. slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
  2416. if (!JSVAL_IS_VOID(slot)) {
  2417. ffi_type* ffiType = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
  2418. cx->array_delete(ffiType->elements);
  2419. cx->delete_(ffiType);
  2420. }
  2421. break;
  2422. }
  2423. default:
  2424. // Nothing to do here.
  2425. break;
  2426. }
  2427. }
  2428. void
  2429. CType::FinalizeProtoClass(JSContext* cx, JSObject* obj)
  2430. {
  2431. // Finalize the CTypeProto class. The only important bit here is our
  2432. // SLOT_CLOSURECX -- it contains the JSContext that was (lazily) instantiated
  2433. // for use with FunctionType closures. And if we're here, in this finalizer,
  2434. // we're guaranteed to not need it anymore. Note that this slot will only
  2435. // be set for the object (of class CTypeProto) ctypes.FunctionType.prototype.
  2436. jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSURECX);
  2437. if (JSVAL_IS_VOID(slot))
  2438. return;
  2439. JSContext* closureCx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
  2440. JS_DestroyContextNoGC(closureCx);
  2441. }
  2442. void
  2443. CType::Trace(JSTracer* trc, JSObject* obj)
  2444. {
  2445. // Make sure our TypeCode slot is legit. If it's not, bail.
  2446. jsval slot = obj->getSlot(SLOT_TYPECODE);
  2447. if (JSVAL_IS_VOID(slot))
  2448. return;
  2449. // The contents of our slots depends on what kind of type we are.
  2450. switch (TypeCode(JSVAL_TO_INT(slot))) {
  2451. case TYPE_struct: {
  2452. slot = obj->getReservedSlot(SLOT_FIELDINFO);
  2453. if (JSVAL_IS_VOID(slot))
  2454. return;
  2455. FieldInfoHash* fields =
  2456. static_cast<FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
  2457. for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
  2458. JS_CALL_TRACER(trc, r.front().key, JSTRACE_STRING, "fieldName");
  2459. JS_CALL_TRACER(trc, r.front().value.mType, JSTRACE_OBJECT, "fieldType");
  2460. }
  2461. break;
  2462. }
  2463. case TYPE_function: {
  2464. // Check if we have a FunctionInfo.
  2465. slot = obj->getReservedSlot(SLOT_FNINFO);
  2466. if (JSVAL_IS_VOID(slot))
  2467. return;
  2468. FunctionInfo* fninfo = static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
  2469. JS_ASSERT(fninfo);
  2470. // Identify our objects to the tracer.
  2471. JS_CALL_TRACER(trc, fninfo->mABI, JSTRACE_OBJECT, "abi");
  2472. JS_CALL_TRACER(trc, fninfo->mReturnType, JSTRACE_OBJECT, "returnType");
  2473. for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i)
  2474. JS_CALL_TRACER(trc, fninfo->mArgTypes[i], JSTRACE_OBJECT, "argType");
  2475. break;
  2476. }
  2477. default:
  2478. // Nothing to do here.
  2479. break;
  2480. }
  2481. }
  2482. bool
  2483. CType::IsCType(JSObject* obj)
  2484. {
  2485. return JS_GetClass(obj) == &sCTypeClass;
  2486. }
  2487. bool
  2488. CType::IsCTypeProto(JSObject* obj)
  2489. {
  2490. return JS_GetClass(obj) == &sCTypeProtoClass;
  2491. }
  2492. TypeCode
  2493. CType::GetTypeCode(JSObject* typeObj)
  2494. {
  2495. JS_ASSERT(IsCType(typeObj));
  2496. jsval result = JS_GetReservedSlot(typeObj, SLOT_TYPECODE);
  2497. return TypeCode(JSVAL_TO_INT(result));
  2498. }
  2499. bool
  2500. CType::TypesEqual(JSObject* t1, JSObject* t2)
  2501. {
  2502. JS_ASSERT(IsCType(t1) && IsCType(t2));
  2503. // Fast path: check for object equality.
  2504. if (t1 == t2)
  2505. return true;
  2506. // First, perform shallow comparison.
  2507. TypeCode c1 = GetTypeCode(t1);
  2508. TypeCode c2 = GetTypeCode(t2);
  2509. if (c1 != c2)
  2510. return false;
  2511. // Determine whether the types require shallow or deep comparison.
  2512. switch (c1) {
  2513. case TYPE_pointer: {
  2514. // Compare base types.
  2515. JSObject* b1 = PointerType::GetBaseType(t1);
  2516. JSObject* b2 = PointerType::GetBaseType(t2);
  2517. return TypesEqual(b1, b2);
  2518. }
  2519. case TYPE_function: {
  2520. FunctionInfo* f1 = FunctionType::GetFunctionInfo(t1);
  2521. FunctionInfo* f2 = FunctionType::GetFunctionInfo(t2);
  2522. // Compare abi, return type, and argument types.
  2523. if (f1->mABI != f2->mABI)
  2524. return false;
  2525. if (!TypesEqual(f1->mReturnType, f2->mReturnType))
  2526. return false;
  2527. if (f1->mArgTypes.length() != f2->mArgTypes.length())
  2528. return false;
  2529. if (f1->mIsVariadic != f2->mIsVariadic)
  2530. return false;
  2531. for (size_t i = 0; i < f1->mArgTypes.length(); ++i) {
  2532. if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i]))
  2533. return false;
  2534. }
  2535. return true;
  2536. }
  2537. case TYPE_array: {
  2538. // Compare length, then base types.
  2539. // An undefined length array matches other undefined length arrays.
  2540. size_t s1 = 0, s2 = 0;
  2541. bool d1 = ArrayType::GetSafeLength(t1, &s1);
  2542. bool d2 = ArrayType::GetSafeLength(t2, &s2);
  2543. if (d1 != d2 || (d1 && s1 != s2))
  2544. return false;
  2545. JSObject* b1 = ArrayType::GetBaseType(t1);
  2546. JSObject* b2 = ArrayType::GetBaseType(t2);
  2547. return TypesEqual(b1, b2);
  2548. }
  2549. case TYPE_struct:
  2550. // Require exact type object equality.
  2551. return false;
  2552. default:
  2553. // Shallow comparison is sufficient.
  2554. return true;
  2555. }
  2556. }
  2557. bool
  2558. CType::GetSafeSize(JSObject* obj, size_t* result)
  2559. {
  2560. JS_ASSERT(CType::IsCType(obj));
  2561. jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
  2562. // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID
  2563. // (for arrays of undefined length), and must always fit in a size_t.
  2564. if (JSVAL_IS_INT(size)) {
  2565. *result = JSVAL_TO_INT(size);
  2566. return true;
  2567. }
  2568. if (JSVAL_IS_DOUBLE(size)) {
  2569. *result = Convert<size_t>(JSVAL_TO_DOUBLE(size));
  2570. return true;
  2571. }
  2572. JS_ASSERT(JSVAL_IS_VOID(size));
  2573. return false;
  2574. }
  2575. size_t
  2576. CType::GetSize(JSObject* obj)
  2577. {
  2578. JS_ASSERT(CType::IsCType(obj));
  2579. jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
  2580. JS_ASSERT(!JSVAL_IS_VOID(size));
  2581. // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID
  2582. // (for arrays of undefined length), and must always fit in a size_t.
  2583. // For callers who know it can never be JSVAL_VOID, return a size_t directly.
  2584. if (JSVAL_IS_INT(size))
  2585. return JSVAL_TO_INT(size);
  2586. return Convert<size_t>(JSVAL_TO_DOUBLE(size));
  2587. }
  2588. bool
  2589. CType::IsSizeDefined(JSObject* obj)
  2590. {
  2591. JS_ASSERT(CType::IsCType(obj));
  2592. jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
  2593. // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID
  2594. // (for arrays of undefined length), and must always fit in a size_t.
  2595. JS_ASSERT(JSVAL_IS_INT(size) || JSVAL_IS_DOUBLE(size) || JSVAL_IS_VOID(size));
  2596. return !JSVAL_IS_VOID(size);
  2597. }
  2598. size_t
  2599. CType::GetAlignment(JSObject* obj)
  2600. {
  2601. JS_ASSERT(CType::IsCType(obj));
  2602. jsval slot = JS_GetReservedSlot(obj, SLOT_ALIGN);
  2603. return static_cast<size_t>(JSVAL_TO_INT(slot));
  2604. }
  2605. ffi_type*
  2606. CType::GetFFIType(JSContext* cx, JSObject* obj)
  2607. {
  2608. JS_ASSERT(CType::IsCType(obj));
  2609. jsval slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
  2610. if (!JSVAL_IS_VOID(slot)) {
  2611. return static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
  2612. }
  2613. AutoPtr<ffi_type> result;
  2614. switch (CType::GetTypeCode(obj)) {
  2615. case TYPE_array:
  2616. result = ArrayType::BuildFFIType(cx, obj);
  2617. break;
  2618. case TYPE_struct:
  2619. result = StructType::BuildFFIType(cx, obj);
  2620. break;
  2621. default:
  2622. JS_NOT_REACHED("simple types must have an ffi_type");
  2623. }
  2624. if (!result)
  2625. return NULL;
  2626. JS_SetReservedSlot(obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get()));
  2627. return result.forget();
  2628. }
  2629. JSString*
  2630. CType::GetName(JSContext* cx, JSObject* obj)
  2631. {
  2632. JS_ASSERT(CType::IsCType(obj));
  2633. jsval string = JS_GetReservedSlot(obj, SLOT_NAME);
  2634. if (JSVAL_IS_VOID(string)) {
  2635. // Build the type name lazily.
  2636. JSString* name = BuildTypeName(cx, obj);
  2637. if (!name)
  2638. return NULL;
  2639. JS_SetReservedSlot(obj, SLOT_NAME, STRING_TO_JSVAL(name));
  2640. return name;
  2641. }
  2642. return JSVAL_TO_STRING(string);
  2643. }
  2644. JSObject*
  2645. CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot)
  2646. {
  2647. // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
  2648. // on the type constructor.
  2649. jsval protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO);
  2650. JSObject* proto = JSVAL_TO_OBJECT(protoslot);
  2651. JS_ASSERT(proto);
  2652. JS_ASSERT(CType::IsCTypeProto(proto));
  2653. // Get the desired prototype.
  2654. jsval result = JS_GetReservedSlot(proto, slot);
  2655. return JSVAL_TO_OBJECT(result);
  2656. }
  2657. JSObject*
  2658. CType::GetProtoFromType(JSObject* obj, CTypeProtoSlot slot)
  2659. {
  2660. JS_ASSERT(IsCType(obj));
  2661. // Get the prototype of the type object.
  2662. JSObject* proto = JS_GetPrototype(obj);
  2663. JS_ASSERT(proto);
  2664. JS_ASSERT(CType::IsCTypeProto(proto));
  2665. // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
  2666. jsval result = JS_GetReservedSlot(proto, slot);
  2667. return JSVAL_TO_OBJECT(result);
  2668. }
  2669. JSBool
  2670. CType::PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  2671. {
  2672. if (!(CType::IsCType(obj) || CType::IsCTypeProto(obj))) {
  2673. JS_ReportError(cx, "not a CType or CTypeProto");
  2674. return JS_FALSE;
  2675. }
  2676. unsigned slot = CType::IsCTypeProto(obj) ? (unsigned) SLOT_OURDATAPROTO
  2677. : (unsigned) SLOT_PROTO;
  2678. *vp = JS_GetReservedSlot(obj, slot);
  2679. JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) || JSVAL_IS_VOID(*vp));
  2680. return JS_TRUE;
  2681. }
  2682. JSBool
  2683. CType::NameGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  2684. {
  2685. if (!CType::IsCType(obj)) {
  2686. JS_ReportError(cx, "not a CType");
  2687. return JS_FALSE;
  2688. }
  2689. JSString* name = CType::GetName(cx, obj);
  2690. if (!name)
  2691. return JS_FALSE;
  2692. *vp = STRING_TO_JSVAL(name);
  2693. return JS_TRUE;
  2694. }
  2695. JSBool
  2696. CType::SizeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  2697. {
  2698. if (!CType::IsCType(obj)) {
  2699. JS_ReportError(cx, "not a CType");
  2700. return JS_FALSE;
  2701. }
  2702. *vp = JS_GetReservedSlot(obj, SLOT_SIZE);
  2703. JS_ASSERT(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_VOID(*vp));
  2704. return JS_TRUE;
  2705. }
  2706. JSBool
  2707. CType::PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  2708. {
  2709. if (!CType::IsCType(obj)) {
  2710. JS_ReportError(cx, "not a CType");
  2711. return JS_FALSE;
  2712. }
  2713. JSObject* pointerType = PointerType::CreateInternal(cx, obj);
  2714. if (!pointerType)
  2715. return JS_FALSE;
  2716. *vp = OBJECT_TO_JSVAL(pointerType);
  2717. return JS_TRUE;
  2718. }
  2719. JSBool
  2720. CType::CreateArray(JSContext* cx, uintN argc, jsval* vp)
  2721. {
  2722. JSObject* baseType = JS_THIS_OBJECT(cx, vp);
  2723. if (!baseType || !CType::IsCType(baseType)) {
  2724. JS_ReportError(cx, "not a CType");
  2725. return JS_FALSE;
  2726. }
  2727. // Construct and return a new ArrayType object.
  2728. if (argc > 1) {
  2729. JS_ReportError(cx, "array takes zero or one argument");
  2730. return JS_FALSE;
  2731. }
  2732. // Convert the length argument to a size_t.
  2733. jsval* argv = JS_ARGV(cx, vp);
  2734. size_t length = 0;
  2735. if (argc == 1 && !jsvalToSize(cx, argv[0], false, &length)) {
  2736. JS_ReportError(cx, "argument must be a nonnegative integer");
  2737. return JS_FALSE;
  2738. }
  2739. JSObject* result = ArrayType::CreateInternal(cx, baseType, length, argc == 1);
  2740. if (!result)
  2741. return JS_FALSE;
  2742. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  2743. return JS_TRUE;
  2744. }
  2745. JSBool
  2746. CType::ToString(JSContext* cx, uintN argc, jsval* vp)
  2747. {
  2748. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  2749. if (!obj || !(CType::IsCType(obj) || CType::IsCTypeProto(obj))) {
  2750. JS_ReportError(cx, "not a CType");
  2751. return JS_FALSE;
  2752. }
  2753. // Create the appropriate string depending on whether we're sCTypeClass or
  2754. // sCTypeProtoClass.
  2755. JSString* result;
  2756. if (CType::IsCType(obj)) {
  2757. AutoString type;
  2758. AppendString(type, "type ");
  2759. AppendString(type, GetName(cx, obj));
  2760. result = NewUCString(cx, type);
  2761. }
  2762. else {
  2763. result = JS_NewStringCopyZ(cx, "[CType proto object]");
  2764. }
  2765. if (!result)
  2766. return JS_FALSE;
  2767. JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
  2768. return JS_TRUE;
  2769. }
  2770. JSBool
  2771. CType::ToSource(JSContext* cx, uintN argc, jsval* vp)
  2772. {
  2773. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  2774. if (!obj ||
  2775. !(CType::IsCType(obj) || CType::IsCTypeProto(obj)))
  2776. {
  2777. JS_ReportError(cx, "not a CType");
  2778. return JS_FALSE;
  2779. }
  2780. // Create the appropriate string depending on whether we're sCTypeClass or
  2781. // sCTypeProtoClass.
  2782. JSString* result;
  2783. if (CType::IsCType(obj)) {
  2784. AutoString source;
  2785. BuildTypeSource(cx, obj, false, source);
  2786. result = NewUCString(cx, source);
  2787. } else {
  2788. result = JS_NewStringCopyZ(cx, "[CType proto object]");
  2789. }
  2790. if (!result)
  2791. return JS_FALSE;
  2792. JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
  2793. return JS_TRUE;
  2794. }
  2795. JSBool
  2796. CType::HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp)
  2797. {
  2798. JS_ASSERT(CType::IsCType(obj));
  2799. jsval slot = JS_GetReservedSlot(obj, SLOT_PROTO);
  2800. JSObject* prototype = JSVAL_TO_OBJECT(slot);
  2801. JS_ASSERT(prototype);
  2802. JS_ASSERT(CData::IsCDataProto(prototype));
  2803. *bp = JS_FALSE;
  2804. if (JSVAL_IS_PRIMITIVE(*v))
  2805. return JS_TRUE;
  2806. JSObject* proto = JSVAL_TO_OBJECT(*v);
  2807. while ((proto = JS_GetPrototype(proto))) {
  2808. if (proto == prototype) {
  2809. *bp = JS_TRUE;
  2810. break;
  2811. }
  2812. }
  2813. return JS_TRUE;
  2814. }
  2815. /*******************************************************************************
  2816. ** PointerType implementation
  2817. *******************************************************************************/
  2818. JSBool
  2819. PointerType::Create(JSContext* cx, uintN argc, jsval* vp)
  2820. {
  2821. // Construct and return a new PointerType object.
  2822. if (argc != 1) {
  2823. JS_ReportError(cx, "PointerType takes one argument");
  2824. return JS_FALSE;
  2825. }
  2826. jsval arg = JS_ARGV(cx, vp)[0];
  2827. if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(JSVAL_TO_OBJECT(arg))) {
  2828. JS_ReportError(cx, "first argument must be a CType");
  2829. return JS_FALSE;
  2830. }
  2831. JSObject* result = CreateInternal(cx, JSVAL_TO_OBJECT(arg));
  2832. if (!result)
  2833. return JS_FALSE;
  2834. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  2835. return JS_TRUE;
  2836. }
  2837. JSObject*
  2838. PointerType::CreateInternal(JSContext* cx, JSObject* baseType)
  2839. {
  2840. // check if we have a cached PointerType on our base CType.
  2841. jsval slot = JS_GetReservedSlot(baseType, SLOT_PTR);
  2842. if (!JSVAL_IS_VOID(slot))
  2843. return JSVAL_TO_OBJECT(slot);
  2844. // Get ctypes.PointerType.prototype and the common prototype for CData objects
  2845. // of this type.
  2846. JSObject* typeProto;
  2847. JSObject* dataProto;
  2848. typeProto = CType::GetProtoFromType(baseType, SLOT_POINTERPROTO);
  2849. dataProto = CType::GetProtoFromType(baseType, SLOT_POINTERDATAPROTO);
  2850. // Create a new CType object with the common properties and slots.
  2851. JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer,
  2852. NULL, INT_TO_JSVAL(sizeof(void*)),
  2853. INT_TO_JSVAL(ffi_type_pointer.alignment),
  2854. &ffi_type_pointer);
  2855. if (!typeObj)
  2856. return NULL;
  2857. // Set the target type. (This will be 'null' for an opaque pointer type.)
  2858. JS_SetReservedSlot(typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType));
  2859. // Finally, cache our newly-created PointerType on our pointed-to CType.
  2860. JS_SetReservedSlot(baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj));
  2861. return typeObj;
  2862. }
  2863. JSBool
  2864. PointerType::ConstructData(JSContext* cx,
  2865. JSObject* obj,
  2866. uintN argc,
  2867. jsval* vp)
  2868. {
  2869. if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) {
  2870. JS_ReportError(cx, "not a PointerType");
  2871. return JS_FALSE;
  2872. }
  2873. if (argc > 3) {
  2874. JS_ReportError(cx, "constructor takes 0, 1, 2, or 3 arguments");
  2875. return JS_FALSE;
  2876. }
  2877. JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
  2878. if (!result)
  2879. return JS_FALSE;
  2880. // Set return value early, must not observe *vp after
  2881. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  2882. // There are 3 things that we might be creating here:
  2883. // 1 - A null pointer (no arguments)
  2884. // 2 - An initialized pointer (1 argument)
  2885. // 3 - A closure (1-3 arguments)
  2886. //
  2887. // The API doesn't give us a perfect way to distinguish 2 and 3, but the
  2888. // heuristics we use should be fine.
  2889. //
  2890. // Case 1 - Null pointer
  2891. //
  2892. if (argc == 0)
  2893. return JS_TRUE;
  2894. // Analyze the arguments a bit to decide what to do next.
  2895. jsval* argv = JS_ARGV(cx, vp);
  2896. JSObject* baseObj = PointerType::GetBaseType(obj);
  2897. bool looksLikeClosure = CType::GetTypeCode(baseObj) == TYPE_function &&
  2898. JSVAL_IS_OBJECT(argv[0]) &&
  2899. JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0]));
  2900. //
  2901. // Case 2 - Initialized pointer
  2902. //
  2903. if (!looksLikeClosure) {
  2904. if (argc != 1) {
  2905. JS_ReportError(cx, "first argument must be a function");
  2906. return JS_FALSE;
  2907. }
  2908. return ExplicitConvert(cx, argv[0], obj, CData::GetData(result));
  2909. }
  2910. //
  2911. // Case 3 - Closure
  2912. //
  2913. // The second argument is an optional 'this' parameter with which to invoke
  2914. // the given js function. Callers may leave this blank, or pass null if they
  2915. // wish to pass the third argument.
  2916. JSObject* thisObj = NULL;
  2917. if (argc >= 2) {
  2918. if (JSVAL_IS_OBJECT(argv[1])) {
  2919. thisObj = JSVAL_TO_OBJECT(argv[1]);
  2920. } else if (!JS_ValueToObject(cx, argv[1], &thisObj)) {
  2921. return JS_FALSE;
  2922. }
  2923. }
  2924. // The third argument is an optional error sentinel that js-ctypes will return
  2925. // if an exception is raised while executing the closure. The type must match
  2926. // the return type of the callback.
  2927. jsval errVal = JSVAL_VOID;
  2928. if (argc == 3)
  2929. errVal = argv[2];
  2930. JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]);
  2931. return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal);
  2932. }
  2933. JSObject*
  2934. PointerType::GetBaseType(JSObject* obj)
  2935. {
  2936. JS_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer);
  2937. jsval type = JS_GetReservedSlot(obj, SLOT_TARGET_T);
  2938. JS_ASSERT(!JSVAL_IS_NULL(type));
  2939. return JSVAL_TO_OBJECT(type);
  2940. }
  2941. JSBool
  2942. PointerType::TargetTypeGetter(JSContext* cx,
  2943. JSObject* obj,
  2944. jsid idval,
  2945. jsval* vp)
  2946. {
  2947. if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) {
  2948. JS_ReportError(cx, "not a PointerType");
  2949. return JS_FALSE;
  2950. }
  2951. *vp = JS_GetReservedSlot(obj, SLOT_TARGET_T);
  2952. JS_ASSERT(JSVAL_IS_OBJECT(*vp));
  2953. return JS_TRUE;
  2954. }
  2955. JSBool
  2956. PointerType::IsNull(JSContext* cx, uintN argc, jsval* vp)
  2957. {
  2958. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  2959. if (!obj || !CData::IsCData(obj)) {
  2960. JS_ReportError(cx, "not a CData");
  2961. return JS_FALSE;
  2962. }
  2963. // Get pointer type and base type.
  2964. JSObject* typeObj = CData::GetCType(obj);
  2965. if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
  2966. JS_ReportError(cx, "not a PointerType");
  2967. return JS_FALSE;
  2968. }
  2969. void* data = *static_cast<void**>(CData::GetData(obj));
  2970. jsval result = BOOLEAN_TO_JSVAL(data == NULL);
  2971. JS_SET_RVAL(cx, vp, result);
  2972. return JS_TRUE;
  2973. }
  2974. JSBool
  2975. PointerType::OffsetBy(JSContext* cx, intN offset, jsval* vp)
  2976. {
  2977. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  2978. if (!obj || !CData::IsCData(obj)) {
  2979. JS_ReportError(cx, "not a CData");
  2980. return JS_FALSE;
  2981. }
  2982. JSObject* typeObj = CData::GetCType(obj);
  2983. if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
  2984. JS_ReportError(cx, "not a PointerType");
  2985. return JS_FALSE;
  2986. }
  2987. JSObject* baseType = PointerType::GetBaseType(typeObj);
  2988. if (!CType::IsSizeDefined(baseType)) {
  2989. JS_ReportError(cx, "cannot modify pointer of undefined size");
  2990. return JS_FALSE;
  2991. }
  2992. size_t elementSize = CType::GetSize(baseType);
  2993. char* data = static_cast<char*>(*static_cast<void**>(CData::GetData(obj)));
  2994. void* address = data + offset * elementSize;
  2995. // Create a PointerType CData object containing the new address.
  2996. JSObject* result = CData::Create(cx, typeObj, NULL, &address, true);
  2997. if (!result)
  2998. return JS_FALSE;
  2999. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  3000. return JS_TRUE;
  3001. }
  3002. JSBool
  3003. PointerType::Increment(JSContext* cx, uintN argc, jsval* vp)
  3004. {
  3005. return OffsetBy(cx, 1, vp);
  3006. }
  3007. JSBool
  3008. PointerType::Decrement(JSContext* cx, uintN argc, jsval* vp)
  3009. {
  3010. return OffsetBy(cx, -1, vp);
  3011. }
  3012. JSBool
  3013. PointerType::ContentsGetter(JSContext* cx,
  3014. JSObject* obj,
  3015. jsid idval,
  3016. jsval* vp)
  3017. {
  3018. if (!CData::IsCData(obj)) {
  3019. JS_ReportError(cx, "not a CData");
  3020. return JS_FALSE;
  3021. }
  3022. // Get pointer type and base type.
  3023. JSObject* typeObj = CData::GetCType(obj);
  3024. if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
  3025. JS_ReportError(cx, "not a PointerType");
  3026. return JS_FALSE;
  3027. }
  3028. JSObject* baseType = GetBaseType(typeObj);
  3029. if (!CType::IsSizeDefined(baseType)) {
  3030. JS_ReportError(cx, "cannot get contents of undefined size");
  3031. return JS_FALSE;
  3032. }
  3033. void* data = *static_cast<void**>(CData::GetData(obj));
  3034. if (data == NULL) {
  3035. JS_ReportError(cx, "cannot read contents of null pointer");
  3036. return JS_FALSE;
  3037. }
  3038. jsval result;
  3039. if (!ConvertToJS(cx, baseType, NULL, data, false, false, &result))
  3040. return JS_FALSE;
  3041. JS_SET_RVAL(cx, vp, result);
  3042. return JS_TRUE;
  3043. }
  3044. JSBool
  3045. PointerType::ContentsSetter(JSContext* cx,
  3046. JSObject* obj,
  3047. jsid idval,
  3048. JSBool strict,
  3049. jsval* vp)
  3050. {
  3051. if (!CData::IsCData(obj)) {
  3052. JS_ReportError(cx, "not a CData");
  3053. return JS_FALSE;
  3054. }
  3055. // Get pointer type and base type.
  3056. JSObject* typeObj = CData::GetCType(obj);
  3057. if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
  3058. JS_ReportError(cx, "not a PointerType");
  3059. return JS_FALSE;
  3060. }
  3061. JSObject* baseType = GetBaseType(typeObj);
  3062. if (!CType::IsSizeDefined(baseType)) {
  3063. JS_ReportError(cx, "cannot set contents of undefined size");
  3064. return JS_FALSE;
  3065. }
  3066. void* data = *static_cast<void**>(CData::GetData(obj));
  3067. if (data == NULL) {
  3068. JS_ReportError(cx, "cannot write contents to null pointer");
  3069. return JS_FALSE;
  3070. }
  3071. return ImplicitConvert(cx, *vp, baseType, data, false, NULL);
  3072. }
  3073. /*******************************************************************************
  3074. ** ArrayType implementation
  3075. *******************************************************************************/
  3076. JSBool
  3077. ArrayType::Create(JSContext* cx, uintN argc, jsval* vp)
  3078. {
  3079. // Construct and return a new ArrayType object.
  3080. if (argc < 1 || argc > 2) {
  3081. JS_ReportError(cx, "ArrayType takes one or two arguments");
  3082. return JS_FALSE;
  3083. }
  3084. jsval* argv = JS_ARGV(cx, vp);
  3085. if (JSVAL_IS_PRIMITIVE(argv[0]) ||
  3086. !CType::IsCType(JSVAL_TO_OBJECT(argv[0]))) {
  3087. JS_ReportError(cx, "first argument must be a CType");
  3088. return JS_FALSE;
  3089. }
  3090. // Convert the length argument to a size_t.
  3091. size_t length = 0;
  3092. if (argc == 2 && !jsvalToSize(cx, argv[1], false, &length)) {
  3093. JS_ReportError(cx, "second argument must be a nonnegative integer");
  3094. return JS_FALSE;
  3095. }
  3096. JSObject* baseType = JSVAL_TO_OBJECT(argv[0]);
  3097. JSObject* result = CreateInternal(cx, baseType, length, argc == 2);
  3098. if (!result)
  3099. return JS_FALSE;
  3100. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  3101. return JS_TRUE;
  3102. }
  3103. JSObject*
  3104. ArrayType::CreateInternal(JSContext* cx,
  3105. JSObject* baseType,
  3106. size_t length,
  3107. bool lengthDefined)
  3108. {
  3109. // Get ctypes.ArrayType.prototype and the common prototype for CData objects
  3110. // of this type, from ctypes.CType.prototype.
  3111. JSObject* typeProto = CType::GetProtoFromType(baseType, SLOT_ARRAYPROTO);
  3112. JSObject* dataProto = CType::GetProtoFromType(baseType, SLOT_ARRAYDATAPROTO);
  3113. // Determine the size of the array from the base type, if possible.
  3114. // The size of the base type must be defined.
  3115. // If our length is undefined, both our size and length will be undefined.
  3116. size_t baseSize;
  3117. if (!CType::GetSafeSize(baseType, &baseSize)) {
  3118. JS_ReportError(cx, "base size must be defined");
  3119. return NULL;
  3120. }
  3121. jsval sizeVal = JSVAL_VOID;
  3122. jsval lengthVal = JSVAL_VOID;
  3123. if (lengthDefined) {
  3124. // Check for overflow, and convert to a jsint or jsdouble as required.
  3125. size_t size = length * baseSize;
  3126. if (length > 0 && size / length != baseSize) {
  3127. JS_ReportError(cx, "size overflow");
  3128. return NULL;
  3129. }
  3130. if (!SizeTojsval(cx, size, &sizeVal) ||
  3131. !SizeTojsval(cx, length, &lengthVal))
  3132. return NULL;
  3133. }
  3134. size_t align = CType::GetAlignment(baseType);
  3135. // Create a new CType object with the common properties and slots.
  3136. JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, NULL,
  3137. sizeVal, INT_TO_JSVAL(align), NULL);
  3138. if (!typeObj)
  3139. return NULL;
  3140. // Set the element type.
  3141. JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType));
  3142. // Set the length.
  3143. JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal);
  3144. return typeObj;
  3145. }
  3146. JSBool
  3147. ArrayType::ConstructData(JSContext* cx,
  3148. JSObject* obj,
  3149. uintN argc,
  3150. jsval* vp)
  3151. {
  3152. if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
  3153. JS_ReportError(cx, "not an ArrayType");
  3154. return JS_FALSE;
  3155. }
  3156. // Decide whether we have an object to initialize from. We'll override this
  3157. // if we get a length argument instead.
  3158. bool convertObject = argc == 1;
  3159. // Check if we're an array of undefined length. If we are, allow construction
  3160. // with a length argument, or with an actual JS array.
  3161. if (CType::IsSizeDefined(obj)) {
  3162. if (argc > 1) {
  3163. JS_ReportError(cx, "constructor takes zero or one argument");
  3164. return JS_FALSE;
  3165. }
  3166. } else {
  3167. if (argc != 1) {
  3168. JS_ReportError(cx, "constructor takes one argument");
  3169. return JS_FALSE;
  3170. }
  3171. JSObject* baseType = GetBaseType(obj);
  3172. jsval* argv = JS_ARGV(cx, vp);
  3173. size_t length;
  3174. if (jsvalToSize(cx, argv[0], false, &length)) {
  3175. // Have a length, rather than an object to initialize from.
  3176. convertObject = false;
  3177. } else if (!JSVAL_IS_PRIMITIVE(argv[0])) {
  3178. // We were given an object with a .length property.
  3179. // This could be a JS array, or a CData array.
  3180. JSObject* arg = JSVAL_TO_OBJECT(argv[0]);
  3181. js::AutoValueRooter lengthVal(cx);
  3182. if (!JS_GetProperty(cx, arg, "length", lengthVal.jsval_addr()) ||
  3183. !jsvalToSize(cx, lengthVal.jsval_value(), false, &length)) {
  3184. JS_ReportError(cx, "argument must be an array object or length");
  3185. return JS_FALSE;
  3186. }
  3187. } else if (JSVAL_IS_STRING(argv[0])) {
  3188. // We were given a string. Size the array to the appropriate length,
  3189. // including space for the terminator.
  3190. JSString* sourceString = JSVAL_TO_STRING(argv[0]);
  3191. size_t sourceLength = sourceString->length();
  3192. const jschar* sourceChars = sourceString->getChars(cx);
  3193. if (!sourceChars)
  3194. return false;
  3195. switch (CType::GetTypeCode(baseType)) {
  3196. case TYPE_char:
  3197. case TYPE_signed_char:
  3198. case TYPE_unsigned_char: {
  3199. // Determine the UTF-8 length.
  3200. length = GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
  3201. if (length == (size_t) -1)
  3202. return false;
  3203. ++length;
  3204. break;
  3205. }
  3206. case TYPE_jschar:
  3207. length = sourceLength + 1;
  3208. break;
  3209. default:
  3210. return TypeError(cx, "array", argv[0]);
  3211. }
  3212. } else {
  3213. JS_ReportError(cx, "argument must be an array object or length");
  3214. return JS_FALSE;
  3215. }
  3216. // Construct a new ArrayType of defined length, for the new CData object.
  3217. obj = CreateInternal(cx, baseType, length, true);
  3218. if (!obj)
  3219. return JS_FALSE;
  3220. }
  3221. // Root the CType object, in case we created one above.
  3222. js::AutoObjectRooter root(cx, obj);
  3223. JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
  3224. if (!result)
  3225. return JS_FALSE;
  3226. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  3227. if (convertObject) {
  3228. if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(result)))
  3229. return JS_FALSE;
  3230. }
  3231. return JS_TRUE;
  3232. }
  3233. JSObject*
  3234. ArrayType::GetBaseType(JSObject* obj)
  3235. {
  3236. JS_ASSERT(CType::IsCType(obj));
  3237. JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
  3238. jsval type = JS_GetReservedSlot(obj, SLOT_ELEMENT_T);
  3239. JS_ASSERT(!JSVAL_IS_NULL(type));
  3240. return JSVAL_TO_OBJECT(type);
  3241. }
  3242. bool
  3243. ArrayType::GetSafeLength(JSObject* obj, size_t* result)
  3244. {
  3245. JS_ASSERT(CType::IsCType(obj));
  3246. JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
  3247. jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH);
  3248. // The "length" property can be a jsint, a jsdouble, or JSVAL_VOID
  3249. // (for arrays of undefined length), and must always fit in a size_t.
  3250. if (JSVAL_IS_INT(length)) {
  3251. *result = JSVAL_TO_INT(length);
  3252. return true;
  3253. }
  3254. if (JSVAL_IS_DOUBLE(length)) {
  3255. *result = Convert<size_t>(JSVAL_TO_DOUBLE(length));
  3256. return true;
  3257. }
  3258. JS_ASSERT(JSVAL_IS_VOID(length));
  3259. return false;
  3260. }
  3261. size_t
  3262. ArrayType::GetLength(JSObject* obj)
  3263. {
  3264. JS_ASSERT(CType::IsCType(obj));
  3265. JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
  3266. jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH);
  3267. JS_ASSERT(!JSVAL_IS_VOID(length));
  3268. // The "length" property can be a jsint, a jsdouble, or JSVAL_VOID
  3269. // (for arrays of undefined length), and must always fit in a size_t.
  3270. // For callers who know it can never be JSVAL_VOID, return a size_t directly.
  3271. if (JSVAL_IS_INT(length))
  3272. return JSVAL_TO_INT(length);
  3273. return Convert<size_t>(JSVAL_TO_DOUBLE(length));
  3274. }
  3275. ffi_type*
  3276. ArrayType::BuildFFIType(JSContext* cx, JSObject* obj)
  3277. {
  3278. JS_ASSERT(CType::IsCType(obj));
  3279. JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
  3280. JS_ASSERT(CType::IsSizeDefined(obj));
  3281. JSObject* baseType = ArrayType::GetBaseType(obj);
  3282. ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
  3283. if (!ffiBaseType)
  3284. return NULL;
  3285. size_t length = ArrayType::GetLength(obj);
  3286. // Create an ffi_type to represent the array. This is necessary for the case
  3287. // where the array is part of a struct. Since libffi has no intrinsic
  3288. // support for array types, we approximate it by creating a struct type
  3289. // with elements of type 'baseType' and with appropriate size and alignment
  3290. // values. It would be nice to not do all the work of setting up 'elements',
  3291. // but some libffi platforms currently require that it be meaningful. I'm
  3292. // looking at you, x86_64.
  3293. AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>());
  3294. if (!ffiType) {
  3295. JS_ReportOutOfMemory(cx);
  3296. return NULL;
  3297. }
  3298. ffiType->type = FFI_TYPE_STRUCT;
  3299. ffiType->size = CType::GetSize(obj);
  3300. ffiType->alignment = CType::GetAlignment(obj);
  3301. ffiType->elements = cx->array_new<ffi_type*>(length + 1);
  3302. if (!ffiType->elements) {
  3303. JS_ReportAllocationOverflow(cx);
  3304. return NULL;
  3305. }
  3306. for (size_t i = 0; i < length; ++i)
  3307. ffiType->elements[i] = ffiBaseType;
  3308. ffiType->elements[length] = NULL;
  3309. return ffiType.forget();
  3310. }
  3311. JSBool
  3312. ArrayType::ElementTypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  3313. {
  3314. if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
  3315. JS_ReportError(cx, "not an ArrayType");
  3316. return JS_FALSE;
  3317. }
  3318. *vp = JS_GetReservedSlot(obj, SLOT_ELEMENT_T);
  3319. JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
  3320. return JS_TRUE;
  3321. }
  3322. JSBool
  3323. ArrayType::LengthGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  3324. {
  3325. // This getter exists for both CTypes and CDatas of the ArrayType persuasion.
  3326. // If we're dealing with a CData, get the CType from it.
  3327. if (CData::IsCData(obj))
  3328. obj = CData::GetCType(obj);
  3329. if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
  3330. JS_ReportError(cx, "not an ArrayType");
  3331. return JS_FALSE;
  3332. }
  3333. *vp = JS_GetReservedSlot(obj, SLOT_LENGTH);
  3334. JS_ASSERT(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_VOID(*vp));
  3335. return JS_TRUE;
  3336. }
  3337. JSBool
  3338. ArrayType::Getter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  3339. {
  3340. // This should never happen, but we'll check to be safe.
  3341. if (!CData::IsCData(obj)) {
  3342. JS_ReportError(cx, "not a CData");
  3343. return JS_FALSE;
  3344. }
  3345. // Bail early if we're not an ArrayType. (This setter is present for all
  3346. // CData, regardless of CType.)
  3347. JSObject* typeObj = CData::GetCType(obj);
  3348. if (CType::GetTypeCode(typeObj) != TYPE_array)
  3349. return JS_TRUE;
  3350. // Convert the index to a size_t and bounds-check it.
  3351. size_t index;
  3352. size_t length = GetLength(typeObj);
  3353. bool ok = jsidToSize(cx, idval, true, &index);
  3354. if (!ok && JSID_IS_STRING(idval)) {
  3355. // String either isn't a number, or doesn't fit in size_t.
  3356. // Chances are it's a regular property lookup, so return.
  3357. return JS_TRUE;
  3358. }
  3359. if (!ok || index >= length) {
  3360. JS_ReportError(cx, "invalid index");
  3361. return JS_FALSE;
  3362. }
  3363. JSObject* baseType = GetBaseType(typeObj);
  3364. size_t elementSize = CType::GetSize(baseType);
  3365. char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
  3366. return ConvertToJS(cx, baseType, obj, data, false, false, vp);
  3367. }
  3368. JSBool
  3369. ArrayType::Setter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
  3370. {
  3371. // This should never happen, but we'll check to be safe.
  3372. if (!CData::IsCData(obj)) {
  3373. JS_ReportError(cx, "not a CData");
  3374. return JS_FALSE;
  3375. }
  3376. // Bail early if we're not an ArrayType. (This setter is present for all
  3377. // CData, regardless of CType.)
  3378. JSObject* typeObj = CData::GetCType(obj);
  3379. if (CType::GetTypeCode(typeObj) != TYPE_array)
  3380. return JS_TRUE;
  3381. // Convert the index to a size_t and bounds-check it.
  3382. size_t index;
  3383. size_t length = GetLength(typeObj);
  3384. bool ok = jsidToSize(cx, idval, true, &index);
  3385. if (!ok && JSID_IS_STRING(idval)) {
  3386. // String either isn't a number, or doesn't fit in size_t.
  3387. // Chances are it's a regular property lookup, so return.
  3388. return JS_TRUE;
  3389. }
  3390. if (!ok || index >= length) {
  3391. JS_ReportError(cx, "invalid index");
  3392. return JS_FALSE;
  3393. }
  3394. JSObject* baseType = GetBaseType(typeObj);
  3395. size_t elementSize = CType::GetSize(baseType);
  3396. char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
  3397. return ImplicitConvert(cx, *vp, baseType, data, false, NULL);
  3398. }
  3399. JSBool
  3400. ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval* vp)
  3401. {
  3402. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  3403. if (!obj || !CData::IsCData(obj)) {
  3404. JS_ReportError(cx, "not a CData");
  3405. return JS_FALSE;
  3406. }
  3407. JSObject* typeObj = CData::GetCType(obj);
  3408. if (CType::GetTypeCode(typeObj) != TYPE_array) {
  3409. JS_ReportError(cx, "not an ArrayType");
  3410. return JS_FALSE;
  3411. }
  3412. if (argc != 1) {
  3413. JS_ReportError(cx, "addressOfElement takes one argument");
  3414. return JS_FALSE;
  3415. }
  3416. JSObject* baseType = GetBaseType(typeObj);
  3417. JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
  3418. if (!pointerType)
  3419. return JS_FALSE;
  3420. js::AutoObjectRooter root(cx, pointerType);
  3421. // Create a PointerType CData object containing null.
  3422. JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
  3423. if (!result)
  3424. return JS_FALSE;
  3425. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  3426. // Convert the index to a size_t and bounds-check it.
  3427. size_t index;
  3428. size_t length = GetLength(typeObj);
  3429. if (!jsvalToSize(cx, JS_ARGV(cx, vp)[0], false, &index) ||
  3430. index >= length) {
  3431. JS_ReportError(cx, "invalid index");
  3432. return JS_FALSE;
  3433. }
  3434. // Manually set the pointer inside the object, so we skip the conversion step.
  3435. void** data = static_cast<void**>(CData::GetData(result));
  3436. size_t elementSize = CType::GetSize(baseType);
  3437. *data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
  3438. return JS_TRUE;
  3439. }
  3440. /*******************************************************************************
  3441. ** StructType implementation
  3442. *******************************************************************************/
  3443. // For a struct field descriptor 'val' of the form { name : type }, extract
  3444. // 'name' and 'type'.
  3445. static JSFlatString*
  3446. ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
  3447. {
  3448. if (JSVAL_IS_PRIMITIVE(val)) {
  3449. JS_ReportError(cx, "struct field descriptors require a valid name and type");
  3450. return NULL;
  3451. }
  3452. JSObject* obj = JSVAL_TO_OBJECT(val);
  3453. JSObject* iter = JS_NewPropertyIterator(cx, obj);
  3454. if (!iter)
  3455. return NULL;
  3456. js::AutoObjectRooter iterroot(cx, iter);
  3457. jsid nameid;
  3458. if (!JS_NextProperty(cx, iter, &nameid))
  3459. return NULL;
  3460. if (JSID_IS_VOID(nameid)) {
  3461. JS_ReportError(cx, "struct field descriptors require a valid name and type");
  3462. return NULL;
  3463. }
  3464. if (!JSID_IS_STRING(nameid)) {
  3465. JS_ReportError(cx, "struct field descriptors require a valid name and type");
  3466. return NULL;
  3467. }
  3468. // make sure we have one, and only one, property
  3469. jsid id;
  3470. if (!JS_NextProperty(cx, iter, &id))
  3471. return NULL;
  3472. if (!JSID_IS_VOID(id)) {
  3473. JS_ReportError(cx, "struct field descriptors must contain one property");
  3474. return NULL;
  3475. }
  3476. js::AutoValueRooter propVal(cx);
  3477. if (!JS_GetPropertyById(cx, obj, nameid, propVal.jsval_addr()))
  3478. return NULL;
  3479. if (propVal.value().isPrimitive() ||
  3480. !CType::IsCType(JSVAL_TO_OBJECT(propVal.jsval_value()))) {
  3481. JS_ReportError(cx, "struct field descriptors require a valid name and type");
  3482. return NULL;
  3483. }
  3484. // Undefined size or zero size struct members are illegal.
  3485. // (Zero-size arrays are legal as struct members in C++, but libffi will
  3486. // choke on a zero-size struct, so we disallow them.)
  3487. *typeObj = JSVAL_TO_OBJECT(propVal.jsval_value());
  3488. size_t size;
  3489. if (!CType::GetSafeSize(*typeObj, &size) || size == 0) {
  3490. JS_ReportError(cx, "struct field types must have defined and nonzero size");
  3491. return NULL;
  3492. }
  3493. return JSID_TO_FLAT_STRING(nameid);
  3494. }
  3495. // For a struct field with 'name' and 'type', add an element of the form
  3496. // { name : type }.
  3497. static JSBool
  3498. AddFieldToArray(JSContext* cx,
  3499. jsval* element,
  3500. JSFlatString* name,
  3501. JSObject* typeObj)
  3502. {
  3503. JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL);
  3504. if (!fieldObj)
  3505. return false;
  3506. *element = OBJECT_TO_JSVAL(fieldObj);
  3507. if (!JS_DefineUCProperty(cx, fieldObj,
  3508. name->chars(), name->length(),
  3509. OBJECT_TO_JSVAL(typeObj), NULL, NULL,
  3510. JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
  3511. return false;
  3512. return JS_FreezeObject(cx, fieldObj);
  3513. }
  3514. JSBool
  3515. StructType::Create(JSContext* cx, uintN argc, jsval* vp)
  3516. {
  3517. // Construct and return a new StructType object.
  3518. if (argc < 1 || argc > 2) {
  3519. JS_ReportError(cx, "StructType takes one or two arguments");
  3520. return JS_FALSE;
  3521. }
  3522. jsval* argv = JS_ARGV(cx, vp);
  3523. jsval name = argv[0];
  3524. if (!JSVAL_IS_STRING(name)) {
  3525. JS_ReportError(cx, "first argument must be a string");
  3526. return JS_FALSE;
  3527. }
  3528. // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
  3529. JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
  3530. JSObject* typeProto = CType::GetProtoFromCtor(callee, SLOT_STRUCTPROTO);
  3531. // Create a simple StructType with no defined fields. The result will be
  3532. // non-instantiable as CData, will have no 'prototype' property, and will
  3533. // have undefined size and alignment and no ffi_type.
  3534. JSObject* result = CType::Create(cx, typeProto, NULL, TYPE_struct,
  3535. JSVAL_TO_STRING(name), JSVAL_VOID, JSVAL_VOID, NULL);
  3536. if (!result)
  3537. return JS_FALSE;
  3538. js::AutoObjectRooter root(cx, result);
  3539. if (argc == 2) {
  3540. if (JSVAL_IS_PRIMITIVE(argv[1]) ||
  3541. !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[1]))) {
  3542. JS_ReportError(cx, "second argument must be an array");
  3543. return JS_FALSE;
  3544. }
  3545. // Define the struct fields.
  3546. if (!DefineInternal(cx, result, JSVAL_TO_OBJECT(argv[1])))
  3547. return JS_FALSE;
  3548. }
  3549. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  3550. return JS_TRUE;
  3551. }
  3552. JSBool
  3553. StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj)
  3554. {
  3555. jsuint len;
  3556. ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
  3557. // Get the common prototype for CData objects of this type from
  3558. // ctypes.CType.prototype.
  3559. JSObject* dataProto = CType::GetProtoFromType(typeObj, SLOT_STRUCTDATAPROTO);
  3560. // Set up the 'prototype' and 'prototype.constructor' properties.
  3561. // The prototype will reflect the struct fields as properties on CData objects
  3562. // created from this type.
  3563. JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, dataProto, NULL);
  3564. if (!prototype)
  3565. return JS_FALSE;
  3566. js::AutoObjectRooter protoroot(cx, prototype);
  3567. if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
  3568. NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
  3569. return JS_FALSE;
  3570. // Create a FieldInfoHash to stash on the type object, and an array to root
  3571. // its constituents. (We cannot simply stash the hash in a reserved slot now
  3572. // to get GC safety for free, since if anything in this function fails we
  3573. // do not want to mutate 'typeObj'.)
  3574. AutoPtr<FieldInfoHash> fields(cx->new_<FieldInfoHash>());
  3575. Array<jsval, 16> fieldRootsArray;
  3576. if (!fields || !fields->init(len) || !fieldRootsArray.appendN(JSVAL_VOID, len)) {
  3577. JS_ReportOutOfMemory(cx);
  3578. return JS_FALSE;
  3579. }
  3580. js::AutoArrayRooter fieldRoots(cx, fieldRootsArray.length(),
  3581. fieldRootsArray.begin());
  3582. // Process the field types.
  3583. size_t structSize, structAlign;
  3584. if (len != 0) {
  3585. structSize = 0;
  3586. structAlign = 0;
  3587. for (jsuint i = 0; i < len; ++i) {
  3588. js::AutoValueRooter item(cx);
  3589. if (!JS_GetElement(cx, fieldsObj, i, item.jsval_addr()))
  3590. return JS_FALSE;
  3591. JSObject* fieldType = NULL;
  3592. JSFlatString* name = ExtractStructField(cx, item.jsval_value(), &fieldType);
  3593. if (!name)
  3594. return JS_FALSE;
  3595. fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType);
  3596. // Make sure each field name is unique, and add it to the hash.
  3597. FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
  3598. if (entryPtr) {
  3599. JS_ReportError(cx, "struct fields must have unique names");
  3600. return JS_FALSE;
  3601. }
  3602. ASSERT_OK(fields->add(entryPtr, name, FieldInfo()));
  3603. FieldInfo& info = entryPtr->value;
  3604. info.mType = fieldType;
  3605. info.mIndex = i;
  3606. // Add the field to the StructType's 'prototype' property.
  3607. if (!JS_DefineUCProperty(cx, prototype,
  3608. name->chars(), name->length(), JSVAL_VOID,
  3609. StructType::FieldGetter, StructType::FieldSetter,
  3610. JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT))
  3611. return JS_FALSE;
  3612. size_t fieldSize = CType::GetSize(fieldType);
  3613. size_t fieldAlign = CType::GetAlignment(fieldType);
  3614. size_t fieldOffset = Align(structSize, fieldAlign);
  3615. // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
  3616. // be zero, we can safely check fieldOffset + fieldSize without first
  3617. // checking fieldOffset for overflow.
  3618. if (fieldOffset + fieldSize < structSize) {
  3619. JS_ReportError(cx, "size overflow");
  3620. return JS_FALSE;
  3621. }
  3622. info.mOffset = fieldOffset;
  3623. structSize = fieldOffset + fieldSize;
  3624. if (fieldAlign > structAlign)
  3625. structAlign = fieldAlign;
  3626. }
  3627. // Pad the struct tail according to struct alignment.
  3628. size_t structTail = Align(structSize, structAlign);
  3629. if (structTail < structSize) {
  3630. JS_ReportError(cx, "size overflow");
  3631. return JS_FALSE;
  3632. }
  3633. structSize = structTail;
  3634. } else {
  3635. // Empty structs are illegal in C, but are legal and have a size of
  3636. // 1 byte in C++. We're going to allow them, and trick libffi into
  3637. // believing this by adding a char member. The resulting struct will have
  3638. // no getters or setters, and will be initialized to zero.
  3639. structSize = 1;
  3640. structAlign = 1;
  3641. }
  3642. jsval sizeVal;
  3643. if (!SizeTojsval(cx, structSize, &sizeVal))
  3644. return JS_FALSE;
  3645. JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PRIVATE_TO_JSVAL(fields.forget()));
  3646. JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal);
  3647. JS_SetReservedSlot(typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign));
  3648. //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212!
  3649. // return false;
  3650. JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype));
  3651. return JS_TRUE;
  3652. }
  3653. ffi_type*
  3654. StructType::BuildFFIType(JSContext* cx, JSObject* obj)
  3655. {
  3656. JS_ASSERT(CType::IsCType(obj));
  3657. JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
  3658. JS_ASSERT(CType::IsSizeDefined(obj));
  3659. const FieldInfoHash* fields = GetFieldInfo(obj);
  3660. size_t len = fields->count();
  3661. size_t structSize = CType::GetSize(obj);
  3662. size_t structAlign = CType::GetAlignment(obj);
  3663. AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>());
  3664. if (!ffiType) {
  3665. JS_ReportOutOfMemory(cx);
  3666. return NULL;
  3667. }
  3668. ffiType->type = FFI_TYPE_STRUCT;
  3669. AutoPtr<ffi_type*>::Array elements;
  3670. if (len != 0) {
  3671. elements = cx->array_new<ffi_type*>(len + 1);
  3672. if (!elements) {
  3673. JS_ReportOutOfMemory(cx);
  3674. return NULL;
  3675. }
  3676. elements[len] = NULL;
  3677. for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
  3678. const FieldInfoHash::Entry& entry = r.front();
  3679. ffi_type* fieldType = CType::GetFFIType(cx, entry.value.mType);
  3680. if (!fieldType)
  3681. return NULL;
  3682. elements[entry.value.mIndex] = fieldType;
  3683. }
  3684. } else {
  3685. // Represent an empty struct as having a size of 1 byte, just like C++.
  3686. JS_ASSERT(structSize == 1);
  3687. JS_ASSERT(structAlign == 1);
  3688. elements = cx->array_new<ffi_type*>(2);
  3689. if (!elements) {
  3690. JS_ReportOutOfMemory(cx);
  3691. return NULL;
  3692. }
  3693. elements[0] = &ffi_type_uint8;
  3694. elements[1] = NULL;
  3695. }
  3696. ffiType->elements = elements.get();
  3697. #ifdef DEBUG
  3698. // Perform a sanity check: the result of our struct size and alignment
  3699. // calculations should match libffi's. We force it to do this calculation
  3700. // by calling ffi_prep_cif.
  3701. ffi_cif cif;
  3702. ffiType->size = 0;
  3703. ffiType->alignment = 0;
  3704. ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType.get(), NULL);
  3705. JS_ASSERT(status == FFI_OK);
  3706. JS_ASSERT(structSize == ffiType->size);
  3707. JS_ASSERT(structAlign == ffiType->alignment);
  3708. #else
  3709. // Fill in the ffi_type's size and align fields. This makes libffi treat the
  3710. // type as initialized; it will not recompute the values. (We assume
  3711. // everything agrees; if it doesn't, we really want to know about it, which
  3712. // is the purpose of the above debug-only check.)
  3713. ffiType->size = structSize;
  3714. ffiType->alignment = structAlign;
  3715. #endif
  3716. elements.forget();
  3717. return ffiType.forget();
  3718. }
  3719. JSBool
  3720. StructType::Define(JSContext* cx, uintN argc, jsval* vp)
  3721. {
  3722. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  3723. if (!obj ||
  3724. !CType::IsCType(obj) ||
  3725. CType::GetTypeCode(obj) != TYPE_struct) {
  3726. JS_ReportError(cx, "not a StructType");
  3727. return JS_FALSE;
  3728. }
  3729. if (CType::IsSizeDefined(obj)) {
  3730. JS_ReportError(cx, "StructType has already been defined");
  3731. return JS_FALSE;
  3732. }
  3733. if (argc != 1) {
  3734. JS_ReportError(cx, "define takes one argument");
  3735. return JS_FALSE;
  3736. }
  3737. jsval arg = JS_ARGV(cx, vp)[0];
  3738. if (JSVAL_IS_PRIMITIVE(arg) ||
  3739. !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(arg))) {
  3740. JS_ReportError(cx, "argument must be an array");
  3741. return JS_FALSE;
  3742. }
  3743. return DefineInternal(cx, obj, JSVAL_TO_OBJECT(arg));
  3744. }
  3745. JSBool
  3746. StructType::ConstructData(JSContext* cx,
  3747. JSObject* obj,
  3748. uintN argc,
  3749. jsval* vp)
  3750. {
  3751. if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) {
  3752. JS_ReportError(cx, "not a StructType");
  3753. return JS_FALSE;
  3754. }
  3755. if (!CType::IsSizeDefined(obj)) {
  3756. JS_ReportError(cx, "cannot construct an opaque StructType");
  3757. return JS_FALSE;
  3758. }
  3759. JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
  3760. if (!result)
  3761. return JS_FALSE;
  3762. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  3763. if (argc == 0)
  3764. return JS_TRUE;
  3765. char* buffer = static_cast<char*>(CData::GetData(result));
  3766. const FieldInfoHash* fields = GetFieldInfo(obj);
  3767. jsval* argv = JS_ARGV(cx, vp);
  3768. if (argc == 1) {
  3769. // There are two possible interpretations of the argument:
  3770. // 1) It may be an object '{ ... }' with properties representing the
  3771. // struct fields intended to ExplicitConvert wholesale to our StructType.
  3772. // 2) If the struct contains one field, the arg may be intended to
  3773. // ImplicitConvert directly to that arg's CType.
  3774. // Thankfully, the conditions for these two possibilities to succeed
  3775. // are mutually exclusive, so we can pick the right one.
  3776. // Try option 1) first.
  3777. if (ExplicitConvert(cx, argv[0], obj, buffer))
  3778. return JS_TRUE;
  3779. if (fields->count() != 1)
  3780. return JS_FALSE;
  3781. // If ExplicitConvert failed, and there is no pending exception, then assume
  3782. // hard failure (out of memory, or some other similarly serious condition).
  3783. if (!JS_IsExceptionPending(cx))
  3784. return JS_FALSE;
  3785. // Otherwise, assume soft failure, and clear the pending exception so that we
  3786. // can throw a different one as required.
  3787. JS_ClearPendingException(cx);
  3788. // Fall through to try option 2).
  3789. }
  3790. // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'.
  3791. // ImplicitConvert each field.
  3792. if (argc == fields->count()) {
  3793. for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
  3794. const FieldInfo& field = r.front().value;
  3795. STATIC_ASSUME(field.mIndex < fields->count()); /* Quantified invariant */
  3796. if (!ImplicitConvert(cx, argv[field.mIndex], field.mType,
  3797. buffer + field.mOffset,
  3798. false, NULL))
  3799. return JS_FALSE;
  3800. }
  3801. return JS_TRUE;
  3802. }
  3803. JS_ReportError(cx, "constructor takes 0, 1, or %u arguments",
  3804. fields->count());
  3805. return JS_FALSE;
  3806. }
  3807. const FieldInfoHash*
  3808. StructType::GetFieldInfo(JSObject* obj)
  3809. {
  3810. JS_ASSERT(CType::IsCType(obj));
  3811. JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
  3812. jsval slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
  3813. JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
  3814. return static_cast<const FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
  3815. }
  3816. const FieldInfo*
  3817. StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name)
  3818. {
  3819. JS_ASSERT(CType::IsCType(obj));
  3820. JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
  3821. FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name);
  3822. if (ptr)
  3823. return &ptr->value;
  3824. JSAutoByteString bytes(cx, name);
  3825. if (!bytes)
  3826. return NULL;
  3827. JS_ReportError(cx, "%s does not name a field", bytes.ptr());
  3828. return NULL;
  3829. }
  3830. JSObject*
  3831. StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
  3832. {
  3833. JS_ASSERT(CType::IsCType(obj));
  3834. JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
  3835. JS_ASSERT(CType::IsSizeDefined(obj));
  3836. const FieldInfoHash* fields = GetFieldInfo(obj);
  3837. size_t len = fields->count();
  3838. // Prepare a new array for the 'fields' property of the StructType.
  3839. Array<jsval, 16> fieldsVec;
  3840. if (!fieldsVec.appendN(JSVAL_VOID, len))
  3841. return NULL;
  3842. js::AutoArrayRooter root(cx, fieldsVec.length(), fieldsVec.begin());
  3843. for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
  3844. const FieldInfoHash::Entry& entry = r.front();
  3845. // Add the field descriptor to the array.
  3846. if (!AddFieldToArray(cx, &fieldsVec[entry.value.mIndex],
  3847. entry.key, entry.value.mType))
  3848. return NULL;
  3849. }
  3850. JSObject* fieldsProp = JS_NewArrayObject(cx, len, fieldsVec.begin());
  3851. if (!fieldsProp)
  3852. return NULL;
  3853. // Seal the fields array.
  3854. if (!JS_FreezeObject(cx, fieldsProp))
  3855. return NULL;
  3856. return fieldsProp;
  3857. }
  3858. JSBool
  3859. StructType::FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  3860. {
  3861. if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) {
  3862. JS_ReportError(cx, "not a StructType");
  3863. return JS_FALSE;
  3864. }
  3865. *vp = JS_GetReservedSlot(obj, SLOT_FIELDS);
  3866. if (!CType::IsSizeDefined(obj)) {
  3867. JS_ASSERT(JSVAL_IS_VOID(*vp));
  3868. return JS_TRUE;
  3869. }
  3870. if (JSVAL_IS_VOID(*vp)) {
  3871. // Build the 'fields' array lazily.
  3872. JSObject* fields = BuildFieldsArray(cx, obj);
  3873. if (!fields)
  3874. return JS_FALSE;
  3875. JS_SetReservedSlot(obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields));
  3876. *vp = OBJECT_TO_JSVAL(fields);
  3877. }
  3878. JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) &&
  3879. JS_IsArrayObject(cx, JSVAL_TO_OBJECT(*vp)));
  3880. return JS_TRUE;
  3881. }
  3882. JSBool
  3883. StructType::FieldGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  3884. {
  3885. if (!CData::IsCData(obj)) {
  3886. JS_ReportError(cx, "not a CData");
  3887. return JS_FALSE;
  3888. }
  3889. JSObject* typeObj = CData::GetCType(obj);
  3890. if (CType::GetTypeCode(typeObj) != TYPE_struct) {
  3891. JS_ReportError(cx, "not a StructType");
  3892. return JS_FALSE;
  3893. }
  3894. const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
  3895. if (!field)
  3896. return JS_FALSE;
  3897. char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
  3898. return ConvertToJS(cx, field->mType, obj, data, false, false, vp);
  3899. }
  3900. JSBool
  3901. StructType::FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
  3902. {
  3903. if (!CData::IsCData(obj)) {
  3904. JS_ReportError(cx, "not a CData");
  3905. return JS_FALSE;
  3906. }
  3907. JSObject* typeObj = CData::GetCType(obj);
  3908. if (CType::GetTypeCode(typeObj) != TYPE_struct) {
  3909. JS_ReportError(cx, "not a StructType");
  3910. return JS_FALSE;
  3911. }
  3912. const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
  3913. if (!field)
  3914. return JS_FALSE;
  3915. char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
  3916. return ImplicitConvert(cx, *vp, field->mType, data, false, NULL);
  3917. }
  3918. JSBool
  3919. StructType::AddressOfField(JSContext* cx, uintN argc, jsval* vp)
  3920. {
  3921. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  3922. if (!obj || !CData::IsCData(obj)) {
  3923. JS_ReportError(cx, "not a CData");
  3924. return JS_FALSE;
  3925. }
  3926. JSObject* typeObj = CData::GetCType(obj);
  3927. if (CType::GetTypeCode(typeObj) != TYPE_struct) {
  3928. JS_ReportError(cx, "not a StructType");
  3929. return JS_FALSE;
  3930. }
  3931. if (argc != 1) {
  3932. JS_ReportError(cx, "addressOfField takes one argument");
  3933. return JS_FALSE;
  3934. }
  3935. JSFlatString *str = JS_FlattenString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]));
  3936. if (!str)
  3937. return JS_FALSE;
  3938. const FieldInfo* field = LookupField(cx, typeObj, str);
  3939. if (!field)
  3940. return JS_FALSE;
  3941. JSObject* baseType = field->mType;
  3942. JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
  3943. if (!pointerType)
  3944. return JS_FALSE;
  3945. js::AutoObjectRooter root(cx, pointerType);
  3946. // Create a PointerType CData object containing null.
  3947. JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
  3948. if (!result)
  3949. return JS_FALSE;
  3950. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  3951. // Manually set the pointer inside the object, so we skip the conversion step.
  3952. void** data = static_cast<void**>(CData::GetData(result));
  3953. *data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
  3954. return JS_TRUE;
  3955. }
  3956. /*******************************************************************************
  3957. ** FunctionType implementation
  3958. *******************************************************************************/
  3959. // Helper class for handling allocation of function arguments.
  3960. struct AutoValue
  3961. {
  3962. AutoValue() : mData(NULL) { }
  3963. ~AutoValue()
  3964. {
  3965. UnwantedForeground::array_delete(static_cast<char*>(mData));
  3966. }
  3967. bool SizeToType(JSContext* cx, JSObject* type)
  3968. {
  3969. // Allocate a minimum of sizeof(ffi_arg) to handle small integers.
  3970. size_t size = Align(CType::GetSize(type), sizeof(ffi_arg));
  3971. mData = cx->array_new<char>(size);
  3972. if (mData)
  3973. memset(mData, 0, size);
  3974. return mData != NULL;
  3975. }
  3976. void* mData;
  3977. };
  3978. static bool
  3979. GetABI(JSContext* cx, jsval abiType, ffi_abi* result)
  3980. {
  3981. if (JSVAL_IS_PRIMITIVE(abiType))
  3982. return false;
  3983. ABICode abi = GetABICode(JSVAL_TO_OBJECT(abiType));
  3984. // determine the ABI from the subset of those available on the
  3985. // given platform. ABI_DEFAULT specifies the default
  3986. // C calling convention (cdecl) on each platform.
  3987. switch (abi) {
  3988. case ABI_DEFAULT:
  3989. *result = FFI_DEFAULT_ABI;
  3990. return true;
  3991. case ABI_STDCALL:
  3992. case ABI_WINAPI:
  3993. #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
  3994. *result = FFI_STDCALL;
  3995. return true;
  3996. #elif (defined(_WIN64))
  3997. // We'd like the same code to work across Win32 and Win64, so stdcall_api
  3998. // and winapi_abi become aliases to the lone Win64 ABI.
  3999. *result = FFI_WIN64;
  4000. return true;
  4001. #endif
  4002. case INVALID_ABI:
  4003. break;
  4004. }
  4005. return false;
  4006. }
  4007. static JSObject*
  4008. PrepareType(JSContext* cx, jsval type)
  4009. {
  4010. if (JSVAL_IS_PRIMITIVE(type) ||
  4011. !CType::IsCType(JSVAL_TO_OBJECT(type))) {
  4012. JS_ReportError(cx, "not a ctypes type");
  4013. return NULL;
  4014. }
  4015. JSObject* result = JSVAL_TO_OBJECT(type);
  4016. TypeCode typeCode = CType::GetTypeCode(result);
  4017. if (typeCode == TYPE_array) {
  4018. // convert array argument types to pointers, just like C.
  4019. // ImplicitConvert will do the same, when passing an array as data.
  4020. JSObject* baseType = ArrayType::GetBaseType(result);
  4021. result = PointerType::CreateInternal(cx, baseType);
  4022. if (!result)
  4023. return NULL;
  4024. } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) {
  4025. // disallow void or function argument types
  4026. JS_ReportError(cx, "Cannot have void or function argument type");
  4027. return NULL;
  4028. }
  4029. if (!CType::IsSizeDefined(result)) {
  4030. JS_ReportError(cx, "Argument type must have defined size");
  4031. return NULL;
  4032. }
  4033. // libffi cannot pass types of zero size by value.
  4034. JS_ASSERT(CType::GetSize(result) != 0);
  4035. return result;
  4036. }
  4037. static JSObject*
  4038. PrepareReturnType(JSContext* cx, jsval type)
  4039. {
  4040. if (JSVAL_IS_PRIMITIVE(type) ||
  4041. !CType::IsCType(JSVAL_TO_OBJECT(type))) {
  4042. JS_ReportError(cx, "not a ctypes type");
  4043. return NULL;
  4044. }
  4045. JSObject* result = JSVAL_TO_OBJECT(type);
  4046. TypeCode typeCode = CType::GetTypeCode(result);
  4047. // Arrays and functions can never be return types.
  4048. if (typeCode == TYPE_array || typeCode == TYPE_function) {
  4049. JS_ReportError(cx, "Return type cannot be an array or function");
  4050. return NULL;
  4051. }
  4052. if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) {
  4053. JS_ReportError(cx, "Return type must have defined size");
  4054. return NULL;
  4055. }
  4056. // libffi cannot pass types of zero size by value.
  4057. JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0);
  4058. return result;
  4059. }
  4060. static JS_ALWAYS_INLINE JSBool
  4061. IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis)
  4062. {
  4063. *isEllipsis = false;
  4064. if (!JSVAL_IS_STRING(v))
  4065. return true;
  4066. JSString* str = JSVAL_TO_STRING(v);
  4067. if (str->length() != 3)
  4068. return true;
  4069. const jschar* chars = str->getChars(cx);
  4070. if (!chars)
  4071. return false;
  4072. jschar dot = '.';
  4073. *isEllipsis = (chars[0] == dot &&
  4074. chars[1] == dot &&
  4075. chars[2] == dot);
  4076. return true;
  4077. }
  4078. static JSBool
  4079. PrepareCIF(JSContext* cx,
  4080. FunctionInfo* fninfo)
  4081. {
  4082. ffi_abi abi;
  4083. if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) {
  4084. JS_ReportError(cx, "Invalid ABI specification");
  4085. return false;
  4086. }
  4087. ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
  4088. if (!rtype)
  4089. return false;
  4090. ffi_status status =
  4091. ffi_prep_cif(&fninfo->mCIF,
  4092. abi,
  4093. fninfo->mFFITypes.length(),
  4094. rtype,
  4095. fninfo->mFFITypes.begin());
  4096. switch (status) {
  4097. case FFI_OK:
  4098. return true;
  4099. case FFI_BAD_ABI:
  4100. JS_ReportError(cx, "Invalid ABI specification");
  4101. return false;
  4102. case FFI_BAD_TYPEDEF:
  4103. JS_ReportError(cx, "Invalid type specification");
  4104. return false;
  4105. default:
  4106. JS_ReportError(cx, "Unknown libffi error");
  4107. return false;
  4108. }
  4109. }
  4110. void
  4111. FunctionType::BuildSymbolName(JSString* name,
  4112. JSObject* typeObj,
  4113. AutoCString& result)
  4114. {
  4115. FunctionInfo* fninfo = GetFunctionInfo(typeObj);
  4116. switch (GetABICode(fninfo->mABI)) {
  4117. case ABI_DEFAULT:
  4118. case ABI_WINAPI:
  4119. // For cdecl or WINAPI functions, no mangling is necessary.
  4120. AppendString(result, name);
  4121. break;
  4122. case ABI_STDCALL: {
  4123. #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
  4124. // On WIN32, stdcall functions look like:
  4125. // _foo@40
  4126. // where 'foo' is the function name, and '40' is the aligned size of the
  4127. // arguments.
  4128. AppendString(result, "_");
  4129. AppendString(result, name);
  4130. AppendString(result, "@");
  4131. // Compute the suffix by aligning each argument to sizeof(ffi_arg).
  4132. size_t size = 0;
  4133. for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
  4134. JSObject* argType = fninfo->mArgTypes[i];
  4135. size += Align(CType::GetSize(argType), sizeof(ffi_arg));
  4136. }
  4137. IntegerToString(size, 10, result);
  4138. #elif defined(_WIN64)
  4139. // On Win64, stdcall is an alias to the default ABI for compatibility, so no
  4140. // mangling is done.
  4141. AppendString(result, name);
  4142. #endif
  4143. break;
  4144. }
  4145. case INVALID_ABI:
  4146. JS_NOT_REACHED("invalid abi");
  4147. break;
  4148. }
  4149. }
  4150. static FunctionInfo*
  4151. NewFunctionInfo(JSContext* cx,
  4152. jsval abiType,
  4153. jsval returnType,
  4154. jsval* argTypes,
  4155. uintN argLength)
  4156. {
  4157. AutoPtr<FunctionInfo> fninfo(cx->new_<FunctionInfo>());
  4158. if (!fninfo) {
  4159. JS_ReportOutOfMemory(cx);
  4160. return NULL;
  4161. }
  4162. ffi_abi abi;
  4163. if (!GetABI(cx, abiType, &abi)) {
  4164. JS_ReportError(cx, "Invalid ABI specification");
  4165. return NULL;
  4166. }
  4167. fninfo->mABI = JSVAL_TO_OBJECT(abiType);
  4168. // prepare the result type
  4169. fninfo->mReturnType = PrepareReturnType(cx, returnType);
  4170. if (!fninfo->mReturnType)
  4171. return NULL;
  4172. // prepare the argument types
  4173. if (!fninfo->mArgTypes.reserve(argLength) ||
  4174. !fninfo->mFFITypes.reserve(argLength)) {
  4175. JS_ReportOutOfMemory(cx);
  4176. return NULL;
  4177. }
  4178. fninfo->mIsVariadic = false;
  4179. for (uint32_t i = 0; i < argLength; ++i) {
  4180. bool isEllipsis;
  4181. if (!IsEllipsis(cx, argTypes[i], &isEllipsis))
  4182. return NULL;
  4183. if (isEllipsis) {
  4184. fninfo->mIsVariadic = true;
  4185. if (i < 1) {
  4186. JS_ReportError(cx, "\"...\" may not be the first and only parameter "
  4187. "type of a variadic function declaration");
  4188. return NULL;
  4189. }
  4190. if (i < argLength - 1) {
  4191. JS_ReportError(cx, "\"...\" must be the last parameter type of a "
  4192. "variadic function declaration");
  4193. return NULL;
  4194. }
  4195. if (GetABICode(fninfo->mABI) != ABI_DEFAULT) {
  4196. JS_ReportError(cx, "Variadic functions must use the __cdecl calling "
  4197. "convention");
  4198. return NULL;
  4199. }
  4200. break;
  4201. }
  4202. JSObject* argType = PrepareType(cx, argTypes[i]);
  4203. if (!argType)
  4204. return NULL;
  4205. ffi_type* ffiType = CType::GetFFIType(cx, argType);
  4206. if (!ffiType)
  4207. return NULL;
  4208. fninfo->mArgTypes.infallibleAppend(argType);
  4209. fninfo->mFFITypes.infallibleAppend(ffiType);
  4210. }
  4211. if (fninfo->mIsVariadic)
  4212. // wait to PrepareCIF until function is called
  4213. return fninfo.forget();
  4214. if (!PrepareCIF(cx, fninfo.get()))
  4215. return NULL;
  4216. return fninfo.forget();
  4217. }
  4218. JSBool
  4219. FunctionType::Create(JSContext* cx, uintN argc, jsval* vp)
  4220. {
  4221. // Construct and return a new FunctionType object.
  4222. if (argc < 2 || argc > 3) {
  4223. JS_ReportError(cx, "FunctionType takes two or three arguments");
  4224. return JS_FALSE;
  4225. }
  4226. jsval* argv = JS_ARGV(cx, vp);
  4227. Array<jsval, 16> argTypes;
  4228. JSObject* arrayObj = NULL;
  4229. if (argc == 3) {
  4230. // Prepare an array of jsvals for the arguments.
  4231. if (JSVAL_IS_PRIMITIVE(argv[2]) ||
  4232. !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[2]))) {
  4233. JS_ReportError(cx, "third argument must be an array");
  4234. return JS_FALSE;
  4235. }
  4236. arrayObj = JSVAL_TO_OBJECT(argv[2]);
  4237. jsuint len;
  4238. ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len));
  4239. if (!argTypes.appendN(JSVAL_VOID, len)) {
  4240. JS_ReportOutOfMemory(cx);
  4241. return JS_FALSE;
  4242. }
  4243. }
  4244. // Pull out the argument types from the array, if any.
  4245. JS_ASSERT(!argTypes.length() || arrayObj);
  4246. js::AutoArrayRooter items(cx, argTypes.length(), argTypes.begin());
  4247. for (jsuint i = 0; i < argTypes.length(); ++i) {
  4248. if (!JS_GetElement(cx, arrayObj, i, &argTypes[i]))
  4249. return JS_FALSE;
  4250. }
  4251. JSObject* result = CreateInternal(cx, argv[0], argv[1],
  4252. argTypes.begin(), argTypes.length());
  4253. if (!result)
  4254. return JS_FALSE;
  4255. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  4256. return JS_TRUE;
  4257. }
  4258. JSObject*
  4259. FunctionType::CreateInternal(JSContext* cx,
  4260. jsval abi,
  4261. jsval rtype,
  4262. jsval* argtypes,
  4263. jsuint arglen)
  4264. {
  4265. // Determine and check the types, and prepare the function CIF.
  4266. AutoPtr<FunctionInfo> fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen));
  4267. if (!fninfo)
  4268. return NULL;
  4269. // Get ctypes.FunctionType.prototype and the common prototype for CData objects
  4270. // of this type, from ctypes.CType.prototype.
  4271. JSObject* typeProto = CType::GetProtoFromType(fninfo->mReturnType,
  4272. SLOT_FUNCTIONPROTO);
  4273. JSObject* dataProto = CType::GetProtoFromType(fninfo->mReturnType,
  4274. SLOT_FUNCTIONDATAPROTO);
  4275. // Create a new CType object with the common properties and slots.
  4276. JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_function,
  4277. NULL, JSVAL_VOID, JSVAL_VOID, NULL);
  4278. if (!typeObj)
  4279. return NULL;
  4280. js::AutoObjectRooter root(cx, typeObj);
  4281. // Stash the FunctionInfo in a reserved slot.
  4282. JS_SetReservedSlot(typeObj, SLOT_FNINFO, PRIVATE_TO_JSVAL(fninfo.forget()));
  4283. return typeObj;
  4284. }
  4285. // Construct a function pointer to a JS function (see CClosure::Create()).
  4286. // Regular function pointers are constructed directly in
  4287. // PointerType::ConstructData().
  4288. JSBool
  4289. FunctionType::ConstructData(JSContext* cx,
  4290. JSObject* typeObj,
  4291. JSObject* dataObj,
  4292. JSObject* fnObj,
  4293. JSObject* thisObj,
  4294. jsval errVal)
  4295. {
  4296. JS_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function);
  4297. PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(dataObj));
  4298. FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
  4299. if (fninfo->mIsVariadic) {
  4300. JS_ReportError(cx, "Can't declare a variadic callback function");
  4301. return JS_FALSE;
  4302. }
  4303. if (GetABICode(fninfo->mABI) == ABI_WINAPI) {
  4304. JS_ReportError(cx, "Can't declare a ctypes.winapi_abi callback function, "
  4305. "use ctypes.stdcall_abi instead");
  4306. return JS_FALSE;
  4307. }
  4308. JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data);
  4309. if (!closureObj)
  4310. return JS_FALSE;
  4311. js::AutoObjectRooter root(cx, closureObj);
  4312. // Set the closure object as the referent of the new CData object.
  4313. JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(closureObj));
  4314. // Seal the CData object, to prevent modification of the function pointer.
  4315. // This permanently associates this object with the closure, and avoids
  4316. // having to do things like reset SLOT_REFERENT when someone tries to
  4317. // change the pointer value.
  4318. // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
  4319. // could be called on a frozen object.
  4320. return JS_FreezeObject(cx, dataObj);
  4321. }
  4322. typedef Array<AutoValue, 16> AutoValueAutoArray;
  4323. static JSBool
  4324. ConvertArgument(JSContext* cx,
  4325. jsval arg,
  4326. JSObject* type,
  4327. AutoValue* value,
  4328. AutoValueAutoArray* strings)
  4329. {
  4330. if (!value->SizeToType(cx, type)) {
  4331. JS_ReportAllocationOverflow(cx);
  4332. return false;
  4333. }
  4334. bool freePointer = false;
  4335. if (!ImplicitConvert(cx, arg, type, value->mData, true, &freePointer))
  4336. return false;
  4337. if (freePointer) {
  4338. // ImplicitConvert converted a string for us, which we have to free.
  4339. // Keep track of it.
  4340. if (!strings->growBy(1)) {
  4341. JS_ReportOutOfMemory(cx);
  4342. return false;
  4343. }
  4344. strings->back().mData = *static_cast<char**>(value->mData);
  4345. }
  4346. return true;
  4347. }
  4348. JSBool
  4349. FunctionType::Call(JSContext* cx,
  4350. uintN argc,
  4351. jsval* vp)
  4352. {
  4353. // get the callee object...
  4354. JSObject* obj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
  4355. if (!CData::IsCData(obj)) {
  4356. JS_ReportError(cx, "not a CData");
  4357. return false;
  4358. }
  4359. JSObject* typeObj = CData::GetCType(obj);
  4360. if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
  4361. JS_ReportError(cx, "not a FunctionType.ptr");
  4362. return false;
  4363. }
  4364. typeObj = PointerType::GetBaseType(typeObj);
  4365. if (CType::GetTypeCode(typeObj) != TYPE_function) {
  4366. JS_ReportError(cx, "not a FunctionType.ptr");
  4367. return false;
  4368. }
  4369. FunctionInfo* fninfo = GetFunctionInfo(typeObj);
  4370. uint32_t argcFixed = fninfo->mArgTypes.length();
  4371. if ((!fninfo->mIsVariadic && argc != argcFixed) ||
  4372. (fninfo->mIsVariadic && argc < argcFixed)) {
  4373. JS_ReportError(cx, "Number of arguments does not match declaration");
  4374. return false;
  4375. }
  4376. // Check if we have a Library object. If we do, make sure it's open.
  4377. jsval slot = JS_GetReservedSlot(obj, SLOT_REFERENT);
  4378. if (!JSVAL_IS_VOID(slot) && Library::IsLibrary(JSVAL_TO_OBJECT(slot))) {
  4379. PRLibrary* library = Library::GetLibrary(JSVAL_TO_OBJECT(slot));
  4380. if (!library) {
  4381. JS_ReportError(cx, "library is not open");
  4382. return false;
  4383. }
  4384. }
  4385. // prepare the values for each argument
  4386. AutoValueAutoArray values;
  4387. AutoValueAutoArray strings;
  4388. if (!values.resize(argc)) {
  4389. JS_ReportOutOfMemory(cx);
  4390. return false;
  4391. }
  4392. jsval* argv = JS_ARGV(cx, vp);
  4393. for (jsuint i = 0; i < argcFixed; ++i)
  4394. if (!ConvertArgument(cx, argv[i], fninfo->mArgTypes[i], &values[i], &strings))
  4395. return false;
  4396. if (fninfo->mIsVariadic) {
  4397. if (!fninfo->mFFITypes.resize(argc)) {
  4398. JS_ReportOutOfMemory(cx);
  4399. return false;
  4400. }
  4401. JSObject* obj; // Could reuse obj instead of declaring a second
  4402. JSObject* type; // JSObject*, but readability would suffer.
  4403. for (uint32_t i = argcFixed; i < argc; ++i) {
  4404. if (JSVAL_IS_PRIMITIVE(argv[i]) ||
  4405. !CData::IsCData(obj = JSVAL_TO_OBJECT(argv[i]))) {
  4406. // Since we know nothing about the CTypes of the ... arguments,
  4407. // they absolutely must be CData objects already.
  4408. JS_ReportError(cx, "argument %d of type %s is not a CData object",
  4409. i, JS_GetTypeName(cx, JS_TypeOfValue(cx, argv[i])));
  4410. return false;
  4411. }
  4412. if (!(type = CData::GetCType(obj)) ||
  4413. !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) ||
  4414. // Relying on ImplicitConvert only for the limited purpose of
  4415. // converting one CType to another (e.g., T[] to T*).
  4416. !ConvertArgument(cx, argv[i], type, &values[i], &strings) ||
  4417. !(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) {
  4418. // These functions report their own errors.
  4419. return false;
  4420. }
  4421. }
  4422. if (!PrepareCIF(cx, fninfo))
  4423. return false;
  4424. }
  4425. // initialize a pointer to an appropriate location, for storing the result
  4426. AutoValue returnValue;
  4427. TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
  4428. if (typeCode != TYPE_void_t &&
  4429. !returnValue.SizeToType(cx, fninfo->mReturnType)) {
  4430. JS_ReportAllocationOverflow(cx);
  4431. return false;
  4432. }
  4433. uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(obj));
  4434. // suspend the request before we call into the function, since the call
  4435. // may block or otherwise take a long time to return.
  4436. {
  4437. JSAutoSuspendRequest suspend(cx);
  4438. ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData,
  4439. reinterpret_cast<void**>(values.begin()));
  4440. }
  4441. // Small integer types get returned as a word-sized ffi_arg. Coerce it back
  4442. // into the correct size for ConvertToJS.
  4443. switch (typeCode) {
  4444. #define DEFINE_INT_TYPE(name, type, ffiType) \
  4445. case TYPE_##name: \
  4446. if (sizeof(type) < sizeof(ffi_arg)) { \
  4447. ffi_arg data = *static_cast<ffi_arg*>(returnValue.mData); \
  4448. *static_cast<type*>(returnValue.mData) = static_cast<type>(data); \
  4449. } \
  4450. break;
  4451. #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4452. #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4453. #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4454. #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4455. #include "typedefs.h"
  4456. default:
  4457. break;
  4458. }
  4459. // prepare a JS object from the result
  4460. return ConvertToJS(cx, fninfo->mReturnType, NULL, returnValue.mData,
  4461. false, true, vp);
  4462. }
  4463. FunctionInfo*
  4464. FunctionType::GetFunctionInfo(JSObject* obj)
  4465. {
  4466. JS_ASSERT(CType::IsCType(obj));
  4467. JS_ASSERT(CType::GetTypeCode(obj) == TYPE_function);
  4468. jsval slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
  4469. JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
  4470. return static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
  4471. }
  4472. static JSBool
  4473. CheckFunctionType(JSContext* cx, JSObject* obj)
  4474. {
  4475. if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_function) {
  4476. JS_ReportError(cx, "not a FunctionType");
  4477. return JS_FALSE;
  4478. }
  4479. return JS_TRUE;
  4480. }
  4481. JSBool
  4482. FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  4483. {
  4484. if (!CheckFunctionType(cx, obj))
  4485. return JS_FALSE;
  4486. // Check if we have a cached argTypes array.
  4487. *vp = JS_GetReservedSlot(obj, SLOT_ARGS_T);
  4488. if (!JSVAL_IS_VOID(*vp))
  4489. return JS_TRUE;
  4490. FunctionInfo* fninfo = GetFunctionInfo(obj);
  4491. size_t len = fninfo->mArgTypes.length();
  4492. // Prepare a new array.
  4493. Array<jsval, 16> vec;
  4494. if (!vec.resize(len))
  4495. return JS_FALSE;
  4496. for (size_t i = 0; i < len; ++i)
  4497. vec[i] = OBJECT_TO_JSVAL(fninfo->mArgTypes[i]);
  4498. JSObject* argTypes = JS_NewArrayObject(cx, len, vec.begin());
  4499. if (!argTypes)
  4500. return JS_FALSE;
  4501. // Seal and cache it.
  4502. if (!JS_FreezeObject(cx, argTypes))
  4503. return JS_FALSE;
  4504. JS_SetReservedSlot(obj, SLOT_ARGS_T, OBJECT_TO_JSVAL(argTypes));
  4505. *vp = OBJECT_TO_JSVAL(argTypes);
  4506. return JS_TRUE;
  4507. }
  4508. JSBool
  4509. FunctionType::ReturnTypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  4510. {
  4511. if (!CheckFunctionType(cx, obj))
  4512. return JS_FALSE;
  4513. // Get the returnType object from the FunctionInfo.
  4514. *vp = OBJECT_TO_JSVAL(GetFunctionInfo(obj)->mReturnType);
  4515. return JS_TRUE;
  4516. }
  4517. JSBool
  4518. FunctionType::ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  4519. {
  4520. if (!CheckFunctionType(cx, obj))
  4521. return JS_FALSE;
  4522. // Get the abi object from the FunctionInfo.
  4523. *vp = OBJECT_TO_JSVAL(GetFunctionInfo(obj)->mABI);
  4524. return JS_TRUE;
  4525. }
  4526. JSBool
  4527. FunctionType::IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  4528. {
  4529. if (!CheckFunctionType(cx, obj))
  4530. return JS_FALSE;
  4531. *vp = BOOLEAN_TO_JSVAL(GetFunctionInfo(obj)->mIsVariadic);
  4532. return JS_TRUE;
  4533. }
  4534. /*******************************************************************************
  4535. ** CClosure implementation
  4536. *******************************************************************************/
  4537. JSObject*
  4538. CClosure::Create(JSContext* cx,
  4539. JSObject* typeObj,
  4540. JSObject* fnObj,
  4541. JSObject* thisObj,
  4542. jsval errVal,
  4543. PRFuncPtr* fnptr)
  4544. {
  4545. JS_ASSERT(fnObj);
  4546. JSObject* result = JS_NewObject(cx, &sCClosureClass, NULL, NULL);
  4547. if (!result)
  4548. return NULL;
  4549. js::AutoObjectRooter root(cx, result);
  4550. // Get the FunctionInfo from the FunctionType.
  4551. FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
  4552. JS_ASSERT(!fninfo->mIsVariadic);
  4553. JS_ASSERT(GetABICode(fninfo->mABI) != ABI_WINAPI);
  4554. AutoPtr<ClosureInfo> cinfo(cx->new_<ClosureInfo>(JS_GetRuntime(cx)));
  4555. if (!cinfo) {
  4556. JS_ReportOutOfMemory(cx);
  4557. return NULL;
  4558. }
  4559. // Get the prototype of the FunctionType object, of class CTypeProto,
  4560. // which stores our JSContext for use with the closure.
  4561. JSObject* proto = JS_GetPrototype(typeObj);
  4562. JS_ASSERT(proto);
  4563. JS_ASSERT(CType::IsCTypeProto(proto));
  4564. // Get a JSContext for use with the closure.
  4565. jsval slot = JS_GetReservedSlot(proto, SLOT_CLOSURECX);
  4566. if (!JSVAL_IS_VOID(slot)) {
  4567. // Use the existing JSContext.
  4568. cinfo->cx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
  4569. JS_ASSERT(cinfo->cx);
  4570. } else {
  4571. // Lazily instantiate a new JSContext, and stash it on
  4572. // ctypes.FunctionType.prototype.
  4573. JSRuntime* runtime = JS_GetRuntime(cx);
  4574. cinfo->cx = JS_NewContext(runtime, 8192);
  4575. if (!cinfo->cx) {
  4576. JS_ReportOutOfMemory(cx);
  4577. return NULL;
  4578. }
  4579. JS_SetReservedSlot(proto, SLOT_CLOSURECX, PRIVATE_TO_JSVAL(cinfo->cx));
  4580. }
  4581. // Prepare the error sentinel value. It's important to do this now, because
  4582. // we might be unable to convert the value to the proper type. If so, we want
  4583. // the caller to know about it _now_, rather than some uncertain time in the
  4584. // future when the error sentinel is actually needed.
  4585. if (!JSVAL_IS_VOID(errVal)) {
  4586. // Make sure the callback returns something.
  4587. if (CType::GetTypeCode(fninfo->mReturnType) == TYPE_void_t) {
  4588. JS_ReportError(cx, "A void callback can't pass an error sentinel");
  4589. return NULL;
  4590. }
  4591. // With the exception of void, the FunctionType constructor ensures that
  4592. // the return type has a defined size.
  4593. JS_ASSERT(CType::IsSizeDefined(fninfo->mReturnType));
  4594. // Allocate a buffer for the return value.
  4595. size_t rvSize = CType::GetSize(fninfo->mReturnType);
  4596. cinfo->errResult = cx->malloc_(rvSize);
  4597. if (!cinfo->errResult)
  4598. return NULL;
  4599. // Do the value conversion. This might fail, in which case we throw.
  4600. if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, cinfo->errResult,
  4601. false, NULL))
  4602. return NULL;
  4603. } else {
  4604. cinfo->errResult = NULL;
  4605. }
  4606. // Copy the important bits of context into cinfo.
  4607. cinfo->closureObj = result;
  4608. cinfo->typeObj = typeObj;
  4609. cinfo->thisObj = thisObj;
  4610. cinfo->jsfnObj = fnObj;
  4611. // Create an ffi_closure object and initialize it.
  4612. void* code;
  4613. cinfo->closure =
  4614. static_cast<ffi_closure*>(ffi_closure_alloc(sizeof(ffi_closure), &code));
  4615. if (!cinfo->closure || !code) {
  4616. JS_ReportError(cx, "couldn't create closure - libffi error");
  4617. return NULL;
  4618. }
  4619. ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF,
  4620. CClosure::ClosureStub, cinfo.get(), code);
  4621. if (status != FFI_OK) {
  4622. JS_ReportError(cx, "couldn't create closure - libffi error");
  4623. return NULL;
  4624. }
  4625. // Stash the ClosureInfo struct on our new object.
  4626. JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PRIVATE_TO_JSVAL(cinfo.forget()));
  4627. // Casting between void* and a function pointer is forbidden in C and C++.
  4628. // Do it via an integral type.
  4629. *fnptr = reinterpret_cast<PRFuncPtr>(reinterpret_cast<uintptr_t>(code));
  4630. return result;
  4631. }
  4632. void
  4633. CClosure::Trace(JSTracer* trc, JSObject* obj)
  4634. {
  4635. // Make sure our ClosureInfo slot is legit. If it's not, bail.
  4636. jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
  4637. if (JSVAL_IS_VOID(slot))
  4638. return;
  4639. ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
  4640. // Identify our objects to the tracer. (There's no need to identify
  4641. // 'closureObj', since that's us.)
  4642. JS_CALL_OBJECT_TRACER(trc, cinfo->typeObj, "typeObj");
  4643. JS_CALL_OBJECT_TRACER(trc, cinfo->jsfnObj, "jsfnObj");
  4644. if (cinfo->thisObj)
  4645. JS_CALL_OBJECT_TRACER(trc, cinfo->thisObj, "thisObj");
  4646. }
  4647. void
  4648. CClosure::Finalize(JSContext* cx, JSObject* obj)
  4649. {
  4650. // Make sure our ClosureInfo slot is legit. If it's not, bail.
  4651. jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
  4652. if (JSVAL_IS_VOID(slot))
  4653. return;
  4654. ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
  4655. cx->delete_(cinfo);
  4656. }
  4657. void
  4658. CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
  4659. {
  4660. JS_ASSERT(cif);
  4661. JS_ASSERT(result);
  4662. JS_ASSERT(args);
  4663. JS_ASSERT(userData);
  4664. // Retrieve the essentials from our closure object.
  4665. ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData);
  4666. JSContext* cx = cinfo->cx;
  4667. JSObject* typeObj = cinfo->typeObj;
  4668. JSObject* thisObj = cinfo->thisObj;
  4669. JSObject* jsfnObj = cinfo->jsfnObj;
  4670. JS_AbortIfWrongThread(JS_GetRuntime(cx));
  4671. JSAutoRequest ar(cx);
  4672. JSAutoEnterCompartment ac;
  4673. if (!ac.enter(cx, jsfnObj))
  4674. return;
  4675. // Assert that our CIFs agree.
  4676. FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
  4677. JS_ASSERT(cif == &fninfo->mCIF);
  4678. TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
  4679. // Initialize the result to zero, in case something fails. Small integer types
  4680. // are promoted to a word-sized ffi_arg, so we must be careful to zero the
  4681. // whole word.
  4682. size_t rvSize = 0;
  4683. if (cif->rtype != &ffi_type_void) {
  4684. rvSize = cif->rtype->size;
  4685. switch (typeCode) {
  4686. #define DEFINE_INT_TYPE(name, type, ffiType) \
  4687. case TYPE_##name:
  4688. #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4689. #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4690. #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4691. #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4692. #include "typedefs.h"
  4693. rvSize = Align(rvSize, sizeof(ffi_arg));
  4694. break;
  4695. default:
  4696. break;
  4697. }
  4698. memset(result, 0, rvSize);
  4699. }
  4700. // Get a death grip on 'closureObj'.
  4701. js::AutoObjectRooter root(cx, cinfo->closureObj);
  4702. // Set up an array for converted arguments.
  4703. Array<jsval, 16> argv;
  4704. if (!argv.appendN(JSVAL_VOID, cif->nargs)) {
  4705. JS_ReportOutOfMemory(cx);
  4706. return;
  4707. }
  4708. js::AutoArrayRooter roots(cx, argv.length(), argv.begin());
  4709. for (uint32_t i = 0; i < cif->nargs; ++i) {
  4710. // Convert each argument, and have any CData objects created depend on
  4711. // the existing buffers.
  4712. if (!ConvertToJS(cx, fninfo->mArgTypes[i], NULL, args[i], false, false,
  4713. &argv[i]))
  4714. return;
  4715. }
  4716. // Call the JS function. 'thisObj' may be NULL, in which case the JS engine
  4717. // will find an appropriate object to use.
  4718. jsval rval;
  4719. JSBool success = JS_CallFunctionValue(cx, thisObj, OBJECT_TO_JSVAL(jsfnObj),
  4720. cif->nargs, argv.begin(), &rval);
  4721. // Convert the result. Note that we pass 'isArgument = false', such that
  4722. // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char
  4723. // type, which would require an allocation that we can't track. The JS
  4724. // function must perform this conversion itself and return a PointerType
  4725. // CData; thusly, the burden of freeing the data is left to the user.
  4726. if (success && cif->rtype != &ffi_type_void)
  4727. success = ImplicitConvert(cx, rval, fninfo->mReturnType, result, false,
  4728. NULL);
  4729. if (!success) {
  4730. // Something failed. The callee may have thrown, or it may not have
  4731. // returned a value that ImplicitConvert() was happy with. Depending on how
  4732. // prudent the consumer has been, we may or may not have a recovery plan.
  4733. // In any case, a JS exception cannot be passed to C code, so report the
  4734. // exception if any and clear it from the cx.
  4735. if (JS_IsExceptionPending(cx))
  4736. JS_ReportPendingException(cx);
  4737. if (cinfo->errResult) {
  4738. // Good case: we have a sentinel that we can return. Copy it in place of
  4739. // the actual return value, and then proceed.
  4740. // The buffer we're returning might be larger than the size of the return
  4741. // type, due to libffi alignment issues (see above). But it should never
  4742. // be smaller.
  4743. size_t copySize = CType::GetSize(fninfo->mReturnType);
  4744. JS_ASSERT(copySize <= rvSize);
  4745. memcpy(result, cinfo->errResult, copySize);
  4746. } else {
  4747. // Bad case: not much we can do here. The rv is already zeroed out, so we
  4748. // just report (another) error and hope for the best. JS_ReportError will
  4749. // actually throw an exception here, so then we have to report it. Again.
  4750. // Ugh.
  4751. JS_ReportError(cx, "JavaScript callback failed, and an error sentinel "
  4752. "was not specified.");
  4753. if (JS_IsExceptionPending(cx))
  4754. JS_ReportPendingException(cx);
  4755. return;
  4756. }
  4757. }
  4758. // Small integer types must be returned as a word-sized ffi_arg. Coerce it
  4759. // back into the size libffi expects.
  4760. switch (typeCode) {
  4761. #define DEFINE_INT_TYPE(name, type, ffiType) \
  4762. case TYPE_##name: \
  4763. if (sizeof(type) < sizeof(ffi_arg)) { \
  4764. ffi_arg data = *static_cast<type*>(result); \
  4765. *static_cast<ffi_arg*>(result) = data; \
  4766. } \
  4767. break;
  4768. #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4769. #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4770. #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4771. #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
  4772. #include "typedefs.h"
  4773. default:
  4774. break;
  4775. }
  4776. }
  4777. /*******************************************************************************
  4778. ** CData implementation
  4779. *******************************************************************************/
  4780. // Create a new CData object of type 'typeObj' containing binary data supplied
  4781. // in 'source', optionally with a referent object 'refObj'.
  4782. //
  4783. // * 'typeObj' must be a CType of defined (but possibly zero) size.
  4784. //
  4785. // * If an object 'refObj' is supplied, the new CData object stores the
  4786. // referent object in a reserved slot for GC safety, such that 'refObj' will
  4787. // be held alive by the resulting CData object. 'refObj' may or may not be
  4788. // a CData object; merely an object we want to keep alive.
  4789. // * If 'refObj' is a CData object, 'ownResult' must be false.
  4790. // * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult'
  4791. // may be true or false.
  4792. // * Otherwise 'refObj' is NULL. In this case, 'ownResult' may be true or false.
  4793. //
  4794. // * If 'ownResult' is true, the CData object will allocate an appropriately
  4795. // sized buffer, and free it upon finalization. If 'source' data is
  4796. // supplied, the data will be copied from 'source' into the buffer;
  4797. // otherwise, the entirety of the new buffer will be initialized to zero.
  4798. // * If 'ownResult' is false, the new CData's buffer refers to a slice of
  4799. // another buffer kept alive by 'refObj'. 'source' data must be provided,
  4800. // and the new CData's buffer will refer to 'source'.
  4801. JSObject*
  4802. CData::Create(JSContext* cx,
  4803. JSObject* typeObj,
  4804. JSObject* refObj,
  4805. void* source,
  4806. bool ownResult)
  4807. {
  4808. JS_ASSERT(typeObj);
  4809. JS_ASSERT(CType::IsCType(typeObj));
  4810. JS_ASSERT(CType::IsSizeDefined(typeObj));
  4811. JS_ASSERT(ownResult || source);
  4812. JS_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult);
  4813. // Get the 'prototype' property from the type.
  4814. jsval slot = JS_GetReservedSlot(typeObj, SLOT_PROTO);
  4815. JS_ASSERT(!JSVAL_IS_PRIMITIVE(slot));
  4816. JSObject* proto = JSVAL_TO_OBJECT(slot);
  4817. JSObject* parent = JS_GetParent(typeObj);
  4818. JS_ASSERT(parent);
  4819. JSObject* dataObj = JS_NewObject(cx, &sCDataClass, proto, parent);
  4820. if (!dataObj)
  4821. return NULL;
  4822. js::AutoObjectRooter root(cx, dataObj);
  4823. // set the CData's associated type
  4824. JS_SetReservedSlot(dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj));
  4825. // Stash the referent object, if any, for GC safety.
  4826. if (refObj)
  4827. JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj));
  4828. // Set our ownership flag.
  4829. JS_SetReservedSlot(dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult));
  4830. // attach the buffer. since it might not be 2-byte aligned, we need to
  4831. // allocate an aligned space for it and store it there. :(
  4832. char** buffer = cx->new_<char*>();
  4833. if (!buffer) {
  4834. JS_ReportOutOfMemory(cx);
  4835. return NULL;
  4836. }
  4837. char* data;
  4838. if (!ownResult) {
  4839. data = static_cast<char*>(source);
  4840. } else {
  4841. // Initialize our own buffer.
  4842. size_t size = CType::GetSize(typeObj);
  4843. data = cx->array_new<char>(size);
  4844. if (!data) {
  4845. // Report a catchable allocation error.
  4846. JS_ReportAllocationOverflow(cx);
  4847. Foreground::delete_(buffer);
  4848. return NULL;
  4849. }
  4850. if (!source)
  4851. memset(data, 0, size);
  4852. else
  4853. memcpy(data, source, size);
  4854. }
  4855. *buffer = data;
  4856. JS_SetReservedSlot(dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer));
  4857. return dataObj;
  4858. }
  4859. void
  4860. CData::Finalize(JSContext* cx, JSObject* obj)
  4861. {
  4862. // Delete our buffer, and the data it contains if we own it.
  4863. jsval slot = JS_GetReservedSlot(obj, SLOT_OWNS);
  4864. if (JSVAL_IS_VOID(slot))
  4865. return;
  4866. JSBool owns = JSVAL_TO_BOOLEAN(slot);
  4867. slot = JS_GetReservedSlot(obj, SLOT_DATA);
  4868. if (JSVAL_IS_VOID(slot))
  4869. return;
  4870. char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot));
  4871. if (owns)
  4872. cx->array_delete(*buffer);
  4873. cx->delete_(buffer);
  4874. }
  4875. JSObject*
  4876. CData::GetCType(JSObject* dataObj)
  4877. {
  4878. JS_ASSERT(CData::IsCData(dataObj));
  4879. jsval slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE);
  4880. JSObject* typeObj = JSVAL_TO_OBJECT(slot);
  4881. JS_ASSERT(CType::IsCType(typeObj));
  4882. return typeObj;
  4883. }
  4884. void*
  4885. CData::GetData(JSObject* dataObj)
  4886. {
  4887. JS_ASSERT(CData::IsCData(dataObj));
  4888. jsval slot = JS_GetReservedSlot(dataObj, SLOT_DATA);
  4889. void** buffer = static_cast<void**>(JSVAL_TO_PRIVATE(slot));
  4890. JS_ASSERT(buffer);
  4891. JS_ASSERT(*buffer);
  4892. return *buffer;
  4893. }
  4894. bool
  4895. CData::IsCData(JSObject* obj)
  4896. {
  4897. return JS_GetClass(obj) == &sCDataClass;
  4898. }
  4899. bool
  4900. CData::IsCDataProto(JSObject* obj)
  4901. {
  4902. return JS_GetClass(obj) == &sCDataProtoClass;
  4903. }
  4904. JSBool
  4905. CData::ValueGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
  4906. {
  4907. if (!IsCData(obj)) {
  4908. JS_ReportError(cx, "not a CData");
  4909. return JS_FALSE;
  4910. }
  4911. // Convert the value to a primitive; do not create a new CData object.
  4912. if (!ConvertToJS(cx, GetCType(obj), NULL, GetData(obj), true, false, vp))
  4913. return JS_FALSE;
  4914. return JS_TRUE;
  4915. }
  4916. JSBool
  4917. CData::ValueSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
  4918. {
  4919. if (!IsCData(obj)) {
  4920. JS_ReportError(cx, "not a CData");
  4921. return JS_FALSE;
  4922. }
  4923. return ImplicitConvert(cx, *vp, GetCType(obj), GetData(obj), false, NULL);
  4924. }
  4925. JSBool
  4926. CData::Address(JSContext* cx, uintN argc, jsval* vp)
  4927. {
  4928. if (argc != 0) {
  4929. JS_ReportError(cx, "address takes zero arguments");
  4930. return JS_FALSE;
  4931. }
  4932. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  4933. if (!obj || !IsCData(obj)) {
  4934. JS_ReportError(cx, "not a CData");
  4935. return JS_FALSE;
  4936. }
  4937. JSObject* typeObj = CData::GetCType(obj);
  4938. JSObject* pointerType = PointerType::CreateInternal(cx, typeObj);
  4939. if (!pointerType)
  4940. return JS_FALSE;
  4941. js::AutoObjectRooter root(cx, pointerType);
  4942. // Create a PointerType CData object containing null.
  4943. JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
  4944. if (!result)
  4945. return JS_FALSE;
  4946. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  4947. // Manually set the pointer inside the object, so we skip the conversion step.
  4948. void** data = static_cast<void**>(GetData(result));
  4949. *data = GetData(obj);
  4950. return JS_TRUE;
  4951. }
  4952. JSBool
  4953. CData::Cast(JSContext* cx, uintN argc, jsval* vp)
  4954. {
  4955. if (argc != 2) {
  4956. JS_ReportError(cx, "cast takes two arguments");
  4957. return JS_FALSE;
  4958. }
  4959. jsval* argv = JS_ARGV(cx, vp);
  4960. if (JSVAL_IS_PRIMITIVE(argv[0]) ||
  4961. !CData::IsCData(JSVAL_TO_OBJECT(argv[0]))) {
  4962. JS_ReportError(cx, "first argument must be a CData");
  4963. return JS_FALSE;
  4964. }
  4965. JSObject* sourceData = JSVAL_TO_OBJECT(argv[0]);
  4966. JSObject* sourceType = CData::GetCType(sourceData);
  4967. if (JSVAL_IS_PRIMITIVE(argv[1]) ||
  4968. !CType::IsCType(JSVAL_TO_OBJECT(argv[1]))) {
  4969. JS_ReportError(cx, "second argument must be a CType");
  4970. return JS_FALSE;
  4971. }
  4972. JSObject* targetType = JSVAL_TO_OBJECT(argv[1]);
  4973. size_t targetSize;
  4974. if (!CType::GetSafeSize(targetType, &targetSize) ||
  4975. targetSize > CType::GetSize(sourceType)) {
  4976. JS_ReportError(cx,
  4977. "target CType has undefined or larger size than source CType");
  4978. return JS_FALSE;
  4979. }
  4980. // Construct a new CData object with a type of 'targetType' and a referent
  4981. // of 'sourceData'.
  4982. void* data = CData::GetData(sourceData);
  4983. JSObject* result = CData::Create(cx, targetType, sourceData, data, false);
  4984. if (!result)
  4985. return JS_FALSE;
  4986. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  4987. return JS_TRUE;
  4988. }
  4989. JSBool
  4990. CData::GetRuntime(JSContext* cx, uintN argc, jsval* vp)
  4991. {
  4992. if (argc != 1) {
  4993. JS_ReportError(cx, "getRuntime takes one argument");
  4994. return JS_FALSE;
  4995. }
  4996. jsval* argv = JS_ARGV(cx, vp);
  4997. if (JSVAL_IS_PRIMITIVE(argv[0]) ||
  4998. !CType::IsCType(JSVAL_TO_OBJECT(argv[0]))) {
  4999. JS_ReportError(cx, "first argument must be a CType");
  5000. return JS_FALSE;
  5001. }
  5002. JSObject* targetType = JSVAL_TO_OBJECT(argv[0]);
  5003. size_t targetSize;
  5004. if (!CType::GetSafeSize(targetType, &targetSize) ||
  5005. targetSize != sizeof(void*)) {
  5006. JS_ReportError(cx, "target CType has non-pointer size");
  5007. return JS_FALSE;
  5008. }
  5009. void* data = static_cast<void*>(cx->runtime);
  5010. JSObject* result = CData::Create(cx, targetType, NULL, &data, true);
  5011. if (!result)
  5012. return JS_FALSE;
  5013. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  5014. return JS_TRUE;
  5015. }
  5016. JSBool
  5017. CData::ReadString(JSContext* cx, uintN argc, jsval* vp)
  5018. {
  5019. if (argc != 0) {
  5020. JS_ReportError(cx, "readString takes zero arguments");
  5021. return JS_FALSE;
  5022. }
  5023. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  5024. if (!obj || !IsCData(obj)) {
  5025. JS_ReportError(cx, "not a CData");
  5026. return JS_FALSE;
  5027. }
  5028. // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
  5029. // character or integer type.
  5030. JSObject* baseType;
  5031. JSObject* typeObj = GetCType(obj);
  5032. TypeCode typeCode = CType::GetTypeCode(typeObj);
  5033. void* data;
  5034. size_t maxLength = -1;
  5035. switch (typeCode) {
  5036. case TYPE_pointer:
  5037. baseType = PointerType::GetBaseType(typeObj);
  5038. data = *static_cast<void**>(GetData(obj));
  5039. if (data == NULL) {
  5040. JS_ReportError(cx, "cannot read contents of null pointer");
  5041. return JS_FALSE;
  5042. }
  5043. break;
  5044. case TYPE_array:
  5045. baseType = ArrayType::GetBaseType(typeObj);
  5046. data = GetData(obj);
  5047. maxLength = ArrayType::GetLength(typeObj);
  5048. break;
  5049. default:
  5050. JS_ReportError(cx, "not a PointerType or ArrayType");
  5051. return JS_FALSE;
  5052. }
  5053. // Convert the string buffer, taking care to determine the correct string
  5054. // length in the case of arrays (which may contain embedded nulls).
  5055. JSString* result;
  5056. switch (CType::GetTypeCode(baseType)) {
  5057. case TYPE_int8_t:
  5058. case TYPE_uint8_t:
  5059. case TYPE_char:
  5060. case TYPE_signed_char:
  5061. case TYPE_unsigned_char: {
  5062. char* bytes = static_cast<char*>(data);
  5063. size_t length = strnlen(bytes, maxLength);
  5064. // Determine the length.
  5065. size_t dstlen;
  5066. if (!InflateUTF8StringToBuffer(cx, bytes, length, NULL, &dstlen))
  5067. return JS_FALSE;
  5068. jschar* dst =
  5069. static_cast<jschar*>(JS_malloc(cx, (dstlen + 1) * sizeof(jschar)));
  5070. if (!dst)
  5071. return JS_FALSE;
  5072. ASSERT_OK(InflateUTF8StringToBuffer(cx, bytes, length, dst, &dstlen));
  5073. dst[dstlen] = 0;
  5074. result = JS_NewUCString(cx, dst, dstlen);
  5075. break;
  5076. }
  5077. case TYPE_int16_t:
  5078. case TYPE_uint16_t:
  5079. case TYPE_short:
  5080. case TYPE_unsigned_short:
  5081. case TYPE_jschar: {
  5082. jschar* chars = static_cast<jschar*>(data);
  5083. size_t length = strnlen(chars, maxLength);
  5084. result = JS_NewUCStringCopyN(cx, chars, length);
  5085. break;
  5086. }
  5087. default:
  5088. JS_ReportError(cx,
  5089. "base type is not an 8-bit or 16-bit integer or character type");
  5090. return JS_FALSE;
  5091. }
  5092. if (!result)
  5093. return JS_FALSE;
  5094. JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
  5095. return JS_TRUE;
  5096. }
  5097. JSBool
  5098. CData::ToSource(JSContext* cx, uintN argc, jsval* vp)
  5099. {
  5100. if (argc != 0) {
  5101. JS_ReportError(cx, "toSource takes zero arguments");
  5102. return JS_FALSE;
  5103. }
  5104. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  5105. if (!obj ||
  5106. !(CData::IsCData(obj) || CData::IsCDataProto(obj))) {
  5107. JS_ReportError(cx, "not a CData");
  5108. return JS_FALSE;
  5109. }
  5110. JSString* result;
  5111. if (CData::IsCData(obj)) {
  5112. JSObject* typeObj = CData::GetCType(obj);
  5113. void* data = CData::GetData(obj);
  5114. // Walk the types, building up the toSource() string.
  5115. // First, we build up the type expression:
  5116. // 't.ptr' for pointers;
  5117. // 't.array([n])' for arrays;
  5118. // 'n' for structs, where n = t.name, the struct's name. (We assume this is
  5119. // bound to a variable in the current scope.)
  5120. AutoString source;
  5121. BuildTypeSource(cx, typeObj, true, source);
  5122. AppendString(source, "(");
  5123. if (!BuildDataSource(cx, typeObj, data, false, source))
  5124. return JS_FALSE;
  5125. AppendString(source, ")");
  5126. result = NewUCString(cx, source);
  5127. }
  5128. else
  5129. result = JS_NewStringCopyZ(cx, "[CData proto object]");
  5130. if (!result)
  5131. return JS_FALSE;
  5132. JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
  5133. return JS_TRUE;
  5134. }
  5135. /*******************************************************************************
  5136. ** Int64 and UInt64 implementation
  5137. *******************************************************************************/
  5138. JSObject*
  5139. Int64Base::Construct(JSContext* cx,
  5140. JSObject* proto,
  5141. uint64_t data,
  5142. bool isUnsigned)
  5143. {
  5144. JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
  5145. JSObject* result = JS_NewObject(cx, clasp, proto, JS_GetParent(proto));
  5146. if (!result)
  5147. return NULL;
  5148. js::AutoObjectRooter root(cx, result);
  5149. // attach the Int64's data
  5150. uint64_t* buffer = cx->new_<uint64_t>(data);
  5151. if (!buffer) {
  5152. JS_ReportOutOfMemory(cx);
  5153. return NULL;
  5154. }
  5155. JS_SetReservedSlot(result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer));
  5156. if (!JS_FreezeObject(cx, result))
  5157. return NULL;
  5158. return result;
  5159. }
  5160. void
  5161. Int64Base::Finalize(JSContext* cx, JSObject* obj)
  5162. {
  5163. jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
  5164. if (JSVAL_IS_VOID(slot))
  5165. return;
  5166. cx->delete_(static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot)));
  5167. }
  5168. uint64_t
  5169. Int64Base::GetInt(JSObject* obj) {
  5170. JS_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj));
  5171. jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
  5172. return *static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot));
  5173. }
  5174. JSBool
  5175. Int64Base::ToString(JSContext* cx,
  5176. JSObject* obj,
  5177. uintN argc,
  5178. jsval* vp,
  5179. bool isUnsigned)
  5180. {
  5181. if (argc > 1) {
  5182. JS_ReportError(cx, "toString takes zero or one argument");
  5183. return JS_FALSE;
  5184. }
  5185. jsuint radix = 10;
  5186. if (argc == 1) {
  5187. jsval arg = JS_ARGV(cx, vp)[0];
  5188. if (JSVAL_IS_INT(arg))
  5189. radix = JSVAL_TO_INT(arg);
  5190. if (!JSVAL_IS_INT(arg) || radix < 2 || radix > 36) {
  5191. JS_ReportError(cx, "radix argument must be an integer between 2 and 36");
  5192. return JS_FALSE;
  5193. }
  5194. }
  5195. AutoString intString;
  5196. if (isUnsigned) {
  5197. IntegerToString(GetInt(obj), radix, intString);
  5198. } else {
  5199. IntegerToString(static_cast<int64_t>(GetInt(obj)), radix, intString);
  5200. }
  5201. JSString *result = NewUCString(cx, intString);
  5202. if (!result)
  5203. return JS_FALSE;
  5204. JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
  5205. return JS_TRUE;
  5206. }
  5207. JSBool
  5208. Int64Base::ToSource(JSContext* cx,
  5209. JSObject* obj,
  5210. uintN argc,
  5211. jsval* vp,
  5212. bool isUnsigned)
  5213. {
  5214. if (argc != 0) {
  5215. JS_ReportError(cx, "toSource takes zero arguments");
  5216. return JS_FALSE;
  5217. }
  5218. // Return a decimal string suitable for constructing the number.
  5219. AutoString source;
  5220. if (isUnsigned) {
  5221. AppendString(source, "ctypes.UInt64(\"");
  5222. IntegerToString(GetInt(obj), 10, source);
  5223. } else {
  5224. AppendString(source, "ctypes.Int64(\"");
  5225. IntegerToString(static_cast<int64_t>(GetInt(obj)), 10, source);
  5226. }
  5227. AppendString(source, "\")");
  5228. JSString *result = NewUCString(cx, source);
  5229. if (!result)
  5230. return JS_FALSE;
  5231. JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
  5232. return JS_TRUE;
  5233. }
  5234. JSBool
  5235. Int64::Construct(JSContext* cx,
  5236. uintN argc,
  5237. jsval* vp)
  5238. {
  5239. // Construct and return a new Int64 object.
  5240. if (argc != 1) {
  5241. JS_ReportError(cx, "Int64 takes one argument");
  5242. return JS_FALSE;
  5243. }
  5244. jsval* argv = JS_ARGV(cx, vp);
  5245. int64_t i = 0;
  5246. if (!jsvalToBigInteger(cx, argv[0], true, &i))
  5247. return TypeError(cx, "int64", argv[0]);
  5248. // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
  5249. jsval slot;
  5250. ASSERT_OK(JS_GetProperty(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)),
  5251. "prototype", &slot));
  5252. JSObject* proto = JSVAL_TO_OBJECT(slot);
  5253. JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass);
  5254. JSObject* result = Int64Base::Construct(cx, proto, i, false);
  5255. if (!result)
  5256. return JS_FALSE;
  5257. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  5258. return JS_TRUE;
  5259. }
  5260. bool
  5261. Int64::IsInt64(JSObject* obj)
  5262. {
  5263. return JS_GetClass(obj) == &sInt64Class;
  5264. }
  5265. JSBool
  5266. Int64::ToString(JSContext* cx, uintN argc, jsval* vp)
  5267. {
  5268. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  5269. if (!obj || !Int64::IsInt64(obj)) {
  5270. JS_ReportError(cx, "not an Int64");
  5271. return JS_FALSE;
  5272. }
  5273. return Int64Base::ToString(cx, obj, argc, vp, false);
  5274. }
  5275. JSBool
  5276. Int64::ToSource(JSContext* cx, uintN argc, jsval* vp)
  5277. {
  5278. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  5279. if (!obj || !Int64::IsInt64(obj)) {
  5280. JS_ReportError(cx, "not an Int64");
  5281. return JS_FALSE;
  5282. }
  5283. return Int64Base::ToSource(cx, obj, argc, vp, false);
  5284. }
  5285. JSBool
  5286. Int64::Compare(JSContext* cx, uintN argc, jsval* vp)
  5287. {
  5288. jsval* argv = JS_ARGV(cx, vp);
  5289. if (argc != 2 ||
  5290. JSVAL_IS_PRIMITIVE(argv[0]) ||
  5291. JSVAL_IS_PRIMITIVE(argv[1]) ||
  5292. !Int64::IsInt64(JSVAL_TO_OBJECT(argv[0])) ||
  5293. !Int64::IsInt64(JSVAL_TO_OBJECT(argv[1]))) {
  5294. JS_ReportError(cx, "compare takes two Int64 arguments");
  5295. return JS_FALSE;
  5296. }
  5297. JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]);
  5298. JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]);
  5299. int64_t i1 = Int64Base::GetInt(obj1);
  5300. int64_t i2 = Int64Base::GetInt(obj2);
  5301. if (i1 == i2)
  5302. JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0));
  5303. else if (i1 < i2)
  5304. JS_SET_RVAL(cx, vp, INT_TO_JSVAL(-1));
  5305. else
  5306. JS_SET_RVAL(cx, vp, INT_TO_JSVAL(1));
  5307. return JS_TRUE;
  5308. }
  5309. #define LO_MASK ((uint64_t(1) << 32) - 1)
  5310. #define INT64_LO(i) ((i) & LO_MASK)
  5311. #define INT64_HI(i) ((i) >> 32)
  5312. JSBool
  5313. Int64::Lo(JSContext* cx, uintN argc, jsval* vp)
  5314. {
  5315. jsval* argv = JS_ARGV(cx, vp);
  5316. if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
  5317. !Int64::IsInt64(JSVAL_TO_OBJECT(argv[0]))) {
  5318. JS_ReportError(cx, "lo takes one Int64 argument");
  5319. return JS_FALSE;
  5320. }
  5321. JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
  5322. int64_t u = Int64Base::GetInt(obj);
  5323. jsdouble d = uint32_t(INT64_LO(u));
  5324. jsval result;
  5325. if (!JS_NewNumberValue(cx, d, &result))
  5326. return JS_FALSE;
  5327. JS_SET_RVAL(cx, vp, result);
  5328. return JS_TRUE;
  5329. }
  5330. JSBool
  5331. Int64::Hi(JSContext* cx, uintN argc, jsval* vp)
  5332. {
  5333. jsval* argv = JS_ARGV(cx, vp);
  5334. if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
  5335. !Int64::IsInt64(JSVAL_TO_OBJECT(argv[0]))) {
  5336. JS_ReportError(cx, "hi takes one Int64 argument");
  5337. return JS_FALSE;
  5338. }
  5339. JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
  5340. int64_t u = Int64Base::GetInt(obj);
  5341. jsdouble d = int32_t(INT64_HI(u));
  5342. jsval result;
  5343. if (!JS_NewNumberValue(cx, d, &result))
  5344. return JS_FALSE;
  5345. JS_SET_RVAL(cx, vp, result);
  5346. return JS_TRUE;
  5347. }
  5348. JSBool
  5349. Int64::Join(JSContext* cx, uintN argc, jsval* vp)
  5350. {
  5351. if (argc != 2) {
  5352. JS_ReportError(cx, "join takes two arguments");
  5353. return JS_FALSE;
  5354. }
  5355. jsval* argv = JS_ARGV(cx, vp);
  5356. int32_t hi;
  5357. uint32_t lo;
  5358. if (!jsvalToInteger(cx, argv[0], &hi))
  5359. return TypeError(cx, "int32", argv[0]);
  5360. if (!jsvalToInteger(cx, argv[1], &lo))
  5361. return TypeError(cx, "uint32", argv[1]);
  5362. int64_t i = (int64_t(hi) << 32) + int64_t(lo);
  5363. // Get Int64.prototype from the function's reserved slot.
  5364. JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
  5365. jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
  5366. JSObject* proto = JSVAL_TO_OBJECT(slot);
  5367. JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass);
  5368. JSObject* result = Int64Base::Construct(cx, proto, i, false);
  5369. if (!result)
  5370. return JS_FALSE;
  5371. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  5372. return JS_TRUE;
  5373. }
  5374. JSBool
  5375. UInt64::Construct(JSContext* cx,
  5376. uintN argc,
  5377. jsval* vp)
  5378. {
  5379. // Construct and return a new UInt64 object.
  5380. if (argc != 1) {
  5381. JS_ReportError(cx, "UInt64 takes one argument");
  5382. return JS_FALSE;
  5383. }
  5384. jsval* argv = JS_ARGV(cx, vp);
  5385. uint64_t u = 0;
  5386. if (!jsvalToBigInteger(cx, argv[0], true, &u))
  5387. return TypeError(cx, "uint64", argv[0]);
  5388. // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
  5389. jsval slot;
  5390. ASSERT_OK(JS_GetProperty(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)),
  5391. "prototype", &slot));
  5392. JSObject* proto = JSVAL_TO_OBJECT(slot);
  5393. JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass);
  5394. JSObject* result = Int64Base::Construct(cx, proto, u, true);
  5395. if (!result)
  5396. return JS_FALSE;
  5397. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  5398. return JS_TRUE;
  5399. }
  5400. bool
  5401. UInt64::IsUInt64(JSObject* obj)
  5402. {
  5403. return JS_GetClass(obj) == &sUInt64Class;
  5404. }
  5405. JSBool
  5406. UInt64::ToString(JSContext* cx, uintN argc, jsval* vp)
  5407. {
  5408. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  5409. if (!obj || !UInt64::IsUInt64(obj)) {
  5410. JS_ReportError(cx, "not a UInt64");
  5411. return JS_FALSE;
  5412. }
  5413. return Int64Base::ToString(cx, obj, argc, vp, true);
  5414. }
  5415. JSBool
  5416. UInt64::ToSource(JSContext* cx, uintN argc, jsval* vp)
  5417. {
  5418. JSObject* obj = JS_THIS_OBJECT(cx, vp);
  5419. if (!obj || !UInt64::IsUInt64(obj)) {
  5420. JS_ReportError(cx, "not a UInt64");
  5421. return JS_FALSE;
  5422. }
  5423. return Int64Base::ToSource(cx, obj, argc, vp, true);
  5424. }
  5425. JSBool
  5426. UInt64::Compare(JSContext* cx, uintN argc, jsval* vp)
  5427. {
  5428. jsval* argv = JS_ARGV(cx, vp);
  5429. if (argc != 2 ||
  5430. JSVAL_IS_PRIMITIVE(argv[0]) ||
  5431. JSVAL_IS_PRIMITIVE(argv[1]) ||
  5432. !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[0])) ||
  5433. !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[1]))) {
  5434. JS_ReportError(cx, "compare takes two UInt64 arguments");
  5435. return JS_FALSE;
  5436. }
  5437. JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]);
  5438. JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]);
  5439. uint64_t u1 = Int64Base::GetInt(obj1);
  5440. uint64_t u2 = Int64Base::GetInt(obj2);
  5441. if (u1 == u2)
  5442. JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0));
  5443. else if (u1 < u2)
  5444. JS_SET_RVAL(cx, vp, INT_TO_JSVAL(-1));
  5445. else
  5446. JS_SET_RVAL(cx, vp, INT_TO_JSVAL(1));
  5447. return JS_TRUE;
  5448. }
  5449. JSBool
  5450. UInt64::Lo(JSContext* cx, uintN argc, jsval* vp)
  5451. {
  5452. jsval* argv = JS_ARGV(cx, vp);
  5453. if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
  5454. !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[0]))) {
  5455. JS_ReportError(cx, "lo takes one UInt64 argument");
  5456. return JS_FALSE;
  5457. }
  5458. JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
  5459. uint64_t u = Int64Base::GetInt(obj);
  5460. jsdouble d = uint32_t(INT64_LO(u));
  5461. jsval result;
  5462. if (!JS_NewNumberValue(cx, d, &result))
  5463. return JS_FALSE;
  5464. JS_SET_RVAL(cx, vp, result);
  5465. return JS_TRUE;
  5466. }
  5467. JSBool
  5468. UInt64::Hi(JSContext* cx, uintN argc, jsval* vp)
  5469. {
  5470. jsval* argv = JS_ARGV(cx, vp);
  5471. if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
  5472. !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[0]))) {
  5473. JS_ReportError(cx, "hi takes one UInt64 argument");
  5474. return JS_FALSE;
  5475. }
  5476. JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
  5477. uint64_t u = Int64Base::GetInt(obj);
  5478. jsdouble d = uint32_t(INT64_HI(u));
  5479. jsval result;
  5480. if (!JS_NewNumberValue(cx, d, &result))
  5481. return JS_FALSE;
  5482. JS_SET_RVAL(cx, vp, result);
  5483. return JS_TRUE;
  5484. }
  5485. JSBool
  5486. UInt64::Join(JSContext* cx, uintN argc, jsval* vp)
  5487. {
  5488. if (argc != 2) {
  5489. JS_ReportError(cx, "join takes two arguments");
  5490. return JS_FALSE;
  5491. }
  5492. jsval* argv = JS_ARGV(cx, vp);
  5493. uint32_t hi;
  5494. uint32_t lo;
  5495. if (!jsvalToInteger(cx, argv[0], &hi))
  5496. return TypeError(cx, "uint32_t", argv[0]);
  5497. if (!jsvalToInteger(cx, argv[1], &lo))
  5498. return TypeError(cx, "uint32_t", argv[1]);
  5499. uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo);
  5500. // Get UInt64.prototype from the function's reserved slot.
  5501. JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
  5502. jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
  5503. JSObject* proto = JSVAL_TO_OBJECT(slot);
  5504. JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass);
  5505. JSObject* result = Int64Base::Construct(cx, proto, u, true);
  5506. if (!result)
  5507. return JS_FALSE;
  5508. JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
  5509. return JS_TRUE;
  5510. }
  5511. }
  5512. }