johnson 2.0.0.pre1 → 2.0.0.pre2

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