johnson 2.0.0.pre1 → 2.0.0.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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