jbarnette-johnson 1.0.0.200806240111 → 1.0.0.200807291507

Sign up to get free protection for your applications and to get access to all the features.
Files changed (269) hide show
  1. data/MANIFEST +1 -0
  2. data/Rakefile +3 -10
  3. data/bin/johnson +2 -1
  4. data/ext/spidermonkey/context.c +3 -4
  5. data/ext/spidermonkey/context.h +1 -1
  6. data/ext/spidermonkey/conversions.c +39 -33
  7. data/ext/spidermonkey/debugger.c +5 -5
  8. data/ext/spidermonkey/immutable_node.c.erb +11 -11
  9. data/ext/spidermonkey/jroot.h +4 -4
  10. data/ext/spidermonkey/js_land_proxy.c +9 -8
  11. data/ext/spidermonkey/ruby_land_proxy.c +5 -4
  12. data/ext/spidermonkey/runtime.c +1 -1
  13. data/johnson.gemspec +36 -0
  14. data/lib/hoe.rb +0 -7
  15. data/lib/johnson/cli/options.rb +10 -4
  16. data/lib/johnson/spidermonkey/runtime.rb +2 -2
  17. data/lib/johnson/version.rb +4 -2
  18. data/lib/johnson.rb +1 -0
  19. data/test/johnson/runtime_test.rb +11 -0
  20. data/test/johnson/spidermonkey/ruby_land_proxy_test.rb +6 -0
  21. data/vendor/spidermonkey/.cvsignore +9 -0
  22. data/vendor/spidermonkey/Makefile.in +462 -0
  23. data/vendor/spidermonkey/Makefile.ref +364 -0
  24. data/vendor/spidermonkey/README.html +820 -0
  25. data/vendor/spidermonkey/SpiderMonkey.rsp +12 -0
  26. data/vendor/spidermonkey/Y.js +19 -0
  27. data/vendor/spidermonkey/build.mk +43 -0
  28. data/vendor/spidermonkey/config/AIX4.1.mk +65 -0
  29. data/vendor/spidermonkey/config/AIX4.2.mk +64 -0
  30. data/vendor/spidermonkey/config/AIX4.3.mk +65 -0
  31. data/vendor/spidermonkey/config/Darwin.mk +83 -0
  32. data/vendor/spidermonkey/config/Darwin1.3.mk +81 -0
  33. data/vendor/spidermonkey/config/Darwin1.4.mk +41 -0
  34. data/vendor/spidermonkey/config/Darwin5.2.mk +81 -0
  35. data/vendor/spidermonkey/config/Darwin5.3.mk +81 -0
  36. data/vendor/spidermonkey/config/HP-UXB.10.10.mk +77 -0
  37. data/vendor/spidermonkey/config/HP-UXB.10.20.mk +77 -0
  38. data/vendor/spidermonkey/config/HP-UXB.11.00.mk +80 -0
  39. data/vendor/spidermonkey/config/IRIX.mk +87 -0
  40. data/vendor/spidermonkey/config/IRIX5.3.mk +44 -0
  41. data/vendor/spidermonkey/config/IRIX6.1.mk +44 -0
  42. data/vendor/spidermonkey/config/IRIX6.2.mk +44 -0
  43. data/vendor/spidermonkey/config/IRIX6.3.mk +44 -0
  44. data/vendor/spidermonkey/config/IRIX6.5.mk +44 -0
  45. data/vendor/spidermonkey/config/Linux_All.mk +103 -0
  46. data/vendor/spidermonkey/config/Mac_OS10.0.mk +82 -0
  47. data/vendor/spidermonkey/config/OSF1V4.0.mk +72 -0
  48. data/vendor/spidermonkey/config/OSF1V5.0.mk +69 -0
  49. data/vendor/spidermonkey/config/SunOS4.1.4.mk +101 -0
  50. data/vendor/spidermonkey/config/SunOS5.10.mk +50 -0
  51. data/vendor/spidermonkey/config/SunOS5.3.mk +91 -0
  52. data/vendor/spidermonkey/config/SunOS5.4.mk +92 -0
  53. data/vendor/spidermonkey/config/SunOS5.5.1.mk +44 -0
  54. data/vendor/spidermonkey/config/SunOS5.5.mk +87 -0
  55. data/vendor/spidermonkey/config/SunOS5.6.mk +89 -0
  56. data/vendor/spidermonkey/config/SunOS5.7.mk +44 -0
  57. data/vendor/spidermonkey/config/SunOS5.8.mk +44 -0
  58. data/vendor/spidermonkey/config/SunOS5.9.mk +44 -0
  59. data/vendor/spidermonkey/config/WINNT4.0.mk +117 -0
  60. data/vendor/spidermonkey/config/WINNT5.0.mk +117 -0
  61. data/vendor/spidermonkey/config/WINNT5.1.mk +117 -0
  62. data/vendor/spidermonkey/config/WINNT5.2.mk +117 -0
  63. data/vendor/spidermonkey/config/WINNT6.0.mk +117 -0
  64. data/vendor/spidermonkey/config/dgux.mk +64 -0
  65. data/vendor/spidermonkey/config.mk +192 -0
  66. data/vendor/spidermonkey/editline/Makefile.ref +144 -0
  67. data/vendor/spidermonkey/editline/README +83 -0
  68. data/vendor/spidermonkey/editline/editline.3 +175 -0
  69. data/vendor/spidermonkey/editline/editline.c +1369 -0
  70. data/vendor/spidermonkey/editline/editline.h +135 -0
  71. data/vendor/spidermonkey/editline/sysunix.c +182 -0
  72. data/vendor/spidermonkey/editline/unix.h +82 -0
  73. data/vendor/spidermonkey/fdlibm/.cvsignore +7 -0
  74. data/vendor/spidermonkey/fdlibm/Makefile.in +127 -0
  75. data/vendor/spidermonkey/fdlibm/Makefile.ref +192 -0
  76. data/vendor/spidermonkey/fdlibm/e_acos.c +147 -0
  77. data/vendor/spidermonkey/fdlibm/e_acosh.c +105 -0
  78. data/vendor/spidermonkey/fdlibm/e_asin.c +156 -0
  79. data/vendor/spidermonkey/fdlibm/e_atan2.c +165 -0
  80. data/vendor/spidermonkey/fdlibm/e_atanh.c +110 -0
  81. data/vendor/spidermonkey/fdlibm/e_cosh.c +133 -0
  82. data/vendor/spidermonkey/fdlibm/e_exp.c +202 -0
  83. data/vendor/spidermonkey/fdlibm/e_fmod.c +184 -0
  84. data/vendor/spidermonkey/fdlibm/e_gamma.c +71 -0
  85. data/vendor/spidermonkey/fdlibm/e_gamma_r.c +70 -0
  86. data/vendor/spidermonkey/fdlibm/e_hypot.c +173 -0
  87. data/vendor/spidermonkey/fdlibm/e_j0.c +524 -0
  88. data/vendor/spidermonkey/fdlibm/e_j1.c +523 -0
  89. data/vendor/spidermonkey/fdlibm/e_jn.c +315 -0
  90. data/vendor/spidermonkey/fdlibm/e_lgamma.c +71 -0
  91. data/vendor/spidermonkey/fdlibm/e_lgamma_r.c +347 -0
  92. data/vendor/spidermonkey/fdlibm/e_log.c +184 -0
  93. data/vendor/spidermonkey/fdlibm/e_log10.c +134 -0
  94. data/vendor/spidermonkey/fdlibm/e_pow.c +386 -0
  95. data/vendor/spidermonkey/fdlibm/e_rem_pio2.c +222 -0
  96. data/vendor/spidermonkey/fdlibm/e_remainder.c +120 -0
  97. data/vendor/spidermonkey/fdlibm/e_scalb.c +89 -0
  98. data/vendor/spidermonkey/fdlibm/e_sinh.c +122 -0
  99. data/vendor/spidermonkey/fdlibm/e_sqrt.c +497 -0
  100. data/vendor/spidermonkey/fdlibm/fdlibm.h +273 -0
  101. data/vendor/spidermonkey/fdlibm/fdlibm.mak +1453 -0
  102. data/vendor/spidermonkey/fdlibm/fdlibm.mdp +0 -0
  103. data/vendor/spidermonkey/fdlibm/k_cos.c +135 -0
  104. data/vendor/spidermonkey/fdlibm/k_rem_pio2.c +354 -0
  105. data/vendor/spidermonkey/fdlibm/k_sin.c +114 -0
  106. data/vendor/spidermonkey/fdlibm/k_standard.c +785 -0
  107. data/vendor/spidermonkey/fdlibm/k_tan.c +170 -0
  108. data/vendor/spidermonkey/fdlibm/s_asinh.c +101 -0
  109. data/vendor/spidermonkey/fdlibm/s_atan.c +175 -0
  110. data/vendor/spidermonkey/fdlibm/s_cbrt.c +133 -0
  111. data/vendor/spidermonkey/fdlibm/s_ceil.c +120 -0
  112. data/vendor/spidermonkey/fdlibm/s_copysign.c +72 -0
  113. data/vendor/spidermonkey/fdlibm/s_cos.c +118 -0
  114. data/vendor/spidermonkey/fdlibm/s_erf.c +356 -0
  115. data/vendor/spidermonkey/fdlibm/s_expm1.c +267 -0
  116. data/vendor/spidermonkey/fdlibm/s_fabs.c +70 -0
  117. data/vendor/spidermonkey/fdlibm/s_finite.c +71 -0
  118. data/vendor/spidermonkey/fdlibm/s_floor.c +121 -0
  119. data/vendor/spidermonkey/fdlibm/s_frexp.c +99 -0
  120. data/vendor/spidermonkey/fdlibm/s_ilogb.c +85 -0
  121. data/vendor/spidermonkey/fdlibm/s_isnan.c +74 -0
  122. data/vendor/spidermonkey/fdlibm/s_ldexp.c +66 -0
  123. data/vendor/spidermonkey/fdlibm/s_lib_version.c +73 -0
  124. data/vendor/spidermonkey/fdlibm/s_log1p.c +211 -0
  125. data/vendor/spidermonkey/fdlibm/s_logb.c +79 -0
  126. data/vendor/spidermonkey/fdlibm/s_matherr.c +64 -0
  127. data/vendor/spidermonkey/fdlibm/s_modf.c +132 -0
  128. data/vendor/spidermonkey/fdlibm/s_nextafter.c +124 -0
  129. data/vendor/spidermonkey/fdlibm/s_rint.c +131 -0
  130. data/vendor/spidermonkey/fdlibm/s_scalbn.c +107 -0
  131. data/vendor/spidermonkey/fdlibm/s_signgam.c +40 -0
  132. data/vendor/spidermonkey/fdlibm/s_significand.c +68 -0
  133. data/vendor/spidermonkey/fdlibm/s_sin.c +118 -0
  134. data/vendor/spidermonkey/fdlibm/s_tan.c +112 -0
  135. data/vendor/spidermonkey/fdlibm/s_tanh.c +122 -0
  136. data/vendor/spidermonkey/fdlibm/w_acos.c +78 -0
  137. data/vendor/spidermonkey/fdlibm/w_acosh.c +78 -0
  138. data/vendor/spidermonkey/fdlibm/w_asin.c +80 -0
  139. data/vendor/spidermonkey/fdlibm/w_atan2.c +79 -0
  140. data/vendor/spidermonkey/fdlibm/w_atanh.c +81 -0
  141. data/vendor/spidermonkey/fdlibm/w_cosh.c +77 -0
  142. data/vendor/spidermonkey/fdlibm/w_exp.c +88 -0
  143. data/vendor/spidermonkey/fdlibm/w_fmod.c +78 -0
  144. data/vendor/spidermonkey/fdlibm/w_gamma.c +85 -0
  145. data/vendor/spidermonkey/fdlibm/w_gamma_r.c +81 -0
  146. data/vendor/spidermonkey/fdlibm/w_hypot.c +78 -0
  147. data/vendor/spidermonkey/fdlibm/w_j0.c +105 -0
  148. data/vendor/spidermonkey/fdlibm/w_j1.c +106 -0
  149. data/vendor/spidermonkey/fdlibm/w_jn.c +128 -0
  150. data/vendor/spidermonkey/fdlibm/w_lgamma.c +85 -0
  151. data/vendor/spidermonkey/fdlibm/w_lgamma_r.c +81 -0
  152. data/vendor/spidermonkey/fdlibm/w_log.c +78 -0
  153. data/vendor/spidermonkey/fdlibm/w_log10.c +81 -0
  154. data/vendor/spidermonkey/fdlibm/w_pow.c +99 -0
  155. data/vendor/spidermonkey/fdlibm/w_remainder.c +77 -0
  156. data/vendor/spidermonkey/fdlibm/w_scalb.c +95 -0
  157. data/vendor/spidermonkey/fdlibm/w_sinh.c +77 -0
  158. data/vendor/spidermonkey/fdlibm/w_sqrt.c +77 -0
  159. data/vendor/spidermonkey/javascript-trace.d +73 -0
  160. data/vendor/spidermonkey/js.c +3951 -0
  161. data/vendor/spidermonkey/js.mak +4438 -0
  162. data/vendor/spidermonkey/js.mdp +0 -0
  163. data/vendor/spidermonkey/js.msg +307 -0
  164. data/vendor/spidermonkey/js.pkg +2 -0
  165. data/vendor/spidermonkey/js3240.rc +79 -0
  166. data/vendor/spidermonkey/jsOS240.def +654 -0
  167. data/vendor/spidermonkey/jsapi.c +5836 -0
  168. data/vendor/spidermonkey/jsapi.h +2624 -0
  169. data/vendor/spidermonkey/jsarena.c +450 -0
  170. data/vendor/spidermonkey/jsarena.h +318 -0
  171. data/vendor/spidermonkey/jsarray.c +2988 -0
  172. data/vendor/spidermonkey/jsarray.h +124 -0
  173. data/vendor/spidermonkey/jsatom.c +1045 -0
  174. data/vendor/spidermonkey/jsatom.h +442 -0
  175. data/vendor/spidermonkey/jsbit.h +253 -0
  176. data/vendor/spidermonkey/jsbool.c +176 -0
  177. data/vendor/spidermonkey/jsbool.h +73 -0
  178. data/vendor/spidermonkey/jsclist.h +139 -0
  179. data/vendor/spidermonkey/jscntxt.c +1348 -0
  180. data/vendor/spidermonkey/jscntxt.h +1120 -0
  181. data/vendor/spidermonkey/jscompat.h +57 -0
  182. data/vendor/spidermonkey/jsconfig.h +248 -0
  183. data/vendor/spidermonkey/jsconfig.mk +181 -0
  184. data/vendor/spidermonkey/jscpucfg.c +383 -0
  185. data/vendor/spidermonkey/jscpucfg.h +212 -0
  186. data/vendor/spidermonkey/jsdate.c +2398 -0
  187. data/vendor/spidermonkey/jsdate.h +124 -0
  188. data/vendor/spidermonkey/jsdbgapi.c +1799 -0
  189. data/vendor/spidermonkey/jsdbgapi.h +464 -0
  190. data/vendor/spidermonkey/jsdhash.c +868 -0
  191. data/vendor/spidermonkey/jsdhash.h +592 -0
  192. data/vendor/spidermonkey/jsdtoa.c +3167 -0
  193. data/vendor/spidermonkey/jsdtoa.h +130 -0
  194. data/vendor/spidermonkey/jsdtracef.c +317 -0
  195. data/vendor/spidermonkey/jsdtracef.h +77 -0
  196. data/vendor/spidermonkey/jsemit.c +6909 -0
  197. data/vendor/spidermonkey/jsemit.h +741 -0
  198. data/vendor/spidermonkey/jsexn.c +1371 -0
  199. data/vendor/spidermonkey/jsexn.h +96 -0
  200. data/vendor/spidermonkey/jsfile.c +2736 -0
  201. data/vendor/spidermonkey/jsfile.h +56 -0
  202. data/vendor/spidermonkey/jsfile.msg +90 -0
  203. data/vendor/spidermonkey/jsfun.c +2634 -0
  204. data/vendor/spidermonkey/jsfun.h +254 -0
  205. data/vendor/spidermonkey/jsgc.c +3554 -0
  206. data/vendor/spidermonkey/jsgc.h +403 -0
  207. data/vendor/spidermonkey/jshash.c +476 -0
  208. data/vendor/spidermonkey/jshash.h +151 -0
  209. data/vendor/spidermonkey/jsify.pl +485 -0
  210. data/vendor/spidermonkey/jsinterp.c +6981 -0
  211. data/vendor/spidermonkey/jsinterp.h +521 -0
  212. data/vendor/spidermonkey/jsinvoke.c +43 -0
  213. data/vendor/spidermonkey/jsiter.c +1067 -0
  214. data/vendor/spidermonkey/jsiter.h +122 -0
  215. data/vendor/spidermonkey/jskeyword.tbl +124 -0
  216. data/vendor/spidermonkey/jskwgen.c +460 -0
  217. data/vendor/spidermonkey/jslibmath.h +266 -0
  218. data/vendor/spidermonkey/jslock.c +1309 -0
  219. data/vendor/spidermonkey/jslock.h +313 -0
  220. data/vendor/spidermonkey/jslocko.asm +60 -0
  221. data/vendor/spidermonkey/jslog2.c +94 -0
  222. data/vendor/spidermonkey/jslong.c +264 -0
  223. data/vendor/spidermonkey/jslong.h +412 -0
  224. data/vendor/spidermonkey/jsmath.c +568 -0
  225. data/vendor/spidermonkey/jsmath.h +57 -0
  226. data/vendor/spidermonkey/jsnum.c +1228 -0
  227. data/vendor/spidermonkey/jsnum.h +283 -0
  228. data/vendor/spidermonkey/jsobj.c +5266 -0
  229. data/vendor/spidermonkey/jsobj.h +709 -0
  230. data/vendor/spidermonkey/jsopcode.c +5245 -0
  231. data/vendor/spidermonkey/jsopcode.h +394 -0
  232. data/vendor/spidermonkey/jsopcode.tbl +523 -0
  233. data/vendor/spidermonkey/jsotypes.h +202 -0
  234. data/vendor/spidermonkey/jsparse.c +6680 -0
  235. data/vendor/spidermonkey/jsparse.h +511 -0
  236. data/vendor/spidermonkey/jsprf.c +1262 -0
  237. data/vendor/spidermonkey/jsprf.h +150 -0
  238. data/vendor/spidermonkey/jsproto.tbl +128 -0
  239. data/vendor/spidermonkey/jsprvtd.h +267 -0
  240. data/vendor/spidermonkey/jspubtd.h +744 -0
  241. data/vendor/spidermonkey/jsregexp.c +4352 -0
  242. data/vendor/spidermonkey/jsregexp.h +183 -0
  243. data/vendor/spidermonkey/jsreops.tbl +145 -0
  244. data/vendor/spidermonkey/jsscan.c +2003 -0
  245. data/vendor/spidermonkey/jsscan.h +387 -0
  246. data/vendor/spidermonkey/jsscope.c +1948 -0
  247. data/vendor/spidermonkey/jsscope.h +418 -0
  248. data/vendor/spidermonkey/jsscript.c +1832 -0
  249. data/vendor/spidermonkey/jsscript.h +287 -0
  250. data/vendor/spidermonkey/jsshell.msg +50 -0
  251. data/vendor/spidermonkey/jsstddef.h +83 -0
  252. data/vendor/spidermonkey/jsstr.c +5004 -0
  253. data/vendor/spidermonkey/jsstr.h +641 -0
  254. data/vendor/spidermonkey/jstypes.h +475 -0
  255. data/vendor/spidermonkey/jsutil.c +345 -0
  256. data/vendor/spidermonkey/jsutil.h +157 -0
  257. data/vendor/spidermonkey/jsxdrapi.c +800 -0
  258. data/vendor/spidermonkey/jsxdrapi.h +218 -0
  259. data/vendor/spidermonkey/jsxml.c +8471 -0
  260. data/vendor/spidermonkey/jsxml.h +349 -0
  261. data/vendor/spidermonkey/lock_SunOS.s +119 -0
  262. data/vendor/spidermonkey/perfect.js +39 -0
  263. data/vendor/spidermonkey/plify_jsdhash.sed +36 -0
  264. data/vendor/spidermonkey/prmjtime.c +846 -0
  265. data/vendor/spidermonkey/prmjtime.h +103 -0
  266. data/vendor/spidermonkey/resource.h +15 -0
  267. data/vendor/spidermonkey/rules.mk +197 -0
  268. data/vendor/spidermonkey/win32.order +384 -0
  269. metadata +4 -3
@@ -0,0 +1,2988 @@
1
+ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
+ * vim: set sw=4 ts=8 et tw=78:
3
+ *
4
+ * ***** BEGIN LICENSE BLOCK *****
5
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6
+ *
7
+ * The contents of this file are subject to the Mozilla Public License Version
8
+ * 1.1 (the "License"); you may not use this file except in compliance with
9
+ * the License. You may obtain a copy of the License at
10
+ * http://www.mozilla.org/MPL/
11
+ *
12
+ * Software distributed under the License is distributed on an "AS IS" basis,
13
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14
+ * for the specific language governing rights and limitations under the
15
+ * License.
16
+ *
17
+ * The Original Code is Mozilla Communicator client code, released
18
+ * March 31, 1998.
19
+ *
20
+ * The Initial Developer of the Original Code is
21
+ * Netscape Communications Corporation.
22
+ * Portions created by the Initial Developer are Copyright (C) 1998
23
+ * the Initial Developer. All Rights Reserved.
24
+ *
25
+ * Contributor(s):
26
+ *
27
+ * Alternatively, the contents of this file may be used under the terms of
28
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
29
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30
+ * in which case the provisions of the GPL or the LGPL are applicable instead
31
+ * of those above. If you wish to allow use of your version of this file only
32
+ * under the terms of either the GPL or the LGPL, and not to allow others to
33
+ * use your version of this file under the terms of the MPL, indicate your
34
+ * decision by deleting the provisions above and replace them with the notice
35
+ * and other provisions required by the GPL or the LGPL. If you do not delete
36
+ * the provisions above, a recipient may use your version of this file under
37
+ * the terms of any one of the MPL, the GPL or the LGPL.
38
+ *
39
+ * ***** END LICENSE BLOCK ***** */
40
+
41
+ /*
42
+ * JS array class.
43
+ *
44
+ * Array objects begin as "dense" arrays, optimized for numeric-only property
45
+ * access over a vector of slots (obj->dslots) with high load factor. Array
46
+ * methods optimize for denseness by testing that the object's class is
47
+ * &js_ArrayClass, and can then directly manipulate the slots for efficiency.
48
+ *
49
+ * We track these pieces of metadata for arrays in dense mode:
50
+ * - the array's length property as a uint32, in JSSLOT_ARRAY_LENGTH,
51
+ * - the number of indices that are filled (non-holes), in JSSLOT_ARRAY_COUNT,
52
+ * - the net number of slots starting at dslots (DENSELEN), in dslots[-1] if
53
+ * dslots is non-NULL.
54
+ *
55
+ * In dense mode, holes in the array are represented by JSVAL_HOLE. The final
56
+ * slot in fslots (JSSLOT_ARRAY_LOOKUP_HOLDER) is used to store the single jsid
57
+ * "in use" by a lookupProperty caller.
58
+ *
59
+ * Arrays are converted to use js_SlowArrayClass when any of these conditions
60
+ * are met:
61
+ * - the load factor (COUNT / DENSELEN) is less than 0.25, and there are
62
+ * more than MIN_SPARSE_INDEX slots total
63
+ * - a property is set that is non-numeric (and not "length"); or
64
+ * - a hole is filled below DENSELEN (possibly implicitly through methods like
65
+ * |reverse| or |splice|).
66
+ *
67
+ * In the latter two cases, property creation order is no longer index order,
68
+ * which necessitates use of a structure that keeps track of property creation
69
+ * order. (ES4, due to expectations baked into web script, requires that
70
+ * enumeration order be the order in which properties were created.)
71
+ *
72
+ * An alternative in the latter case (out-of-order index set) would be to
73
+ * maintain the scope to track property enumeration order, but still use
74
+ * the fast slot access. That would have the same memory cost as just using
75
+ * a js_SlowArrayClass, but have the same performance characteristics as
76
+ * a dense array for slot accesses, at some cost in code complexity.
77
+ */
78
+ #include "jsstddef.h"
79
+ #include <stdlib.h>
80
+ #include <string.h>
81
+ #include "jstypes.h"
82
+ #include "jsutil.h" /* Added by JSIFY */
83
+ #include "jsapi.h"
84
+ #include "jsarray.h"
85
+ #include "jsatom.h"
86
+ #include "jsbit.h"
87
+ #include "jsbool.h"
88
+ #include "jscntxt.h"
89
+ #include "jsconfig.h"
90
+ #include "jsdbgapi.h" /* for js_TraceWatchPoints */
91
+ #include "jsdtoa.h"
92
+ #include "jsfun.h"
93
+ #include "jsgc.h"
94
+ #include "jsinterp.h"
95
+ #include "jslock.h"
96
+ #include "jsnum.h"
97
+ #include "jsobj.h"
98
+ #include "jsscope.h"
99
+ #include "jsstr.h"
100
+
101
+ /* 2^32 - 1 as a number and a string */
102
+ #define MAXINDEX 4294967295u
103
+ #define MAXSTR "4294967295"
104
+
105
+ #define ARRAY_SET_DENSE_LENGTH(obj, max) \
106
+ (JS_ASSERT((obj)->dslots), (obj)->dslots[-1] = (jsval)(max))
107
+
108
+ /* Small arrays are dense, no matter what. */
109
+ #define MIN_SPARSE_INDEX 32
110
+
111
+ #define INDEX_TOO_BIG(index) ((index) > JS_BIT(29) - 1)
112
+ #define INDEX_TOO_SPARSE(array, index) \
113
+ (INDEX_TOO_BIG(index) || \
114
+ ((index) > ARRAY_DENSE_LENGTH(array) && (index) >= MIN_SPARSE_INDEX && \
115
+ (index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4))
116
+
117
+ JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval));
118
+
119
+ static JSBool
120
+ MakeArraySlow(JSContext *cx, JSObject *obj);
121
+
122
+ #define ENSURE_SLOW_ARRAY(cx, obj) \
123
+ (OBJ_GET_CLASS(cx, obj) == &js_SlowArrayClass || MakeArraySlow(cx, obj))
124
+
125
+ /*
126
+ * Determine if the id represents an array index or an XML property index.
127
+ *
128
+ * An id is an array index according to ECMA by (15.4):
129
+ *
130
+ * "Array objects give special treatment to a certain class of property names.
131
+ * A property name P (in the form of a string value) is an array index if and
132
+ * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
133
+ * to 2^32-1."
134
+ *
135
+ * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
136
+ * except that by using signed 32-bit integers we miss the top half of the
137
+ * valid range. This function checks the string representation itself; note
138
+ * that calling a standard conversion routine might allow strings such as
139
+ * "08" or "4.0" as array indices, which they are not.
140
+ */
141
+ JSBool
142
+ js_IdIsIndex(jsval id, jsuint *indexp)
143
+ {
144
+ JSString *str;
145
+ jschar *cp;
146
+
147
+ if (JSVAL_IS_INT(id)) {
148
+ jsint i;
149
+ i = JSVAL_TO_INT(id);
150
+ if (i < 0)
151
+ return JS_FALSE;
152
+ *indexp = (jsuint)i;
153
+ return JS_TRUE;
154
+ }
155
+
156
+ /* NB: id should be a string, but jsxml.c may call us with an object id. */
157
+ if (!JSVAL_IS_STRING(id))
158
+ return JS_FALSE;
159
+
160
+ str = JSVAL_TO_STRING(id);
161
+ cp = JSSTRING_CHARS(str);
162
+ if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) {
163
+ jsuint index = JS7_UNDEC(*cp++);
164
+ jsuint oldIndex = 0;
165
+ jsuint c = 0;
166
+ if (index != 0) {
167
+ while (JS7_ISDEC(*cp)) {
168
+ oldIndex = index;
169
+ c = JS7_UNDEC(*cp);
170
+ index = 10*index + c;
171
+ cp++;
172
+ }
173
+ }
174
+
175
+ /* Ensure that all characters were consumed and we didn't overflow. */
176
+ if (*cp == 0 &&
177
+ (oldIndex < (MAXINDEX / 10) ||
178
+ (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10))))
179
+ {
180
+ *indexp = index;
181
+ return JS_TRUE;
182
+ }
183
+ }
184
+ return JS_FALSE;
185
+ }
186
+
187
+ static jsuint
188
+ ValueIsLength(JSContext *cx, jsval* vp)
189
+ {
190
+ jsint i;
191
+ jsdouble d;
192
+ jsuint length;
193
+
194
+ if (JSVAL_IS_INT(*vp)) {
195
+ i = JSVAL_TO_INT(*vp);
196
+ if (i < 0)
197
+ goto error;
198
+ return (jsuint) i;
199
+ }
200
+
201
+ d = js_ValueToNumber(cx, vp);
202
+ if (JSVAL_IS_NULL(*vp))
203
+ goto error;
204
+
205
+ if (JSDOUBLE_IS_NaN(d))
206
+ goto error;
207
+ length = (jsuint) d;
208
+ if (d != (jsdouble) length)
209
+ goto error;
210
+ return length;
211
+
212
+ error:
213
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
214
+ JSMSG_BAD_ARRAY_LENGTH);
215
+ *vp = JSVAL_NULL;
216
+ return 0;
217
+ }
218
+
219
+ JSBool
220
+ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
221
+ {
222
+ JSTempValueRooter tvr;
223
+ jsid id;
224
+ JSBool ok;
225
+ jsint i;
226
+
227
+ if (OBJ_IS_ARRAY(cx, obj)) {
228
+ *lengthp = obj->fslots[JSSLOT_ARRAY_LENGTH];
229
+ return JS_TRUE;
230
+ }
231
+
232
+ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
233
+ id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
234
+ ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
235
+ if (ok) {
236
+ if (JSVAL_IS_INT(tvr.u.value)) {
237
+ i = JSVAL_TO_INT(tvr.u.value);
238
+ *lengthp = (jsuint)i; /* jsuint cast does ToUint32 */
239
+ } else {
240
+ *lengthp = js_ValueToECMAUint32(cx, &tvr.u.value);
241
+ ok = !JSVAL_IS_NULL(tvr.u.value);
242
+ }
243
+ }
244
+ JS_POP_TEMP_ROOT(cx, &tvr);
245
+ return ok;
246
+ }
247
+
248
+ static JSBool
249
+ IndexToValue(JSContext *cx, jsuint index, jsval *vp)
250
+ {
251
+ if (index <= JSVAL_INT_MAX) {
252
+ *vp = INT_TO_JSVAL(index);
253
+ return JS_TRUE;
254
+ }
255
+ return JS_NewDoubleValue(cx, (jsdouble)index, vp);
256
+ }
257
+
258
+ static JSBool
259
+ IndexToId(JSContext *cx, jsuint index, jsid *idp)
260
+ {
261
+ JSString *str;
262
+
263
+ if (index <= JSVAL_INT_MAX) {
264
+ *idp = INT_TO_JSID(index);
265
+ return JS_TRUE;
266
+ }
267
+ str = js_NumberToString(cx, index);
268
+ if (!str)
269
+ return JS_FALSE;
270
+ return js_ValueToStringId(cx, STRING_TO_JSVAL(str), idp);
271
+ }
272
+
273
+ static JSBool
274
+ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
275
+ jsid *idp)
276
+ {
277
+ jschar buf[10], *start;
278
+ JSClass *clasp;
279
+ JSAtom *atom;
280
+ JS_STATIC_ASSERT((jsuint)-1 == 4294967295U);
281
+
282
+ JS_ASSERT(index > JSVAL_INT_MAX);
283
+
284
+ start = JS_ARRAY_END(buf);
285
+ do {
286
+ --start;
287
+ *start = (jschar)('0' + index % 10);
288
+ index /= 10;
289
+ } while (index != 0);
290
+
291
+ /*
292
+ * Skip the atomization if the class is known to store atoms corresponding
293
+ * to big indexes together with elements. In such case we know that the
294
+ * array does not have an element at the given index if its atom does not
295
+ * exist. Fast arrays (clasp == &js_ArrayClass) don't use atoms for
296
+ * any indexes, though it would be rare to see them have a big index
297
+ * in any case.
298
+ */
299
+ if (!createAtom &&
300
+ ((clasp = OBJ_GET_CLASS(cx, obj)) == &js_SlowArrayClass ||
301
+ clasp == &js_ArgumentsClass ||
302
+ clasp == &js_ObjectClass)) {
303
+ atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start);
304
+ if (!atom) {
305
+ *idp = JSVAL_VOID;
306
+ return JS_TRUE;
307
+ }
308
+ } else {
309
+ atom = js_AtomizeChars(cx, start, JS_ARRAY_END(buf) - start, 0);
310
+ if (!atom)
311
+ return JS_FALSE;
312
+ }
313
+
314
+ *idp = ATOM_TO_JSID(atom);
315
+ return JS_TRUE;
316
+ }
317
+
318
+ static JSBool
319
+ ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 len)
320
+ {
321
+ jsval *slots, *newslots;
322
+
323
+ if (len == 0) {
324
+ if (obj->dslots) {
325
+ JS_free(cx, obj->dslots - 1);
326
+ obj->dslots = NULL;
327
+ }
328
+ return JS_TRUE;
329
+ }
330
+
331
+ if (len > ~(uint32)0 / sizeof(jsval)) {
332
+ js_ReportAllocationOverflow(cx);
333
+ return JS_FALSE;
334
+ }
335
+
336
+ slots = obj->dslots ? obj->dslots - 1 : NULL;
337
+ newslots = (jsval *) JS_realloc(cx, slots, sizeof (jsval) * (len + 1));
338
+ if (!newslots)
339
+ return JS_FALSE;
340
+
341
+ obj->dslots = newslots + 1;
342
+ ARRAY_SET_DENSE_LENGTH(obj, len);
343
+
344
+ for (slots = obj->dslots + oldlen; slots < obj->dslots + len; slots++)
345
+ *slots = JSVAL_HOLE;
346
+
347
+ return JS_TRUE;
348
+ }
349
+
350
+ #define ARRAY_GROWBY 8
351
+
352
+ static JSBool
353
+ EnsureLength(JSContext *cx, JSObject *obj, uint32 len)
354
+ {
355
+ uint32 oldlen = ARRAY_DENSE_LENGTH(obj);
356
+
357
+ if (len > oldlen) {
358
+ return ResizeSlots(cx, obj, oldlen,
359
+ len + ARRAY_GROWBY - (len % ARRAY_GROWBY));
360
+ }
361
+ return JS_TRUE;
362
+ }
363
+
364
+ /*
365
+ * If the property at the given index exists, get its value into location
366
+ * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
367
+ * to JSVAL_VOID. This function assumes that the location pointed by vp is
368
+ * properly rooted and can be used as GC-protected storage for temporaries.
369
+ */
370
+ static JSBool
371
+ GetArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool *hole,
372
+ jsval *vp)
373
+ {
374
+ jsid id;
375
+ JSObject *obj2;
376
+ JSProperty *prop;
377
+
378
+ if (OBJ_IS_DENSE_ARRAY(cx, obj) && index < ARRAY_DENSE_LENGTH(obj) &&
379
+ (*vp = obj->dslots[index]) != JSVAL_HOLE) {
380
+ *hole = JS_FALSE;
381
+ return JS_TRUE;
382
+ }
383
+
384
+ if (index <= JSVAL_INT_MAX) {
385
+ id = INT_TO_JSID(index);
386
+ } else {
387
+ if (!BigIndexToId(cx, obj, index, JS_FALSE, &id))
388
+ return JS_FALSE;
389
+ if (id == JSVAL_VOID) {
390
+ *hole = JS_TRUE;
391
+ *vp = JSVAL_VOID;
392
+ return JS_TRUE;
393
+ }
394
+ }
395
+
396
+ if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
397
+ return JS_FALSE;
398
+ if (!prop) {
399
+ *hole = JS_TRUE;
400
+ *vp = JSVAL_VOID;
401
+ } else {
402
+ OBJ_DROP_PROPERTY(cx, obj2, prop);
403
+ if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
404
+ return JS_FALSE;
405
+ *hole = JS_FALSE;
406
+ }
407
+ return JS_TRUE;
408
+ }
409
+
410
+ /*
411
+ * Set the value of the property at the given index to v assuming v is rooted.
412
+ */
413
+ static JSBool
414
+ SetArrayElement(JSContext *cx, JSObject *obj, jsuint index, jsval v)
415
+ {
416
+ jsid id;
417
+
418
+ if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
419
+ if (INDEX_TOO_SPARSE(obj, index)) {
420
+ if (!MakeArraySlow(cx, obj))
421
+ return JS_FALSE;
422
+ } else {
423
+
424
+ if (!EnsureLength(cx, obj, index + 1))
425
+ return JS_FALSE;
426
+ if (index >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])
427
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1;
428
+ if (obj->dslots[index] == JSVAL_HOLE)
429
+ obj->fslots[JSSLOT_ARRAY_COUNT]++;
430
+ obj->dslots[index] = v;
431
+ return JS_TRUE;
432
+ }
433
+ }
434
+
435
+ if (index <= JSVAL_INT_MAX) {
436
+ id = INT_TO_JSID(index);
437
+ } else {
438
+ if (!BigIndexToId(cx, obj, index, JS_TRUE, &id))
439
+ return JS_FALSE;
440
+ JS_ASSERT(id != JSVAL_VOID);
441
+ }
442
+ return OBJ_SET_PROPERTY(cx, obj, id, &v);
443
+ }
444
+
445
+ static JSBool
446
+ DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index)
447
+ {
448
+ jsid id;
449
+ jsval junk;
450
+
451
+ if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
452
+ if (index < ARRAY_DENSE_LENGTH(obj)) {
453
+ if (obj->dslots[index] != JSVAL_HOLE)
454
+ obj->fslots[JSSLOT_ARRAY_COUNT]--;
455
+ obj->dslots[index] = JSVAL_HOLE;
456
+ }
457
+ return JS_TRUE;
458
+ }
459
+
460
+ if (index <= JSVAL_INT_MAX) {
461
+ id = INT_TO_JSID(index);
462
+ } else {
463
+ if (!BigIndexToId(cx, obj, index, JS_FALSE, &id))
464
+ return JS_FALSE;
465
+ if (id == JSVAL_VOID)
466
+ return JS_TRUE;
467
+ }
468
+ return OBJ_DELETE_PROPERTY(cx, obj, id, &junk);
469
+ }
470
+
471
+ /*
472
+ * When hole is true, delete the property at the given index. Otherwise set
473
+ * its value to v assuming v is rooted.
474
+ */
475
+ static JSBool
476
+ SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index,
477
+ JSBool hole, jsval v)
478
+ {
479
+ if (hole) {
480
+ JS_ASSERT(v == JSVAL_VOID);
481
+ return DeleteArrayElement(cx, obj, index);
482
+ }
483
+ return SetArrayElement(cx, obj, index, v);
484
+ }
485
+
486
+ JSBool
487
+ js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length)
488
+ {
489
+ jsval v;
490
+ jsid id;
491
+
492
+ if (!IndexToValue(cx, length, &v))
493
+ return JS_FALSE;
494
+ id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
495
+ return OBJ_SET_PROPERTY(cx, obj, id, &v);
496
+ }
497
+
498
+ JSBool
499
+ js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
500
+ {
501
+ JSErrorReporter older;
502
+ JSTempValueRooter tvr;
503
+ jsid id;
504
+ JSBool ok;
505
+
506
+ older = JS_SetErrorReporter(cx, NULL);
507
+ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
508
+ id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
509
+ ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
510
+ JS_SetErrorReporter(cx, older);
511
+ if (ok) {
512
+ *lengthp = ValueIsLength(cx, &tvr.u.value);
513
+ ok = !JSVAL_IS_NULL(tvr.u.value);
514
+ }
515
+ JS_POP_TEMP_ROOT(cx, &tvr);
516
+ return ok;
517
+ }
518
+
519
+ JSBool
520
+ js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp)
521
+ {
522
+ JSClass *clasp;
523
+
524
+ clasp = OBJ_GET_CLASS(cx, obj);
525
+ *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass ||
526
+ clasp == &js_SlowArrayClass);
527
+ if (!*answerp) {
528
+ *lengthp = 0;
529
+ return JS_TRUE;
530
+ }
531
+ return js_GetLengthProperty(cx, obj, lengthp);
532
+ }
533
+
534
+ /*
535
+ * The 'length' property of all native Array instances is a shared permanent
536
+ * property of Array.prototype, so it appears to be a direct property of each
537
+ * array instance delegating to that Array.prototype. It accesses the private
538
+ * slot reserved by js_ArrayClass.
539
+ *
540
+ * Since SpiderMonkey supports cross-class prototype-based delegation, we have
541
+ * to be careful about the length getter and setter being called on an object
542
+ * not of Array class. For the getter, we search obj's prototype chain for the
543
+ * array that caused this getter to be invoked. In the setter case to overcome
544
+ * the JSPROP_SHARED attribute, we must define a shadowing length property.
545
+ */
546
+ static JSBool
547
+ array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
548
+ {
549
+ do {
550
+ if (OBJ_IS_ARRAY(cx, obj))
551
+ return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp);
552
+ } while ((obj = OBJ_GET_PROTO(cx, obj)) != NULL);
553
+ return JS_TRUE;
554
+ }
555
+
556
+ static JSBool
557
+ array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
558
+ {
559
+ jsuint newlen, oldlen, gap, index;
560
+ jsval junk;
561
+ JSObject *iter;
562
+ JSTempValueRooter tvr;
563
+ JSBool ok;
564
+
565
+ if (!OBJ_IS_ARRAY(cx, obj)) {
566
+ jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
567
+
568
+ return OBJ_DEFINE_PROPERTY(cx, obj, lengthId, *vp, NULL, NULL,
569
+ JSPROP_ENUMERATE, NULL);
570
+ }
571
+
572
+ newlen = ValueIsLength(cx, vp);
573
+ if (JSVAL_IS_NULL(*vp))
574
+ return JS_FALSE;
575
+ oldlen = obj->fslots[JSSLOT_ARRAY_LENGTH];
576
+
577
+ if (oldlen == newlen)
578
+ return JS_TRUE;
579
+
580
+ if (!IndexToValue(cx, newlen, vp))
581
+ return JS_FALSE;
582
+
583
+ if (oldlen < newlen) {
584
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen;
585
+ return JS_TRUE;
586
+ }
587
+
588
+ if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
589
+ if (ARRAY_DENSE_LENGTH(obj) && !ResizeSlots(cx, obj, oldlen, newlen))
590
+ return JS_FALSE;
591
+ } else if (oldlen - newlen < (1 << 24)) {
592
+ do {
593
+ --oldlen;
594
+ if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
595
+ !DeleteArrayElement(cx, obj, oldlen)) {
596
+ return JS_FALSE;
597
+ }
598
+ } while (oldlen != newlen);
599
+ } else {
600
+ /*
601
+ * We are going to remove a lot of indexes in a presumably sparse
602
+ * array. So instead of looping through indexes between newlen and
603
+ * oldlen, we iterate through all properties and remove those that
604
+ * correspond to indexes in the half-open range [newlen, oldlen). See
605
+ * bug 322135.
606
+ */
607
+ iter = JS_NewPropertyIterator(cx, obj);
608
+ if (!iter)
609
+ return JS_FALSE;
610
+
611
+ /* Protect iter against GC in OBJ_DELETE_PROPERTY. */
612
+ JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr);
613
+ gap = oldlen - newlen;
614
+ for (;;) {
615
+ ok = (JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
616
+ JS_NextProperty(cx, iter, &id));
617
+ if (!ok)
618
+ break;
619
+ if (id == JSVAL_VOID)
620
+ break;
621
+ if (js_IdIsIndex(id, &index) && index - newlen < gap) {
622
+ ok = OBJ_DELETE_PROPERTY(cx, obj, id, &junk);
623
+ if (!ok)
624
+ break;
625
+ }
626
+ }
627
+ JS_POP_TEMP_ROOT(cx, &tvr);
628
+ if (!ok)
629
+ return JS_FALSE;
630
+ }
631
+
632
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen;
633
+ return JS_TRUE;
634
+ }
635
+
636
+ static JSBool
637
+ array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
638
+ JSProperty **propp)
639
+ {
640
+ uint32 i;
641
+
642
+ if (!OBJ_IS_DENSE_ARRAY(cx, obj))
643
+ return js_LookupProperty(cx, obj, id, objp, propp);
644
+
645
+ /*
646
+ * We have only indexed properties up to DENSELEN (excepting holes), plus
647
+ * the length property. For all else, we delegate to the prototype.
648
+ */
649
+ if ((!js_IdIsIndex(id, &i) &&
650
+ id != ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) ||
651
+ obj->fslots[JSSLOT_ARRAY_LENGTH] == 0 ||
652
+ i >= ARRAY_DENSE_LENGTH(obj) ||
653
+ obj->dslots[i] == JSVAL_HOLE)
654
+ {
655
+ JSObject *proto = STOBJ_GET_PROTO(obj);
656
+
657
+ if (!proto) {
658
+ *objp = NULL;
659
+ *propp = NULL;
660
+ return JS_TRUE;
661
+ }
662
+
663
+ return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
664
+ }
665
+
666
+ /* FIXME 417501: threadsafety: could race with a lookup on another thread.
667
+ * If we can only have a single lookup active per context, we could
668
+ * pigeonhole this on the context instead. */
669
+ JS_ASSERT(STOBJ_GET_SLOT(obj, JSSLOT_ARRAY_LOOKUP_HOLDER) == JSVAL_VOID);
670
+ STOBJ_SET_SLOT(obj, JSSLOT_ARRAY_LOOKUP_HOLDER, (jsval)id);
671
+ *propp = (JSProperty *)&(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]);
672
+ *objp = obj;
673
+ return JS_TRUE;
674
+ }
675
+
676
+ static void
677
+ array_dropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
678
+ {
679
+ JS_ASSERT(!OBJ_IS_DENSE_ARRAY(cx, obj) ||
680
+ STOBJ_GET_SLOT(obj, JSSLOT_ARRAY_LOOKUP_HOLDER) != JSVAL_VOID);
681
+ #ifdef DEBUG
682
+ STOBJ_SET_SLOT(obj, JSSLOT_ARRAY_LOOKUP_HOLDER, JSVAL_VOID);
683
+ #endif
684
+ }
685
+
686
+ static JSBool
687
+ array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
688
+ {
689
+ uint32 i;
690
+
691
+ if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
692
+ return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp);
693
+
694
+ if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) {
695
+ *vp = STOBJ_GET_SLOT(obj, JSSLOT_PROTO);
696
+ return JS_TRUE;
697
+ }
698
+
699
+ if (!OBJ_IS_DENSE_ARRAY(cx, obj))
700
+ return js_GetProperty(cx, obj, id, vp);
701
+
702
+ if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= ARRAY_DENSE_LENGTH(obj) ||
703
+ obj->dslots[i] == JSVAL_HOLE) {
704
+ JSObject *proto = STOBJ_GET_PROTO(obj);
705
+ if (!proto) {
706
+ *vp = JSVAL_VOID;
707
+ return JS_TRUE;
708
+ }
709
+
710
+ return OBJ_GET_PROPERTY(cx, proto, id, vp);
711
+ }
712
+
713
+ *vp = obj->dslots[i];
714
+ return JS_TRUE;
715
+ }
716
+
717
+ static JSBool
718
+ slowarray_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
719
+ {
720
+ jsuint index, length;
721
+
722
+ if (!js_IdIsIndex(id, &index))
723
+ return JS_TRUE;
724
+ length = obj->fslots[JSSLOT_ARRAY_LENGTH];
725
+ if (index >= length)
726
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1;
727
+ return JS_TRUE;
728
+ }
729
+
730
+ static void
731
+ slowarray_trace(JSTracer *trc, JSObject *obj)
732
+ {
733
+ uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
734
+
735
+ JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_SlowArrayClass);
736
+
737
+ /*
738
+ * Move JSSLOT_ARRAY_LENGTH aside to prevent the GC from treating
739
+ * untagged integer values as objects or strings.
740
+ */
741
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = JSVAL_VOID;
742
+ js_TraceObject(trc, obj);
743
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = length;
744
+ }
745
+
746
+ static JSObjectOps js_SlowArrayObjectOps;
747
+
748
+ static JSObjectOps *
749
+ slowarray_getObjectOps(JSContext *cx, JSClass *clasp)
750
+ {
751
+ return &js_SlowArrayObjectOps;
752
+ }
753
+
754
+ static JSBool
755
+ array_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
756
+ {
757
+ uint32 i;
758
+
759
+ if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
760
+ return array_length_setter(cx, obj, id, vp);
761
+
762
+ if (!OBJ_IS_DENSE_ARRAY(cx, obj))
763
+ return js_SetProperty(cx, obj, id, vp);
764
+
765
+ if (!js_IdIsIndex(id, &i) || INDEX_TOO_SPARSE(obj, i)) {
766
+ if (!MakeArraySlow(cx, obj))
767
+ return JS_FALSE;
768
+ return js_SetProperty(cx, obj, id, vp);
769
+ }
770
+
771
+ if (!EnsureLength(cx, obj, i + 1))
772
+ return JS_FALSE;
773
+
774
+ if (i >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])
775
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;
776
+ if (obj->dslots[i] == JSVAL_HOLE)
777
+ obj->fslots[JSSLOT_ARRAY_COUNT]++;
778
+ obj->dslots[i] = *vp;
779
+ return JS_TRUE;
780
+ }
781
+
782
+ static JSBool
783
+ array_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
784
+ JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
785
+ JSProperty **propp)
786
+ {
787
+ uint32 i;
788
+
789
+ if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
790
+ return JS_TRUE;
791
+
792
+ if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || attrs != JSPROP_ENUMERATE) {
793
+ if (!ENSURE_SLOW_ARRAY(cx, obj))
794
+ return JS_FALSE;
795
+ return js_DefineProperty(cx, obj, id, value, getter, setter, attrs,
796
+ propp);
797
+ }
798
+
799
+ return array_setProperty(cx, obj, id, &value);
800
+ }
801
+
802
+ static JSBool
803
+ array_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
804
+ uintN *attrsp)
805
+ {
806
+ *attrsp = id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
807
+ ? JSPROP_PERMANENT : JSPROP_ENUMERATE;
808
+ return JS_TRUE;
809
+ }
810
+
811
+ static JSBool
812
+ array_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
813
+ uintN *attrsp)
814
+ {
815
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
816
+ JSMSG_CANT_SET_ARRAY_ATTRS);
817
+ return JS_FALSE;
818
+ }
819
+
820
+ static JSBool
821
+ array_deleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
822
+ {
823
+ uint32 i;
824
+
825
+ if (!OBJ_IS_DENSE_ARRAY(cx, obj))
826
+ return js_DeleteProperty(cx, obj, id, rval);
827
+
828
+ if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
829
+ *rval = JSVAL_FALSE;
830
+ return JS_TRUE;
831
+ }
832
+
833
+ if (js_IdIsIndex(id, &i) && i < ARRAY_DENSE_LENGTH(obj) &&
834
+ obj->dslots[i] != JSVAL_HOLE) {
835
+ obj->fslots[JSSLOT_ARRAY_COUNT]--;
836
+ obj->dslots[i] = JSVAL_HOLE;
837
+ }
838
+
839
+ *rval = JSVAL_TRUE;
840
+ return JS_TRUE;
841
+ }
842
+
843
+ /*
844
+ * JSObjectOps.enumerate implementation.
845
+ *
846
+ * For a fast array, JSENUMERATE_INIT captures in the enumeration state both
847
+ * the length of the array and the bitmap indicating the positions of holes in
848
+ * the array. This ensures that adding or deleting array elements does not
849
+ * affect the sequence of indexes JSENUMERATE_NEXT returns.
850
+ *
851
+ * For a common case of an array without holes, to represent the state we pack
852
+ * the (nextEnumerationIndex, arrayLength) pair as a pseudo-boolean jsval.
853
+ * This is possible when length <= PACKED_UINT_PAIR_BITS. For arrays with
854
+ * greater length or holes we allocate the JSIndexIterState structure and
855
+ * store it as an int-tagged private pointer jsval. For a slow array we
856
+ * delegate the enumeration implementation to js_Enumerate in
857
+ * slowarray_enumerate.
858
+ *
859
+ * Array mutations can turn a fast array into a slow one after the enumeration
860
+ * starts. When this happens, slowarray_enumerate receives a state created
861
+ * when the array was fast. To distinguish such fast state from a slow state,
862
+ * which is an int-tagged pointer that js_Enumerate creates, we set not one
863
+ * but two lowest bits when tagging a JSIndexIterState pointer -- see
864
+ * INDEX_ITER_TAG usage below. Thus, when slowarray_enumerate receives a state
865
+ * tagged with JSVAL_BOOLEAN or with two lowest bits set, it knows that this
866
+ * is a fast state so it calls array_enumerate to continue enumerating the
867
+ * indexes present in the original fast array.
868
+ */
869
+
870
+ #define PACKED_UINT_PAIR_BITS 14
871
+ #define PACKED_UINT_PAIR_MASK JS_BITMASK(PACKED_UINT_PAIR_BITS)
872
+
873
+ #define UINT_PAIR_TO_BOOLEAN_JSVAL(i,j) \
874
+ (JS_ASSERT((uint32) (i) <= PACKED_UINT_PAIR_MASK), \
875
+ JS_ASSERT((uint32) (j) <= PACKED_UINT_PAIR_MASK), \
876
+ ((jsval) (i) << (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)) | \
877
+ ((jsval) (j) << (JSVAL_TAGBITS)) | \
878
+ (jsval) JSVAL_BOOLEAN)
879
+
880
+ #define BOOLEAN_JSVAL_TO_UINT_PAIR(v,i,j) \
881
+ (JS_ASSERT(JSVAL_TAG(v) == JSVAL_BOOLEAN), \
882
+ (i) = (uint32) ((v) >> (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)), \
883
+ (j) = (uint32) ((v) >> JSVAL_TAGBITS) & PACKED_UINT_PAIR_MASK, \
884
+ JS_ASSERT((i) <= PACKED_UINT_PAIR_MASK))
885
+
886
+ JS_STATIC_ASSERT(PACKED_UINT_PAIR_BITS * 2 + JSVAL_TAGBITS <= JS_BITS_PER_WORD);
887
+
888
+ typedef struct JSIndexIterState {
889
+ uint32 index;
890
+ uint32 length;
891
+ JSBool hasHoles;
892
+
893
+ /*
894
+ * Variable-length bitmap representing array's holes. It must not be
895
+ * accessed when hasHoles is false.
896
+ */
897
+ jsbitmap holes[1];
898
+ } JSIndexIterState;
899
+
900
+ #define INDEX_ITER_TAG 3
901
+
902
+ JS_STATIC_ASSERT(JSVAL_INT == 1);
903
+
904
+ static JSBool
905
+ array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
906
+ jsval *statep, jsid *idp)
907
+ {
908
+ uint32 length, i;
909
+ JSIndexIterState *ii;
910
+
911
+ switch (enum_op) {
912
+ case JSENUMERATE_INIT:
913
+ JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
914
+ length = ARRAY_DENSE_LENGTH(obj);
915
+ if (idp && !IndexToId(cx, length, idp))
916
+ return JS_FALSE;
917
+ ii = NULL;
918
+ for (i = 0; i != length; ++i) {
919
+ if (obj->dslots[i] == JSVAL_HOLE) {
920
+ if (!ii) {
921
+ ii = (JSIndexIterState *)
922
+ JS_malloc(cx, offsetof(JSIndexIterState, holes) +
923
+ JS_BITMAP_SIZE(length));
924
+ if (!ii)
925
+ return JS_FALSE;
926
+ ii->hasHoles = JS_TRUE;
927
+ memset(ii->holes, 0, JS_BITMAP_SIZE(length));
928
+ }
929
+ JS_SET_BIT(ii->holes, i);
930
+ }
931
+ }
932
+ if (!ii) {
933
+ /* Array has no holes. */
934
+ if (length <= PACKED_UINT_PAIR_MASK) {
935
+ *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, length);
936
+ break;
937
+ }
938
+ ii = (JSIndexIterState *)
939
+ JS_malloc(cx, offsetof(JSIndexIterState, holes));
940
+ if (!ii)
941
+ return JS_FALSE;
942
+ ii->hasHoles = JS_FALSE;
943
+ }
944
+ ii->index = 0;
945
+ ii->length = length;
946
+ *statep = (jsval) ii | INDEX_ITER_TAG;
947
+ JS_ASSERT(*statep & JSVAL_INT);
948
+ break;
949
+
950
+ case JSENUMERATE_NEXT:
951
+ if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN) {
952
+ BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, length);
953
+ if (i != length) {
954
+ *idp = INT_TO_JSID(i);
955
+ *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, length);
956
+ break;
957
+ }
958
+ } else {
959
+ JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG);
960
+ ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG);
961
+ i = ii->index;
962
+ if (i != ii->length) {
963
+ /* Skip holes if any. */
964
+ if (ii->hasHoles) {
965
+ while (JS_TEST_BIT(ii->holes, i) && ++i != ii->length)
966
+ continue;
967
+ }
968
+ if (i != ii->length) {
969
+ ii->index = i + 1;
970
+ return IndexToId(cx, i, idp);
971
+ }
972
+ }
973
+ }
974
+ /* FALL THROUGH */
975
+
976
+ case JSENUMERATE_DESTROY:
977
+ if (JSVAL_TAG(*statep) != JSVAL_BOOLEAN) {
978
+ JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG);
979
+ ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG);
980
+ JS_free(cx, ii);
981
+ }
982
+ *statep = JSVAL_NULL;
983
+ break;
984
+ }
985
+ return JS_TRUE;
986
+ }
987
+
988
+ static JSBool
989
+ slowarray_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
990
+ jsval *statep, jsid *idp)
991
+ {
992
+ JSBool ok;
993
+
994
+ /* Are we continuing an enumeration that started when we were dense? */
995
+ if (enum_op != JSENUMERATE_INIT) {
996
+ if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN ||
997
+ (*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG) {
998
+ return array_enumerate(cx, obj, enum_op, statep, idp);
999
+ }
1000
+ JS_ASSERT((*statep & INDEX_ITER_TAG) == JSVAL_INT);
1001
+ }
1002
+ ok = js_Enumerate(cx, obj, enum_op, statep, idp);
1003
+ JS_ASSERT(*statep == JSVAL_NULL || (*statep & INDEX_ITER_TAG) == JSVAL_INT);
1004
+ return ok;
1005
+ }
1006
+
1007
+ static void
1008
+ array_finalize(JSContext *cx, JSObject *obj)
1009
+ {
1010
+ if (obj->dslots)
1011
+ JS_free(cx, obj->dslots - 1);
1012
+ obj->dslots = NULL;
1013
+ }
1014
+
1015
+ static void
1016
+ array_trace(JSTracer *trc, JSObject *obj)
1017
+ {
1018
+ uint32 length;
1019
+ size_t i;
1020
+ jsval v;
1021
+
1022
+ JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
1023
+
1024
+ length = ARRAY_DENSE_LENGTH(obj);
1025
+ for (i = 0; i < length; i++) {
1026
+ v = obj->dslots[i];
1027
+ if (JSVAL_IS_TRACEABLE(v)) {
1028
+ JS_SET_TRACING_INDEX(trc, "array_dslots", i);
1029
+ JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
1030
+ }
1031
+ }
1032
+
1033
+ for (i = JSSLOT_PROTO; i <= JSSLOT_PARENT; ++i) {
1034
+ v = STOBJ_GET_SLOT(obj, i);
1035
+ if (JSVAL_IS_TRACEABLE(v)) {
1036
+ JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
1037
+ JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
1038
+ }
1039
+ }
1040
+ }
1041
+
1042
+ static JSObjectMap *
1043
+ array_newObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
1044
+ JSClass *clasp, JSObject *obj)
1045
+ {
1046
+ #ifdef DEBUG
1047
+ extern JSClass js_ArrayClass;
1048
+ extern JSObjectOps js_ArrayObjectOps;
1049
+ #endif
1050
+ JSObjectMap *map = (JSObjectMap *) JS_malloc(cx, sizeof(*map));
1051
+ if (!map)
1052
+ return NULL;
1053
+
1054
+ map->nrefs = nrefs;
1055
+ JS_ASSERT(ops == &js_ArrayObjectOps);
1056
+ map->ops = ops;
1057
+ JS_ASSERT(clasp == &js_ArrayClass);
1058
+ map->freeslot = JSSLOT_FREE(clasp);
1059
+
1060
+ return map;
1061
+ }
1062
+
1063
+ void
1064
+ array_destroyObjectMap(JSContext *cx, JSObjectMap *map)
1065
+ {
1066
+ JS_free(cx, map);
1067
+ }
1068
+
1069
+ JSObjectOps js_ArrayObjectOps = {
1070
+ array_newObjectMap, array_destroyObjectMap,
1071
+ array_lookupProperty, array_defineProperty,
1072
+ array_getProperty, array_setProperty,
1073
+ array_getAttributes, array_setAttributes,
1074
+ array_deleteProperty, js_DefaultValue,
1075
+ array_enumerate, js_CheckAccess,
1076
+ NULL, array_dropProperty,
1077
+ NULL, NULL,
1078
+ NULL, js_HasInstance,
1079
+ js_SetProtoOrParent, js_SetProtoOrParent,
1080
+ array_trace, NULL,
1081
+ NULL, NULL
1082
+ };
1083
+
1084
+ static JSObjectOps *
1085
+ array_getObjectOps(JSContext *cx, JSClass *clasp)
1086
+ {
1087
+ return &js_ArrayObjectOps;
1088
+ }
1089
+
1090
+ JSClass js_ArrayClass = {
1091
+ "Array",
1092
+ JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) |
1093
+ JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_ENUMERATE,
1094
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
1095
+ JS_EnumerateStub, JS_ResolveStub, js_TryValueOf, array_finalize,
1096
+ array_getObjectOps, NULL, NULL, NULL,
1097
+ NULL, NULL, NULL, NULL
1098
+ };
1099
+
1100
+ JSClass js_SlowArrayClass = {
1101
+ "Array",
1102
+ JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
1103
+ slowarray_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
1104
+ JS_EnumerateStub, JS_ResolveStub, js_TryValueOf, JS_FinalizeStub,
1105
+ slowarray_getObjectOps, NULL, NULL, NULL,
1106
+ NULL, NULL, NULL, NULL
1107
+ };
1108
+
1109
+ /*
1110
+ * Convert an array object from fast-and-dense to slow-and-flexible.
1111
+ */
1112
+ static JSBool
1113
+ MakeArraySlow(JSContext *cx, JSObject *obj)
1114
+ {
1115
+ JSObjectMap *map, *oldmap;
1116
+ uint32 i, length;
1117
+
1118
+ JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);
1119
+
1120
+ /* Create a native scope. */
1121
+ map = js_NewObjectMap(cx, obj->map->nrefs, &js_SlowArrayObjectOps,
1122
+ &js_SlowArrayClass, obj);
1123
+ if (!map)
1124
+ return JS_FALSE;
1125
+
1126
+ length = ARRAY_DENSE_LENGTH(obj);
1127
+ if (length) {
1128
+ map->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS;
1129
+ obj->dslots[-1] = JS_INITIAL_NSLOTS + length;
1130
+ } else {
1131
+ map->freeslot = STOBJ_NSLOTS(obj);
1132
+ }
1133
+
1134
+ /* Create new properties pointing to existing values in dslots */
1135
+ for (i = 0; i < length; i++) {
1136
+ jsid id;
1137
+ JSScopeProperty *sprop;
1138
+
1139
+ if (!JS_ValueToId(cx, INT_TO_JSVAL(i), &id))
1140
+ goto out_bad;
1141
+
1142
+ if (obj->dslots[i] == JSVAL_HOLE) {
1143
+ obj->dslots[i] = JSVAL_VOID;
1144
+ continue;
1145
+ }
1146
+
1147
+ sprop = js_AddScopeProperty(cx, (JSScope *)map, id, NULL, NULL,
1148
+ i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE,
1149
+ 0, 0);
1150
+ if (!sprop)
1151
+ goto out_bad;
1152
+ }
1153
+
1154
+ /* Render our formerly-reserved count property GC-safe. */
1155
+ obj->fslots[JSSLOT_ARRAY_COUNT] = JSVAL_VOID;
1156
+
1157
+ /* Make sure we preserve any flags borrowing bits in JSSLOT_CLASS. */
1158
+ obj->fslots[JSSLOT_CLASS] ^= (jsval) &js_ArrayClass;
1159
+ obj->fslots[JSSLOT_CLASS] |= (jsval) &js_SlowArrayClass;
1160
+
1161
+ /* Swap in our new map. */
1162
+ oldmap = obj->map;
1163
+ obj->map = map;
1164
+ array_destroyObjectMap(cx, oldmap);
1165
+
1166
+ return JS_TRUE;
1167
+
1168
+ out_bad:
1169
+ js_DestroyObjectMap(cx, map);
1170
+ return JS_FALSE;
1171
+ }
1172
+
1173
+ enum ArrayToStringOp {
1174
+ TO_STRING,
1175
+ TO_LOCALE_STRING,
1176
+ TO_SOURCE
1177
+ };
1178
+
1179
+ /*
1180
+ * When op is TO_STRING or TO_LOCALE_STRING sep indicates a separator to use
1181
+ * or "," when sep is NULL.
1182
+ * When op is TO_SOURCE sep must be NULL.
1183
+ */
1184
+ static JSBool
1185
+ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op,
1186
+ JSString *sep, jsval *rval)
1187
+ {
1188
+ JSBool ok, hole;
1189
+ jsuint length, index;
1190
+ jschar *chars, *ochars;
1191
+ size_t nchars, growth, seplen, tmplen, extratail;
1192
+ const jschar *sepstr;
1193
+ JSString *str;
1194
+ JSHashEntry *he;
1195
+ JSAtom *atom;
1196
+
1197
+ JS_CHECK_RECURSION(cx, return JS_FALSE);
1198
+
1199
+ ok = js_GetLengthProperty(cx, obj, &length);
1200
+ if (!ok)
1201
+ return JS_FALSE;
1202
+
1203
+ he = js_EnterSharpObject(cx, obj, NULL, &chars);
1204
+ if (!he)
1205
+ return JS_FALSE;
1206
+ #ifdef DEBUG
1207
+ growth = (size_t) -1;
1208
+ #endif
1209
+
1210
+ if (op == TO_SOURCE) {
1211
+ if (IS_SHARP(he)) {
1212
+ #if JS_HAS_SHARP_VARS
1213
+ nchars = js_strlen(chars);
1214
+ #else
1215
+ chars[0] = '[';
1216
+ chars[1] = ']';
1217
+ chars[2] = 0;
1218
+ nchars = 2;
1219
+ #endif
1220
+ goto make_string;
1221
+ }
1222
+
1223
+ /*
1224
+ * Always allocate 2 extra chars for closing ']' and terminating 0
1225
+ * and then preallocate 1 + extratail to include starting '['.
1226
+ */
1227
+ extratail = 2;
1228
+ growth = (1 + extratail) * sizeof(jschar);
1229
+ if (!chars) {
1230
+ nchars = 0;
1231
+ chars = (jschar *) malloc(growth);
1232
+ if (!chars)
1233
+ goto done;
1234
+ } else {
1235
+ MAKE_SHARP(he);
1236
+ nchars = js_strlen(chars);
1237
+ growth += nchars * sizeof(jschar);
1238
+ chars = (jschar *)realloc((ochars = chars), growth);
1239
+ if (!chars) {
1240
+ free(ochars);
1241
+ goto done;
1242
+ }
1243
+ }
1244
+ chars[nchars++] = '[';
1245
+ JS_ASSERT(sep == NULL);
1246
+ sepstr = NULL; /* indicates to use ", " as separator */
1247
+ seplen = 2;
1248
+ } else {
1249
+ /*
1250
+ * Free any sharp variable definition in chars. Normally, we would
1251
+ * MAKE_SHARP(he) so that only the first sharp variable annotation is
1252
+ * a definition, and all the rest are references, but in the current
1253
+ * case of (op != TO_SOURCE), we don't need chars at all.
1254
+ */
1255
+ if (chars)
1256
+ JS_free(cx, chars);
1257
+ chars = NULL;
1258
+ nchars = 0;
1259
+ extratail = 1; /* allocate extra char for terminating 0 */
1260
+
1261
+ /* Return the empty string on a cycle as well as on empty join. */
1262
+ if (IS_BUSY(he) || length == 0) {
1263
+ js_LeaveSharpObject(cx, NULL);
1264
+ *rval = JS_GetEmptyStringValue(cx);
1265
+ return ok;
1266
+ }
1267
+
1268
+ /* Flag he as BUSY so we can distinguish a cycle from a join-point. */
1269
+ MAKE_BUSY(he);
1270
+
1271
+ if (sep) {
1272
+ JSSTRING_CHARS_AND_LENGTH(sep, sepstr, seplen);
1273
+ } else {
1274
+ sepstr = NULL; /* indicates to use "," as separator */
1275
+ seplen = 1;
1276
+ }
1277
+ }
1278
+
1279
+ /* Use rval to locally root each element value as we loop and convert. */
1280
+ for (index = 0; index < length; index++) {
1281
+ ok = (JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
1282
+ GetArrayElement(cx, obj, index, &hole, rval));
1283
+ if (!ok)
1284
+ goto done;
1285
+ if (hole ||
1286
+ (op != TO_SOURCE &&
1287
+ (JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval)))) {
1288
+ str = cx->runtime->emptyString;
1289
+ } else {
1290
+ if (op == TO_LOCALE_STRING) {
1291
+ JSObject *robj;
1292
+
1293
+ atom = cx->runtime->atomState.toLocaleStringAtom;
1294
+ ok = js_ValueToObject(cx, *rval, &robj);
1295
+ if (ok) {
1296
+ /* Re-use *rval to protect robj temporarily. */
1297
+ *rval = OBJECT_TO_JSVAL(robj);
1298
+ ok = js_TryMethod(cx, robj, atom, 0, NULL, rval);
1299
+ }
1300
+ if (!ok)
1301
+ goto done;
1302
+ str = js_ValueToString(cx, *rval);
1303
+ } else if (op == TO_STRING) {
1304
+ str = js_ValueToString(cx, *rval);
1305
+ } else {
1306
+ JS_ASSERT(op == TO_SOURCE);
1307
+ str = js_ValueToSource(cx, *rval);
1308
+ }
1309
+ if (!str) {
1310
+ ok = JS_FALSE;
1311
+ goto done;
1312
+ }
1313
+ }
1314
+
1315
+ /*
1316
+ * Do not append separator after the last element unless it is a hole
1317
+ * and we are in toSource. In that case we append single ",".
1318
+ */
1319
+ if (index + 1 == length)
1320
+ seplen = (hole && op == TO_SOURCE) ? 1 : 0;
1321
+
1322
+ /* Allocate 1 at end for closing bracket and zero. */
1323
+ tmplen = JSSTRING_LENGTH(str);
1324
+ growth = nchars + tmplen + seplen + extratail;
1325
+ if (nchars > growth || tmplen > growth ||
1326
+ growth > (size_t)-1 / sizeof(jschar)) {
1327
+ if (chars) {
1328
+ free(chars);
1329
+ chars = NULL;
1330
+ }
1331
+ goto done;
1332
+ }
1333
+ growth *= sizeof(jschar);
1334
+ JS_COUNT_OPERATION(cx, JSOW_ALLOCATION);
1335
+ if (!chars) {
1336
+ chars = (jschar *) malloc(growth);
1337
+ if (!chars)
1338
+ goto done;
1339
+ } else {
1340
+ chars = (jschar *) realloc((ochars = chars), growth);
1341
+ if (!chars) {
1342
+ free(ochars);
1343
+ goto done;
1344
+ }
1345
+ }
1346
+
1347
+ js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen);
1348
+ nchars += tmplen;
1349
+
1350
+ if (seplen) {
1351
+ if (sepstr) {
1352
+ js_strncpy(&chars[nchars], sepstr, seplen);
1353
+ } else {
1354
+ JS_ASSERT(seplen == 1 || seplen == 2);
1355
+ chars[nchars] = ',';
1356
+ if (seplen == 2)
1357
+ chars[nchars + 1] = ' ';
1358
+ }
1359
+ nchars += seplen;
1360
+ }
1361
+ }
1362
+
1363
+ done:
1364
+ if (op == TO_SOURCE) {
1365
+ if (chars)
1366
+ chars[nchars++] = ']';
1367
+ } else {
1368
+ CLEAR_BUSY(he);
1369
+ }
1370
+ js_LeaveSharpObject(cx, NULL);
1371
+ if (!ok) {
1372
+ if (chars)
1373
+ free(chars);
1374
+ return ok;
1375
+ }
1376
+
1377
+ make_string:
1378
+ if (!chars) {
1379
+ JS_ReportOutOfMemory(cx);
1380
+ return JS_FALSE;
1381
+ }
1382
+ chars[nchars] = 0;
1383
+ JS_ASSERT(growth == (size_t)-1 || (nchars + 1) * sizeof(jschar) == growth);
1384
+ str = js_NewString(cx, chars, nchars);
1385
+ if (!str) {
1386
+ free(chars);
1387
+ return JS_FALSE;
1388
+ }
1389
+ *rval = STRING_TO_JSVAL(str);
1390
+ return JS_TRUE;
1391
+ }
1392
+
1393
+ #if JS_HAS_TOSOURCE
1394
+ static JSBool
1395
+ array_toSource(JSContext *cx, uintN argc, jsval *vp)
1396
+ {
1397
+ JSObject *obj;
1398
+
1399
+ obj = JS_THIS_OBJECT(cx, vp);
1400
+ if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&
1401
+ !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) {
1402
+ return JS_FALSE;
1403
+ }
1404
+ return array_join_sub(cx, obj, TO_SOURCE, NULL, vp);
1405
+ }
1406
+ #endif
1407
+
1408
+ static JSBool
1409
+ array_toString(JSContext *cx, uintN argc, jsval *vp)
1410
+ {
1411
+ JSObject *obj;
1412
+
1413
+ obj = JS_THIS_OBJECT(cx, vp);
1414
+ if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&
1415
+ !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) {
1416
+ return JS_FALSE;
1417
+ }
1418
+ return array_join_sub(cx, obj, TO_STRING, NULL, vp);
1419
+ }
1420
+
1421
+ static JSBool
1422
+ array_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
1423
+ {
1424
+ JSObject *obj;
1425
+
1426
+ obj = JS_THIS_OBJECT(cx, vp);
1427
+ if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&
1428
+ !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) {
1429
+ return JS_FALSE;
1430
+ }
1431
+
1432
+ /*
1433
+ * Passing comma here as the separator. Need a way to get a
1434
+ * locale-specific version.
1435
+ */
1436
+ return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, vp);
1437
+ }
1438
+
1439
+ static JSBool
1440
+ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint end,
1441
+ jsval *vector)
1442
+ {
1443
+ if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
1444
+ if (!EnsureLength(cx, obj, end))
1445
+ return JS_FALSE;
1446
+
1447
+ if (end > (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])
1448
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = end;
1449
+
1450
+ memcpy(obj->dslots + start, vector, sizeof(jsval) * (end - start));
1451
+ return JS_TRUE;
1452
+ }
1453
+
1454
+ while (start != end) {
1455
+ if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
1456
+ !SetArrayElement(cx, obj, start++, *vector++)) {
1457
+ return JS_FALSE;
1458
+ }
1459
+ }
1460
+ return JS_TRUE;
1461
+ }
1462
+
1463
+ static JSBool
1464
+ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector)
1465
+ {
1466
+ JS_ASSERT(OBJ_IS_ARRAY(cx, obj));
1467
+
1468
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = length;
1469
+
1470
+ if (vector) {
1471
+ if (!EnsureLength(cx, obj, length))
1472
+ return JS_FALSE;
1473
+ memcpy(obj->dslots, vector, length * sizeof (jsval));
1474
+ obj->fslots[JSSLOT_ARRAY_COUNT] = length;
1475
+ } else {
1476
+ obj->fslots[JSSLOT_ARRAY_COUNT] = 0;
1477
+ }
1478
+ return JS_TRUE;
1479
+ }
1480
+
1481
+ /*
1482
+ * Perl-inspired join, reverse, and sort.
1483
+ */
1484
+ static JSBool
1485
+ array_join(JSContext *cx, uintN argc, jsval *vp)
1486
+ {
1487
+ JSString *str;
1488
+ JSObject *obj;
1489
+
1490
+ if (JSVAL_IS_VOID(vp[2])) {
1491
+ str = NULL;
1492
+ } else {
1493
+ str = js_ValueToString(cx, vp[2]);
1494
+ if (!str)
1495
+ return JS_FALSE;
1496
+ vp[2] = STRING_TO_JSVAL(str);
1497
+ }
1498
+ obj = JS_THIS_OBJECT(cx, vp);
1499
+ return obj && array_join_sub(cx, obj, TO_STRING, str, vp);
1500
+ }
1501
+
1502
+ static JSBool
1503
+ array_reverse(JSContext *cx, uintN argc, jsval *vp)
1504
+ {
1505
+ JSObject *obj;
1506
+ JSTempValueRooter tvr;
1507
+ jsuint len, half, i;
1508
+ JSBool ok, hole, hole2;
1509
+
1510
+ obj = JS_THIS_OBJECT(cx, vp);
1511
+ if (!obj || !js_GetLengthProperty(cx, obj, &len))
1512
+ return JS_FALSE;
1513
+
1514
+ ok = JS_TRUE;
1515
+ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
1516
+ half = len / 2;
1517
+ for (i = 0; i < half; i++) {
1518
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
1519
+ GetArrayElement(cx, obj, i, &hole, &tvr.u.value) &&
1520
+ GetArrayElement(cx, obj, len - i - 1, &hole2, vp) &&
1521
+ SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.u.value) &&
1522
+ SetOrDeleteArrayElement(cx, obj, i, hole2, *vp);
1523
+ if (!ok)
1524
+ break;
1525
+ }
1526
+ JS_POP_TEMP_ROOT(cx, &tvr);
1527
+
1528
+ *vp = OBJECT_TO_JSVAL(obj);
1529
+ return ok;
1530
+ }
1531
+
1532
+ typedef struct MSortArgs {
1533
+ size_t elsize;
1534
+ JSComparator cmp;
1535
+ void *arg;
1536
+ JSBool fastcopy;
1537
+ } MSortArgs;
1538
+
1539
+ /* Helper function for js_MergeSort. */
1540
+ static JSBool
1541
+ MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2)
1542
+ {
1543
+ void *arg, *a, *b, *c;
1544
+ size_t elsize, runtotal;
1545
+ int cmp_result;
1546
+ JSComparator cmp;
1547
+ JSBool fastcopy;
1548
+
1549
+ runtotal = run1 + run2;
1550
+
1551
+ elsize = msa->elsize;
1552
+ cmp = msa->cmp;
1553
+ arg = msa->arg;
1554
+ fastcopy = msa->fastcopy;
1555
+
1556
+ #define CALL_CMP(a, b) \
1557
+ if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
1558
+
1559
+ /* Copy runs already in sorted order. */
1560
+ b = (char *)src + run1 * elsize;
1561
+ a = (char *)b - elsize;
1562
+ CALL_CMP(a, b);
1563
+ if (cmp_result <= 0) {
1564
+ memcpy(dest, src, runtotal * elsize);
1565
+ return JS_TRUE;
1566
+ }
1567
+
1568
+ #define COPY_ONE(p,q,n) \
1569
+ (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n))
1570
+
1571
+ a = src;
1572
+ c = dest;
1573
+ for (; runtotal != 0; runtotal--) {
1574
+ JSBool from_a = run2 == 0;
1575
+ if (!from_a && run1 != 0) {
1576
+ CALL_CMP(a,b);
1577
+ from_a = cmp_result <= 0;
1578
+ }
1579
+
1580
+ if (from_a) {
1581
+ COPY_ONE(c, a, elsize);
1582
+ run1--;
1583
+ a = (char *)a + elsize;
1584
+ } else {
1585
+ COPY_ONE(c, b, elsize);
1586
+ run2--;
1587
+ b = (char *)b + elsize;
1588
+ }
1589
+ c = (char *)c + elsize;
1590
+ }
1591
+ #undef COPY_ONE
1592
+ #undef CALL_CMP
1593
+
1594
+ return JS_TRUE;
1595
+ }
1596
+
1597
+ /*
1598
+ * This sort is stable, i.e. sequence of equal elements is preserved.
1599
+ * See also bug #224128.
1600
+ */
1601
+ JSBool
1602
+ js_MergeSort(void *src, size_t nel, size_t elsize,
1603
+ JSComparator cmp, void *arg, void *tmp)
1604
+ {
1605
+ void *swap, *vec1, *vec2;
1606
+ MSortArgs msa;
1607
+ size_t i, j, lo, hi, run;
1608
+ JSBool fastcopy;
1609
+ int cmp_result;
1610
+
1611
+ /* Avoid memcpy overhead for word-sized and word-aligned elements. */
1612
+ fastcopy = (elsize == sizeof(jsval) &&
1613
+ (((jsuword) src | (jsuword) tmp) & JSVAL_ALIGN) == 0);
1614
+ #define COPY_ONE(p,q,n) \
1615
+ (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n))
1616
+ #define CALL_CMP(a, b) \
1617
+ if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
1618
+ #define INS_SORT_INT 4
1619
+
1620
+ /*
1621
+ * Apply insertion sort to small chunks to reduce the number of merge
1622
+ * passes needed.
1623
+ */
1624
+ for (lo = 0; lo < nel; lo += INS_SORT_INT) {
1625
+ hi = lo + INS_SORT_INT;
1626
+ if (hi >= nel)
1627
+ hi = nel;
1628
+ for (i = lo + 1; i < hi; i++) {
1629
+ vec1 = (char *)src + i * elsize;
1630
+ vec2 = (char *)vec1 - elsize;
1631
+ for (j = i; j > lo; j--) {
1632
+ CALL_CMP(vec2, vec1);
1633
+ /* "<=" instead of "<" insures the sort is stable */
1634
+ if (cmp_result <= 0) {
1635
+ break;
1636
+ }
1637
+
1638
+ /* Swap elements, using "tmp" as tmp storage */
1639
+ COPY_ONE(tmp, vec2, elsize);
1640
+ COPY_ONE(vec2, vec1, elsize);
1641
+ COPY_ONE(vec1, tmp, elsize);
1642
+ vec1 = vec2;
1643
+ vec2 = (char *)vec1 - elsize;
1644
+ }
1645
+ }
1646
+ }
1647
+ #undef CALL_CMP
1648
+ #undef COPY_ONE
1649
+
1650
+ msa.elsize = elsize;
1651
+ msa.cmp = cmp;
1652
+ msa.arg = arg;
1653
+ msa.fastcopy = fastcopy;
1654
+
1655
+ vec1 = src;
1656
+ vec2 = tmp;
1657
+ for (run = INS_SORT_INT; run < nel; run *= 2) {
1658
+ for (lo = 0; lo < nel; lo += 2 * run) {
1659
+ hi = lo + run;
1660
+ if (hi >= nel) {
1661
+ memcpy((char *)vec2 + lo * elsize, (char *)vec1 + lo * elsize,
1662
+ (nel - lo) * elsize);
1663
+ break;
1664
+ }
1665
+ if (!MergeArrays(&msa, (char *)vec1 + lo * elsize,
1666
+ (char *)vec2 + lo * elsize, run,
1667
+ hi + run > nel ? nel - hi : run)) {
1668
+ return JS_FALSE;
1669
+ }
1670
+ }
1671
+ swap = vec1;
1672
+ vec1 = vec2;
1673
+ vec2 = swap;
1674
+ }
1675
+ if (src != vec1)
1676
+ memcpy(src, tmp, nel * elsize);
1677
+
1678
+ return JS_TRUE;
1679
+ }
1680
+
1681
+ typedef struct CompareArgs {
1682
+ JSContext *context;
1683
+ jsval fval;
1684
+ jsval *elemroot; /* stack needed for js_Invoke */
1685
+ } CompareArgs;
1686
+
1687
+ static JSBool
1688
+ sort_compare(void *arg, const void *a, const void *b, int *result)
1689
+ {
1690
+ jsval av = *(const jsval *)a, bv = *(const jsval *)b;
1691
+ CompareArgs *ca = (CompareArgs *) arg;
1692
+ JSContext *cx = ca->context;
1693
+ jsval *invokevp, *sp;
1694
+ jsdouble cmp;
1695
+
1696
+ /**
1697
+ * array_sort deals with holes and undefs on its own and they should not
1698
+ * come here.
1699
+ */
1700
+ JS_ASSERT(av != JSVAL_VOID);
1701
+ JS_ASSERT(bv != JSVAL_VOID);
1702
+
1703
+ if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP))
1704
+ return JS_FALSE;
1705
+
1706
+ invokevp = ca->elemroot;
1707
+ sp = invokevp;
1708
+ *sp++ = ca->fval;
1709
+ *sp++ = JSVAL_NULL;
1710
+ *sp++ = av;
1711
+ *sp++ = bv;
1712
+
1713
+ if (!js_Invoke(cx, 2, invokevp, 0))
1714
+ return JS_FALSE;
1715
+
1716
+ cmp = js_ValueToNumber(cx, invokevp);
1717
+ if (JSVAL_IS_NULL(*invokevp))
1718
+ return JS_FALSE;
1719
+
1720
+ /* Clamp cmp to -1, 0, 1. */
1721
+ *result = 0;
1722
+ if (!JSDOUBLE_IS_NaN(cmp) && cmp != 0)
1723
+ *result = cmp > 0 ? 1 : -1;
1724
+
1725
+ /*
1726
+ * XXX else report some kind of error here? ECMA talks about 'consistent
1727
+ * compare functions' that don't return NaN, but is silent about what the
1728
+ * result should be. So we currently ignore it.
1729
+ */
1730
+
1731
+ return JS_TRUE;
1732
+ }
1733
+
1734
+ static int
1735
+ sort_compare_strings(void *arg, const void *a, const void *b, int *result)
1736
+ {
1737
+ jsval av = *(const jsval *)a, bv = *(const jsval *)b;
1738
+
1739
+ JS_ASSERT(JSVAL_IS_STRING(av));
1740
+ JS_ASSERT(JSVAL_IS_STRING(bv));
1741
+ if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg, JSOW_JUMP))
1742
+ return JS_FALSE;
1743
+
1744
+ *result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv));
1745
+ return JS_TRUE;
1746
+ }
1747
+
1748
+ /*
1749
+ * The array_sort function below assumes JSVAL_NULL is zero in order to
1750
+ * perform initialization using memset. Other parts of SpiderMonkey likewise
1751
+ * "know" that JSVAL_NULL is zero; this static assertion covers all cases.
1752
+ */
1753
+ JS_STATIC_ASSERT(JSVAL_NULL == 0);
1754
+
1755
+ static JSBool
1756
+ array_sort(JSContext *cx, uintN argc, jsval *vp)
1757
+ {
1758
+ jsval *argv, fval, *vec, *mergesort_tmp, v;
1759
+ JSObject *obj;
1760
+ CompareArgs ca;
1761
+ jsuint len, newlen, i, undefs;
1762
+ JSTempValueRooter tvr;
1763
+ JSBool hole, ok;
1764
+ size_t elemsize;
1765
+ JSString *str;
1766
+
1767
+ /*
1768
+ * Optimize the default compare function case if all of obj's elements
1769
+ * have values of type string.
1770
+ */
1771
+ JSBool all_strings;
1772
+
1773
+ argv = JS_ARGV(cx, vp);
1774
+ if (argc > 0) {
1775
+ if (JSVAL_IS_PRIMITIVE(argv[0])) {
1776
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1777
+ JSMSG_BAD_SORT_ARG);
1778
+ return JS_FALSE;
1779
+ }
1780
+ fval = argv[0]; /* non-default compare function */
1781
+ } else {
1782
+ fval = JSVAL_NULL;
1783
+ }
1784
+
1785
+ obj = JS_THIS_OBJECT(cx, vp);
1786
+ if (!obj || !js_GetLengthProperty(cx, obj, &len))
1787
+ return JS_FALSE;
1788
+ if (len == 0) {
1789
+ *vp = OBJECT_TO_JSVAL(obj);
1790
+ return JS_TRUE;
1791
+ }
1792
+
1793
+ /*
1794
+ * We need a temporary array of 2 * len jsvals to hold the array elements
1795
+ * and the scratch space for merge sort. Check that its size does not
1796
+ * overflow size_t, which would allow for indexing beyond the end of the
1797
+ * malloc'd vector.
1798
+ */
1799
+ #if JS_BITS_PER_WORD == 32
1800
+ if ((size_t)len > ~(size_t)0 / (2 * sizeof(jsval))) {
1801
+ js_ReportAllocationOverflow(cx);
1802
+ return JS_FALSE;
1803
+ }
1804
+ #endif
1805
+ vec = (jsval *) JS_malloc(cx, 2 * (size_t) len * sizeof(jsval));
1806
+ if (!vec)
1807
+ return JS_FALSE;
1808
+
1809
+ /*
1810
+ * Initialize vec as a root. We will clear elements of vec one by
1811
+ * one while increasing tvr.count when we know that the property at
1812
+ * the corresponding index exists and its value must be rooted.
1813
+ *
1814
+ * In this way when sorting a huge mostly sparse array we will not
1815
+ * access the tail of vec corresponding to properties that do not
1816
+ * exist, allowing OS to avoiding committing RAM. See bug 330812.
1817
+ *
1818
+ * After this point control must flow through label out: to exit.
1819
+ */
1820
+ JS_PUSH_TEMP_ROOT(cx, 0, vec, &tvr);
1821
+
1822
+ /*
1823
+ * By ECMA 262, 15.4.4.11, a property that does not exist (which we
1824
+ * call a "hole") is always greater than an existing property with
1825
+ * value undefined and that is always greater than any other property.
1826
+ * Thus to sort holes and undefs we simply count them, sort the rest
1827
+ * of elements, append undefs after them and then make holes after
1828
+ * undefs.
1829
+ */
1830
+ undefs = 0;
1831
+ newlen = 0;
1832
+ all_strings = JS_TRUE;
1833
+ for (i = 0; i < len; i++) {
1834
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP);
1835
+ if (!ok)
1836
+ goto out;
1837
+
1838
+ /* Clear vec[newlen] before including it in the rooted set. */
1839
+ vec[newlen] = JSVAL_NULL;
1840
+ tvr.count = newlen + 1;
1841
+ ok = GetArrayElement(cx, obj, i, &hole, &vec[newlen]);
1842
+ if (!ok)
1843
+ goto out;
1844
+
1845
+ if (hole)
1846
+ continue;
1847
+
1848
+ if (vec[newlen] == JSVAL_VOID) {
1849
+ ++undefs;
1850
+ continue;
1851
+ }
1852
+
1853
+ /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */
1854
+ all_strings &= JSVAL_IS_STRING(vec[newlen]);
1855
+
1856
+ ++newlen;
1857
+ }
1858
+
1859
+ if (newlen == 0) {
1860
+ /* The array has only holes and undefs. */
1861
+ ok = JS_TRUE;
1862
+ goto out;
1863
+ }
1864
+
1865
+ /*
1866
+ * The first newlen elements of vec are copied from the array object
1867
+ * (above). The remaining newlen positions are used as GC-rooted scratch
1868
+ * space for mergesort. We must clear the space before including it to
1869
+ * the root set covered by tvr.count. We assume JSVAL_NULL==0 to optimize
1870
+ * initialization using memset.
1871
+ */
1872
+ mergesort_tmp = vec + newlen;
1873
+ memset(mergesort_tmp, 0, newlen * sizeof(jsval));
1874
+ tvr.count = newlen * 2;
1875
+
1876
+ /* Here len == 2 * (newlen + undefs + number_of_holes). */
1877
+ if (fval == JSVAL_NULL) {
1878
+ /*
1879
+ * Sort using the default comparator converting all elements to
1880
+ * strings.
1881
+ */
1882
+ if (all_strings) {
1883
+ elemsize = sizeof(jsval);
1884
+ } else {
1885
+ /*
1886
+ * To avoid string conversion on each compare we do it only once
1887
+ * prior to sorting. But we also need the space for the original
1888
+ * values to recover the sorting result. To reuse
1889
+ * sort_compare_strings we move the original values to the odd
1890
+ * indexes in vec, put the string conversion results in the even
1891
+ * indexes and pass 2 * sizeof(jsval) as an element size to the
1892
+ * sorting function. In this way sort_compare_strings will only
1893
+ * see the string values when it casts the compare arguments as
1894
+ * pointers to jsval.
1895
+ *
1896
+ * This requires doubling the temporary storage including the
1897
+ * scratch space for the merge sort. Since vec already contains
1898
+ * the rooted scratch space for newlen elements at the tail, we
1899
+ * can use it to rearrange and convert to strings first and try
1900
+ * realloc only when we know that we successfully converted all
1901
+ * the elements.
1902
+ */
1903
+ #if JS_BITS_PER_WORD == 32
1904
+ if ((size_t)newlen > ~(size_t)0 / (4 * sizeof(jsval))) {
1905
+ js_ReportAllocationOverflow(cx);
1906
+ ok = JS_FALSE;
1907
+ goto out;
1908
+ }
1909
+ #endif
1910
+
1911
+ /*
1912
+ * Rearrange and string-convert the elements of the vector from
1913
+ * the tail here and, after sorting, move the results back
1914
+ * starting from the start to prevent overwrite the existing
1915
+ * elements.
1916
+ */
1917
+ i = newlen;
1918
+ do {
1919
+ --i;
1920
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP);
1921
+ if (!ok)
1922
+ goto out;
1923
+ v = vec[i];
1924
+ str = js_ValueToString(cx, v);
1925
+ if (!str) {
1926
+ ok = JS_FALSE;
1927
+ goto out;
1928
+ }
1929
+ vec[2 * i] = STRING_TO_JSVAL(str);
1930
+ vec[2 * i + 1] = v;
1931
+ } while (i != 0);
1932
+
1933
+ JS_ASSERT(tvr.u.array == vec);
1934
+ vec = (jsval *) JS_realloc(cx, vec,
1935
+ 4 * (size_t) newlen * sizeof(jsval));
1936
+ if (!vec) {
1937
+ vec = tvr.u.array;
1938
+ ok = JS_FALSE;
1939
+ goto out;
1940
+ }
1941
+ tvr.u.array = vec;
1942
+ mergesort_tmp = vec + 2 * newlen;
1943
+ memset(mergesort_tmp, 0, newlen * 2 * sizeof(jsval));
1944
+ tvr.count = newlen * 4;
1945
+ elemsize = 2 * sizeof(jsval);
1946
+ }
1947
+ ok = js_MergeSort(vec, (size_t) newlen, elemsize,
1948
+ sort_compare_strings, cx, mergesort_tmp);
1949
+ if (!ok)
1950
+ goto out;
1951
+ if (!all_strings) {
1952
+ /*
1953
+ * We want to make the following loop fast and to unroot the
1954
+ * cached results of toString invocations before the operation
1955
+ * callback has a chance to run the GC. For this reason we do
1956
+ * not call JS_CHECK_OPERATION_LIMIT in the loop.
1957
+ */
1958
+ i = 0;
1959
+ do {
1960
+ vec[i] = vec[2 * i + 1];
1961
+ } while (++i != newlen);
1962
+ }
1963
+ } else {
1964
+ void *mark;
1965
+
1966
+ ca.context = cx;
1967
+ ca.fval = fval;
1968
+ ca.elemroot = js_AllocStack(cx, 2 + 2, &mark);
1969
+ if (!ca.elemroot) {
1970
+ ok = JS_FALSE;
1971
+ goto out;
1972
+ }
1973
+ ok = js_MergeSort(vec, (size_t) newlen, sizeof(jsval),
1974
+ sort_compare, &ca, mergesort_tmp);
1975
+ js_FreeStack(cx, mark);
1976
+ if (!ok)
1977
+ goto out;
1978
+ }
1979
+
1980
+ /*
1981
+ * We no longer need to root the scratch space for the merge sort, so
1982
+ * unroot it now to make the job of a potential GC under InitArrayElements
1983
+ * easier.
1984
+ */
1985
+ tvr.count = newlen;
1986
+ ok = InitArrayElements(cx, obj, 0, newlen, vec);
1987
+ if (!ok)
1988
+ goto out;
1989
+
1990
+ out:
1991
+ JS_POP_TEMP_ROOT(cx, &tvr);
1992
+ JS_free(cx, vec);
1993
+ if (!ok)
1994
+ return JS_FALSE;
1995
+
1996
+ /* Set undefs that sorted after the rest of elements. */
1997
+ while (undefs != 0) {
1998
+ --undefs;
1999
+ if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
2000
+ !SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) {
2001
+ return JS_FALSE;
2002
+ }
2003
+ }
2004
+
2005
+ /* Re-create any holes that sorted to the end of the array. */
2006
+ while (len > newlen) {
2007
+ if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
2008
+ !DeleteArrayElement(cx, obj, --len)) {
2009
+ return JS_FALSE;
2010
+ }
2011
+ }
2012
+ *vp = OBJECT_TO_JSVAL(obj);
2013
+ return JS_TRUE;
2014
+ }
2015
+
2016
+ /*
2017
+ * Perl-inspired push, pop, shift, unshift, and splice methods.
2018
+ */
2019
+ static JSBool
2020
+ array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, jsval *vp)
2021
+ {
2022
+ jsuint length, newlength;
2023
+
2024
+ if (!js_GetLengthProperty(cx, obj, &length))
2025
+ return JS_FALSE;
2026
+ newlength = length + argc;
2027
+ if (!InitArrayElements(cx, obj, length, newlength, vp + 2))
2028
+ return JS_FALSE;
2029
+
2030
+ /* Per ECMA-262, return the new array length. */
2031
+ if (!IndexToValue(cx, newlength, vp))
2032
+ return JS_FALSE;
2033
+ return js_SetLengthProperty(cx, obj, newlength);
2034
+ }
2035
+
2036
+ static JSBool
2037
+ array_push(JSContext *cx, uintN argc, jsval *vp)
2038
+ {
2039
+ JSObject *obj;
2040
+ uint32 length;
2041
+
2042
+ /* Insist on one argument and obj of the expected class. */
2043
+ obj = JS_THIS_OBJECT(cx, vp);
2044
+ if (!obj)
2045
+ return JS_FALSE;
2046
+ if (argc != 1 || !OBJ_IS_DENSE_ARRAY(cx, obj))
2047
+ return array_push_slowly(cx, obj, argc, vp);
2048
+
2049
+ length = obj->fslots[JSSLOT_ARRAY_LENGTH];
2050
+ if (INDEX_TOO_SPARSE(obj, length)) {
2051
+ if (!MakeArraySlow(cx, obj))
2052
+ return JS_FALSE;
2053
+ return array_push_slowly(cx, obj, argc, vp);
2054
+ }
2055
+
2056
+ if (!EnsureLength(cx, obj, length + 1))
2057
+ return JS_FALSE;
2058
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1;
2059
+
2060
+ JS_ASSERT(obj->dslots[length] == JSVAL_HOLE);
2061
+ obj->fslots[JSSLOT_ARRAY_COUNT]++;
2062
+ obj->dslots[length] = vp[2];
2063
+ return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp);
2064
+ }
2065
+
2066
+ JSBool
2067
+ array_pop(JSContext *cx, uintN argc, jsval *vp)
2068
+ {
2069
+ JSObject *obj;
2070
+ jsuint index;
2071
+ JSBool hole;
2072
+
2073
+ obj = JS_THIS_OBJECT(cx, vp);
2074
+ if (!obj)
2075
+ return JS_FALSE;
2076
+ if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
2077
+ *vp = JSVAL_VOID;
2078
+ index = obj->fslots[JSSLOT_ARRAY_LENGTH];
2079
+ if (index == 0)
2080
+ return JS_TRUE;
2081
+ index--;
2082
+ if (index < ARRAY_DENSE_LENGTH(obj)) {
2083
+ *vp = obj->dslots[index];
2084
+ JS_ASSERT(*vp != JSVAL_HOLE);
2085
+ if (index == 0) {
2086
+ JS_free(cx, obj->dslots - 1);
2087
+ obj->dslots = NULL;
2088
+ } else {
2089
+ ARRAY_SET_DENSE_LENGTH(obj, index);
2090
+ }
2091
+ obj->fslots[JSSLOT_ARRAY_COUNT]--;
2092
+ }
2093
+
2094
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = index;
2095
+ return JS_TRUE;
2096
+ }
2097
+
2098
+ if (!js_GetLengthProperty(cx, obj, &index))
2099
+ return JS_FALSE;
2100
+ if (index == 0) {
2101
+ *vp = JSVAL_VOID;
2102
+ } else {
2103
+ index--;
2104
+
2105
+ /* Get the to-be-deleted property's value into vp. */
2106
+ if (!GetArrayElement(cx, obj, index, &hole, vp))
2107
+ return JS_FALSE;
2108
+ if (!hole && !DeleteArrayElement(cx, obj, index))
2109
+ return JS_FALSE;
2110
+ }
2111
+ return js_SetLengthProperty(cx, obj, index);
2112
+ }
2113
+
2114
+ static JSBool
2115
+ array_shift(JSContext *cx, uintN argc, jsval *vp)
2116
+ {
2117
+ JSObject *obj;
2118
+ jsuint length, i;
2119
+ JSBool hole, ok;
2120
+ JSTempValueRooter tvr;
2121
+
2122
+ obj = JS_THIS_OBJECT(cx, vp);
2123
+ if (!obj || !js_GetLengthProperty(cx, obj, &length))
2124
+ return JS_FALSE;
2125
+ if (length == 0) {
2126
+ *vp = JSVAL_VOID;
2127
+ } else {
2128
+ length--;
2129
+
2130
+ /* Get the to-be-deleted property's value into vp ASAP. */
2131
+ if (!GetArrayElement(cx, obj, 0, &hole, vp))
2132
+ return JS_FALSE;
2133
+
2134
+ /* Slide down the array above the first element. */
2135
+ ok = JS_TRUE;
2136
+ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
2137
+ for (i = 0; i != length; i++) {
2138
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
2139
+ GetArrayElement(cx, obj, i + 1, &hole, &tvr.u.value) &&
2140
+ SetOrDeleteArrayElement(cx, obj, i, hole, tvr.u.value);
2141
+ if (!ok)
2142
+ break;
2143
+ }
2144
+ JS_POP_TEMP_ROOT(cx, &tvr);
2145
+ if (!ok)
2146
+ return JS_FALSE;
2147
+
2148
+ /* Delete the only or last element when it exist. */
2149
+ if (!hole && !DeleteArrayElement(cx, obj, length))
2150
+ return JS_FALSE;
2151
+ }
2152
+ return js_SetLengthProperty(cx, obj, length);
2153
+ }
2154
+
2155
+ static JSBool
2156
+ array_unshift(JSContext *cx, uintN argc, jsval *vp)
2157
+ {
2158
+ JSObject *obj;
2159
+ jsval *argv;
2160
+ jsuint length, last;
2161
+ JSBool hole, ok;
2162
+ JSTempValueRooter tvr;
2163
+
2164
+ obj = JS_THIS_OBJECT(cx, vp);
2165
+ if (!obj || !js_GetLengthProperty(cx, obj, &length))
2166
+ return JS_FALSE;
2167
+ if (argc > 0) {
2168
+ /* Slide up the array to make room for argc at the bottom. */
2169
+ argv = JS_ARGV(cx, vp);
2170
+ if (length > 0) {
2171
+ last = length;
2172
+ ok = JS_TRUE;
2173
+ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
2174
+ do {
2175
+ --last;
2176
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
2177
+ GetArrayElement(cx, obj, last, &hole, &tvr.u.value) &&
2178
+ SetOrDeleteArrayElement(cx, obj, last + argc, hole,
2179
+ tvr.u.value);
2180
+ if (!ok)
2181
+ break;
2182
+ } while (last != 0);
2183
+ JS_POP_TEMP_ROOT(cx, &tvr);
2184
+ if (!ok)
2185
+ return JS_FALSE;
2186
+ }
2187
+
2188
+ /* Copy from argv to the bottom of the array. */
2189
+ if (!InitArrayElements(cx, obj, 0, argc, argv))
2190
+ return JS_FALSE;
2191
+
2192
+ length += argc;
2193
+ if (!js_SetLengthProperty(cx, obj, length))
2194
+ return JS_FALSE;
2195
+ }
2196
+
2197
+ /* Follow Perl by returning the new array length. */
2198
+ return IndexToValue(cx, length, vp);
2199
+ }
2200
+
2201
+ static JSBool
2202
+ array_splice(JSContext *cx, uintN argc, jsval *vp)
2203
+ {
2204
+ jsval *argv;
2205
+ JSObject *obj;
2206
+ jsuint length, begin, end, count, delta, last;
2207
+ jsdouble d;
2208
+ JSBool hole, ok;
2209
+ JSObject *obj2;
2210
+ JSTempValueRooter tvr;
2211
+
2212
+ /* Nothing to do if no args. Otherwise get length. */
2213
+ if (argc == 0)
2214
+ return JS_TRUE;
2215
+ argv = JS_ARGV(cx, vp);
2216
+ obj = JS_THIS_OBJECT(cx, vp);
2217
+ if (!obj || !js_GetLengthProperty(cx, obj, &length))
2218
+ return JS_FALSE;
2219
+
2220
+ /* Convert the first argument into a starting index. */
2221
+ d = js_ValueToNumber(cx, argv);
2222
+ if (JSVAL_IS_NULL(*argv))
2223
+ return JS_FALSE;
2224
+ d = js_DoubleToInteger(d);
2225
+ if (d < 0) {
2226
+ d += length;
2227
+ if (d < 0)
2228
+ d = 0;
2229
+ } else if (d > length) {
2230
+ d = length;
2231
+ }
2232
+ begin = (jsuint)d; /* d has been clamped to uint32 */
2233
+ argc--;
2234
+ argv++;
2235
+
2236
+ /* Convert the second argument from a count into a fencepost index. */
2237
+ delta = length - begin;
2238
+ if (argc == 0) {
2239
+ count = delta;
2240
+ end = length;
2241
+ } else {
2242
+ d = js_ValueToNumber(cx, argv);
2243
+ if (JSVAL_IS_NULL(*argv))
2244
+ return JS_FALSE;
2245
+ d = js_DoubleToInteger(d);
2246
+ if (d < 0)
2247
+ d = 0;
2248
+ else if (d > delta)
2249
+ d = delta;
2250
+ count = (jsuint)d;
2251
+ end = begin + count;
2252
+ argc--;
2253
+ argv++;
2254
+ }
2255
+
2256
+ /*
2257
+ * Create a new array value to return. Our ECMA v2 proposal specs
2258
+ * that splice always returns an array value, even when given no
2259
+ * arguments. We think this is best because it eliminates the need
2260
+ * for callers to do an extra test to handle the empty splice case.
2261
+ */
2262
+ obj2 = js_NewArrayObject(cx, 0, NULL);
2263
+ if (!obj2)
2264
+ return JS_FALSE;
2265
+ *vp = OBJECT_TO_JSVAL(obj2);
2266
+
2267
+ /* After this, control must flow through label out: to exit. */
2268
+ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
2269
+
2270
+ /* If there are elements to remove, put them into the return value. */
2271
+ if (count > 0) {
2272
+ for (last = begin; last < end; last++) {
2273
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
2274
+ GetArrayElement(cx, obj, last, &hole, &tvr.u.value);
2275
+ if (!ok)
2276
+ goto out;
2277
+
2278
+ /* Copy tvr.u.value to new array unless it's a hole. */
2279
+ if (!hole) {
2280
+ ok = SetArrayElement(cx, obj2, last - begin, tvr.u.value);
2281
+ if (!ok)
2282
+ goto out;
2283
+ }
2284
+ }
2285
+
2286
+ ok = js_SetLengthProperty(cx, obj2, end - begin);
2287
+ if (!ok)
2288
+ goto out;
2289
+ }
2290
+
2291
+ /* Find the direction (up or down) to copy and make way for argv. */
2292
+ if (argc > count) {
2293
+ delta = (jsuint)argc - count;
2294
+ last = length;
2295
+ /* (uint) end could be 0, so can't use vanilla >= test */
2296
+ while (last-- > end) {
2297
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
2298
+ GetArrayElement(cx, obj, last, &hole, &tvr.u.value) &&
2299
+ SetOrDeleteArrayElement(cx, obj, last + delta, hole,
2300
+ tvr.u.value);
2301
+ if (!ok)
2302
+ goto out;
2303
+ }
2304
+ length += delta;
2305
+ } else if (argc < count) {
2306
+ delta = count - (jsuint)argc;
2307
+ for (last = end; last < length; last++) {
2308
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
2309
+ GetArrayElement(cx, obj, last, &hole, &tvr.u.value) &&
2310
+ SetOrDeleteArrayElement(cx, obj, last - delta, hole,
2311
+ tvr.u.value);
2312
+ if (!ok)
2313
+ goto out;
2314
+ }
2315
+ length -= delta;
2316
+ }
2317
+
2318
+ /* Copy from argv into the hole to complete the splice. */
2319
+ ok = InitArrayElements(cx, obj, begin, begin + argc, argv);
2320
+ if (!ok)
2321
+ goto out;
2322
+
2323
+ /* Update length in case we deleted elements from the end. */
2324
+ ok = js_SetLengthProperty(cx, obj, length);
2325
+
2326
+ out:
2327
+ JS_POP_TEMP_ROOT(cx, &tvr);
2328
+ return ok;
2329
+ }
2330
+
2331
+ /*
2332
+ * Python-esque sequence operations.
2333
+ */
2334
+ static JSBool
2335
+ array_concat(JSContext *cx, uintN argc, jsval *vp)
2336
+ {
2337
+ jsval *argv, v;
2338
+ JSObject *nobj, *aobj;
2339
+ jsuint length, alength, slot;
2340
+ uintN i;
2341
+ JSBool hole, ok;
2342
+ JSTempValueRooter tvr;
2343
+
2344
+ /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
2345
+ argv = JS_ARGV(cx, vp) - 1;
2346
+ JS_ASSERT(JS_THIS_OBJECT(cx, vp) == JSVAL_TO_OBJECT(argv[0]));
2347
+
2348
+ /* Create a new Array object and root it using *vp. */
2349
+ aobj = JS_THIS_OBJECT(cx, vp);
2350
+ if (OBJ_IS_DENSE_ARRAY(cx, aobj)) {
2351
+ nobj = js_NewArrayObject(cx, ARRAY_DENSE_LENGTH(aobj), aobj->dslots);
2352
+ if (!nobj)
2353
+ return JS_FALSE;
2354
+ length = aobj->fslots[JSSLOT_ARRAY_LENGTH];
2355
+ nobj->fslots[JSSLOT_ARRAY_LENGTH] = length;
2356
+ *vp = OBJECT_TO_JSVAL(nobj);
2357
+ if (argc == 0)
2358
+ return JS_TRUE;
2359
+ argc--;
2360
+ argv++;
2361
+ } else {
2362
+ nobj = js_NewArrayObject(cx, 0, NULL);
2363
+ if (!nobj)
2364
+ return JS_FALSE;
2365
+ *vp = OBJECT_TO_JSVAL(nobj);
2366
+ length = 0;
2367
+ }
2368
+
2369
+ /* After this, control must flow through label out: to exit. */
2370
+ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
2371
+
2372
+ /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
2373
+ for (i = 0; i <= argc; i++) {
2374
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP);
2375
+ if (!ok)
2376
+ goto out;
2377
+ v = argv[i];
2378
+ if (!JSVAL_IS_PRIMITIVE(v)) {
2379
+ JSObject *wobj;
2380
+
2381
+ aobj = JSVAL_TO_OBJECT(v);
2382
+ wobj = js_GetWrappedObject(cx, aobj);
2383
+ if (OBJ_IS_ARRAY(cx, wobj)) {
2384
+ ok = OBJ_GET_PROPERTY(cx, aobj,
2385
+ ATOM_TO_JSID(cx->runtime->atomState
2386
+ .lengthAtom),
2387
+ &tvr.u.value);
2388
+ if (!ok)
2389
+ goto out;
2390
+ alength = ValueIsLength(cx, &tvr.u.value);
2391
+ ok = !JSVAL_IS_NULL(tvr.u.value);
2392
+ if (!ok)
2393
+ goto out;
2394
+ for (slot = 0; slot < alength; slot++) {
2395
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
2396
+ GetArrayElement(cx, aobj, slot, &hole,
2397
+ &tvr.u.value);
2398
+ if (!ok)
2399
+ goto out;
2400
+
2401
+ /*
2402
+ * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent
2403
+ * properties.
2404
+ */
2405
+ if (!hole) {
2406
+ ok = SetArrayElement(cx, nobj, length + slot,
2407
+ tvr.u.value);
2408
+ if (!ok)
2409
+ goto out;
2410
+ }
2411
+ }
2412
+ length += alength;
2413
+ continue;
2414
+ }
2415
+ }
2416
+
2417
+ ok = SetArrayElement(cx, nobj, length, v);
2418
+ if (!ok)
2419
+ goto out;
2420
+ length++;
2421
+ }
2422
+
2423
+ ok = js_SetLengthProperty(cx, nobj, length);
2424
+
2425
+ out:
2426
+ JS_POP_TEMP_ROOT(cx, &tvr);
2427
+ return ok;
2428
+ }
2429
+
2430
+ static JSBool
2431
+ array_slice(JSContext *cx, uintN argc, jsval *vp)
2432
+ {
2433
+ jsval *argv;
2434
+ JSObject *nobj, *obj;
2435
+ jsuint length, begin, end, slot;
2436
+ jsdouble d;
2437
+ JSBool hole, ok;
2438
+ JSTempValueRooter tvr;
2439
+
2440
+ argv = JS_ARGV(cx, vp);
2441
+
2442
+ obj = JS_THIS_OBJECT(cx, vp);
2443
+ if (!obj || !js_GetLengthProperty(cx, obj, &length))
2444
+ return JS_FALSE;
2445
+ begin = 0;
2446
+ end = length;
2447
+
2448
+ if (argc > 0) {
2449
+ d = js_ValueToNumber(cx, &argv[0]);
2450
+ if (JSVAL_IS_NULL(argv[0]))
2451
+ return JS_FALSE;
2452
+ d = js_DoubleToInteger(d);
2453
+ if (d < 0) {
2454
+ d += length;
2455
+ if (d < 0)
2456
+ d = 0;
2457
+ } else if (d > length) {
2458
+ d = length;
2459
+ }
2460
+ begin = (jsuint)d;
2461
+
2462
+ if (argc > 1) {
2463
+ d = js_ValueToNumber(cx, &argv[1]);
2464
+ if (JSVAL_IS_NULL(argv[1]))
2465
+ return JS_FALSE;
2466
+ d = js_DoubleToInteger(d);
2467
+ if (d < 0) {
2468
+ d += length;
2469
+ if (d < 0)
2470
+ d = 0;
2471
+ } else if (d > length) {
2472
+ d = length;
2473
+ }
2474
+ end = (jsuint)d;
2475
+ }
2476
+ }
2477
+
2478
+ if (begin > end)
2479
+ begin = end;
2480
+
2481
+ if (OBJ_IS_DENSE_ARRAY(cx, obj) && end <= ARRAY_DENSE_LENGTH(obj)) {
2482
+ nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin);
2483
+ if (!nobj)
2484
+ return JS_FALSE;
2485
+ *vp = OBJECT_TO_JSVAL(nobj);
2486
+ return JS_TRUE;
2487
+ }
2488
+
2489
+ /* Create a new Array object and root it using *vp. */
2490
+ nobj = js_NewArrayObject(cx, 0, NULL);
2491
+ if (!nobj)
2492
+ return JS_FALSE;
2493
+ *vp = OBJECT_TO_JSVAL(nobj);
2494
+
2495
+ /* After this, control must flow through label out: to exit. */
2496
+ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
2497
+
2498
+ for (slot = begin; slot < end; slot++) {
2499
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
2500
+ GetArrayElement(cx, obj, slot, &hole, &tvr.u.value);
2501
+ if (!ok)
2502
+ goto out;
2503
+ if (!hole) {
2504
+ ok = SetArrayElement(cx, nobj, slot - begin, tvr.u.value);
2505
+ if (!ok)
2506
+ goto out;
2507
+ }
2508
+ }
2509
+ ok = js_SetLengthProperty(cx, nobj, end - begin);
2510
+
2511
+ out:
2512
+ JS_POP_TEMP_ROOT(cx, &tvr);
2513
+ return ok;
2514
+ }
2515
+
2516
+ #if JS_HAS_ARRAY_EXTRAS
2517
+
2518
+ static JSBool
2519
+ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, jsval *vp)
2520
+ {
2521
+ JSObject *obj;
2522
+ jsuint length, i, stop;
2523
+ jsint direction;
2524
+ JSBool hole;
2525
+
2526
+ obj = JS_THIS_OBJECT(cx, vp);
2527
+ if (!obj || !js_GetLengthProperty(cx, obj, &length))
2528
+ return JS_FALSE;
2529
+ if (length == 0)
2530
+ goto not_found;
2531
+
2532
+ if (argc <= 1) {
2533
+ i = isLast ? length - 1 : 0;
2534
+ } else {
2535
+ jsdouble start;
2536
+
2537
+ start = js_ValueToNumber(cx, &vp[3]);
2538
+ if (JSVAL_IS_NULL(vp[3]))
2539
+ return JS_FALSE;
2540
+ start = js_DoubleToInteger(start);
2541
+ if (start < 0) {
2542
+ start += length;
2543
+ if (start < 0) {
2544
+ if (isLast)
2545
+ goto not_found;
2546
+ i = 0;
2547
+ } else {
2548
+ i = (jsuint)start;
2549
+ }
2550
+ } else if (start >= length) {
2551
+ if (!isLast)
2552
+ goto not_found;
2553
+ i = length - 1;
2554
+ } else {
2555
+ i = (jsuint)start;
2556
+ }
2557
+ }
2558
+
2559
+ if (isLast) {
2560
+ stop = 0;
2561
+ direction = -1;
2562
+ } else {
2563
+ stop = length - 1;
2564
+ direction = 1;
2565
+ }
2566
+
2567
+ for (;;) {
2568
+ if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
2569
+ !GetArrayElement(cx, obj, (jsuint)i, &hole, vp)) {
2570
+ return JS_FALSE;
2571
+ }
2572
+ if (!hole && js_StrictlyEqual(cx, *vp, vp[2]))
2573
+ return js_NewNumberInRootedValue(cx, i, vp);
2574
+ if (i == stop)
2575
+ goto not_found;
2576
+ i += direction;
2577
+ }
2578
+
2579
+ not_found:
2580
+ *vp = INT_TO_JSVAL(-1);
2581
+ return JS_TRUE;
2582
+ }
2583
+
2584
+ static JSBool
2585
+ array_indexOf(JSContext *cx, uintN argc, jsval *vp)
2586
+ {
2587
+ return array_indexOfHelper(cx, JS_FALSE, argc, vp);
2588
+ }
2589
+
2590
+ static JSBool
2591
+ array_lastIndexOf(JSContext *cx, uintN argc, jsval *vp)
2592
+ {
2593
+ return array_indexOfHelper(cx, JS_TRUE, argc, vp);
2594
+ }
2595
+
2596
+ /* Order is important; extras that take a predicate funarg must follow MAP. */
2597
+ typedef enum ArrayExtraMode {
2598
+ FOREACH,
2599
+ REDUCE,
2600
+ REDUCE_RIGHT,
2601
+ MAP,
2602
+ FILTER,
2603
+ SOME,
2604
+ EVERY
2605
+ } ArrayExtraMode;
2606
+
2607
+ #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
2608
+
2609
+ static JSBool
2610
+ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp)
2611
+ {
2612
+ JSObject *obj;
2613
+ jsuint length, newlen;
2614
+ jsval *argv, *elemroot, *invokevp, *sp;
2615
+ JSBool ok, cond, hole;
2616
+ JSObject *callable, *thisp, *newarr;
2617
+ jsint start, end, step, i;
2618
+ void *mark;
2619
+
2620
+ obj = JS_THIS_OBJECT(cx, vp);
2621
+ if (!obj || !js_GetLengthProperty(cx, obj, &length))
2622
+ return JS_FALSE;
2623
+
2624
+ /*
2625
+ * First, get or compute our callee, so that we error out consistently
2626
+ * when passed a non-callable object.
2627
+ */
2628
+ argv = vp + 2;
2629
+ callable = js_ValueToCallableObject(cx, &argv[0], JSV2F_SEARCH_STACK);
2630
+ if (!callable)
2631
+ return JS_FALSE;
2632
+
2633
+ /*
2634
+ * Set our initial return condition, used for zero-length array cases
2635
+ * (and pre-size our map return to match our known length, for all cases).
2636
+ */
2637
+ #ifdef __GNUC__ /* quell GCC overwarning */
2638
+ newlen = 0;
2639
+ newarr = NULL;
2640
+ #endif
2641
+ start = 0, end = length, step = 1;
2642
+
2643
+ switch (mode) {
2644
+ case REDUCE_RIGHT:
2645
+ start = length - 1, end = -1, step = -1;
2646
+ /* FALL THROUGH */
2647
+ case REDUCE:
2648
+ if (length == 0 && argc == 1) {
2649
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2650
+ JSMSG_EMPTY_ARRAY_REDUCE);
2651
+ return JS_FALSE;
2652
+ }
2653
+ if (argc >= 2) {
2654
+ *vp = argv[1];
2655
+ } else {
2656
+ do {
2657
+ if (!GetArrayElement(cx, obj, start, &hole, vp))
2658
+ return JS_FALSE;
2659
+ start += step;
2660
+ } while (hole && start != end);
2661
+
2662
+ if (hole && start == end) {
2663
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2664
+ JSMSG_EMPTY_ARRAY_REDUCE);
2665
+ return JS_FALSE;
2666
+ }
2667
+ }
2668
+ break;
2669
+ case MAP:
2670
+ case FILTER:
2671
+ newlen = (mode == MAP) ? length : 0;
2672
+ newarr = js_NewArrayObject(cx, newlen, NULL);
2673
+ if (!newarr)
2674
+ return JS_FALSE;
2675
+ *vp = OBJECT_TO_JSVAL(newarr);
2676
+ break;
2677
+ case SOME:
2678
+ *vp = JSVAL_FALSE;
2679
+ break;
2680
+ case EVERY:
2681
+ *vp = JSVAL_TRUE;
2682
+ break;
2683
+ case FOREACH:
2684
+ *vp = JSVAL_VOID;
2685
+ break;
2686
+ }
2687
+
2688
+ if (length == 0)
2689
+ return JS_TRUE;
2690
+
2691
+ if (argc > 1 && !REDUCE_MODE(mode)) {
2692
+ if (!js_ValueToObject(cx, argv[1], &thisp))
2693
+ return JS_FALSE;
2694
+ argv[1] = OBJECT_TO_JSVAL(thisp);
2695
+ } else {
2696
+ thisp = NULL;
2697
+ }
2698
+
2699
+ /*
2700
+ * For all but REDUCE, we call with 3 args (value, index, array). REDUCE
2701
+ * requires 4 args (accum, value, index, array).
2702
+ */
2703
+ argc = 3 + REDUCE_MODE(mode);
2704
+ elemroot = js_AllocStack(cx, 1 + 2 + argc, &mark);
2705
+ if (!elemroot)
2706
+ return JS_FALSE;
2707
+
2708
+ /* From this point the control must flow through out:. */
2709
+ ok = JS_TRUE;
2710
+ invokevp = elemroot + 1;
2711
+
2712
+ for (i = start; i != end; i += step) {
2713
+ ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
2714
+ GetArrayElement(cx, obj, i, &hole, elemroot);
2715
+ if (!ok)
2716
+ goto out;
2717
+ if (hole)
2718
+ continue;
2719
+
2720
+ /*
2721
+ * Push callable and 'this', then args. We must do this for every
2722
+ * iteration around the loop since js_Invoke uses spbase[0] for return
2723
+ * value storage, while some native functions use spbase[1] for local
2724
+ * rooting.
2725
+ */
2726
+ sp = invokevp;
2727
+ *sp++ = OBJECT_TO_JSVAL(callable);
2728
+ *sp++ = OBJECT_TO_JSVAL(thisp);
2729
+ if (REDUCE_MODE(mode))
2730
+ *sp++ = *vp;
2731
+ *sp++ = *elemroot;
2732
+ *sp++ = INT_TO_JSVAL(i);
2733
+ *sp++ = OBJECT_TO_JSVAL(obj);
2734
+
2735
+ /* Do the call. */
2736
+ ok = js_Invoke(cx, argc, invokevp, 0);
2737
+ if (!ok)
2738
+ break;
2739
+
2740
+ if (mode > MAP)
2741
+ cond = js_ValueToBoolean(*invokevp);
2742
+ #ifdef __GNUC__ /* quell GCC overwarning */
2743
+ else
2744
+ cond = JS_FALSE;
2745
+ #endif
2746
+
2747
+ switch (mode) {
2748
+ case FOREACH:
2749
+ break;
2750
+ case REDUCE:
2751
+ case REDUCE_RIGHT:
2752
+ *vp = *invokevp;
2753
+ break;
2754
+ case MAP:
2755
+ ok = SetArrayElement(cx, newarr, i, *invokevp);
2756
+ if (!ok)
2757
+ goto out;
2758
+ break;
2759
+ case FILTER:
2760
+ if (!cond)
2761
+ break;
2762
+ /* The filter passed *elemroot, so push it onto our result. */
2763
+ ok = SetArrayElement(cx, newarr, newlen++, *elemroot);
2764
+ if (!ok)
2765
+ goto out;
2766
+ break;
2767
+ case SOME:
2768
+ if (cond) {
2769
+ *vp = JSVAL_TRUE;
2770
+ goto out;
2771
+ }
2772
+ break;
2773
+ case EVERY:
2774
+ if (!cond) {
2775
+ *vp = JSVAL_FALSE;
2776
+ goto out;
2777
+ }
2778
+ break;
2779
+ }
2780
+ }
2781
+
2782
+ out:
2783
+ js_FreeStack(cx, mark);
2784
+ if (ok && mode == FILTER)
2785
+ ok = js_SetLengthProperty(cx, newarr, newlen);
2786
+ return ok;
2787
+ }
2788
+
2789
+ static JSBool
2790
+ array_forEach(JSContext *cx, uintN argc, jsval *vp)
2791
+ {
2792
+ return array_extra(cx, FOREACH, argc, vp);
2793
+ }
2794
+
2795
+ static JSBool
2796
+ array_map(JSContext *cx, uintN argc, jsval *vp)
2797
+ {
2798
+ return array_extra(cx, MAP, argc, vp);
2799
+ }
2800
+
2801
+ static JSBool
2802
+ array_reduce(JSContext *cx, uintN argc, jsval *vp)
2803
+ {
2804
+ return array_extra(cx, REDUCE, argc, vp);
2805
+ }
2806
+
2807
+ static JSBool
2808
+ array_reduceRight(JSContext *cx, uintN argc, jsval *vp)
2809
+ {
2810
+ return array_extra(cx, REDUCE_RIGHT, argc, vp);
2811
+ }
2812
+
2813
+ static JSBool
2814
+ array_filter(JSContext *cx, uintN argc, jsval *vp)
2815
+ {
2816
+ return array_extra(cx, FILTER, argc, vp);
2817
+ }
2818
+
2819
+ static JSBool
2820
+ array_some(JSContext *cx, uintN argc, jsval *vp)
2821
+ {
2822
+ return array_extra(cx, SOME, argc, vp);
2823
+ }
2824
+
2825
+ static JSBool
2826
+ array_every(JSContext *cx, uintN argc, jsval *vp)
2827
+ {
2828
+ return array_extra(cx, EVERY, argc, vp);
2829
+ }
2830
+ #endif
2831
+
2832
+ static JSPropertySpec array_props[] = {
2833
+ {js_length_str, -1, JSPROP_SHARED | JSPROP_PERMANENT,
2834
+ array_length_getter, array_length_setter},
2835
+ {0,0,0,0,0}
2836
+ };
2837
+
2838
+ static JSFunctionSpec array_methods[] = {
2839
+ #if JS_HAS_TOSOURCE
2840
+ JS_FN(js_toSource_str, array_toSource, 0,0,0),
2841
+ #endif
2842
+ JS_FN(js_toString_str, array_toString, 0,0,0),
2843
+ JS_FN(js_toLocaleString_str,array_toLocaleString,0,0,0),
2844
+
2845
+ /* Perl-ish methods. */
2846
+ JS_FN("join", array_join, 1,1,JSFUN_GENERIC_NATIVE),
2847
+ JS_FN("reverse", array_reverse, 0,0,JSFUN_GENERIC_NATIVE),
2848
+ JS_FN("sort", array_sort, 0,1,JSFUN_GENERIC_NATIVE),
2849
+ JS_FN("push", array_push, 1,1,JSFUN_GENERIC_NATIVE),
2850
+ JS_FN("pop", array_pop, 0,0,JSFUN_GENERIC_NATIVE),
2851
+ JS_FN("shift", array_shift, 0,0,JSFUN_GENERIC_NATIVE),
2852
+ JS_FN("unshift", array_unshift, 0,1,JSFUN_GENERIC_NATIVE),
2853
+ JS_FN("splice", array_splice, 0,2,JSFUN_GENERIC_NATIVE),
2854
+
2855
+ /* Pythonic sequence methods. */
2856
+ JS_FN("concat", array_concat, 0,1,JSFUN_GENERIC_NATIVE),
2857
+ JS_FN("slice", array_slice, 0,2,JSFUN_GENERIC_NATIVE),
2858
+
2859
+ #if JS_HAS_ARRAY_EXTRAS
2860
+ JS_FN("indexOf", array_indexOf, 1,1,JSFUN_GENERIC_NATIVE),
2861
+ JS_FN("lastIndexOf", array_lastIndexOf, 1,1,JSFUN_GENERIC_NATIVE),
2862
+ JS_FN("forEach", array_forEach, 1,1,JSFUN_GENERIC_NATIVE),
2863
+ JS_FN("map", array_map, 1,1,JSFUN_GENERIC_NATIVE),
2864
+ JS_FN("reduce", array_reduce, 1,1,JSFUN_GENERIC_NATIVE),
2865
+ JS_FN("reduceRight", array_reduceRight, 1,1,JSFUN_GENERIC_NATIVE),
2866
+ JS_FN("filter", array_filter, 1,1,JSFUN_GENERIC_NATIVE),
2867
+ JS_FN("some", array_some, 1,1,JSFUN_GENERIC_NATIVE),
2868
+ JS_FN("every", array_every, 1,1,JSFUN_GENERIC_NATIVE),
2869
+ #endif
2870
+
2871
+ JS_FS_END
2872
+ };
2873
+
2874
+ static JSBool
2875
+ Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2876
+ {
2877
+ jsuint length;
2878
+ jsval *vector;
2879
+
2880
+ /* If called without new, replace obj with a new Array object. */
2881
+ if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
2882
+ obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0);
2883
+ if (!obj)
2884
+ return JS_FALSE;
2885
+ *rval = OBJECT_TO_JSVAL(obj);
2886
+ }
2887
+
2888
+ if (argc == 0) {
2889
+ length = 0;
2890
+ vector = NULL;
2891
+ } else if (argc > 1) {
2892
+ length = (jsuint) argc;
2893
+ vector = argv;
2894
+ } else if (!JSVAL_IS_NUMBER(argv[0])) {
2895
+ length = 1;
2896
+ vector = argv;
2897
+ } else {
2898
+ length = ValueIsLength(cx, &argv[0]);
2899
+ if (JSVAL_IS_NULL(argv[0]))
2900
+ return JS_FALSE;
2901
+ vector = NULL;
2902
+ }
2903
+ return InitArrayObject(cx, obj, length, vector);
2904
+ }
2905
+
2906
+ JSObject *
2907
+ js_InitArrayClass(JSContext *cx, JSObject *obj)
2908
+ {
2909
+ JSObject *proto;
2910
+
2911
+ /* Initialize the ops structure used by slow arrays */
2912
+ memcpy(&js_SlowArrayObjectOps, &js_ObjectOps, sizeof(JSObjectOps));
2913
+ js_SlowArrayObjectOps.trace = slowarray_trace;
2914
+ js_SlowArrayObjectOps.enumerate = slowarray_enumerate;
2915
+ js_SlowArrayObjectOps.call = NULL;
2916
+
2917
+ proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1,
2918
+ array_props, array_methods, NULL, NULL);
2919
+
2920
+ /* Initialize the Array prototype object so it gets a length property. */
2921
+ if (!proto || !InitArrayObject(cx, proto, 0, NULL))
2922
+ return NULL;
2923
+ return proto;
2924
+ }
2925
+
2926
+ JSObject *
2927
+ js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector)
2928
+ {
2929
+ JSTempValueRooter tvr;
2930
+ JSObject *obj;
2931
+
2932
+ obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0);
2933
+ if (!obj)
2934
+ return NULL;
2935
+
2936
+ JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
2937
+ if (!InitArrayObject(cx, obj, length, vector))
2938
+ obj = NULL;
2939
+ JS_POP_TEMP_ROOT(cx, &tvr);
2940
+
2941
+ /* Set/clear newborn root, in case we lost it. */
2942
+ cx->weakRoots.newborn[GCX_OBJECT] = obj;
2943
+ return obj;
2944
+ }
2945
+
2946
+ JSObject *
2947
+ js_NewSlowArrayObject(JSContext *cx)
2948
+ {
2949
+ JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL, 0);
2950
+ if (obj)
2951
+ obj->fslots[JSSLOT_ARRAY_LENGTH] = 0;
2952
+ return obj;
2953
+ }
2954
+
2955
+ #ifdef DEBUG_ARRAYS
2956
+ JSBool
2957
+ js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2958
+ {
2959
+ uintN i;
2960
+ JSObject *array;
2961
+
2962
+ for (i = 0; i < argc; i++) {
2963
+ char *bytes;
2964
+
2965
+ bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[i],
2966
+ NULL);
2967
+ if (!bytes)
2968
+ return JS_FALSE;
2969
+ if (JSVAL_IS_PRIMITIVE(argv[i]) ||
2970
+ !OBJ_IS_ARRAY(cx, (array = JSVAL_TO_OBJECT(argv[i])))) {
2971
+ fprintf(stderr, "%s: not array\n", bytes);
2972
+ JS_free(cx, bytes);
2973
+ continue;
2974
+ }
2975
+ fprintf(stderr, "%s: %s (len %lu", bytes,
2976
+ OBJ_IS_DENSE_ARRAY(cx, array) ? "dense" : "sparse",
2977
+ array->fslots[JSSLOT_ARRAY_LENGTH]);
2978
+ if (OBJ_IS_DENSE_ARRAY(cx, array)) {
2979
+ fprintf(stderr, ", count %lu, denselen %lu",
2980
+ array->fslots[JSSLOT_ARRAY_COUNT],
2981
+ ARRAY_DENSE_LENGTH(array));
2982
+ }
2983
+ fputs(")\n", stderr);
2984
+ JS_free(cx, bytes);
2985
+ }
2986
+ return JS_TRUE;
2987
+ }
2988
+ #endif