jbarnette-johnson 1.0.0.200806240111 → 1.0.0.200807291507

Sign up to get free protection for your applications and to get access to all the features.
Files changed (269) hide show
  1. data/MANIFEST +1 -0
  2. data/Rakefile +3 -10
  3. data/bin/johnson +2 -1
  4. data/ext/spidermonkey/context.c +3 -4
  5. data/ext/spidermonkey/context.h +1 -1
  6. data/ext/spidermonkey/conversions.c +39 -33
  7. data/ext/spidermonkey/debugger.c +5 -5
  8. data/ext/spidermonkey/immutable_node.c.erb +11 -11
  9. data/ext/spidermonkey/jroot.h +4 -4
  10. data/ext/spidermonkey/js_land_proxy.c +9 -8
  11. data/ext/spidermonkey/ruby_land_proxy.c +5 -4
  12. data/ext/spidermonkey/runtime.c +1 -1
  13. data/johnson.gemspec +36 -0
  14. data/lib/hoe.rb +0 -7
  15. data/lib/johnson/cli/options.rb +10 -4
  16. data/lib/johnson/spidermonkey/runtime.rb +2 -2
  17. data/lib/johnson/version.rb +4 -2
  18. data/lib/johnson.rb +1 -0
  19. data/test/johnson/runtime_test.rb +11 -0
  20. data/test/johnson/spidermonkey/ruby_land_proxy_test.rb +6 -0
  21. data/vendor/spidermonkey/.cvsignore +9 -0
  22. data/vendor/spidermonkey/Makefile.in +462 -0
  23. data/vendor/spidermonkey/Makefile.ref +364 -0
  24. data/vendor/spidermonkey/README.html +820 -0
  25. data/vendor/spidermonkey/SpiderMonkey.rsp +12 -0
  26. data/vendor/spidermonkey/Y.js +19 -0
  27. data/vendor/spidermonkey/build.mk +43 -0
  28. data/vendor/spidermonkey/config/AIX4.1.mk +65 -0
  29. data/vendor/spidermonkey/config/AIX4.2.mk +64 -0
  30. data/vendor/spidermonkey/config/AIX4.3.mk +65 -0
  31. data/vendor/spidermonkey/config/Darwin.mk +83 -0
  32. data/vendor/spidermonkey/config/Darwin1.3.mk +81 -0
  33. data/vendor/spidermonkey/config/Darwin1.4.mk +41 -0
  34. data/vendor/spidermonkey/config/Darwin5.2.mk +81 -0
  35. data/vendor/spidermonkey/config/Darwin5.3.mk +81 -0
  36. data/vendor/spidermonkey/config/HP-UXB.10.10.mk +77 -0
  37. data/vendor/spidermonkey/config/HP-UXB.10.20.mk +77 -0
  38. data/vendor/spidermonkey/config/HP-UXB.11.00.mk +80 -0
  39. data/vendor/spidermonkey/config/IRIX.mk +87 -0
  40. data/vendor/spidermonkey/config/IRIX5.3.mk +44 -0
  41. data/vendor/spidermonkey/config/IRIX6.1.mk +44 -0
  42. data/vendor/spidermonkey/config/IRIX6.2.mk +44 -0
  43. data/vendor/spidermonkey/config/IRIX6.3.mk +44 -0
  44. data/vendor/spidermonkey/config/IRIX6.5.mk +44 -0
  45. data/vendor/spidermonkey/config/Linux_All.mk +103 -0
  46. data/vendor/spidermonkey/config/Mac_OS10.0.mk +82 -0
  47. data/vendor/spidermonkey/config/OSF1V4.0.mk +72 -0
  48. data/vendor/spidermonkey/config/OSF1V5.0.mk +69 -0
  49. data/vendor/spidermonkey/config/SunOS4.1.4.mk +101 -0
  50. data/vendor/spidermonkey/config/SunOS5.10.mk +50 -0
  51. data/vendor/spidermonkey/config/SunOS5.3.mk +91 -0
  52. data/vendor/spidermonkey/config/SunOS5.4.mk +92 -0
  53. data/vendor/spidermonkey/config/SunOS5.5.1.mk +44 -0
  54. data/vendor/spidermonkey/config/SunOS5.5.mk +87 -0
  55. data/vendor/spidermonkey/config/SunOS5.6.mk +89 -0
  56. data/vendor/spidermonkey/config/SunOS5.7.mk +44 -0
  57. data/vendor/spidermonkey/config/SunOS5.8.mk +44 -0
  58. data/vendor/spidermonkey/config/SunOS5.9.mk +44 -0
  59. data/vendor/spidermonkey/config/WINNT4.0.mk +117 -0
  60. data/vendor/spidermonkey/config/WINNT5.0.mk +117 -0
  61. data/vendor/spidermonkey/config/WINNT5.1.mk +117 -0
  62. data/vendor/spidermonkey/config/WINNT5.2.mk +117 -0
  63. data/vendor/spidermonkey/config/WINNT6.0.mk +117 -0
  64. data/vendor/spidermonkey/config/dgux.mk +64 -0
  65. data/vendor/spidermonkey/config.mk +192 -0
  66. data/vendor/spidermonkey/editline/Makefile.ref +144 -0
  67. data/vendor/spidermonkey/editline/README +83 -0
  68. data/vendor/spidermonkey/editline/editline.3 +175 -0
  69. data/vendor/spidermonkey/editline/editline.c +1369 -0
  70. data/vendor/spidermonkey/editline/editline.h +135 -0
  71. data/vendor/spidermonkey/editline/sysunix.c +182 -0
  72. data/vendor/spidermonkey/editline/unix.h +82 -0
  73. data/vendor/spidermonkey/fdlibm/.cvsignore +7 -0
  74. data/vendor/spidermonkey/fdlibm/Makefile.in +127 -0
  75. data/vendor/spidermonkey/fdlibm/Makefile.ref +192 -0
  76. data/vendor/spidermonkey/fdlibm/e_acos.c +147 -0
  77. data/vendor/spidermonkey/fdlibm/e_acosh.c +105 -0
  78. data/vendor/spidermonkey/fdlibm/e_asin.c +156 -0
  79. data/vendor/spidermonkey/fdlibm/e_atan2.c +165 -0
  80. data/vendor/spidermonkey/fdlibm/e_atanh.c +110 -0
  81. data/vendor/spidermonkey/fdlibm/e_cosh.c +133 -0
  82. data/vendor/spidermonkey/fdlibm/e_exp.c +202 -0
  83. data/vendor/spidermonkey/fdlibm/e_fmod.c +184 -0
  84. data/vendor/spidermonkey/fdlibm/e_gamma.c +71 -0
  85. data/vendor/spidermonkey/fdlibm/e_gamma_r.c +70 -0
  86. data/vendor/spidermonkey/fdlibm/e_hypot.c +173 -0
  87. data/vendor/spidermonkey/fdlibm/e_j0.c +524 -0
  88. data/vendor/spidermonkey/fdlibm/e_j1.c +523 -0
  89. data/vendor/spidermonkey/fdlibm/e_jn.c +315 -0
  90. data/vendor/spidermonkey/fdlibm/e_lgamma.c +71 -0
  91. data/vendor/spidermonkey/fdlibm/e_lgamma_r.c +347 -0
  92. data/vendor/spidermonkey/fdlibm/e_log.c +184 -0
  93. data/vendor/spidermonkey/fdlibm/e_log10.c +134 -0
  94. data/vendor/spidermonkey/fdlibm/e_pow.c +386 -0
  95. data/vendor/spidermonkey/fdlibm/e_rem_pio2.c +222 -0
  96. data/vendor/spidermonkey/fdlibm/e_remainder.c +120 -0
  97. data/vendor/spidermonkey/fdlibm/e_scalb.c +89 -0
  98. data/vendor/spidermonkey/fdlibm/e_sinh.c +122 -0
  99. data/vendor/spidermonkey/fdlibm/e_sqrt.c +497 -0
  100. data/vendor/spidermonkey/fdlibm/fdlibm.h +273 -0
  101. data/vendor/spidermonkey/fdlibm/fdlibm.mak +1453 -0
  102. data/vendor/spidermonkey/fdlibm/fdlibm.mdp +0 -0
  103. data/vendor/spidermonkey/fdlibm/k_cos.c +135 -0
  104. data/vendor/spidermonkey/fdlibm/k_rem_pio2.c +354 -0
  105. data/vendor/spidermonkey/fdlibm/k_sin.c +114 -0
  106. data/vendor/spidermonkey/fdlibm/k_standard.c +785 -0
  107. data/vendor/spidermonkey/fdlibm/k_tan.c +170 -0
  108. data/vendor/spidermonkey/fdlibm/s_asinh.c +101 -0
  109. data/vendor/spidermonkey/fdlibm/s_atan.c +175 -0
  110. data/vendor/spidermonkey/fdlibm/s_cbrt.c +133 -0
  111. data/vendor/spidermonkey/fdlibm/s_ceil.c +120 -0
  112. data/vendor/spidermonkey/fdlibm/s_copysign.c +72 -0
  113. data/vendor/spidermonkey/fdlibm/s_cos.c +118 -0
  114. data/vendor/spidermonkey/fdlibm/s_erf.c +356 -0
  115. data/vendor/spidermonkey/fdlibm/s_expm1.c +267 -0
  116. data/vendor/spidermonkey/fdlibm/s_fabs.c +70 -0
  117. data/vendor/spidermonkey/fdlibm/s_finite.c +71 -0
  118. data/vendor/spidermonkey/fdlibm/s_floor.c +121 -0
  119. data/vendor/spidermonkey/fdlibm/s_frexp.c +99 -0
  120. data/vendor/spidermonkey/fdlibm/s_ilogb.c +85 -0
  121. data/vendor/spidermonkey/fdlibm/s_isnan.c +74 -0
  122. data/vendor/spidermonkey/fdlibm/s_ldexp.c +66 -0
  123. data/vendor/spidermonkey/fdlibm/s_lib_version.c +73 -0
  124. data/vendor/spidermonkey/fdlibm/s_log1p.c +211 -0
  125. data/vendor/spidermonkey/fdlibm/s_logb.c +79 -0
  126. data/vendor/spidermonkey/fdlibm/s_matherr.c +64 -0
  127. data/vendor/spidermonkey/fdlibm/s_modf.c +132 -0
  128. data/vendor/spidermonkey/fdlibm/s_nextafter.c +124 -0
  129. data/vendor/spidermonkey/fdlibm/s_rint.c +131 -0
  130. data/vendor/spidermonkey/fdlibm/s_scalbn.c +107 -0
  131. data/vendor/spidermonkey/fdlibm/s_signgam.c +40 -0
  132. data/vendor/spidermonkey/fdlibm/s_significand.c +68 -0
  133. data/vendor/spidermonkey/fdlibm/s_sin.c +118 -0
  134. data/vendor/spidermonkey/fdlibm/s_tan.c +112 -0
  135. data/vendor/spidermonkey/fdlibm/s_tanh.c +122 -0
  136. data/vendor/spidermonkey/fdlibm/w_acos.c +78 -0
  137. data/vendor/spidermonkey/fdlibm/w_acosh.c +78 -0
  138. data/vendor/spidermonkey/fdlibm/w_asin.c +80 -0
  139. data/vendor/spidermonkey/fdlibm/w_atan2.c +79 -0
  140. data/vendor/spidermonkey/fdlibm/w_atanh.c +81 -0
  141. data/vendor/spidermonkey/fdlibm/w_cosh.c +77 -0
  142. data/vendor/spidermonkey/fdlibm/w_exp.c +88 -0
  143. data/vendor/spidermonkey/fdlibm/w_fmod.c +78 -0
  144. data/vendor/spidermonkey/fdlibm/w_gamma.c +85 -0
  145. data/vendor/spidermonkey/fdlibm/w_gamma_r.c +81 -0
  146. data/vendor/spidermonkey/fdlibm/w_hypot.c +78 -0
  147. data/vendor/spidermonkey/fdlibm/w_j0.c +105 -0
  148. data/vendor/spidermonkey/fdlibm/w_j1.c +106 -0
  149. data/vendor/spidermonkey/fdlibm/w_jn.c +128 -0
  150. data/vendor/spidermonkey/fdlibm/w_lgamma.c +85 -0
  151. data/vendor/spidermonkey/fdlibm/w_lgamma_r.c +81 -0
  152. data/vendor/spidermonkey/fdlibm/w_log.c +78 -0
  153. data/vendor/spidermonkey/fdlibm/w_log10.c +81 -0
  154. data/vendor/spidermonkey/fdlibm/w_pow.c +99 -0
  155. data/vendor/spidermonkey/fdlibm/w_remainder.c +77 -0
  156. data/vendor/spidermonkey/fdlibm/w_scalb.c +95 -0
  157. data/vendor/spidermonkey/fdlibm/w_sinh.c +77 -0
  158. data/vendor/spidermonkey/fdlibm/w_sqrt.c +77 -0
  159. data/vendor/spidermonkey/javascript-trace.d +73 -0
  160. data/vendor/spidermonkey/js.c +3951 -0
  161. data/vendor/spidermonkey/js.mak +4438 -0
  162. data/vendor/spidermonkey/js.mdp +0 -0
  163. data/vendor/spidermonkey/js.msg +307 -0
  164. data/vendor/spidermonkey/js.pkg +2 -0
  165. data/vendor/spidermonkey/js3240.rc +79 -0
  166. data/vendor/spidermonkey/jsOS240.def +654 -0
  167. data/vendor/spidermonkey/jsapi.c +5836 -0
  168. data/vendor/spidermonkey/jsapi.h +2624 -0
  169. data/vendor/spidermonkey/jsarena.c +450 -0
  170. data/vendor/spidermonkey/jsarena.h +318 -0
  171. data/vendor/spidermonkey/jsarray.c +2988 -0
  172. data/vendor/spidermonkey/jsarray.h +124 -0
  173. data/vendor/spidermonkey/jsatom.c +1045 -0
  174. data/vendor/spidermonkey/jsatom.h +442 -0
  175. data/vendor/spidermonkey/jsbit.h +253 -0
  176. data/vendor/spidermonkey/jsbool.c +176 -0
  177. data/vendor/spidermonkey/jsbool.h +73 -0
  178. data/vendor/spidermonkey/jsclist.h +139 -0
  179. data/vendor/spidermonkey/jscntxt.c +1348 -0
  180. data/vendor/spidermonkey/jscntxt.h +1120 -0
  181. data/vendor/spidermonkey/jscompat.h +57 -0
  182. data/vendor/spidermonkey/jsconfig.h +248 -0
  183. data/vendor/spidermonkey/jsconfig.mk +181 -0
  184. data/vendor/spidermonkey/jscpucfg.c +383 -0
  185. data/vendor/spidermonkey/jscpucfg.h +212 -0
  186. data/vendor/spidermonkey/jsdate.c +2398 -0
  187. data/vendor/spidermonkey/jsdate.h +124 -0
  188. data/vendor/spidermonkey/jsdbgapi.c +1799 -0
  189. data/vendor/spidermonkey/jsdbgapi.h +464 -0
  190. data/vendor/spidermonkey/jsdhash.c +868 -0
  191. data/vendor/spidermonkey/jsdhash.h +592 -0
  192. data/vendor/spidermonkey/jsdtoa.c +3167 -0
  193. data/vendor/spidermonkey/jsdtoa.h +130 -0
  194. data/vendor/spidermonkey/jsdtracef.c +317 -0
  195. data/vendor/spidermonkey/jsdtracef.h +77 -0
  196. data/vendor/spidermonkey/jsemit.c +6909 -0
  197. data/vendor/spidermonkey/jsemit.h +741 -0
  198. data/vendor/spidermonkey/jsexn.c +1371 -0
  199. data/vendor/spidermonkey/jsexn.h +96 -0
  200. data/vendor/spidermonkey/jsfile.c +2736 -0
  201. data/vendor/spidermonkey/jsfile.h +56 -0
  202. data/vendor/spidermonkey/jsfile.msg +90 -0
  203. data/vendor/spidermonkey/jsfun.c +2634 -0
  204. data/vendor/spidermonkey/jsfun.h +254 -0
  205. data/vendor/spidermonkey/jsgc.c +3554 -0
  206. data/vendor/spidermonkey/jsgc.h +403 -0
  207. data/vendor/spidermonkey/jshash.c +476 -0
  208. data/vendor/spidermonkey/jshash.h +151 -0
  209. data/vendor/spidermonkey/jsify.pl +485 -0
  210. data/vendor/spidermonkey/jsinterp.c +6981 -0
  211. data/vendor/spidermonkey/jsinterp.h +521 -0
  212. data/vendor/spidermonkey/jsinvoke.c +43 -0
  213. data/vendor/spidermonkey/jsiter.c +1067 -0
  214. data/vendor/spidermonkey/jsiter.h +122 -0
  215. data/vendor/spidermonkey/jskeyword.tbl +124 -0
  216. data/vendor/spidermonkey/jskwgen.c +460 -0
  217. data/vendor/spidermonkey/jslibmath.h +266 -0
  218. data/vendor/spidermonkey/jslock.c +1309 -0
  219. data/vendor/spidermonkey/jslock.h +313 -0
  220. data/vendor/spidermonkey/jslocko.asm +60 -0
  221. data/vendor/spidermonkey/jslog2.c +94 -0
  222. data/vendor/spidermonkey/jslong.c +264 -0
  223. data/vendor/spidermonkey/jslong.h +412 -0
  224. data/vendor/spidermonkey/jsmath.c +568 -0
  225. data/vendor/spidermonkey/jsmath.h +57 -0
  226. data/vendor/spidermonkey/jsnum.c +1228 -0
  227. data/vendor/spidermonkey/jsnum.h +283 -0
  228. data/vendor/spidermonkey/jsobj.c +5266 -0
  229. data/vendor/spidermonkey/jsobj.h +709 -0
  230. data/vendor/spidermonkey/jsopcode.c +5245 -0
  231. data/vendor/spidermonkey/jsopcode.h +394 -0
  232. data/vendor/spidermonkey/jsopcode.tbl +523 -0
  233. data/vendor/spidermonkey/jsotypes.h +202 -0
  234. data/vendor/spidermonkey/jsparse.c +6680 -0
  235. data/vendor/spidermonkey/jsparse.h +511 -0
  236. data/vendor/spidermonkey/jsprf.c +1262 -0
  237. data/vendor/spidermonkey/jsprf.h +150 -0
  238. data/vendor/spidermonkey/jsproto.tbl +128 -0
  239. data/vendor/spidermonkey/jsprvtd.h +267 -0
  240. data/vendor/spidermonkey/jspubtd.h +744 -0
  241. data/vendor/spidermonkey/jsregexp.c +4352 -0
  242. data/vendor/spidermonkey/jsregexp.h +183 -0
  243. data/vendor/spidermonkey/jsreops.tbl +145 -0
  244. data/vendor/spidermonkey/jsscan.c +2003 -0
  245. data/vendor/spidermonkey/jsscan.h +387 -0
  246. data/vendor/spidermonkey/jsscope.c +1948 -0
  247. data/vendor/spidermonkey/jsscope.h +418 -0
  248. data/vendor/spidermonkey/jsscript.c +1832 -0
  249. data/vendor/spidermonkey/jsscript.h +287 -0
  250. data/vendor/spidermonkey/jsshell.msg +50 -0
  251. data/vendor/spidermonkey/jsstddef.h +83 -0
  252. data/vendor/spidermonkey/jsstr.c +5004 -0
  253. data/vendor/spidermonkey/jsstr.h +641 -0
  254. data/vendor/spidermonkey/jstypes.h +475 -0
  255. data/vendor/spidermonkey/jsutil.c +345 -0
  256. data/vendor/spidermonkey/jsutil.h +157 -0
  257. data/vendor/spidermonkey/jsxdrapi.c +800 -0
  258. data/vendor/spidermonkey/jsxdrapi.h +218 -0
  259. data/vendor/spidermonkey/jsxml.c +8471 -0
  260. data/vendor/spidermonkey/jsxml.h +349 -0
  261. data/vendor/spidermonkey/lock_SunOS.s +119 -0
  262. data/vendor/spidermonkey/perfect.js +39 -0
  263. data/vendor/spidermonkey/plify_jsdhash.sed +36 -0
  264. data/vendor/spidermonkey/prmjtime.c +846 -0
  265. data/vendor/spidermonkey/prmjtime.h +103 -0
  266. data/vendor/spidermonkey/resource.h +15 -0
  267. data/vendor/spidermonkey/rules.mk +197 -0
  268. data/vendor/spidermonkey/win32.order +384 -0
  269. metadata +4 -3
@@ -0,0 +1,254 @@
1
+ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
+ *
3
+ * ***** BEGIN LICENSE BLOCK *****
4
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
+ *
6
+ * The contents of this file are subject to the Mozilla Public License Version
7
+ * 1.1 (the "License"); you may not use this file except in compliance with
8
+ * the License. You may obtain a copy of the License at
9
+ * http://www.mozilla.org/MPL/
10
+ *
11
+ * Software distributed under the License is distributed on an "AS IS" basis,
12
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
+ * for the specific language governing rights and limitations under the
14
+ * License.
15
+ *
16
+ * The Original Code is Mozilla Communicator client code, released
17
+ * March 31, 1998.
18
+ *
19
+ * The Initial Developer of the Original Code is
20
+ * Netscape Communications Corporation.
21
+ * Portions created by the Initial Developer are Copyright (C) 1998
22
+ * the Initial Developer. All Rights Reserved.
23
+ *
24
+ * Contributor(s):
25
+ *
26
+ * Alternatively, the contents of this file may be used under the terms of
27
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
28
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29
+ * in which case the provisions of the GPL or the LGPL are applicable instead
30
+ * of those above. If you wish to allow use of your version of this file only
31
+ * under the terms of either the GPL or the LGPL, and not to allow others to
32
+ * use your version of this file under the terms of the MPL, indicate your
33
+ * decision by deleting the provisions above and replace them with the notice
34
+ * and other provisions required by the GPL or the LGPL. If you do not delete
35
+ * the provisions above, a recipient may use your version of this file under
36
+ * the terms of any one of the MPL, the GPL or the LGPL.
37
+ *
38
+ * ***** END LICENSE BLOCK ***** */
39
+
40
+ #ifndef jsfun_h___
41
+ #define jsfun_h___
42
+ /*
43
+ * JS function definitions.
44
+ */
45
+ #include "jsprvtd.h"
46
+ #include "jspubtd.h"
47
+ #include "jsobj.h"
48
+
49
+ JS_BEGIN_EXTERN_C
50
+
51
+ typedef struct JSLocalNameMap JSLocalNameMap;
52
+
53
+ /*
54
+ * Depending on the number of arguments and variables in the function their
55
+ * names and attributes are stored either as a single atom or as an array of
56
+ * tagged atoms (when there are few locals) or as a hash-based map (when there
57
+ * are many locals). In the first 2 cases the lowest bit of the atom is used
58
+ * as a tag to distinguish const from var. See jsfun.c for details.
59
+ */
60
+ typedef union JSLocalNames {
61
+ jsuword taggedAtom;
62
+ jsuword *array;
63
+ JSLocalNameMap *map;
64
+ } JSLocalNames;
65
+
66
+ struct JSFunction {
67
+ JSObject object; /* GC'ed object header */
68
+ uint16 nargs; /* maximum number of specified arguments,
69
+ reflected as f.length/f.arity */
70
+ uint16 flags; /* bound method and other flags, see jsapi.h */
71
+ union {
72
+ struct {
73
+ uint16 extra; /* number of arg slots for local GC roots */
74
+ uint16 minargs;/* minimum number of specified arguments, used
75
+ only when calling fast native */
76
+ JSNative native; /* native method pointer or null */
77
+ JSClass *clasp; /* if non-null, constructor for this class */
78
+ } n;
79
+ struct {
80
+ uint16 nvars; /* number of local variables */
81
+ uint16 spare; /* reserved for future use */
82
+ JSScript *script;/* interpreted bytecode descriptor or null */
83
+ JSLocalNames names; /* argument and variable names */
84
+ } i;
85
+ } u;
86
+ JSAtom *atom; /* name for diagnostics and decompiling */
87
+ };
88
+
89
+ #define JSFUN_EXPR_CLOSURE 0x4000 /* expression closure: function(x)x*x */
90
+ #define JSFUN_INTERPRETED 0x8000 /* use u.i if set, u.n if unset */
91
+
92
+ #define JSFUN_SCRIPT_OR_FAST_NATIVE (JSFUN_INTERPRETED | JSFUN_FAST_NATIVE)
93
+
94
+ #define FUN_OBJECT(fun) (&(fun)->object)
95
+ #define FUN_INTERPRETED(fun) ((fun)->flags & JSFUN_INTERPRETED)
96
+ #define FUN_SLOW_NATIVE(fun) (!((fun)->flags & JSFUN_SCRIPT_OR_FAST_NATIVE))
97
+ #define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL)
98
+ #define FUN_NATIVE(fun) (FUN_SLOW_NATIVE(fun) ? (fun)->u.n.native : NULL)
99
+ #define FUN_FAST_NATIVE(fun) (((fun)->flags & JSFUN_FAST_NATIVE) \
100
+ ? (JSFastNative) (fun)->u.n.native \
101
+ : NULL)
102
+ #define FUN_MINARGS(fun) (((fun)->flags & JSFUN_FAST_NATIVE) \
103
+ ? (fun)->u.n.minargs \
104
+ : (fun)->nargs)
105
+
106
+ extern JSClass js_ArgumentsClass;
107
+ extern JS_FRIEND_DATA(JSClass) js_CallClass;
108
+
109
+ /* JS_FRIEND_DATA so that VALUE_IS_FUNCTION is callable from the shell. */
110
+ extern JS_FRIEND_DATA(JSClass) js_FunctionClass;
111
+
112
+ #define HAS_FUNCTION_CLASS(obj) (STOBJ_GET_CLASS(obj) == &js_FunctionClass)
113
+
114
+ /*
115
+ * NB: jsapi.h and jsobj.h must be included before any call to this macro.
116
+ */
117
+ #define VALUE_IS_FUNCTION(cx, v) \
118
+ (!JSVAL_IS_PRIMITIVE(v) && HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v)))
119
+
120
+ /*
121
+ * Macro to access the private slot of the function object after the slot is
122
+ * initialized.
123
+ */
124
+ #define GET_FUNCTION_PRIVATE(cx, funobj) \
125
+ (JS_ASSERT(HAS_FUNCTION_CLASS(funobj)), \
126
+ (JSFunction *) OBJ_GET_PRIVATE(cx, funobj))
127
+
128
+ extern JSObject *
129
+ js_InitFunctionClass(JSContext *cx, JSObject *obj);
130
+
131
+ extern JSObject *
132
+ js_InitArgumentsClass(JSContext *cx, JSObject *obj);
133
+
134
+ extern JSObject *
135
+ js_InitCallClass(JSContext *cx, JSObject *obj);
136
+
137
+ extern JSFunction *
138
+ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
139
+ uintN flags, JSObject *parent, JSAtom *atom);
140
+
141
+ extern void
142
+ js_TraceFunction(JSTracer *trc, JSFunction *fun);
143
+
144
+ extern void
145
+ js_FinalizeFunction(JSContext *cx, JSFunction *fun);
146
+
147
+ extern JSObject *
148
+ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent);
149
+
150
+ extern JSBool
151
+ js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object);
152
+
153
+ extern JSFunction *
154
+ js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
155
+ uintN nargs, uintN flags);
156
+
157
+ /*
158
+ * Flags for js_ValueToFunction and js_ReportIsNotFunction. We depend on the
159
+ * fact that JSINVOKE_CONSTRUCT (aka JSFRAME_CONSTRUCTING) is 1, and test that
160
+ * with #if/#error in jsfun.c.
161
+ */
162
+ #define JSV2F_CONSTRUCT JSINVOKE_CONSTRUCT
163
+ #define JSV2F_ITERATOR JSINVOKE_ITERATOR
164
+ #define JSV2F_SEARCH_STACK 0x10000
165
+
166
+ extern JSFunction *
167
+ js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags);
168
+
169
+ extern JSObject *
170
+ js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags);
171
+
172
+ extern JSObject *
173
+ js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags);
174
+
175
+ extern void
176
+ js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags);
177
+
178
+ extern JSObject *
179
+ js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent);
180
+
181
+ extern JS_FRIEND_API(JSBool)
182
+ js_PutCallObject(JSContext *cx, JSStackFrame *fp);
183
+
184
+ extern JSBool
185
+ js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
186
+
187
+ extern JSBool
188
+ js_GetCallVar(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
189
+
190
+ extern JSBool
191
+ js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp);
192
+
193
+ extern JSBool
194
+ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp);
195
+
196
+ extern JSObject *
197
+ js_GetArgsObject(JSContext *cx, JSStackFrame *fp);
198
+
199
+ extern JS_FRIEND_API(JSBool)
200
+ js_PutArgsObject(JSContext *cx, JSStackFrame *fp);
201
+
202
+ extern JSBool
203
+ js_XDRFunction(JSXDRState *xdr, JSObject **objp);
204
+
205
+ typedef enum JSLocalKind {
206
+ JSLOCAL_NONE,
207
+ JSLOCAL_ARG,
208
+ JSLOCAL_VAR,
209
+ JSLOCAL_CONST
210
+ } JSLocalKind;
211
+
212
+ #define JS_GET_LOCAL_NAME_COUNT(fun) ((fun)->nargs + (fun)->u.i.nvars)
213
+
214
+ extern JSBool
215
+ js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind);
216
+
217
+ /*
218
+ * Look up an argument or variable name returning its kind when found or
219
+ * JSLOCAL_NONE when no such name exists. When indexp is not null and the name
220
+ * exists, *indexp will receive the index of the corresponding argument or
221
+ * variable.
222
+ */
223
+ extern JSLocalKind
224
+ js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp);
225
+
226
+ /*
227
+ * Functions to work with local names as an array of words.
228
+ *
229
+ * js_GetLocalNameArray returns the array or null when it cannot be allocated
230
+ * The function must be called only when JS_GET_LOCAL_NAME_COUNT(fun) is not
231
+ * zero. The function use the supplied pool to allocate the array.
232
+ *
233
+ * The elements of the array with index below fun->nargs correspond to the
234
+ * names of function arguments and of function variables otherwise. Use
235
+ * JS_LOCAL_NAME_TO_ATOM to convert array's element into an atom. It can be
236
+ * null when the element is an argument corresponding to a destructuring
237
+ * pattern. For a variable use JS_LOCAL_NAME_IS_CONST to check if it
238
+ * corresponds to the const declaration.
239
+ */
240
+ extern jsuword *
241
+ js_GetLocalNameArray(JSContext *cx, JSFunction *fun, struct JSArenaPool *pool);
242
+
243
+ #define JS_LOCAL_NAME_TO_ATOM(nameWord) \
244
+ ((JSAtom *) ((nameWord) & ~(jsuword) 1))
245
+
246
+ #define JS_LOCAL_NAME_IS_CONST(nameWord) \
247
+ ((((nameWord) & (jsuword) 1)) != 0)
248
+
249
+ extern void
250
+ js_FreezeLocalNames(JSContext *cx, JSFunction *fun);
251
+
252
+ JS_END_EXTERN_C
253
+
254
+ #endif /* jsfun_h___ */
@@ -0,0 +1,3554 @@
1
+ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
+ * vim: set ts=8 sw=4 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 Mark-and-Sweep Garbage Collector.
43
+ *
44
+ * This GC allocates fixed-sized things with sizes up to GC_NBYTES_MAX (see
45
+ * jsgc.h). It allocates from a special GC arena pool with each arena allocated
46
+ * using malloc. It uses an ideally parallel array of flag bytes to hold the
47
+ * mark bit, finalizer type index, etc.
48
+ *
49
+ * XXX swizzle page to freelist for better locality of reference
50
+ */
51
+ #include "jsstddef.h"
52
+ #include <stdlib.h> /* for free */
53
+ #include <string.h> /* for memset used when DEBUG */
54
+ #include "jstypes.h"
55
+ #include "jsutil.h" /* Added by JSIFY */
56
+ #include "jshash.h" /* Added by JSIFY */
57
+ #include "jsapi.h"
58
+ #include "jsatom.h"
59
+ #include "jsbit.h"
60
+ #include "jsclist.h"
61
+ #include "jscntxt.h"
62
+ #include "jsconfig.h"
63
+ #include "jsdbgapi.h"
64
+ #include "jsexn.h"
65
+ #include "jsfun.h"
66
+ #include "jsgc.h"
67
+ #include "jsinterp.h"
68
+ #include "jsiter.h"
69
+ #include "jslock.h"
70
+ #include "jsnum.h"
71
+ #include "jsobj.h"
72
+ #include "jsparse.h"
73
+ #include "jsscope.h"
74
+ #include "jsscript.h"
75
+ #include "jsstr.h"
76
+
77
+ #if JS_HAS_XML_SUPPORT
78
+ #include "jsxml.h"
79
+ #endif
80
+
81
+ /*
82
+ * Check if posix_memalign is available.
83
+ */
84
+ #if _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || MOZ_MEMORY
85
+ # define HAS_POSIX_MEMALIGN 1
86
+ #else
87
+ # define HAS_POSIX_MEMALIGN 0
88
+ #endif
89
+
90
+ /*
91
+ * jemalloc provides posix_memalign but the function has to be explicitly
92
+ * declared on Windows.
93
+ */
94
+ #if HAS_POSIX_MEMALIGN && MOZ_MEMORY_WINDOWS
95
+ JS_BEGIN_EXTERN_C
96
+ extern int
97
+ posix_memalign(void **memptr, size_t alignment, size_t size);
98
+ JS_END_EXTERN_C
99
+ #endif
100
+
101
+ /*
102
+ * Include the headers for mmap unless we have posix_memalign and do not
103
+ * insist on mmap.
104
+ */
105
+ #if JS_GC_USE_MMAP || (!defined JS_GC_USE_MMAP && !HAS_POSIX_MEMALIGN)
106
+ # if defined(XP_WIN)
107
+ # ifndef JS_GC_USE_MMAP
108
+ # define JS_GC_USE_MMAP 1
109
+ # endif
110
+ # include <windows.h>
111
+ # else
112
+ # if defined(XP_UNIX) || defined(XP_BEOS)
113
+ # include <unistd.h>
114
+ # endif
115
+ # if _POSIX_MAPPED_FILES > 0
116
+ # ifndef JS_GC_USE_MMAP
117
+ # define JS_GC_USE_MMAP 1
118
+ # endif
119
+ # include <sys/mman.h>
120
+
121
+ /* On Mac OS X MAP_ANONYMOUS is not defined. */
122
+ # if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
123
+ # define MAP_ANONYMOUS MAP_ANON
124
+ # endif
125
+ # else
126
+ # if JS_GC_USE_MMAP
127
+ # error "JS_GC_USE_MMAP is set when mmap is not available"
128
+ # endif
129
+ # endif
130
+ # endif
131
+ #endif
132
+
133
+ /*
134
+ * A GC arena contains a fixed number of flag bits for each thing in its heap,
135
+ * and supports O(1) lookup of a flag given its thing's address.
136
+ *
137
+ * To implement this, we allocate things of the same size from a GC arena
138
+ * containing GC_ARENA_SIZE bytes aligned on GC_ARENA_SIZE boundary. The
139
+ * following picture shows arena's layout:
140
+ *
141
+ * +------------------------------+--------------------+---------------+
142
+ * | allocation area for GC thing | flags of GC things | JSGCArenaInfo |
143
+ * +------------------------------+--------------------+---------------+
144
+ *
145
+ * To find the flag bits for the thing we calculate the thing index counting
146
+ * from arena's start using:
147
+ *
148
+ * thingIndex = (thingAddress & GC_ARENA_MASK) / thingSize
149
+ *
150
+ * The details of flag's lookup depend on thing's kind. For all GC things
151
+ * except doubles we use one byte of flags where the 4 bits determine thing's
152
+ * type and the rest is used to implement GC marking, finalization and
153
+ * locking. We calculate the address of flag's byte using:
154
+ *
155
+ * flagByteAddress =
156
+ * (thingAddress | GC_ARENA_MASK) - sizeof(JSGCArenaInfo) - thingIndex
157
+ *
158
+ * where
159
+ *
160
+ * (thingAddress | GC_ARENA_MASK) - sizeof(JSGCArenaInfo)
161
+ *
162
+ * is the last byte of flags' area.
163
+ *
164
+ * This implies that the things are allocated from the start of their area and
165
+ * flags are allocated from the end. This arrangement avoids a relatively
166
+ * expensive calculation of the location of the boundary separating things and
167
+ * flags. The boundary's offset from the start of the arena is given by:
168
+ *
169
+ * thingsPerArena * thingSize
170
+ *
171
+ * where thingsPerArena is the number of things that the arena can hold:
172
+ *
173
+ * (GC_ARENA_SIZE - sizeof(JSGCArenaInfo)) / (thingSize + 1).
174
+ *
175
+ * To allocate doubles we use a specialized arena. It can contain only numbers
176
+ * so we do not need the type bits. Moreover, since the doubles do not require
177
+ * a finalizer and very few of them are locked via js_LockGCThing API, we use
178
+ * just one bit of flags per double to denote if it was marked during the
179
+ * marking phase of the GC. The locking is implemented via a hash table. Thus
180
+ * for doubles the flag area becomes a bitmap.
181
+ *
182
+ * JS_GC_USE_MMAP macro governs the choice of the aligned arena allocator.
183
+ * When it is true, a platform-dependent function like mmap is used to get
184
+ * memory aligned on CPU page boundaries. If the macro is false or undefined,
185
+ * posix_memalign is used when available. Otherwise the code uses malloc to
186
+ * over-allocate a chunk with js_gcArenasPerChunk aligned arenas. The
187
+ * approximate space overhead of this is 1/js_gcArenasPerChunk. For details,
188
+ * see NewGCChunk/DestroyGCChunk below.
189
+ *
190
+ * The code also allocates arenas in chunks when JS_GC_USE_MMAP is 1 to
191
+ * minimize the overhead of mmap/munmap. In this case js_gcArenasPerChunk can
192
+ * not be a compile-time constant as the system page size is not known until
193
+ * runtime.
194
+ */
195
+ #if JS_GC_USE_MMAP
196
+ static uint32 js_gcArenasPerChunk = 0;
197
+ static JSBool js_gcUseMmap = JS_FALSE;
198
+ #elif HAS_POSIX_MEMALIGN
199
+ # define js_gcArenasPerChunk 1
200
+ #else
201
+ # define js_gcArenasPerChunk 7
202
+ #endif
203
+
204
+ #if defined(js_gcArenasPerChunk) && js_gcArenasPerChunk == 1
205
+ # define CHUNKED_ARENA_ALLOCATION 0
206
+ #else
207
+ # define CHUNKED_ARENA_ALLOCATION 1
208
+ #endif
209
+
210
+ #define GC_ARENA_SHIFT 12
211
+ #define GC_ARENA_MASK ((jsuword) JS_BITMASK(GC_ARENA_SHIFT))
212
+ #define GC_ARENA_SIZE JS_BIT(GC_ARENA_SHIFT)
213
+
214
+ /*
215
+ * JS_GC_ARENA_PAD defines the number of bytes to pad JSGCArenaInfo structure.
216
+ * It is used to improve allocation efficiency when using posix_memalign. If
217
+ * malloc's implementation uses internal headers, then calling
218
+ *
219
+ * posix_memalign(&p, GC_ARENA_SIZE, GC_ARENA_SIZE * js_gcArenasPerChunk)
220
+ *
221
+ * in a sequence leaves holes between allocations of the size GC_ARENA_SIZE
222
+ * due to the need to fit headers. JS_GC_ARENA_PAD mitigates that so the code
223
+ * calls
224
+ *
225
+ * posix_memalign(&p, GC_ARENA_SIZE,
226
+ * GC_ARENA_SIZE * js_gcArenasPerChunk - JS_GC_ARENA_PAD)
227
+ *
228
+ * When JS_GC_ARENA_PAD is equal or greater than the number of words in the
229
+ * system header, the system can pack all allocations together without holes.
230
+ *
231
+ * With JS_GC_USE_MEMALIGN we want at least 2 word pad unless posix_memalign
232
+ * comes from jemalloc that does not use any headers/trailers.
233
+ */
234
+ #ifndef JS_GC_ARENA_PAD
235
+ # if HAS_POSIX_MEMALIGN && !MOZ_MEMORY
236
+ # define JS_GC_ARENA_PAD (2 * JS_BYTES_PER_WORD)
237
+ # else
238
+ # define JS_GC_ARENA_PAD 0
239
+ # endif
240
+ #endif
241
+
242
+ struct JSGCArenaInfo {
243
+ /*
244
+ * Allocation list for the arena or NULL if the arena holds double values.
245
+ */
246
+ JSGCArenaList *list;
247
+
248
+ /*
249
+ * Pointer to the previous arena in a linked list. The arena can either
250
+ * belong to one of JSContext.gcArenaList lists or, when it does not have
251
+ * any allocated GC things, to the list of free arenas in the chunk with
252
+ * head stored in JSGCChunkInfo.lastFreeArena.
253
+ */
254
+ JSGCArenaInfo *prev;
255
+
256
+ #if !CHUNKED_ARENA_ALLOCATION
257
+ jsuword prevUntracedPage;
258
+ #else
259
+ /*
260
+ * A link field for the list of arenas with marked but not yet traced
261
+ * things. The field is encoded as arena's page to share the space with
262
+ * firstArena and arenaIndex fields.
263
+ */
264
+ jsuword prevUntracedPage : JS_BITS_PER_WORD - GC_ARENA_SHIFT;
265
+
266
+ /*
267
+ * When firstArena is false, the index of arena in the chunk. When
268
+ * firstArena is true, the index of a free arena holding JSGCChunkInfo or
269
+ * NO_FREE_ARENAS if there are no free arenas in the chunk.
270
+ *
271
+ * GET_ARENA_INDEX and GET_CHUNK_INFO_INDEX are convenience macros to
272
+ * access either of indexes.
273
+ */
274
+ jsuword arenaIndex : GC_ARENA_SHIFT - 1;
275
+
276
+ /* Flag indicating if the arena is the first in the chunk. */
277
+ jsuword firstArena : 1;
278
+ #endif
279
+
280
+ union {
281
+ jsuword untracedThings; /* bitset for fast search of marked
282
+ but not yet traced things */
283
+ JSBool hasMarkedDoubles; /* the arena has marked doubles */
284
+ } u;
285
+
286
+ #if JS_GC_ARENA_PAD != 0
287
+ uint8 pad[JS_GC_ARENA_PAD];
288
+ #endif
289
+ };
290
+
291
+ /*
292
+ * Verify that the bit fields are indeed shared and JSGCArenaInfo is as small
293
+ * as possible. The code does not rely on this check so if on a particular
294
+ * platform this does not compile, then, as a workaround, comment the assert
295
+ * out and submit a bug report.
296
+ */
297
+ JS_STATIC_ASSERT(offsetof(JSGCArenaInfo, u) == 3 * sizeof(jsuword));
298
+
299
+ /*
300
+ * Macros to convert between JSGCArenaInfo, the start address of the arena and
301
+ * arena's page defined as (start address) >> GC_ARENA_SHIFT.
302
+ */
303
+ #define ARENA_INFO_OFFSET (GC_ARENA_SIZE - (uint32) sizeof(JSGCArenaInfo))
304
+
305
+ #define IS_ARENA_INFO_ADDRESS(arena) \
306
+ (((jsuword) (arena) & GC_ARENA_MASK) == ARENA_INFO_OFFSET)
307
+
308
+ #define ARENA_START_TO_INFO(arenaStart) \
309
+ (JS_ASSERT(((arenaStart) & (jsuword) GC_ARENA_MASK) == 0), \
310
+ (JSGCArenaInfo *) ((arenaStart) + (jsuword) ARENA_INFO_OFFSET))
311
+
312
+ #define ARENA_INFO_TO_START(arena) \
313
+ (JS_ASSERT(IS_ARENA_INFO_ADDRESS(arena)), \
314
+ (jsuword) (arena) & ~(jsuword) GC_ARENA_MASK)
315
+
316
+ #define ARENA_PAGE_TO_INFO(arenaPage) \
317
+ (JS_ASSERT(arenaPage != 0), \
318
+ JS_ASSERT(!((jsuword)(arenaPage) >> (JS_BITS_PER_WORD-GC_ARENA_SHIFT))), \
319
+ ARENA_START_TO_INFO((arenaPage) << GC_ARENA_SHIFT))
320
+
321
+ #define ARENA_INFO_TO_PAGE(arena) \
322
+ (JS_ASSERT(IS_ARENA_INFO_ADDRESS(arena)), \
323
+ ((jsuword) (arena) >> GC_ARENA_SHIFT))
324
+
325
+ #define GET_ARENA_INFO(chunk, index) \
326
+ (JS_ASSERT((index) < js_gcArenasPerChunk), \
327
+ ARENA_START_TO_INFO(chunk + ((index) << GC_ARENA_SHIFT)))
328
+
329
+ #if CHUNKED_ARENA_ALLOCATION
330
+ /*
331
+ * Definitions for allocating arenas in chunks.
332
+ *
333
+ * All chunks that have at least one free arena are put on the doubly-linked
334
+ * list with the head stored in JSRuntime.gcChunkList. JSGCChunkInfo contains
335
+ * the head of the chunk's free arena list together with the link fields for
336
+ * gcChunkList.
337
+ *
338
+ * Structure stored in one of chunk's free arenas. GET_CHUNK_INFO_INDEX gives
339
+ * the index of this arena. When all arenas in the chunk are used, it is
340
+ * removed from the list and the index is set to NO_FREE_ARENAS indicating
341
+ * that the chunk is not on gcChunkList and has no JSGCChunkInfo available.
342
+ */
343
+
344
+ struct JSGCChunkInfo {
345
+ JSGCChunkInfo **prevp;
346
+ JSGCChunkInfo *next;
347
+ JSGCArenaInfo *lastFreeArena;
348
+ uint32 numFreeArenas;
349
+ };
350
+
351
+ #define NO_FREE_ARENAS JS_BITMASK(GC_ARENA_SHIFT - 1)
352
+
353
+ #ifdef js_gcArenasPerChunk
354
+ JS_STATIC_ASSERT(1 <= js_gcArenasPerChunk &&
355
+ js_gcArenasPerChunk <= NO_FREE_ARENAS);
356
+ #endif
357
+
358
+ #define GET_ARENA_CHUNK(arena, index) \
359
+ (JS_ASSERT(GET_ARENA_INDEX(arena) == index), \
360
+ ARENA_INFO_TO_START(arena) - ((index) << GC_ARENA_SHIFT))
361
+
362
+ #define GET_ARENA_INDEX(arena) \
363
+ ((arena)->firstArena ? 0 : (uint32) (arena)->arenaIndex)
364
+
365
+ #define GET_CHUNK_INFO_INDEX(chunk) \
366
+ ((uint32) ARENA_START_TO_INFO(chunk)->arenaIndex)
367
+
368
+ #define SET_CHUNK_INFO_INDEX(chunk, index) \
369
+ (JS_ASSERT((index) < js_gcArenasPerChunk || (index) == NO_FREE_ARENAS), \
370
+ (void) (ARENA_START_TO_INFO(chunk)->arenaIndex = (jsuword) (index)))
371
+
372
+ #define GET_CHUNK_INFO(chunk, infoIndex) \
373
+ (JS_ASSERT(GET_CHUNK_INFO_INDEX(chunk) == (infoIndex)), \
374
+ JS_ASSERT((uint32) (infoIndex) < js_gcArenasPerChunk), \
375
+ (JSGCChunkInfo *) ((chunk) + ((infoIndex) << GC_ARENA_SHIFT)))
376
+
377
+ #define CHUNK_INFO_TO_INDEX(ci) \
378
+ GET_ARENA_INDEX(ARENA_START_TO_INFO((jsuword)ci))
379
+
380
+ #endif
381
+
382
+ /*
383
+ * Macros for GC-thing operations.
384
+ */
385
+ #define THINGS_PER_ARENA(thingSize) \
386
+ ((GC_ARENA_SIZE - (uint32) sizeof(JSGCArenaInfo)) / ((thingSize) + 1U))
387
+
388
+ #define THING_TO_ARENA(thing) \
389
+ ((JSGCArenaInfo *)(((jsuword) (thing) | GC_ARENA_MASK) + \
390
+ 1 - sizeof(JSGCArenaInfo)))
391
+
392
+ #define THING_TO_INDEX(thing, thingSize) \
393
+ ((uint32) ((jsuword) (thing) & GC_ARENA_MASK) / (uint32) (thingSize))
394
+
395
+ #define THING_FLAGS_END(arena) ((uint8 *)(arena))
396
+
397
+ #define THING_FLAGP(arena, thingIndex) \
398
+ (JS_ASSERT((jsuword) (thingIndex) \
399
+ < (jsuword) THINGS_PER_ARENA((arena)->list->thingSize)), \
400
+ (uint8 *)(arena) - 1 - (thingIndex))
401
+
402
+ #define THING_TO_FLAGP(thing, thingSize) \
403
+ THING_FLAGP(THING_TO_ARENA(thing), THING_TO_INDEX(thing, thingSize))
404
+
405
+ #define FLAGP_TO_ARENA(flagp) THING_TO_ARENA(flagp)
406
+
407
+ #define FLAGP_TO_INDEX(flagp) \
408
+ (JS_ASSERT(((jsuword) (flagp) & GC_ARENA_MASK) < ARENA_INFO_OFFSET), \
409
+ (ARENA_INFO_OFFSET - 1 - (uint32) ((jsuword) (flagp) & GC_ARENA_MASK)))
410
+
411
+ #define FLAGP_TO_THING(flagp, thingSize) \
412
+ (JS_ASSERT(((jsuword) (flagp) & GC_ARENA_MASK) >= \
413
+ (ARENA_INFO_OFFSET - THINGS_PER_ARENA(thingSize))), \
414
+ (JSGCThing *)(((jsuword) (flagp) & ~GC_ARENA_MASK) + \
415
+ (thingSize) * FLAGP_TO_INDEX(flagp)))
416
+
417
+ /*
418
+ * Macros for the specialized arena for doubles.
419
+ *
420
+ * DOUBLES_PER_ARENA defines the maximum number of doubles that the arena can
421
+ * hold. We find it as the following. Let n be the number of doubles in the
422
+ * arena. Together with the bitmap of flags and JSGCArenaInfo they should fit
423
+ * the arena. Hence DOUBLES_PER_ARENA or n_max is the maximum value of n for
424
+ * which the following holds:
425
+ *
426
+ * n*s + ceil(n/B) <= M (1)
427
+ *
428
+ * where "/" denotes normal real division,
429
+ * ceil(r) gives the least integer not smaller than the number r,
430
+ * s is the number of words in jsdouble,
431
+ * B is number of bits per word or B == JS_BITS_PER_WORD
432
+ * M is the number of words in the arena before JSGCArenaInfo or
433
+ * M == (GC_ARENA_SIZE - sizeof(JSGCArenaInfo)) / sizeof(jsuword).
434
+ * M == ARENA_INFO_OFFSET / sizeof(jsuword)
435
+ *
436
+ * We rewrite the inequality as
437
+ *
438
+ * n*B*s/B + ceil(n/B) <= M,
439
+ * ceil(n*B*s/B + n/B) <= M,
440
+ * ceil(n*(B*s + 1)/B) <= M (2)
441
+ *
442
+ * We define a helper function e(n, s, B),
443
+ *
444
+ * e(n, s, B) := ceil(n*(B*s + 1)/B) - n*(B*s + 1)/B, 0 <= e(n, s, B) < 1.
445
+ *
446
+ * It gives:
447
+ *
448
+ * n*(B*s + 1)/B + e(n, s, B) <= M,
449
+ * n + e*B/(B*s + 1) <= M*B/(B*s + 1)
450
+ *
451
+ * We apply the floor function to both sides of the last equation, where
452
+ * floor(r) gives the biggest integer not greater than r. As a consequence we
453
+ * have:
454
+ *
455
+ * floor(n + e*B/(B*s + 1)) <= floor(M*B/(B*s + 1)),
456
+ * n + floor(e*B/(B*s + 1)) <= floor(M*B/(B*s + 1)),
457
+ * n <= floor(M*B/(B*s + 1)), (3)
458
+ *
459
+ * where floor(e*B/(B*s + 1)) is zero as e*B/(B*s + 1) < B/(B*s + 1) < 1.
460
+ * Thus any n that satisfies the original constraint (1) or its equivalent (2),
461
+ * must also satisfy (3). That is, we got an upper estimate for the maximum
462
+ * value of n. Lets show that this upper estimate,
463
+ *
464
+ * floor(M*B/(B*s + 1)), (4)
465
+ *
466
+ * also satisfies (1) and, as such, gives the required maximum value.
467
+ * Substituting it into (2) gives:
468
+ *
469
+ * ceil(floor(M*B/(B*s + 1))*(B*s + 1)/B) == ceil(floor(M/X)*X)
470
+ *
471
+ * where X == (B*s + 1)/B > 1. But then floor(M/X)*X <= M/X*X == M and
472
+ *
473
+ * ceil(floor(M/X)*X) <= ceil(M) == M.
474
+ *
475
+ * Thus the value of (4) gives the maximum n satisfying (1).
476
+ *
477
+ * For the final result we observe that in (4)
478
+ *
479
+ * M*B == ARENA_INFO_OFFSET / sizeof(jsuword) * JS_BITS_PER_WORD
480
+ * == ARENA_INFO_OFFSET * JS_BITS_PER_BYTE
481
+ *
482
+ * and
483
+ *
484
+ * B*s == JS_BITS_PER_WORD * sizeof(jsdouble) / sizeof(jsuword)
485
+ * == JS_BITS_PER_DOUBLE.
486
+ */
487
+ #define DOUBLES_PER_ARENA \
488
+ ((ARENA_INFO_OFFSET * JS_BITS_PER_BYTE) / (JS_BITS_PER_DOUBLE + 1))
489
+
490
+ /*
491
+ * Check that ARENA_INFO_OFFSET and sizeof(jsdouble) divides sizeof(jsuword).
492
+ */
493
+ JS_STATIC_ASSERT(ARENA_INFO_OFFSET % sizeof(jsuword) == 0);
494
+ JS_STATIC_ASSERT(sizeof(jsdouble) % sizeof(jsuword) == 0);
495
+ JS_STATIC_ASSERT(sizeof(jsbitmap) == sizeof(jsuword));
496
+
497
+ #define DOUBLES_ARENA_BITMAP_WORDS \
498
+ (JS_HOWMANY(DOUBLES_PER_ARENA, JS_BITS_PER_WORD))
499
+
500
+ /* Check that DOUBLES_PER_ARENA indeed maximises (1). */
501
+ JS_STATIC_ASSERT(DOUBLES_PER_ARENA * sizeof(jsdouble) +
502
+ DOUBLES_ARENA_BITMAP_WORDS * sizeof(jsuword) <=
503
+ ARENA_INFO_OFFSET);
504
+
505
+ JS_STATIC_ASSERT((DOUBLES_PER_ARENA + 1) * sizeof(jsdouble) +
506
+ sizeof(jsuword) *
507
+ JS_HOWMANY((DOUBLES_PER_ARENA + 1), JS_BITS_PER_WORD) >
508
+ ARENA_INFO_OFFSET);
509
+
510
+ /*
511
+ * When DOUBLES_PER_ARENA % BITS_PER_DOUBLE_FLAG_UNIT != 0, some bits in the
512
+ * last byte of the occupation bitmap are unused.
513
+ */
514
+ #define UNUSED_DOUBLE_BITMAP_BITS \
515
+ (DOUBLES_ARENA_BITMAP_WORDS * JS_BITS_PER_WORD - DOUBLES_PER_ARENA)
516
+
517
+ JS_STATIC_ASSERT(UNUSED_DOUBLE_BITMAP_BITS < JS_BITS_PER_WORD);
518
+
519
+ #define DOUBLES_ARENA_BITMAP_OFFSET \
520
+ (ARENA_INFO_OFFSET - DOUBLES_ARENA_BITMAP_WORDS * sizeof(jsuword))
521
+
522
+ #define CHECK_DOUBLE_ARENA_INFO(arenaInfo) \
523
+ (JS_ASSERT(IS_ARENA_INFO_ADDRESS(arenaInfo)), \
524
+ JS_ASSERT(!(arenaInfo)->list)) \
525
+
526
+ /*
527
+ * Get the start of the bitmap area containing double mark flags in the arena.
528
+ * To access the flag the code uses
529
+ *
530
+ * JS_TEST_BIT(bitmapStart, index)
531
+ *
532
+ * That is, compared with the case of arenas with non-double things, we count
533
+ * flags from the start of the bitmap area, not from the end.
534
+ */
535
+ #define DOUBLE_ARENA_BITMAP(arenaInfo) \
536
+ (CHECK_DOUBLE_ARENA_INFO(arenaInfo), \
537
+ (jsbitmap *) arenaInfo - DOUBLES_ARENA_BITMAP_WORDS)
538
+
539
+ #define DOUBLE_THING_TO_INDEX(thing) \
540
+ (CHECK_DOUBLE_ARENA_INFO(THING_TO_ARENA(thing)), \
541
+ JS_ASSERT(((jsuword) (thing) & GC_ARENA_MASK) < \
542
+ DOUBLES_ARENA_BITMAP_OFFSET), \
543
+ ((uint32) (((jsuword) (thing) & GC_ARENA_MASK) / sizeof(jsdouble))))
544
+
545
+ static void
546
+ ClearDoubleArenaFlags(JSGCArenaInfo *a)
547
+ {
548
+ jsbitmap *bitmap, mask;
549
+ uintN nused;
550
+
551
+ /*
552
+ * When some high bits in the last byte of the double occupation bitmap
553
+ * are unused, we must set them. Otherwise RefillDoubleFreeList will
554
+ * assume that they corresponds to some free cells and tries to allocate
555
+ * them.
556
+ *
557
+ * Note that the code works correctly with UNUSED_DOUBLE_BITMAP_BITS == 0.
558
+ */
559
+ bitmap = DOUBLE_ARENA_BITMAP(a);
560
+ memset(bitmap, 0, (DOUBLES_ARENA_BITMAP_WORDS - 1) * sizeof *bitmap);
561
+ mask = ((jsbitmap) 1 << UNUSED_DOUBLE_BITMAP_BITS) - 1;
562
+ nused = JS_BITS_PER_WORD - UNUSED_DOUBLE_BITMAP_BITS;
563
+ bitmap[DOUBLES_ARENA_BITMAP_WORDS - 1] = mask << nused;
564
+ }
565
+
566
+ static JS_INLINE JSBool
567
+ IsMarkedDouble(JSGCArenaInfo *a, uint32 index)
568
+ {
569
+ jsbitmap *bitmap;
570
+
571
+ JS_ASSERT(a->u.hasMarkedDoubles);
572
+ bitmap = DOUBLE_ARENA_BITMAP(a);
573
+ return JS_TEST_BIT(bitmap, index);
574
+ }
575
+
576
+ /*
577
+ * JSRuntime.gcDoubleArenaList.nextDoubleFlags points either to:
578
+ *
579
+ * 1. The next byte in the bitmap area for doubles to check for unmarked
580
+ * (or free) doubles.
581
+ * 2. Or to the end of the bitmap area when all GC cells of the arena are
582
+ * allocated.
583
+ * 3. Or to a special sentinel value indicating that there are no arenas
584
+ * to check for unmarked doubles.
585
+ *
586
+ * We set the sentinel to ARENA_INFO_OFFSET so the single check
587
+ *
588
+ * ((jsuword) nextDoubleFlags & GC_ARENA_MASK) == ARENA_INFO_OFFSET
589
+ *
590
+ * will cover both the second and the third cases.
591
+ */
592
+ #define DOUBLE_BITMAP_SENTINEL ((jsbitmap *) ARENA_INFO_OFFSET)
593
+
594
+ #ifdef JS_THREADSAFE
595
+ /*
596
+ * The maximum number of things to put on the local free list by taking
597
+ * several things from the global free list or from the tail of the last
598
+ * allocated arena to amortize the cost of rt->gcLock.
599
+ *
600
+ * We use number 8 based on benchmarks from bug 312238.
601
+ */
602
+ #define MAX_THREAD_LOCAL_THINGS 8
603
+
604
+ #endif
605
+
606
+ JS_STATIC_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval));
607
+
608
+ JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(JSString));
609
+ JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(jsdouble));
610
+
611
+ /* We want to use all the available GC thing space for object's slots. */
612
+ JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(JSGCThing) == 0);
613
+
614
+ /*
615
+ * Ensure that JSObject is allocated from a different GC-list rather than
616
+ * jsdouble and JSString so we can easily finalize JSObject before these 2
617
+ * types of GC things. See comments in js_GC.
618
+ */
619
+ JS_STATIC_ASSERT(GC_FREELIST_INDEX(sizeof(JSString)) !=
620
+ GC_FREELIST_INDEX(sizeof(JSObject)));
621
+ JS_STATIC_ASSERT(GC_FREELIST_INDEX(sizeof(jsdouble)) !=
622
+ GC_FREELIST_INDEX(sizeof(JSObject)));
623
+
624
+ /*
625
+ * JSPtrTable capacity growth descriptor. The table grows by powers of two
626
+ * starting from capacity JSPtrTableInfo.minCapacity, but switching to linear
627
+ * growth when capacity reaches JSPtrTableInfo.linearGrowthThreshold.
628
+ */
629
+ typedef struct JSPtrTableInfo {
630
+ uint16 minCapacity;
631
+ uint16 linearGrowthThreshold;
632
+ } JSPtrTableInfo;
633
+
634
+ #define GC_ITERATOR_TABLE_MIN 4
635
+ #define GC_ITERATOR_TABLE_LINEAR 1024
636
+
637
+ static const JSPtrTableInfo iteratorTableInfo = {
638
+ GC_ITERATOR_TABLE_MIN,
639
+ GC_ITERATOR_TABLE_LINEAR
640
+ };
641
+
642
+ /* Calculate table capacity based on the current value of JSPtrTable.count. */
643
+ static size_t
644
+ PtrTableCapacity(size_t count, const JSPtrTableInfo *info)
645
+ {
646
+ size_t linear, log, capacity;
647
+
648
+ linear = info->linearGrowthThreshold;
649
+ JS_ASSERT(info->minCapacity <= linear);
650
+
651
+ if (count == 0) {
652
+ capacity = 0;
653
+ } else if (count < linear) {
654
+ log = JS_CEILING_LOG2W(count);
655
+ JS_ASSERT(log != JS_BITS_PER_WORD);
656
+ capacity = (size_t)1 << log;
657
+ if (capacity < info->minCapacity)
658
+ capacity = info->minCapacity;
659
+ } else {
660
+ capacity = JS_ROUNDUP(count, linear);
661
+ }
662
+
663
+ JS_ASSERT(capacity >= count);
664
+ return capacity;
665
+ }
666
+
667
+ static void
668
+ FreePtrTable(JSPtrTable *table, const JSPtrTableInfo *info)
669
+ {
670
+ if (table->array) {
671
+ JS_ASSERT(table->count > 0);
672
+ free(table->array);
673
+ table->array = NULL;
674
+ table->count = 0;
675
+ }
676
+ JS_ASSERT(table->count == 0);
677
+ }
678
+
679
+ static JSBool
680
+ AddToPtrTable(JSContext *cx, JSPtrTable *table, const JSPtrTableInfo *info,
681
+ void *ptr)
682
+ {
683
+ size_t count, capacity;
684
+ void **array;
685
+
686
+ count = table->count;
687
+ capacity = PtrTableCapacity(count, info);
688
+
689
+ if (count == capacity) {
690
+ if (capacity < info->minCapacity) {
691
+ JS_ASSERT(capacity == 0);
692
+ JS_ASSERT(!table->array);
693
+ capacity = info->minCapacity;
694
+ } else {
695
+ /*
696
+ * Simplify the overflow detection assuming pointer is bigger
697
+ * than byte.
698
+ */
699
+ JS_STATIC_ASSERT(2 <= sizeof table->array[0]);
700
+ capacity = (capacity < info->linearGrowthThreshold)
701
+ ? 2 * capacity
702
+ : capacity + info->linearGrowthThreshold;
703
+ if (capacity > (size_t)-1 / sizeof table->array[0])
704
+ goto bad;
705
+ }
706
+ array = (void **) realloc(table->array,
707
+ capacity * sizeof table->array[0]);
708
+ if (!array)
709
+ goto bad;
710
+ #ifdef DEBUG
711
+ memset(array + count, JS_FREE_PATTERN,
712
+ (capacity - count) * sizeof table->array[0]);
713
+ #endif
714
+ table->array = array;
715
+ }
716
+
717
+ table->array[count] = ptr;
718
+ table->count = count + 1;
719
+
720
+ return JS_TRUE;
721
+
722
+ bad:
723
+ JS_ReportOutOfMemory(cx);
724
+ return JS_FALSE;
725
+ }
726
+
727
+ static void
728
+ ShrinkPtrTable(JSPtrTable *table, const JSPtrTableInfo *info,
729
+ size_t newCount)
730
+ {
731
+ size_t oldCapacity, capacity;
732
+ void **array;
733
+
734
+ JS_ASSERT(newCount <= table->count);
735
+ if (newCount == table->count)
736
+ return;
737
+
738
+ oldCapacity = PtrTableCapacity(table->count, info);
739
+ table->count = newCount;
740
+ capacity = PtrTableCapacity(newCount, info);
741
+
742
+ if (oldCapacity != capacity) {
743
+ array = table->array;
744
+ JS_ASSERT(array);
745
+ if (capacity == 0) {
746
+ free(array);
747
+ table->array = NULL;
748
+ return;
749
+ }
750
+ array = (void **) realloc(array, capacity * sizeof array[0]);
751
+ if (array)
752
+ table->array = array;
753
+ }
754
+ #ifdef DEBUG
755
+ memset(table->array + newCount, JS_FREE_PATTERN,
756
+ (capacity - newCount) * sizeof table->array[0]);
757
+ #endif
758
+ }
759
+
760
+ #ifdef JS_GCMETER
761
+ # define METER(x) ((void) (x))
762
+ # define METER_IF(condition, x) ((void) ((condition) && (x)))
763
+ #else
764
+ # define METER(x) ((void) 0)
765
+ # define METER_IF(condition, x) ((void) 0)
766
+ #endif
767
+
768
+ #define METER_UPDATE_MAX(maxLval, rval) \
769
+ METER_IF((maxLval) < (rval), (maxLval) = (rval))
770
+
771
+ #if JS_GC_USE_MMAP || !HAS_POSIX_MEMALIGN
772
+
773
+ /*
774
+ * For chunks allocated via over-sized malloc, get a pointer to store the gap
775
+ * between the malloc's result and the first arena in the chunk.
776
+ */
777
+ static uint32 *
778
+ GetMallocedChunkGapPtr(jsuword chunk)
779
+ {
780
+ JS_ASSERT((chunk & GC_ARENA_MASK) == 0);
781
+
782
+ /* Use the memory after the chunk, see NewGCChunk for details. */
783
+ return (uint32 *) (chunk + (js_gcArenasPerChunk << GC_ARENA_SHIFT));
784
+ }
785
+
786
+ #endif
787
+
788
+ static jsuword
789
+ NewGCChunk(void)
790
+ {
791
+ void *p;
792
+
793
+ #if JS_GC_USE_MMAP
794
+ if (js_gcUseMmap) {
795
+ # if defined(XP_WIN)
796
+ p = VirtualAlloc(NULL, js_gcArenasPerChunk << GC_ARENA_SHIFT,
797
+ MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
798
+ return (jsuword) p;
799
+ # else
800
+ p = mmap(NULL, js_gcArenasPerChunk << GC_ARENA_SHIFT,
801
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
802
+ return (p == MAP_FAILED) ? 0 : (jsuword) p;
803
+ # endif
804
+ }
805
+ #endif
806
+
807
+ #if HAS_POSIX_MEMALIGN
808
+ if (0 != posix_memalign(&p, GC_ARENA_SIZE,
809
+ GC_ARENA_SIZE * js_gcArenasPerChunk -
810
+ JS_GC_ARENA_PAD)) {
811
+ return 0;
812
+ }
813
+ return (jsuword) p;
814
+ #else
815
+ /*
816
+ * Implement chunk allocation using oversized malloc if mmap and
817
+ * posix_memalign are not available.
818
+ *
819
+ * Since malloc allocates pointers aligned on the word boundary, to get
820
+ * js_gcArenasPerChunk aligned arenas, we need to malloc only
821
+ *
822
+ * ((js_gcArenasPerChunk + 1) << GC_ARENA_SHIFT) - sizeof(size_t)
823
+ *
824
+ * bytes. But since we stores the gap between the malloced pointer and the
825
+ * first arena in the chunk after the chunk, we need to ask for
826
+ *
827
+ * ((js_gcArenasPerChunk + 1) << GC_ARENA_SHIFT)
828
+ *
829
+ * bytes to ensure that we always have room to store the gap.
830
+ */
831
+ p = malloc((js_gcArenasPerChunk + 1) << GC_ARENA_SHIFT);
832
+ if (!p)
833
+ return 0;
834
+
835
+ {
836
+ jsuword chunk;
837
+
838
+ chunk = ((jsuword) p + GC_ARENA_MASK) & ~GC_ARENA_MASK;
839
+ *GetMallocedChunkGapPtr(chunk) = (uint32) (chunk - (jsuword) p);
840
+ return chunk;
841
+ }
842
+ #endif
843
+ }
844
+
845
+ static void
846
+ DestroyGCChunk(jsuword chunk)
847
+ {
848
+ JS_ASSERT((chunk & GC_ARENA_MASK) == 0);
849
+ #if JS_GC_USE_MMAP
850
+ if (js_gcUseMmap) {
851
+ # if defined(XP_WIN)
852
+ VirtualFree((void *) chunk, 0, MEM_RELEASE);
853
+ # else
854
+ munmap((void *) chunk, js_gcArenasPerChunk << GC_ARENA_SHIFT);
855
+ # endif
856
+ return;
857
+ }
858
+ #endif
859
+
860
+ #if HAS_POSIX_MEMALIGN
861
+ free((void *) chunk);
862
+ #else
863
+ /* See comments in NewGCChunk. */
864
+ JS_ASSERT(*GetMallocedChunkGapPtr(chunk) < GC_ARENA_SIZE);
865
+ free((void *) (chunk - *GetMallocedChunkGapPtr(chunk)));
866
+ #endif
867
+ }
868
+
869
+ #if CHUNKED_ARENA_ALLOCATION
870
+
871
+ static void
872
+ AddChunkToList(JSRuntime *rt, JSGCChunkInfo *ci)
873
+ {
874
+ ci->prevp = &rt->gcChunkList;
875
+ ci->next = rt->gcChunkList;
876
+ if (rt->gcChunkList) {
877
+ JS_ASSERT(rt->gcChunkList->prevp == &rt->gcChunkList);
878
+ rt->gcChunkList->prevp = &ci->next;
879
+ }
880
+ rt->gcChunkList = ci;
881
+ }
882
+
883
+ static void
884
+ RemoveChunkFromList(JSRuntime *rt, JSGCChunkInfo *ci)
885
+ {
886
+ *ci->prevp = ci->next;
887
+ if (ci->next) {
888
+ JS_ASSERT(ci->next->prevp == &ci->next);
889
+ ci->next->prevp = ci->prevp;
890
+ }
891
+ }
892
+
893
+ #endif
894
+
895
+ static JSGCArenaInfo *
896
+ NewGCArena(JSRuntime *rt)
897
+ {
898
+ jsuword chunk;
899
+ JSGCArenaInfo *a;
900
+
901
+ if (rt->gcBytes >= rt->gcMaxBytes)
902
+ return NULL;
903
+
904
+ #if CHUNKED_ARENA_ALLOCATION
905
+ if (js_gcArenasPerChunk == 1) {
906
+ #endif
907
+ chunk = NewGCChunk();
908
+ if (chunk == 0)
909
+ return NULL;
910
+ a = ARENA_START_TO_INFO(chunk);
911
+ #if CHUNKED_ARENA_ALLOCATION
912
+ } else {
913
+ JSGCChunkInfo *ci;
914
+ uint32 i;
915
+ JSGCArenaInfo *aprev;
916
+
917
+ ci = rt->gcChunkList;
918
+ if (!ci) {
919
+ chunk = NewGCChunk();
920
+ if (chunk == 0)
921
+ return NULL;
922
+ JS_ASSERT((chunk & GC_ARENA_MASK) == 0);
923
+ a = GET_ARENA_INFO(chunk, 0);
924
+ a->firstArena = JS_TRUE;
925
+ a->arenaIndex = 0;
926
+ aprev = NULL;
927
+ i = 0;
928
+ do {
929
+ a->prev = aprev;
930
+ aprev = a;
931
+ ++i;
932
+ a = GET_ARENA_INFO(chunk, i);
933
+ a->firstArena = JS_FALSE;
934
+ a->arenaIndex = i;
935
+ } while (i != js_gcArenasPerChunk - 1);
936
+ ci = GET_CHUNK_INFO(chunk, 0);
937
+ ci->lastFreeArena = aprev;
938
+ ci->numFreeArenas = js_gcArenasPerChunk - 1;
939
+ AddChunkToList(rt, ci);
940
+ } else {
941
+ JS_ASSERT(ci->prevp == &rt->gcChunkList);
942
+ a = ci->lastFreeArena;
943
+ aprev = a->prev;
944
+ if (!aprev) {
945
+ JS_ASSERT(ci->numFreeArenas == 1);
946
+ JS_ASSERT(ARENA_INFO_TO_START(a) == (jsuword) ci);
947
+ RemoveChunkFromList(rt, ci);
948
+ chunk = GET_ARENA_CHUNK(a, GET_ARENA_INDEX(a));
949
+ SET_CHUNK_INFO_INDEX(chunk, NO_FREE_ARENAS);
950
+ } else {
951
+ JS_ASSERT(ci->numFreeArenas >= 2);
952
+ JS_ASSERT(ARENA_INFO_TO_START(a) != (jsuword) ci);
953
+ ci->lastFreeArena = aprev;
954
+ ci->numFreeArenas--;
955
+ }
956
+ }
957
+ }
958
+ #endif
959
+
960
+ rt->gcBytes += GC_ARENA_SIZE;
961
+ a->prevUntracedPage = 0;
962
+ memset(&a->u, 0, sizeof(a->u));
963
+
964
+ return a;
965
+ }
966
+
967
+ static void
968
+ DestroyGCArenas(JSRuntime *rt, JSGCArenaInfo *last)
969
+ {
970
+ JSGCArenaInfo *a;
971
+
972
+ while (last) {
973
+ a = last;
974
+ last = last->prev;
975
+
976
+ METER(rt->gcStats.afree++);
977
+ JS_ASSERT(rt->gcBytes >= GC_ARENA_SIZE);
978
+ rt->gcBytes -= GC_ARENA_SIZE;
979
+
980
+ #if CHUNKED_ARENA_ALLOCATION
981
+ if (js_gcArenasPerChunk == 1) {
982
+ #endif
983
+ DestroyGCChunk(ARENA_INFO_TO_START(a));
984
+ #if CHUNKED_ARENA_ALLOCATION
985
+ } else {
986
+ uint32 arenaIndex;
987
+ jsuword chunk;
988
+ uint32 chunkInfoIndex;
989
+ JSGCChunkInfo *ci;
990
+ # ifdef DEBUG
991
+ jsuword firstArena;
992
+
993
+ firstArena = a->firstArena;
994
+ arenaIndex = a->arenaIndex;
995
+ memset((void *) ARENA_INFO_TO_START(a), JS_FREE_PATTERN,
996
+ GC_ARENA_SIZE - JS_GC_ARENA_PAD);
997
+ a->firstArena = firstArena;
998
+ a->arenaIndex = arenaIndex;
999
+ # endif
1000
+ arenaIndex = GET_ARENA_INDEX(a);
1001
+ chunk = GET_ARENA_CHUNK(a, arenaIndex);
1002
+ chunkInfoIndex = GET_CHUNK_INFO_INDEX(chunk);
1003
+ if (chunkInfoIndex == NO_FREE_ARENAS) {
1004
+ chunkInfoIndex = arenaIndex;
1005
+ SET_CHUNK_INFO_INDEX(chunk, arenaIndex);
1006
+ ci = GET_CHUNK_INFO(chunk, chunkInfoIndex);
1007
+ a->prev = NULL;
1008
+ ci->lastFreeArena = a;
1009
+ ci->numFreeArenas = 1;
1010
+ AddChunkToList(rt, ci);
1011
+ } else {
1012
+ JS_ASSERT(chunkInfoIndex != arenaIndex);
1013
+ ci = GET_CHUNK_INFO(chunk, chunkInfoIndex);
1014
+ JS_ASSERT(ci->numFreeArenas != 0);
1015
+ JS_ASSERT(ci->lastFreeArena);
1016
+ JS_ASSERT(a != ci->lastFreeArena);
1017
+ if (ci->numFreeArenas == js_gcArenasPerChunk - 1) {
1018
+ RemoveChunkFromList(rt, ci);
1019
+ DestroyGCChunk(chunk);
1020
+ } else {
1021
+ ++ci->numFreeArenas;
1022
+ a->prev = ci->lastFreeArena;
1023
+ ci->lastFreeArena = a;
1024
+ }
1025
+ }
1026
+ }
1027
+ # endif
1028
+ }
1029
+ }
1030
+
1031
+ static void
1032
+ InitGCArenaLists(JSRuntime *rt)
1033
+ {
1034
+ uintN i, thingSize;
1035
+ JSGCArenaList *arenaList;
1036
+
1037
+ for (i = 0; i < GC_NUM_FREELISTS; i++) {
1038
+ arenaList = &rt->gcArenaList[i];
1039
+ thingSize = GC_FREELIST_NBYTES(i);
1040
+ JS_ASSERT((size_t)(uint16)thingSize == thingSize);
1041
+ arenaList->last = NULL;
1042
+ arenaList->lastCount = (uint16) THINGS_PER_ARENA(thingSize);
1043
+ arenaList->thingSize = (uint16) thingSize;
1044
+ arenaList->freeList = NULL;
1045
+ }
1046
+ rt->gcDoubleArenaList.first = NULL;
1047
+ rt->gcDoubleArenaList.nextDoubleFlags = DOUBLE_BITMAP_SENTINEL;
1048
+ }
1049
+
1050
+ static void
1051
+ FinishGCArenaLists(JSRuntime *rt)
1052
+ {
1053
+ uintN i;
1054
+ JSGCArenaList *arenaList;
1055
+
1056
+ for (i = 0; i < GC_NUM_FREELISTS; i++) {
1057
+ arenaList = &rt->gcArenaList[i];
1058
+ DestroyGCArenas(rt, arenaList->last);
1059
+ arenaList->last = NULL;
1060
+ arenaList->lastCount = THINGS_PER_ARENA(arenaList->thingSize);
1061
+ arenaList->freeList = NULL;
1062
+ }
1063
+ DestroyGCArenas(rt, rt->gcDoubleArenaList.first);
1064
+ rt->gcDoubleArenaList.first = NULL;
1065
+ rt->gcDoubleArenaList.nextDoubleFlags = DOUBLE_BITMAP_SENTINEL;
1066
+
1067
+ rt->gcBytes = 0;
1068
+ JS_ASSERT(rt->gcChunkList == 0);
1069
+ }
1070
+
1071
+ /*
1072
+ * This function must not be called when thing is jsdouble.
1073
+ */
1074
+ static uint8 *
1075
+ GetGCThingFlags(void *thing)
1076
+ {
1077
+ JSGCArenaInfo *a;
1078
+ uint32 index;
1079
+
1080
+ a = THING_TO_ARENA(thing);
1081
+ index = THING_TO_INDEX(thing, a->list->thingSize);
1082
+ return THING_FLAGP(a, index);
1083
+ }
1084
+
1085
+ /*
1086
+ * This function returns null when thing is jsdouble.
1087
+ */
1088
+ static uint8 *
1089
+ GetGCThingFlagsOrNull(void *thing)
1090
+ {
1091
+ JSGCArenaInfo *a;
1092
+ uint32 index;
1093
+
1094
+ a = THING_TO_ARENA(thing);
1095
+ if (!a->list)
1096
+ return NULL;
1097
+ index = THING_TO_INDEX(thing, a->list->thingSize);
1098
+ return THING_FLAGP(a, index);
1099
+ }
1100
+
1101
+ intN
1102
+ js_GetExternalStringGCType(JSString *str)
1103
+ {
1104
+ uintN type;
1105
+
1106
+ type = (uintN) *GetGCThingFlags(str) & GCF_TYPEMASK;
1107
+ JS_ASSERT(type == GCX_STRING || type >= GCX_EXTERNAL_STRING);
1108
+ return (type == GCX_STRING) ? -1 : (intN) (type - GCX_EXTERNAL_STRING);
1109
+ }
1110
+
1111
+ static uint32
1112
+ MapGCFlagsToTraceKind(uintN flags)
1113
+ {
1114
+ uint32 type;
1115
+
1116
+ type = flags & GCF_TYPEMASK;
1117
+ JS_ASSERT(type != GCX_DOUBLE);
1118
+ JS_ASSERT(type < GCX_NTYPES);
1119
+ return (type < GCX_EXTERNAL_STRING) ? type : JSTRACE_STRING;
1120
+ }
1121
+
1122
+ JS_FRIEND_API(uint32)
1123
+ js_GetGCThingTraceKind(void *thing)
1124
+ {
1125
+ JSGCArenaInfo *a;
1126
+ uint32 index;
1127
+
1128
+ a = THING_TO_ARENA(thing);
1129
+ if (!a->list)
1130
+ return JSTRACE_DOUBLE;
1131
+
1132
+ index = THING_TO_INDEX(thing, a->list->thingSize);
1133
+ return MapGCFlagsToTraceKind(*THING_FLAGP(a, index));
1134
+ }
1135
+
1136
+ JSRuntime*
1137
+ js_GetGCStringRuntime(JSString *str)
1138
+ {
1139
+ JSGCArenaList *list;
1140
+
1141
+ list = THING_TO_ARENA(str)->list;
1142
+
1143
+ JS_ASSERT(list->thingSize == sizeof(JSGCThing));
1144
+ JS_ASSERT(GC_FREELIST_INDEX(sizeof(JSGCThing)) == 0);
1145
+
1146
+ return (JSRuntime *)((uint8 *)list - offsetof(JSRuntime, gcArenaList));
1147
+ }
1148
+
1149
+ JSBool
1150
+ js_IsAboutToBeFinalized(JSContext *cx, void *thing)
1151
+ {
1152
+ JSGCArenaInfo *a;
1153
+ uint32 index, flags;
1154
+
1155
+ a = THING_TO_ARENA(thing);
1156
+ if (!a->list) {
1157
+ /*
1158
+ * Check if arena has no marked doubles. In that case the bitmap with
1159
+ * the mark flags contains all garbage as it is initialized only when
1160
+ * marking the first double in the arena.
1161
+ */
1162
+ if (!a->u.hasMarkedDoubles)
1163
+ return JS_TRUE;
1164
+ index = DOUBLE_THING_TO_INDEX(thing);
1165
+ return !IsMarkedDouble(a, index);
1166
+ }
1167
+ index = THING_TO_INDEX(thing, a->list->thingSize);
1168
+ flags = *THING_FLAGP(a, index);
1169
+ return !(flags & (GCF_MARK | GCF_LOCK | GCF_FINAL));
1170
+ }
1171
+
1172
+ /* This is compatible with JSDHashEntryStub. */
1173
+ typedef struct JSGCRootHashEntry {
1174
+ JSDHashEntryHdr hdr;
1175
+ void *root;
1176
+ const char *name;
1177
+ } JSGCRootHashEntry;
1178
+
1179
+ /* Initial size of the gcRootsHash table (SWAG, small enough to amortize). */
1180
+ #define GC_ROOTS_SIZE 256
1181
+
1182
+ #if CHUNKED_ARENA_ALLOCATION
1183
+
1184
+ /*
1185
+ * For a CPU with extremely large pages using them for GC things wastes
1186
+ * too much memory.
1187
+ */
1188
+ # define GC_ARENAS_PER_CPU_PAGE_LIMIT JS_BIT(18 - GC_ARENA_SHIFT)
1189
+
1190
+ JS_STATIC_ASSERT(GC_ARENAS_PER_CPU_PAGE_LIMIT <= NO_FREE_ARENAS);
1191
+
1192
+ #endif
1193
+
1194
+ JSBool
1195
+ js_InitGC(JSRuntime *rt, uint32 maxbytes)
1196
+ {
1197
+ #if JS_GC_USE_MMAP
1198
+ if (js_gcArenasPerChunk == 0) {
1199
+ size_t cpuPageSize, arenasPerPage;
1200
+ # if defined(XP_WIN)
1201
+ SYSTEM_INFO si;
1202
+
1203
+ GetSystemInfo(&si);
1204
+ cpuPageSize = si.dwPageSize;
1205
+
1206
+ # elif defined(XP_UNIX) || defined(XP_BEOS)
1207
+ cpuPageSize = (size_t) sysconf(_SC_PAGESIZE);
1208
+ # else
1209
+ # error "Not implemented"
1210
+ # endif
1211
+ /* cpuPageSize is a power of 2. */
1212
+ JS_ASSERT((cpuPageSize & (cpuPageSize - 1)) == 0);
1213
+ arenasPerPage = cpuPageSize >> GC_ARENA_SHIFT;
1214
+ #ifdef DEBUG
1215
+ if (arenasPerPage == 0) {
1216
+ fprintf(stderr,
1217
+ "JS engine warning: the size of the CPU page, %u bytes, is too low to use\n"
1218
+ "paged allocation for the garbage collector. Please report this.\n",
1219
+ (unsigned) cpuPageSize);
1220
+ }
1221
+ #endif
1222
+ if (arenasPerPage - 1 <= (size_t) (GC_ARENAS_PER_CPU_PAGE_LIMIT - 1)) {
1223
+ /*
1224
+ * Use at least 4 GC arenas per paged allocation chunk to minimize
1225
+ * the overhead of mmap/VirtualAlloc.
1226
+ */
1227
+ js_gcUseMmap = JS_TRUE;
1228
+ js_gcArenasPerChunk = JS_MAX((uint32) arenasPerPage, 4);
1229
+ } else {
1230
+ js_gcUseMmap = JS_FALSE;
1231
+ js_gcArenasPerChunk = 7;
1232
+ }
1233
+ }
1234
+ JS_ASSERT(1 <= js_gcArenasPerChunk &&
1235
+ js_gcArenasPerChunk <= NO_FREE_ARENAS);
1236
+ #endif
1237
+
1238
+ InitGCArenaLists(rt);
1239
+ if (!JS_DHashTableInit(&rt->gcRootsHash, JS_DHashGetStubOps(), NULL,
1240
+ sizeof(JSGCRootHashEntry), GC_ROOTS_SIZE)) {
1241
+ rt->gcRootsHash.ops = NULL;
1242
+ return JS_FALSE;
1243
+ }
1244
+ rt->gcLocksHash = NULL; /* create lazily */
1245
+
1246
+ /*
1247
+ * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
1248
+ * for default backward API compatibility.
1249
+ */
1250
+ rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes;
1251
+ rt->gcStackPoolLifespan = 30000;
1252
+
1253
+ METER(memset(&rt->gcStats, 0, sizeof rt->gcStats));
1254
+ return JS_TRUE;
1255
+ }
1256
+
1257
+ #ifdef JS_GCMETER
1258
+
1259
+ static void
1260
+ UpdateArenaStats(JSGCArenaStats *st, uint32 nlivearenas, uint32 nkilledArenas,
1261
+ uint32 nthings)
1262
+ {
1263
+ size_t narenas;
1264
+
1265
+ narenas = nlivearenas + nkilledArenas;
1266
+ JS_ASSERT(narenas >= st->livearenas);
1267
+
1268
+ st->newarenas = narenas - st->livearenas;
1269
+ st->narenas = narenas;
1270
+ st->livearenas = nlivearenas;
1271
+ if (st->maxarenas < narenas)
1272
+ st->maxarenas = narenas;
1273
+ st->totalarenas += narenas;
1274
+
1275
+ st->nthings = nthings;
1276
+ if (st->maxthings < nthings)
1277
+ st->maxthings = nthings;
1278
+ st->totalthings += nthings;
1279
+ }
1280
+
1281
+ JS_FRIEND_API(void)
1282
+ js_DumpGCStats(JSRuntime *rt, FILE *fp)
1283
+ {
1284
+ int i;
1285
+ size_t sumArenas, sumTotalArenas;
1286
+ size_t sumThings, sumMaxThings;
1287
+ size_t sumThingSize, sumTotalThingSize;
1288
+ size_t sumArenaCapacity, sumTotalArenaCapacity;
1289
+ JSGCArenaStats *st;
1290
+ size_t thingSize, thingsPerArena;
1291
+ size_t sumAlloc, sumLocalAlloc, sumFail, sumRetry;
1292
+
1293
+ fprintf(fp, "\nGC allocation statistics:\n");
1294
+
1295
+ #define UL(x) ((unsigned long)(x))
1296
+ #define ULSTAT(x) UL(rt->gcStats.x)
1297
+ #define PERCENT(x,y) (100.0 * (double) (x) / (double) (y))
1298
+
1299
+ sumArenas = 0;
1300
+ sumTotalArenas = 0;
1301
+ sumThings = 0;
1302
+ sumMaxThings = 0;
1303
+ sumThingSize = 0;
1304
+ sumTotalThingSize = 0;
1305
+ sumArenaCapacity = 0;
1306
+ sumTotalArenaCapacity = 0;
1307
+ sumAlloc = 0;
1308
+ sumLocalAlloc = 0;
1309
+ sumFail = 0;
1310
+ sumRetry = 0;
1311
+ for (i = -1; i < (int) GC_NUM_FREELISTS; i++) {
1312
+ if (i == -1) {
1313
+ thingSize = sizeof(jsdouble);
1314
+ thingsPerArena = DOUBLES_PER_ARENA;
1315
+ st = &rt->gcStats.doubleArenaStats;
1316
+ fprintf(fp,
1317
+ "Arena list for double values (%lu doubles per arena):",
1318
+ UL(thingsPerArena));
1319
+ } else {
1320
+ thingSize = rt->gcArenaList[i].thingSize;
1321
+ thingsPerArena = THINGS_PER_ARENA(thingSize);
1322
+ st = &rt->gcStats.arenaStats[i];
1323
+ fprintf(fp,
1324
+ "Arena list %d (thing size %lu, %lu things per arena):",
1325
+ i, UL(GC_FREELIST_NBYTES(i)), UL(thingsPerArena));
1326
+ }
1327
+ if (st->maxarenas == 0) {
1328
+ fputs(" NEVER USED\n", fp);
1329
+ continue;
1330
+ }
1331
+ putc('\n', fp);
1332
+ fprintf(fp, " arenas before GC: %lu\n", UL(st->narenas));
1333
+ fprintf(fp, " new arenas before GC: %lu (%.1f%%)\n",
1334
+ UL(st->newarenas), PERCENT(st->newarenas, st->narenas));
1335
+ fprintf(fp, " arenas after GC: %lu (%.1f%%)\n",
1336
+ UL(st->livearenas), PERCENT(st->livearenas, st->narenas));
1337
+ fprintf(fp, " max arenas: %lu\n", UL(st->maxarenas));
1338
+ fprintf(fp, " things: %lu\n", UL(st->nthings));
1339
+ fprintf(fp, " GC cell utilization: %.1f%%\n",
1340
+ PERCENT(st->nthings, thingsPerArena * st->narenas));
1341
+ fprintf(fp, " average cell utilization: %.1f%%\n",
1342
+ PERCENT(st->totalthings, thingsPerArena * st->totalarenas));
1343
+ fprintf(fp, " max things: %lu\n", UL(st->maxthings));
1344
+ fprintf(fp, " alloc attempts: %lu\n", UL(st->alloc));
1345
+ fprintf(fp, " alloc without locks: %1u (%.1f%%)\n",
1346
+ UL(st->localalloc), PERCENT(st->localalloc, st->alloc));
1347
+ sumArenas += st->narenas;
1348
+ sumTotalArenas += st->totalarenas;
1349
+ sumThings += st->nthings;
1350
+ sumMaxThings += st->maxthings;
1351
+ sumThingSize += thingSize * st->nthings;
1352
+ sumTotalThingSize += thingSize * st->totalthings;
1353
+ sumArenaCapacity += thingSize * thingsPerArena * st->narenas;
1354
+ sumTotalArenaCapacity += thingSize * thingsPerArena * st->totalarenas;
1355
+ sumAlloc += st->alloc;
1356
+ sumLocalAlloc += st->localalloc;
1357
+ sumFail += st->fail;
1358
+ sumRetry += st->retry;
1359
+ }
1360
+ fprintf(fp, "TOTAL STATS:\n");
1361
+ fprintf(fp, " bytes allocated: %lu\n", UL(rt->gcBytes));
1362
+ fprintf(fp, " total GC arenas: %lu\n", UL(sumArenas));
1363
+ fprintf(fp, " total GC things: %lu\n", UL(sumThings));
1364
+ fprintf(fp, " max total GC things: %lu\n", UL(sumMaxThings));
1365
+ fprintf(fp, " GC cell utilization: %.1f%%\n",
1366
+ PERCENT(sumThingSize, sumArenaCapacity));
1367
+ fprintf(fp, " average cell utilization: %.1f%%\n",
1368
+ PERCENT(sumTotalThingSize, sumTotalArenaCapacity));
1369
+ fprintf(fp, "allocation retries after GC: %lu\n", UL(sumRetry));
1370
+ fprintf(fp, " alloc attempts: %lu\n", UL(sumAlloc));
1371
+ fprintf(fp, " alloc without locks: %1u (%.1f%%)\n",
1372
+ UL(sumLocalAlloc), PERCENT(sumLocalAlloc, sumAlloc));
1373
+ fprintf(fp, " allocation failures: %lu\n", UL(sumFail));
1374
+ fprintf(fp, " things born locked: %lu\n", ULSTAT(lockborn));
1375
+ fprintf(fp, " valid lock calls: %lu\n", ULSTAT(lock));
1376
+ fprintf(fp, " valid unlock calls: %lu\n", ULSTAT(unlock));
1377
+ fprintf(fp, " mark recursion depth: %lu\n", ULSTAT(depth));
1378
+ fprintf(fp, " maximum mark recursion: %lu\n", ULSTAT(maxdepth));
1379
+ fprintf(fp, " mark C recursion depth: %lu\n", ULSTAT(cdepth));
1380
+ fprintf(fp, " maximum mark C recursion: %lu\n", ULSTAT(maxcdepth));
1381
+ fprintf(fp, " delayed tracing calls: %lu\n", ULSTAT(untraced));
1382
+ #ifdef DEBUG
1383
+ fprintf(fp, " max trace later count: %lu\n", ULSTAT(maxuntraced));
1384
+ #endif
1385
+ fprintf(fp, " maximum GC nesting level: %lu\n", ULSTAT(maxlevel));
1386
+ fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke));
1387
+ fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree));
1388
+ fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg));
1389
+ fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots));
1390
+ fprintf(fp, "reachable closeable objects: %lu\n", ULSTAT(nclose));
1391
+ fprintf(fp, " max reachable closeable: %lu\n", ULSTAT(maxnclose));
1392
+ fprintf(fp, " scheduled close hooks: %lu\n", ULSTAT(closelater));
1393
+ fprintf(fp, " max scheduled close hooks: %lu\n", ULSTAT(maxcloselater));
1394
+
1395
+ #undef UL
1396
+ #undef ULSTAT
1397
+ #undef PERCENT
1398
+
1399
+ #ifdef JS_ARENAMETER
1400
+ JS_DumpArenaStats(fp);
1401
+ #endif
1402
+ }
1403
+ #endif
1404
+
1405
+ #ifdef DEBUG
1406
+ static void
1407
+ CheckLeakedRoots(JSRuntime *rt);
1408
+ #endif
1409
+
1410
+ void
1411
+ js_FinishGC(JSRuntime *rt)
1412
+ {
1413
+ #ifdef JS_ARENAMETER
1414
+ JS_DumpArenaStats(stdout);
1415
+ #endif
1416
+ #ifdef JS_GCMETER
1417
+ js_DumpGCStats(rt, stdout);
1418
+ #endif
1419
+
1420
+ FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo);
1421
+ FinishGCArenaLists(rt);
1422
+
1423
+ if (rt->gcRootsHash.ops) {
1424
+ #ifdef DEBUG
1425
+ CheckLeakedRoots(rt);
1426
+ #endif
1427
+ JS_DHashTableFinish(&rt->gcRootsHash);
1428
+ rt->gcRootsHash.ops = NULL;
1429
+ }
1430
+ if (rt->gcLocksHash) {
1431
+ JS_DHashTableDestroy(rt->gcLocksHash);
1432
+ rt->gcLocksHash = NULL;
1433
+ }
1434
+ }
1435
+
1436
+ JSBool
1437
+ js_AddRoot(JSContext *cx, void *rp, const char *name)
1438
+ {
1439
+ JSBool ok = js_AddRootRT(cx->runtime, rp, name);
1440
+ if (!ok)
1441
+ JS_ReportOutOfMemory(cx);
1442
+ return ok;
1443
+ }
1444
+
1445
+ JSBool
1446
+ js_AddRootRT(JSRuntime *rt, void *rp, const char *name)
1447
+ {
1448
+ JSBool ok;
1449
+ JSGCRootHashEntry *rhe;
1450
+
1451
+ /*
1452
+ * Due to the long-standing, but now removed, use of rt->gcLock across the
1453
+ * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking
1454
+ * properly with a racing GC, without calling JS_AddRoot from a request.
1455
+ * We have to preserve API compatibility here, now that we avoid holding
1456
+ * rt->gcLock across the mark phase (including the root hashtable mark).
1457
+ *
1458
+ * If the GC is running and we're called on another thread, wait for this
1459
+ * GC activation to finish. We can safely wait here (in the case where we
1460
+ * are called within a request on another thread's context) without fear
1461
+ * of deadlock because the GC doesn't set rt->gcRunning until after it has
1462
+ * waited for all active requests to end.
1463
+ */
1464
+ JS_LOCK_GC(rt);
1465
+ #ifdef JS_THREADSAFE
1466
+ JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0);
1467
+ if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
1468
+ do {
1469
+ JS_AWAIT_GC_DONE(rt);
1470
+ } while (rt->gcLevel > 0);
1471
+ }
1472
+ #endif
1473
+ rhe = (JSGCRootHashEntry *)
1474
+ JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_ADD);
1475
+ if (rhe) {
1476
+ rhe->root = rp;
1477
+ rhe->name = name;
1478
+ ok = JS_TRUE;
1479
+ } else {
1480
+ ok = JS_FALSE;
1481
+ }
1482
+ JS_UNLOCK_GC(rt);
1483
+ return ok;
1484
+ }
1485
+
1486
+ JSBool
1487
+ js_RemoveRoot(JSRuntime *rt, void *rp)
1488
+ {
1489
+ /*
1490
+ * Due to the JS_RemoveRootRT API, we may be called outside of a request.
1491
+ * Same synchronization drill as above in js_AddRoot.
1492
+ */
1493
+ JS_LOCK_GC(rt);
1494
+ #ifdef JS_THREADSAFE
1495
+ JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0);
1496
+ if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
1497
+ do {
1498
+ JS_AWAIT_GC_DONE(rt);
1499
+ } while (rt->gcLevel > 0);
1500
+ }
1501
+ #endif
1502
+ (void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE);
1503
+ rt->gcPoke = JS_TRUE;
1504
+ JS_UNLOCK_GC(rt);
1505
+ return JS_TRUE;
1506
+ }
1507
+
1508
+ #ifdef DEBUG
1509
+
1510
+ JS_STATIC_DLL_CALLBACK(JSDHashOperator)
1511
+ js_root_printer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 i, void *arg)
1512
+ {
1513
+ uint32 *leakedroots = (uint32 *)arg;
1514
+ JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr;
1515
+
1516
+ (*leakedroots)++;
1517
+ fprintf(stderr,
1518
+ "JS engine warning: leaking GC root \'%s\' at %p\n",
1519
+ rhe->name ? (char *)rhe->name : "", rhe->root);
1520
+
1521
+ return JS_DHASH_NEXT;
1522
+ }
1523
+
1524
+ static void
1525
+ CheckLeakedRoots(JSRuntime *rt)
1526
+ {
1527
+ uint32 leakedroots = 0;
1528
+
1529
+ /* Warn (but don't assert) debug builds of any remaining roots. */
1530
+ JS_DHashTableEnumerate(&rt->gcRootsHash, js_root_printer,
1531
+ &leakedroots);
1532
+ if (leakedroots > 0) {
1533
+ if (leakedroots == 1) {
1534
+ fprintf(stderr,
1535
+ "JS engine warning: 1 GC root remains after destroying the JSRuntime at %p.\n"
1536
+ " This root may point to freed memory. Objects reachable\n"
1537
+ " through it have not been finalized.\n",
1538
+ (void *) rt);
1539
+ } else {
1540
+ fprintf(stderr,
1541
+ "JS engine warning: %lu GC roots remain after destroying the JSRuntime at %p.\n"
1542
+ " These roots may point to freed memory. Objects reachable\n"
1543
+ " through them have not been finalized.\n",
1544
+ (unsigned long) leakedroots, (void *) rt);
1545
+ }
1546
+ }
1547
+ }
1548
+
1549
+ typedef struct NamedRootDumpArgs {
1550
+ void (*dump)(const char *name, void *rp, void *data);
1551
+ void *data;
1552
+ } NamedRootDumpArgs;
1553
+
1554
+ JS_STATIC_DLL_CALLBACK(JSDHashOperator)
1555
+ js_named_root_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number,
1556
+ void *arg)
1557
+ {
1558
+ NamedRootDumpArgs *args = (NamedRootDumpArgs *) arg;
1559
+ JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr;
1560
+
1561
+ if (rhe->name)
1562
+ args->dump(rhe->name, rhe->root, args->data);
1563
+ return JS_DHASH_NEXT;
1564
+ }
1565
+
1566
+ void
1567
+ js_DumpNamedRoots(JSRuntime *rt,
1568
+ void (*dump)(const char *name, void *rp, void *data),
1569
+ void *data)
1570
+ {
1571
+ NamedRootDumpArgs args;
1572
+
1573
+ args.dump = dump;
1574
+ args.data = data;
1575
+ JS_DHashTableEnumerate(&rt->gcRootsHash, js_named_root_dumper, &args);
1576
+ }
1577
+
1578
+ #endif /* DEBUG */
1579
+
1580
+ typedef struct GCRootMapArgs {
1581
+ JSGCRootMapFun map;
1582
+ void *data;
1583
+ } GCRootMapArgs;
1584
+
1585
+ JS_STATIC_DLL_CALLBACK(JSDHashOperator)
1586
+ js_gcroot_mapper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number,
1587
+ void *arg)
1588
+ {
1589
+ GCRootMapArgs *args = (GCRootMapArgs *) arg;
1590
+ JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr;
1591
+ intN mapflags;
1592
+ int op;
1593
+
1594
+ mapflags = args->map(rhe->root, rhe->name, args->data);
1595
+
1596
+ #if JS_MAP_GCROOT_NEXT == JS_DHASH_NEXT && \
1597
+ JS_MAP_GCROOT_STOP == JS_DHASH_STOP && \
1598
+ JS_MAP_GCROOT_REMOVE == JS_DHASH_REMOVE
1599
+ op = (JSDHashOperator)mapflags;
1600
+ #else
1601
+ op = JS_DHASH_NEXT;
1602
+ if (mapflags & JS_MAP_GCROOT_STOP)
1603
+ op |= JS_DHASH_STOP;
1604
+ if (mapflags & JS_MAP_GCROOT_REMOVE)
1605
+ op |= JS_DHASH_REMOVE;
1606
+ #endif
1607
+
1608
+ return (JSDHashOperator) op;
1609
+ }
1610
+
1611
+ uint32
1612
+ js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data)
1613
+ {
1614
+ GCRootMapArgs args;
1615
+ uint32 rv;
1616
+
1617
+ args.map = map;
1618
+ args.data = data;
1619
+ JS_LOCK_GC(rt);
1620
+ rv = JS_DHashTableEnumerate(&rt->gcRootsHash, js_gcroot_mapper, &args);
1621
+ JS_UNLOCK_GC(rt);
1622
+ return rv;
1623
+ }
1624
+
1625
+ JSBool
1626
+ js_RegisterCloseableIterator(JSContext *cx, JSObject *obj)
1627
+ {
1628
+ JSRuntime *rt;
1629
+ JSBool ok;
1630
+
1631
+ rt = cx->runtime;
1632
+ JS_ASSERT(!rt->gcRunning);
1633
+
1634
+ JS_LOCK_GC(rt);
1635
+ ok = AddToPtrTable(cx, &rt->gcIteratorTable, &iteratorTableInfo, obj);
1636
+ JS_UNLOCK_GC(rt);
1637
+ return ok;
1638
+ }
1639
+
1640
+ static void
1641
+ CloseNativeIterators(JSContext *cx)
1642
+ {
1643
+ JSRuntime *rt;
1644
+ size_t count, newCount, i;
1645
+ void **array;
1646
+ JSObject *obj;
1647
+
1648
+ rt = cx->runtime;
1649
+ count = rt->gcIteratorTable.count;
1650
+ array = rt->gcIteratorTable.array;
1651
+
1652
+ newCount = 0;
1653
+ for (i = 0; i != count; ++i) {
1654
+ obj = (JSObject *)array[i];
1655
+ if (js_IsAboutToBeFinalized(cx, obj))
1656
+ js_CloseNativeIterator(cx, obj);
1657
+ else
1658
+ array[newCount++] = obj;
1659
+ }
1660
+ ShrinkPtrTable(&rt->gcIteratorTable, &iteratorTableInfo, newCount);
1661
+ }
1662
+
1663
+ #if defined(DEBUG_brendan) || defined(DEBUG_timeless)
1664
+ #define DEBUG_gchist
1665
+ #endif
1666
+
1667
+ #ifdef DEBUG_gchist
1668
+ #define NGCHIST 64
1669
+
1670
+ static struct GCHist {
1671
+ JSBool lastDitch;
1672
+ JSGCThing *freeList;
1673
+ } gchist[NGCHIST];
1674
+
1675
+ unsigned gchpos = 0;
1676
+ #endif
1677
+
1678
+ void *
1679
+ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
1680
+ {
1681
+ JSRuntime *rt;
1682
+ uintN flindex;
1683
+ JSBool doGC;
1684
+ JSGCThing *thing;
1685
+ uint8 *flagp;
1686
+ JSGCArenaList *arenaList;
1687
+ JSGCArenaInfo *a;
1688
+ uintN thingsLimit;
1689
+ JSLocalRootStack *lrs;
1690
+ #ifdef JS_GCMETER
1691
+ JSGCArenaStats *astats;
1692
+ #endif
1693
+ #ifdef JS_THREADSAFE
1694
+ JSBool gcLocked;
1695
+ uintN localMallocBytes;
1696
+ JSGCThing **flbase, **lastptr;
1697
+ JSGCThing *tmpthing;
1698
+ uint8 *tmpflagp;
1699
+ uintN maxFreeThings; /* max to take from the global free list */
1700
+ #endif
1701
+
1702
+ JS_ASSERT((flags & GCF_TYPEMASK) != GCX_DOUBLE);
1703
+ rt = cx->runtime;
1704
+ nbytes = JS_ROUNDUP(nbytes, sizeof(JSGCThing));
1705
+ flindex = GC_FREELIST_INDEX(nbytes);
1706
+
1707
+ /* Updates of metering counters here may not be thread-safe. */
1708
+ METER(astats = &cx->runtime->gcStats.arenaStats[flindex]);
1709
+ METER(astats->alloc++);
1710
+
1711
+ #ifdef JS_THREADSAFE
1712
+ gcLocked = JS_FALSE;
1713
+ JS_ASSERT(cx->thread);
1714
+ flbase = cx->thread->gcFreeLists;
1715
+ JS_ASSERT(flbase);
1716
+ thing = flbase[flindex];
1717
+ localMallocBytes = cx->thread->gcMallocBytes;
1718
+ if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) {
1719
+ flagp = thing->flagp;
1720
+ flbase[flindex] = thing->next;
1721
+ METER(astats->localalloc++);
1722
+ goto success;
1723
+ }
1724
+
1725
+ JS_LOCK_GC(rt);
1726
+ gcLocked = JS_TRUE;
1727
+
1728
+ /* Transfer thread-local counter to global one. */
1729
+ if (localMallocBytes != 0) {
1730
+ cx->thread->gcMallocBytes = 0;
1731
+ if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes)
1732
+ rt->gcMallocBytes = rt->gcMaxMallocBytes;
1733
+ else
1734
+ rt->gcMallocBytes += localMallocBytes;
1735
+ }
1736
+ #endif
1737
+ JS_ASSERT(!rt->gcRunning);
1738
+ if (rt->gcRunning) {
1739
+ METER(rt->gcStats.finalfail++);
1740
+ JS_UNLOCK_GC(rt);
1741
+ return NULL;
1742
+ }
1743
+
1744
+ doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke);
1745
+ #ifdef JS_GC_ZEAL
1746
+ doGC = doGC || rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke);
1747
+ #endif
1748
+
1749
+ arenaList = &rt->gcArenaList[flindex];
1750
+ for (;;) {
1751
+ if (doGC) {
1752
+ /*
1753
+ * Keep rt->gcLock across the call into js_GC so we don't starve
1754
+ * and lose to racing threads who deplete the heap just after
1755
+ * js_GC has replenished it (or has synchronized with a racing
1756
+ * GC that collected a bunch of garbage). This unfair scheduling
1757
+ * can happen on certain operating systems. For the gory details,
1758
+ * see bug 162779 at https://bugzilla.mozilla.org/.
1759
+ */
1760
+ js_GC(cx, GC_LAST_DITCH);
1761
+ METER(astats->retry++);
1762
+ }
1763
+
1764
+ /* Try to get thing from the free list. */
1765
+ thing = arenaList->freeList;
1766
+ if (thing) {
1767
+ arenaList->freeList = thing->next;
1768
+ flagp = thing->flagp;
1769
+ JS_ASSERT(*flagp & GCF_FINAL);
1770
+
1771
+ #ifdef JS_THREADSAFE
1772
+ /*
1773
+ * Refill the local free list by taking several things from the
1774
+ * global free list unless we are still at rt->gcMaxMallocBytes
1775
+ * barrier or the free list is already populated. The former
1776
+ * happens when GC is canceled due to !gcCallback(cx, JSGC_BEGIN)
1777
+ * or no gcPoke. The latter is caused via allocating new things
1778
+ * in gcCallback(cx, JSGC_END).
1779
+ */
1780
+ if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex])
1781
+ break;
1782
+ tmpthing = arenaList->freeList;
1783
+ if (tmpthing) {
1784
+ maxFreeThings = MAX_THREAD_LOCAL_THINGS;
1785
+ do {
1786
+ if (!tmpthing->next)
1787
+ break;
1788
+ tmpthing = tmpthing->next;
1789
+ } while (--maxFreeThings != 0);
1790
+
1791
+ flbase[flindex] = arenaList->freeList;
1792
+ arenaList->freeList = tmpthing->next;
1793
+ tmpthing->next = NULL;
1794
+ }
1795
+ #endif
1796
+ break;
1797
+ }
1798
+
1799
+ /*
1800
+ * Try to allocate things from the last arena. If it is fully used,
1801
+ * check if we can allocate a new one and, if we cannot, consider
1802
+ * doing a "last ditch" GC unless already tried.
1803
+ */
1804
+ thingsLimit = THINGS_PER_ARENA(nbytes);
1805
+ if (arenaList->lastCount != thingsLimit) {
1806
+ JS_ASSERT(arenaList->lastCount < thingsLimit);
1807
+ a = arenaList->last;
1808
+ } else {
1809
+ a = NewGCArena(rt);
1810
+ if (!a) {
1811
+ if (doGC)
1812
+ goto fail;
1813
+ doGC = JS_TRUE;
1814
+ continue;
1815
+ }
1816
+ a->list = arenaList;
1817
+ a->prev = arenaList->last;
1818
+ a->prevUntracedPage = 0;
1819
+ a->u.untracedThings = 0;
1820
+ arenaList->last = a;
1821
+ arenaList->lastCount = 0;
1822
+ }
1823
+
1824
+ flagp = THING_FLAGP(a, arenaList->lastCount);
1825
+ thing = FLAGP_TO_THING(flagp, nbytes);
1826
+ arenaList->lastCount++;
1827
+
1828
+ #ifdef JS_THREADSAFE
1829
+ /*
1830
+ * Refill the local free list by taking free things from the last
1831
+ * arena. Prefer to order free things by ascending address in the
1832
+ * (unscientific) hope of better cache locality.
1833
+ */
1834
+ if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex])
1835
+ break;
1836
+ lastptr = &flbase[flindex];
1837
+ maxFreeThings = thingsLimit - arenaList->lastCount;
1838
+ if (maxFreeThings > MAX_THREAD_LOCAL_THINGS)
1839
+ maxFreeThings = MAX_THREAD_LOCAL_THINGS;
1840
+ while (maxFreeThings != 0) {
1841
+ --maxFreeThings;
1842
+
1843
+ tmpflagp = THING_FLAGP(a, arenaList->lastCount);
1844
+ tmpthing = FLAGP_TO_THING(tmpflagp, nbytes);
1845
+ arenaList->lastCount++;
1846
+ tmpthing->flagp = tmpflagp;
1847
+ *tmpflagp = GCF_FINAL; /* signifying that thing is free */
1848
+
1849
+ *lastptr = tmpthing;
1850
+ lastptr = &tmpthing->next;
1851
+ }
1852
+ *lastptr = NULL;
1853
+ #endif
1854
+ break;
1855
+ }
1856
+
1857
+ /* We successfully allocated the thing. */
1858
+ #ifdef JS_THREADSAFE
1859
+ success:
1860
+ #endif
1861
+ lrs = cx->localRootStack;
1862
+ if (lrs) {
1863
+ /*
1864
+ * If we're in a local root scope, don't set newborn[type] at all, to
1865
+ * avoid entraining garbage from it for an unbounded amount of time
1866
+ * on this context. A caller will leave the local root scope and pop
1867
+ * this reference, allowing thing to be GC'd if it has no other refs.
1868
+ * See JS_EnterLocalRootScope and related APIs.
1869
+ */
1870
+ if (js_PushLocalRoot(cx, lrs, (jsval) thing) < 0) {
1871
+ /*
1872
+ * When we fail for a thing allocated through the tail of the last
1873
+ * arena, thing's flag byte is not initialized. So to prevent GC
1874
+ * accessing the uninitialized flags during the finalization, we
1875
+ * always mark the thing as final. See bug 337407.
1876
+ */
1877
+ *flagp = GCF_FINAL;
1878
+ goto fail;
1879
+ }
1880
+ } else {
1881
+ /*
1882
+ * No local root scope, so we're stuck with the old, fragile model of
1883
+ * depending on a pigeon-hole newborn per type per context.
1884
+ */
1885
+ cx->weakRoots.newborn[flags & GCF_TYPEMASK] = thing;
1886
+ }
1887
+
1888
+ /* We can't fail now, so update flags. */
1889
+ *flagp = (uint8)flags;
1890
+
1891
+ #ifdef DEBUG_gchist
1892
+ gchist[gchpos].lastDitch = doGC;
1893
+ gchist[gchpos].freeList = rt->gcArenaList[flindex].freeList;
1894
+ if (++gchpos == NGCHIST)
1895
+ gchpos = 0;
1896
+ #endif
1897
+
1898
+ /* This is not thread-safe for thread-local allocations. */
1899
+ METER_IF(flags & GCF_LOCK, rt->gcStats.lockborn++);
1900
+
1901
+ #ifdef JS_THREADSAFE
1902
+ if (gcLocked)
1903
+ JS_UNLOCK_GC(rt);
1904
+ #endif
1905
+ JS_COUNT_OPERATION(cx, JSOW_ALLOCATION);
1906
+ return thing;
1907
+
1908
+ fail:
1909
+ #ifdef JS_THREADSAFE
1910
+ if (gcLocked)
1911
+ JS_UNLOCK_GC(rt);
1912
+ #endif
1913
+ METER(astats->fail++);
1914
+ JS_ReportOutOfMemory(cx);
1915
+ return NULL;
1916
+ }
1917
+
1918
+ static JSGCDoubleCell *
1919
+ RefillDoubleFreeList(JSContext *cx)
1920
+ {
1921
+ JSRuntime *rt;
1922
+ jsbitmap *doubleFlags, usedBits;
1923
+ JSBool doGC;
1924
+ JSGCArenaInfo *a;
1925
+ uintN bit, index;
1926
+ JSGCDoubleCell *cell, *list, *lastcell;
1927
+
1928
+ JS_ASSERT(!cx->doubleFreeList);
1929
+
1930
+ rt = cx->runtime;
1931
+ JS_LOCK_GC(rt);
1932
+
1933
+ JS_ASSERT(!rt->gcRunning);
1934
+ if (rt->gcRunning) {
1935
+ METER(rt->gcStats.finalfail++);
1936
+ JS_UNLOCK_GC(rt);
1937
+ return NULL;
1938
+ }
1939
+
1940
+ doGC = rt->gcMallocBytes >= rt->gcMaxMallocBytes && rt->gcPoke;
1941
+ #ifdef JS_GC_ZEAL
1942
+ doGC = doGC || rt->gcZeal >= 2 || (rt->gcZeal >= 1 && rt->gcPoke);
1943
+ #endif
1944
+ if (doGC)
1945
+ goto do_gc;
1946
+
1947
+ /*
1948
+ * Loop until we find a flag bitmap byte with unset bits indicating free
1949
+ * double cells, then set all bits as used and put the cells to the free
1950
+ * list for the current context.
1951
+ */
1952
+ doubleFlags = rt->gcDoubleArenaList.nextDoubleFlags;
1953
+ for (;;) {
1954
+ if (((jsuword) doubleFlags & GC_ARENA_MASK) ==
1955
+ ARENA_INFO_OFFSET) {
1956
+ if (doubleFlags == DOUBLE_BITMAP_SENTINEL ||
1957
+ !((JSGCArenaInfo *) doubleFlags)->prev) {
1958
+ a = NewGCArena(rt);
1959
+ if (!a) {
1960
+ if (doGC) {
1961
+ METER(rt->gcStats.doubleArenaStats.fail++);
1962
+ JS_UNLOCK_GC(rt);
1963
+ JS_ReportOutOfMemory(cx);
1964
+ return NULL;
1965
+ }
1966
+ doGC = JS_TRUE;
1967
+ do_gc:
1968
+ js_GC(cx, GC_LAST_DITCH);
1969
+ METER(rt->gcStats.doubleArenaStats.retry++);
1970
+ doubleFlags = rt->gcDoubleArenaList.nextDoubleFlags;
1971
+ continue;
1972
+ }
1973
+ a->list = NULL;
1974
+ a->prev = NULL;
1975
+ if (doubleFlags == DOUBLE_BITMAP_SENTINEL) {
1976
+ JS_ASSERT(!rt->gcDoubleArenaList.first);
1977
+ rt->gcDoubleArenaList.first = a;
1978
+ } else {
1979
+ JS_ASSERT(rt->gcDoubleArenaList.first);
1980
+ ((JSGCArenaInfo *) doubleFlags)->prev = a;
1981
+ }
1982
+ ClearDoubleArenaFlags(a);
1983
+ doubleFlags = DOUBLE_ARENA_BITMAP(a);
1984
+ break;
1985
+ }
1986
+ doubleFlags =
1987
+ DOUBLE_ARENA_BITMAP(((JSGCArenaInfo *) doubleFlags)->prev);
1988
+ }
1989
+
1990
+ /*
1991
+ * When doubleFlags points the last bitmap's word in the arena, its
1992
+ * high bits corresponds to non-existing cells. ClearDoubleArenaFlags
1993
+ * sets such bits to 1. Thus even for this last word its bit is unset
1994
+ * iff the corresponding cell exists and free.
1995
+ */
1996
+ if (*doubleFlags != (jsbitmap) -1)
1997
+ break;
1998
+ ++doubleFlags;
1999
+ }
2000
+
2001
+ rt->gcDoubleArenaList.nextDoubleFlags = doubleFlags + 1;
2002
+ usedBits = *doubleFlags;
2003
+ JS_ASSERT(usedBits != (jsbitmap) -1);
2004
+ *doubleFlags = (jsbitmap) -1;
2005
+ JS_UNLOCK_GC(rt);
2006
+
2007
+ /*
2008
+ * Find the index corresponding to the first bit in *doubleFlags. The last
2009
+ * bit will have "index + JS_BITS_PER_WORD - 1".
2010
+ */
2011
+ index = ((uintN) ((jsuword) doubleFlags & GC_ARENA_MASK) -
2012
+ DOUBLES_ARENA_BITMAP_OFFSET) * JS_BITS_PER_BYTE;
2013
+ cell = (JSGCDoubleCell *) ((jsuword) doubleFlags & ~GC_ARENA_MASK) + index;
2014
+
2015
+ if (usedBits == 0) {
2016
+ /* The common case when all doubles from *doubleFlags are free. */
2017
+ JS_ASSERT(index + JS_BITS_PER_WORD <= DOUBLES_PER_ARENA);
2018
+ list = cell;
2019
+ for (lastcell = cell + JS_BITS_PER_WORD - 1; cell != lastcell; ++cell)
2020
+ cell->link = cell + 1;
2021
+ lastcell->link = NULL;
2022
+ } else {
2023
+ /*
2024
+ * Assemble the free list from free cells from *doubleFlags starting
2025
+ * from the tail. In the loop
2026
+ *
2027
+ * index + bit >= DOUBLES_PER_ARENA
2028
+ *
2029
+ * when bit is one of the unused bits. We do not check for such bits
2030
+ * explicitly as they must be set and the "if" check filters them out.
2031
+ */
2032
+ JS_ASSERT(index + JS_BITS_PER_WORD <=
2033
+ DOUBLES_PER_ARENA + UNUSED_DOUBLE_BITMAP_BITS);
2034
+ bit = JS_BITS_PER_WORD;
2035
+ cell += bit;
2036
+ list = NULL;
2037
+ do {
2038
+ --bit;
2039
+ --cell;
2040
+ if (!(((jsbitmap) 1 << bit) & usedBits)) {
2041
+ JS_ASSERT(index + bit < DOUBLES_PER_ARENA);
2042
+ JS_ASSERT_IF(index + bit == DOUBLES_PER_ARENA - 1, !list);
2043
+ cell->link = list;
2044
+ list = cell;
2045
+ }
2046
+ } while (bit != 0);
2047
+ }
2048
+ JS_ASSERT(list);
2049
+ JS_COUNT_OPERATION(cx, JSOW_ALLOCATION * JS_BITS_PER_WORD);
2050
+
2051
+ /*
2052
+ * We delegate assigning cx->doubleFreeList to js_NewDoubleInRootedValue as
2053
+ * it immediately consumes the head of the list.
2054
+ */
2055
+ return list;
2056
+ }
2057
+
2058
+ JSBool
2059
+ js_NewDoubleInRootedValue(JSContext *cx, jsdouble d, jsval *vp)
2060
+ {
2061
+ #ifdef JS_GCMETER
2062
+ JSGCArenaStats *astats;
2063
+ #endif
2064
+ JSGCDoubleCell *cell;
2065
+
2066
+ /* Updates of metering counters here are not thread-safe. */
2067
+ METER(astats = &cx->runtime->gcStats.doubleArenaStats);
2068
+ METER(astats->alloc++);
2069
+ cell = cx->doubleFreeList;
2070
+ if (!cell) {
2071
+ cell = RefillDoubleFreeList(cx);
2072
+ if (!cell) {
2073
+ METER(astats->fail++);
2074
+ return JS_FALSE;
2075
+ }
2076
+ } else {
2077
+ METER(astats->localalloc++);
2078
+ }
2079
+ cx->doubleFreeList = cell->link;
2080
+ cell->number = d;
2081
+ *vp = DOUBLE_TO_JSVAL(&cell->number);
2082
+ return JS_TRUE;
2083
+ }
2084
+
2085
+ jsdouble *
2086
+ js_NewWeaklyRootedDouble(JSContext *cx, jsdouble d)
2087
+ {
2088
+ jsval v;
2089
+ jsdouble *dp;
2090
+
2091
+ if (!js_NewDoubleInRootedValue(cx, d, &v))
2092
+ return NULL;
2093
+
2094
+ JS_ASSERT(JSVAL_IS_DOUBLE(v));
2095
+ dp = JSVAL_TO_DOUBLE(v);
2096
+ if (cx->localRootStack) {
2097
+ if (js_PushLocalRoot(cx, cx->localRootStack, v) < 0)
2098
+ return NULL;
2099
+ } else {
2100
+ cx->weakRoots.newborn[GCX_DOUBLE] = dp;
2101
+ }
2102
+ return dp;
2103
+ }
2104
+
2105
+ /*
2106
+ * Shallow GC-things can be locked just by setting the GCF_LOCK bit, because
2107
+ * they have no descendants to mark during the GC. Currently the optimization
2108
+ * is only used for non-dependant strings.
2109
+ */
2110
+ #define GC_THING_IS_SHALLOW(flagp, thing) \
2111
+ ((flagp) && \
2112
+ ((*(flagp) & GCF_TYPEMASK) >= GCX_EXTERNAL_STRING || \
2113
+ ((*(flagp) & GCF_TYPEMASK) == GCX_STRING && \
2114
+ !JSSTRING_IS_DEPENDENT((JSString *) (thing)))))
2115
+
2116
+ /* This is compatible with JSDHashEntryStub. */
2117
+ typedef struct JSGCLockHashEntry {
2118
+ JSDHashEntryHdr hdr;
2119
+ const void *thing;
2120
+ uint32 count;
2121
+ } JSGCLockHashEntry;
2122
+
2123
+ JSBool
2124
+ js_LockGCThingRT(JSRuntime *rt, void *thing)
2125
+ {
2126
+ JSBool shallow, ok;
2127
+ uint8 *flagp;
2128
+ JSGCLockHashEntry *lhe;
2129
+
2130
+ if (!thing)
2131
+ return JS_TRUE;
2132
+
2133
+ flagp = GetGCThingFlagsOrNull(thing);
2134
+ JS_LOCK_GC(rt);
2135
+ shallow = GC_THING_IS_SHALLOW(flagp, thing);
2136
+
2137
+ /*
2138
+ * Avoid adding a rt->gcLocksHash entry for shallow things until someone
2139
+ * nests a lock.
2140
+ */
2141
+ if (shallow && !(*flagp & GCF_LOCK)) {
2142
+ *flagp |= GCF_LOCK;
2143
+ METER(rt->gcStats.lock++);
2144
+ ok = JS_TRUE;
2145
+ goto out;
2146
+ }
2147
+
2148
+ if (!rt->gcLocksHash) {
2149
+ rt->gcLocksHash = JS_NewDHashTable(JS_DHashGetStubOps(), NULL,
2150
+ sizeof(JSGCLockHashEntry),
2151
+ GC_ROOTS_SIZE);
2152
+ if (!rt->gcLocksHash) {
2153
+ ok = JS_FALSE;
2154
+ goto out;
2155
+ }
2156
+ }
2157
+
2158
+ lhe = (JSGCLockHashEntry *)
2159
+ JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_ADD);
2160
+ if (!lhe) {
2161
+ ok = JS_FALSE;
2162
+ goto out;
2163
+ }
2164
+ if (!lhe->thing) {
2165
+ lhe->thing = thing;
2166
+ lhe->count = 1;
2167
+ } else {
2168
+ JS_ASSERT(lhe->count >= 1);
2169
+ lhe->count++;
2170
+ }
2171
+
2172
+ METER(rt->gcStats.lock++);
2173
+ ok = JS_TRUE;
2174
+ out:
2175
+ JS_UNLOCK_GC(rt);
2176
+ return ok;
2177
+ }
2178
+
2179
+ JSBool
2180
+ js_UnlockGCThingRT(JSRuntime *rt, void *thing)
2181
+ {
2182
+ uint8 *flagp;
2183
+ JSBool shallow;
2184
+ JSGCLockHashEntry *lhe;
2185
+
2186
+ if (!thing)
2187
+ return JS_TRUE;
2188
+
2189
+ flagp = GetGCThingFlagsOrNull(thing);
2190
+ JS_LOCK_GC(rt);
2191
+ shallow = GC_THING_IS_SHALLOW(flagp, thing);
2192
+
2193
+ if (shallow && !(*flagp & GCF_LOCK))
2194
+ goto out;
2195
+ if (!rt->gcLocksHash ||
2196
+ (lhe = (JSGCLockHashEntry *)
2197
+ JS_DHashTableOperate(rt->gcLocksHash, thing,
2198
+ JS_DHASH_LOOKUP),
2199
+ JS_DHASH_ENTRY_IS_FREE(&lhe->hdr))) {
2200
+ /* Shallow entry is not in the hash -> clear its lock bit. */
2201
+ if (shallow)
2202
+ *flagp &= ~GCF_LOCK;
2203
+ else
2204
+ goto out;
2205
+ } else {
2206
+ if (--lhe->count != 0)
2207
+ goto out;
2208
+ JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_REMOVE);
2209
+ }
2210
+
2211
+ rt->gcPoke = JS_TRUE;
2212
+ METER(rt->gcStats.unlock++);
2213
+ out:
2214
+ JS_UNLOCK_GC(rt);
2215
+ return JS_TRUE;
2216
+ }
2217
+
2218
+ JS_PUBLIC_API(void)
2219
+ JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
2220
+ {
2221
+ JSObject *obj;
2222
+ size_t nslots, i;
2223
+ jsval v;
2224
+ JSString *str;
2225
+
2226
+ switch (kind) {
2227
+ case JSTRACE_OBJECT:
2228
+ /* If obj has no map, it must be a newborn. */
2229
+ obj = (JSObject *) thing;
2230
+ if (!obj->map)
2231
+ break;
2232
+ if (obj->map->ops->trace) {
2233
+ obj->map->ops->trace(trc, obj);
2234
+ } else {
2235
+ nslots = STOBJ_NSLOTS(obj);
2236
+ for (i = 0; i != nslots; ++i) {
2237
+ v = STOBJ_GET_SLOT(obj, i);
2238
+ if (JSVAL_IS_TRACEABLE(v)) {
2239
+ JS_SET_TRACING_INDEX(trc, "slot", i);
2240
+ JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v),
2241
+ JSVAL_TRACE_KIND(v));
2242
+ }
2243
+ }
2244
+ }
2245
+ break;
2246
+
2247
+ case JSTRACE_STRING:
2248
+ str = (JSString *)thing;
2249
+ if (JSSTRING_IS_DEPENDENT(str))
2250
+ JS_CALL_STRING_TRACER(trc, JSSTRDEP_BASE(str), "base");
2251
+ break;
2252
+
2253
+ #if JS_HAS_XML_SUPPORT
2254
+ case JSTRACE_NAMESPACE:
2255
+ js_TraceXMLNamespace(trc, (JSXMLNamespace *)thing);
2256
+ break;
2257
+
2258
+ case JSTRACE_QNAME:
2259
+ js_TraceXMLQName(trc, (JSXMLQName *)thing);
2260
+ break;
2261
+
2262
+ case JSTRACE_XML:
2263
+ js_TraceXML(trc, (JSXML *)thing);
2264
+ break;
2265
+ #endif
2266
+ }
2267
+ }
2268
+
2269
+ /*
2270
+ * Number of things covered by a single bit of JSGCArenaInfo.u.untracedThings.
2271
+ */
2272
+ #define THINGS_PER_UNTRACED_BIT(thingSize) \
2273
+ JS_HOWMANY(THINGS_PER_ARENA(thingSize), JS_BITS_PER_WORD)
2274
+
2275
+ static void
2276
+ DelayTracingChildren(JSRuntime *rt, uint8 *flagp)
2277
+ {
2278
+ JSGCArenaInfo *a;
2279
+ uint32 untracedBitIndex;
2280
+ jsuword bit;
2281
+
2282
+ /*
2283
+ * Things with children to be traced later are marked with
2284
+ * GCF_MARK | GCF_FINAL flags.
2285
+ */
2286
+ JS_ASSERT((*flagp & (GCF_MARK | GCF_FINAL)) == GCF_MARK);
2287
+ *flagp |= GCF_FINAL;
2288
+
2289
+ METER(rt->gcStats.untraced++);
2290
+ #ifdef DEBUG
2291
+ ++rt->gcTraceLaterCount;
2292
+ METER_UPDATE_MAX(rt->gcStats.maxuntraced, rt->gcTraceLaterCount);
2293
+ #endif
2294
+
2295
+ a = FLAGP_TO_ARENA(flagp);
2296
+ untracedBitIndex = FLAGP_TO_INDEX(flagp) /
2297
+ THINGS_PER_UNTRACED_BIT(a->list->thingSize);
2298
+ JS_ASSERT(untracedBitIndex < JS_BITS_PER_WORD);
2299
+ bit = (jsuword)1 << untracedBitIndex;
2300
+ if (a->u.untracedThings != 0) {
2301
+ JS_ASSERT(rt->gcUntracedArenaStackTop);
2302
+ if (a->u.untracedThings & bit) {
2303
+ /* bit already covers things with children to trace later. */
2304
+ return;
2305
+ }
2306
+ a->u.untracedThings |= bit;
2307
+ } else {
2308
+ /*
2309
+ * The thing is the first thing with not yet traced children in the
2310
+ * whole arena, so push the arena on the stack of arenas with things
2311
+ * to be traced later unless the arena has already been pushed. We
2312
+ * detect that through checking prevUntracedPage as the field is 0
2313
+ * only for not yet pushed arenas. To ensure that
2314
+ * prevUntracedPage != 0
2315
+ * even when the stack contains one element, we make prevUntracedPage
2316
+ * for the arena at the bottom to point to itself.
2317
+ *
2318
+ * See comments in TraceDelayedChildren.
2319
+ */
2320
+ a->u.untracedThings = bit;
2321
+ if (a->prevUntracedPage == 0) {
2322
+ if (!rt->gcUntracedArenaStackTop) {
2323
+ /* Stack was empty, mark the arena as the bottom element. */
2324
+ a->prevUntracedPage = ARENA_INFO_TO_PAGE(a);
2325
+ } else {
2326
+ JS_ASSERT(rt->gcUntracedArenaStackTop->prevUntracedPage != 0);
2327
+ a->prevUntracedPage =
2328
+ ARENA_INFO_TO_PAGE(rt->gcUntracedArenaStackTop);
2329
+ }
2330
+ rt->gcUntracedArenaStackTop = a;
2331
+ }
2332
+ }
2333
+ JS_ASSERT(rt->gcUntracedArenaStackTop);
2334
+ }
2335
+
2336
+ static void
2337
+ TraceDelayedChildren(JSTracer *trc)
2338
+ {
2339
+ JSRuntime *rt;
2340
+ JSGCArenaInfo *a, *aprev;
2341
+ uint32 thingSize;
2342
+ uint32 thingsPerUntracedBit;
2343
+ uint32 untracedBitIndex, thingIndex, indexLimit, endIndex;
2344
+ JSGCThing *thing;
2345
+ uint8 *flagp;
2346
+
2347
+ rt = trc->context->runtime;
2348
+ a = rt->gcUntracedArenaStackTop;
2349
+ if (!a) {
2350
+ JS_ASSERT(rt->gcTraceLaterCount == 0);
2351
+ return;
2352
+ }
2353
+
2354
+ for (;;) {
2355
+ /*
2356
+ * The following assert verifies that the current arena belongs to the
2357
+ * untraced stack, since DelayTracingChildren ensures that even for
2358
+ * stack's bottom prevUntracedPage != 0 but rather points to itself.
2359
+ */
2360
+ JS_ASSERT(a->prevUntracedPage != 0);
2361
+ JS_ASSERT(rt->gcUntracedArenaStackTop->prevUntracedPage != 0);
2362
+ thingSize = a->list->thingSize;
2363
+ indexLimit = (a == a->list->last)
2364
+ ? a->list->lastCount
2365
+ : THINGS_PER_ARENA(thingSize);
2366
+ thingsPerUntracedBit = THINGS_PER_UNTRACED_BIT(thingSize);
2367
+
2368
+ /*
2369
+ * We cannot use do-while loop here as a->u.untracedThings can be zero
2370
+ * before the loop as a leftover from the previous iterations. See
2371
+ * comments after the loop.
2372
+ */
2373
+ while (a->u.untracedThings != 0) {
2374
+ untracedBitIndex = JS_FLOOR_LOG2W(a->u.untracedThings);
2375
+ a->u.untracedThings &= ~((jsuword)1 << untracedBitIndex);
2376
+ thingIndex = untracedBitIndex * thingsPerUntracedBit;
2377
+ endIndex = thingIndex + thingsPerUntracedBit;
2378
+
2379
+ /*
2380
+ * endIndex can go beyond the last allocated thing as the real
2381
+ * limit can be "inside" the bit.
2382
+ */
2383
+ if (endIndex > indexLimit)
2384
+ endIndex = indexLimit;
2385
+ JS_ASSERT(thingIndex < indexLimit);
2386
+
2387
+ do {
2388
+ /*
2389
+ * Skip free or already traced things that share the bit
2390
+ * with untraced ones.
2391
+ */
2392
+ flagp = THING_FLAGP(a, thingIndex);
2393
+ if ((*flagp & (GCF_MARK|GCF_FINAL)) != (GCF_MARK|GCF_FINAL))
2394
+ continue;
2395
+ *flagp &= ~GCF_FINAL;
2396
+ #ifdef DEBUG
2397
+ JS_ASSERT(rt->gcTraceLaterCount != 0);
2398
+ --rt->gcTraceLaterCount;
2399
+ #endif
2400
+ thing = FLAGP_TO_THING(flagp, thingSize);
2401
+ JS_TraceChildren(trc, thing, MapGCFlagsToTraceKind(*flagp));
2402
+ } while (++thingIndex != endIndex);
2403
+ }
2404
+
2405
+ /*
2406
+ * We finished tracing of all things in the the arena but we can only
2407
+ * pop it from the stack if the arena is the stack's top.
2408
+ *
2409
+ * When JS_TraceChildren from the above calls JS_CallTracer that in
2410
+ * turn on low C stack calls DelayTracingChildren and the latter
2411
+ * pushes new arenas to the untraced stack, we have to skip popping
2412
+ * of this arena until it becomes the top of the stack again.
2413
+ */
2414
+ if (a == rt->gcUntracedArenaStackTop) {
2415
+ aprev = ARENA_PAGE_TO_INFO(a->prevUntracedPage);
2416
+ a->prevUntracedPage = 0;
2417
+ if (a == aprev) {
2418
+ /*
2419
+ * prevUntracedPage points to itself and we reached the
2420
+ * bottom of the stack.
2421
+ */
2422
+ break;
2423
+ }
2424
+ rt->gcUntracedArenaStackTop = a = aprev;
2425
+ } else {
2426
+ a = rt->gcUntracedArenaStackTop;
2427
+ }
2428
+ }
2429
+ JS_ASSERT(rt->gcUntracedArenaStackTop);
2430
+ JS_ASSERT(rt->gcUntracedArenaStackTop->prevUntracedPage == 0);
2431
+ rt->gcUntracedArenaStackTop = NULL;
2432
+ JS_ASSERT(rt->gcTraceLaterCount == 0);
2433
+ }
2434
+
2435
+ JS_PUBLIC_API(void)
2436
+ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind)
2437
+ {
2438
+ JSContext *cx;
2439
+ JSRuntime *rt;
2440
+ JSGCArenaInfo *a;
2441
+ uintN index;
2442
+ uint8 *flagp;
2443
+
2444
+ JS_ASSERT(thing);
2445
+ JS_ASSERT(JS_IS_VALID_TRACE_KIND(kind));
2446
+ JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
2447
+
2448
+ if (!IS_GC_MARKING_TRACER(trc)) {
2449
+ trc->callback(trc, thing, kind);
2450
+ goto out;
2451
+ }
2452
+
2453
+ cx = trc->context;
2454
+ rt = cx->runtime;
2455
+ JS_ASSERT(rt->gcMarkingTracer == trc);
2456
+ JS_ASSERT(rt->gcLevel > 0);
2457
+
2458
+ /*
2459
+ * Optimize for string and double as their size is known and their tracing
2460
+ * is not recursive.
2461
+ */
2462
+ switch (kind) {
2463
+ case JSTRACE_DOUBLE:
2464
+ a = THING_TO_ARENA(thing);
2465
+ JS_ASSERT(!a->list);
2466
+ if (!a->u.hasMarkedDoubles) {
2467
+ ClearDoubleArenaFlags(a);
2468
+ a->u.hasMarkedDoubles = JS_TRUE;
2469
+ }
2470
+ index = DOUBLE_THING_TO_INDEX(thing);
2471
+ JS_SET_BIT(DOUBLE_ARENA_BITMAP(a), index);
2472
+ goto out;
2473
+
2474
+ case JSTRACE_STRING:
2475
+ for (;;) {
2476
+ flagp = THING_TO_FLAGP(thing, sizeof(JSGCThing));
2477
+ JS_ASSERT((*flagp & GCF_FINAL) == 0);
2478
+ JS_ASSERT(kind == MapGCFlagsToTraceKind(*flagp));
2479
+ if (!JSSTRING_IS_DEPENDENT((JSString *) thing)) {
2480
+ *flagp |= GCF_MARK;
2481
+ goto out;
2482
+ }
2483
+ if (*flagp & GCF_MARK)
2484
+ goto out;
2485
+ *flagp |= GCF_MARK;
2486
+ thing = JSSTRDEP_BASE((JSString *) thing);
2487
+ }
2488
+ /* NOTREACHED */
2489
+ }
2490
+
2491
+ flagp = GetGCThingFlags(thing);
2492
+ JS_ASSERT(kind == MapGCFlagsToTraceKind(*flagp));
2493
+ if (*flagp & GCF_MARK)
2494
+ goto out;
2495
+
2496
+ /*
2497
+ * We check for non-final flag only if mark is unset as
2498
+ * DelayTracingChildren uses the flag. See comments in the function.
2499
+ */
2500
+ JS_ASSERT(*flagp != GCF_FINAL);
2501
+ *flagp |= GCF_MARK;
2502
+ if (!cx->insideGCMarkCallback) {
2503
+ /*
2504
+ * With JS_GC_ASSUME_LOW_C_STACK defined the mark phase of GC always
2505
+ * uses the non-recursive code that otherwise would be called only on
2506
+ * a low C stack condition.
2507
+ */
2508
+ #ifdef JS_GC_ASSUME_LOW_C_STACK
2509
+ # define RECURSION_TOO_DEEP() JS_TRUE
2510
+ #else
2511
+ int stackDummy;
2512
+ # define RECURSION_TOO_DEEP() (!JS_CHECK_STACK_SIZE(cx, stackDummy))
2513
+ #endif
2514
+ if (RECURSION_TOO_DEEP())
2515
+ DelayTracingChildren(rt, flagp);
2516
+ else
2517
+ JS_TraceChildren(trc, thing, kind);
2518
+ } else {
2519
+ /*
2520
+ * For API compatibility we allow for the callback to assume that
2521
+ * after it calls JS_MarkGCThing for the last time, the callback can
2522
+ * start to finalize its own objects that are only referenced by
2523
+ * unmarked GC things.
2524
+ *
2525
+ * Since we do not know which call from inside the callback is the
2526
+ * last, we ensure that children of all marked things are traced and
2527
+ * call TraceDelayedChildren(trc) after tracing the thing.
2528
+ *
2529
+ * As TraceDelayedChildren unconditionally invokes JS_TraceChildren
2530
+ * for the things with untraced children, calling DelayTracingChildren
2531
+ * is useless here. Hence we always trace thing's children even with a
2532
+ * low native stack.
2533
+ */
2534
+ cx->insideGCMarkCallback = JS_FALSE;
2535
+ JS_TraceChildren(trc, thing, kind);
2536
+ TraceDelayedChildren(trc);
2537
+ cx->insideGCMarkCallback = JS_TRUE;
2538
+ }
2539
+
2540
+ out:
2541
+ #ifdef DEBUG
2542
+ trc->debugPrinter = NULL;
2543
+ trc->debugPrintArg = NULL;
2544
+ #endif
2545
+ return; /* to avoid out: right_curl when DEBUG is not defined */
2546
+ }
2547
+
2548
+ void
2549
+ js_CallValueTracerIfGCThing(JSTracer *trc, jsval v)
2550
+ {
2551
+ void *thing;
2552
+ uint32 kind;
2553
+
2554
+ if (JSVAL_IS_DOUBLE(v) || JSVAL_IS_STRING(v)) {
2555
+ thing = JSVAL_TO_TRACEABLE(v);
2556
+ kind = JSVAL_TRACE_KIND(v);
2557
+ JS_ASSERT(kind == js_GetGCThingTraceKind(JSVAL_TO_GCTHING(v)));
2558
+ } else if (JSVAL_IS_OBJECT(v) && v != JSVAL_NULL) {
2559
+ /* v can be an arbitrary GC thing reinterpreted as an object. */
2560
+ thing = JSVAL_TO_OBJECT(v);
2561
+ kind = js_GetGCThingTraceKind(thing);
2562
+ } else {
2563
+ return;
2564
+ }
2565
+ JS_CallTracer(trc, thing, kind);
2566
+ }
2567
+
2568
+ JS_STATIC_DLL_CALLBACK(JSDHashOperator)
2569
+ gc_root_traversal(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num,
2570
+ void *arg)
2571
+ {
2572
+ JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr;
2573
+ JSTracer *trc = (JSTracer *)arg;
2574
+ jsval *rp = (jsval *)rhe->root;
2575
+ jsval v = *rp;
2576
+
2577
+ /* Ignore null object and scalar values. */
2578
+ if (!JSVAL_IS_NULL(v) && JSVAL_IS_GCTHING(v)) {
2579
+ #ifdef DEBUG
2580
+ JSBool root_points_to_gcArenaList = JS_FALSE;
2581
+ jsuword thing = (jsuword) JSVAL_TO_GCTHING(v);
2582
+ JSRuntime *rt;
2583
+ uintN i;
2584
+ JSGCArenaList *arenaList;
2585
+ uint32 thingSize;
2586
+ JSGCArenaInfo *a;
2587
+ size_t limit;
2588
+
2589
+ rt = trc->context->runtime;
2590
+ for (i = 0; i < GC_NUM_FREELISTS; i++) {
2591
+ arenaList = &rt->gcArenaList[i];
2592
+ thingSize = arenaList->thingSize;
2593
+ limit = (size_t) arenaList->lastCount * thingSize;
2594
+ for (a = arenaList->last; a; a = a->prev) {
2595
+ if (thing - ARENA_INFO_TO_START(a) < limit) {
2596
+ root_points_to_gcArenaList = JS_TRUE;
2597
+ break;
2598
+ }
2599
+ limit = (size_t) THINGS_PER_ARENA(thingSize) * thingSize;
2600
+ }
2601
+ }
2602
+ if (!root_points_to_gcArenaList) {
2603
+ for (a = rt->gcDoubleArenaList.first; a; a = a->prev) {
2604
+ if (thing - ARENA_INFO_TO_START(a) <
2605
+ DOUBLES_PER_ARENA * sizeof(jsdouble)) {
2606
+ root_points_to_gcArenaList = JS_TRUE;
2607
+ break;
2608
+ }
2609
+ }
2610
+ }
2611
+ if (!root_points_to_gcArenaList && rhe->name) {
2612
+ fprintf(stderr,
2613
+ "JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n"
2614
+ "invalid jsval. This is usually caused by a missing call to JS_RemoveRoot.\n"
2615
+ "The root's name is \"%s\".\n",
2616
+ rhe->name);
2617
+ }
2618
+ JS_ASSERT(root_points_to_gcArenaList);
2619
+ #endif
2620
+ JS_SET_TRACING_NAME(trc, rhe->name ? rhe->name : "root");
2621
+ js_CallValueTracerIfGCThing(trc, v);
2622
+ }
2623
+
2624
+ return JS_DHASH_NEXT;
2625
+ }
2626
+
2627
+ JS_STATIC_DLL_CALLBACK(JSDHashOperator)
2628
+ gc_lock_traversal(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num,
2629
+ void *arg)
2630
+ {
2631
+ JSGCLockHashEntry *lhe = (JSGCLockHashEntry *)hdr;
2632
+ void *thing = (void *)lhe->thing;
2633
+ JSTracer *trc = (JSTracer *)arg;
2634
+ uint32 traceKind;
2635
+
2636
+ JS_ASSERT(lhe->count >= 1);
2637
+ traceKind = js_GetGCThingTraceKind(thing);
2638
+ JS_CALL_TRACER(trc, thing, traceKind, "locked object");
2639
+ return JS_DHASH_NEXT;
2640
+ }
2641
+
2642
+ #define TRACE_JSVALS(trc, len, vec, name) \
2643
+ JS_BEGIN_MACRO \
2644
+ jsval _v, *_vp, *_end; \
2645
+ \
2646
+ for (_vp = vec, _end = _vp + len; _vp < _end; _vp++) { \
2647
+ _v = *_vp; \
2648
+ if (JSVAL_IS_TRACEABLE(_v)) { \
2649
+ JS_SET_TRACING_INDEX(trc, name, _vp - (vec)); \
2650
+ JS_CallTracer(trc, JSVAL_TO_TRACEABLE(_v), \
2651
+ JSVAL_TRACE_KIND(_v)); \
2652
+ } \
2653
+ } \
2654
+ JS_END_MACRO
2655
+
2656
+ void
2657
+ js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp)
2658
+ {
2659
+ uintN nslots, minargs, skip;
2660
+
2661
+ if (fp->callobj)
2662
+ JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call");
2663
+ if (fp->argsobj)
2664
+ JS_CALL_OBJECT_TRACER(trc, fp->argsobj, "arguments");
2665
+ if (fp->varobj)
2666
+ JS_CALL_OBJECT_TRACER(trc, fp->varobj, "variables");
2667
+ if (fp->script) {
2668
+ js_TraceScript(trc, fp->script);
2669
+ /*
2670
+ * Don't mark what has not been pushed yet, or what has been
2671
+ * popped already.
2672
+ */
2673
+ if (fp->regs) {
2674
+ nslots = (uintN) (fp->regs->sp - fp->spbase);
2675
+ JS_ASSERT(nslots <= fp->script->depth);
2676
+ TRACE_JSVALS(trc, nslots, fp->spbase, "operand");
2677
+ }
2678
+ }
2679
+
2680
+ /* Allow for primitive this parameter due to JSFUN_THISP_* flags. */
2681
+ JS_ASSERT(JSVAL_IS_OBJECT((jsval)fp->thisp) ||
2682
+ (fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags)));
2683
+ JS_CALL_VALUE_TRACER(trc, (jsval)fp->thisp, "this");
2684
+
2685
+ if (fp->callee)
2686
+ JS_CALL_OBJECT_TRACER(trc, fp->callee, "callee");
2687
+
2688
+ if (fp->argv) {
2689
+ nslots = fp->argc;
2690
+ skip = 0;
2691
+ if (fp->fun) {
2692
+ minargs = FUN_MINARGS(fp->fun);
2693
+ if (minargs > nslots)
2694
+ nslots = minargs;
2695
+ if (!FUN_INTERPRETED(fp->fun)) {
2696
+ JS_ASSERT(!(fp->fun->flags & JSFUN_FAST_NATIVE));
2697
+ nslots += fp->fun->u.n.extra;
2698
+ }
2699
+ if (fp->fun->flags & JSFRAME_ROOTED_ARGV)
2700
+ skip = 2 + fp->argc;
2701
+ }
2702
+ TRACE_JSVALS(trc, 2 + nslots - skip, fp->argv - 2 + skip, "operand");
2703
+ }
2704
+ JS_CALL_VALUE_TRACER(trc, fp->rval, "rval");
2705
+ if (fp->vars)
2706
+ TRACE_JSVALS(trc, fp->nvars, fp->vars, "var");
2707
+ if (fp->scopeChain)
2708
+ JS_CALL_OBJECT_TRACER(trc, fp->scopeChain, "scope chain");
2709
+ if (fp->sharpArray)
2710
+ JS_CALL_OBJECT_TRACER(trc, fp->sharpArray, "sharp array");
2711
+
2712
+ if (fp->xmlNamespace)
2713
+ JS_CALL_OBJECT_TRACER(trc, fp->xmlNamespace, "xmlNamespace");
2714
+ }
2715
+
2716
+ static void
2717
+ TraceWeakRoots(JSTracer *trc, JSWeakRoots *wr)
2718
+ {
2719
+ uint32 i;
2720
+ void *thing;
2721
+
2722
+ #ifdef DEBUG
2723
+ static const char *weakRootNames[JSTRACE_LIMIT] = {
2724
+ "newborn object",
2725
+ "newborn double",
2726
+ "newborn string",
2727
+ "newborn namespace",
2728
+ "newborn qname",
2729
+ "newborn xml"
2730
+ };
2731
+ #endif
2732
+
2733
+ for (i = 0; i != JSTRACE_LIMIT; i++) {
2734
+ thing = wr->newborn[i];
2735
+ if (thing)
2736
+ JS_CALL_TRACER(trc, thing, i, weakRootNames[i]);
2737
+ }
2738
+ JS_ASSERT(i == GCX_EXTERNAL_STRING);
2739
+ for (; i != GCX_NTYPES; ++i) {
2740
+ thing = wr->newborn[i];
2741
+ if (thing) {
2742
+ JS_SET_TRACING_INDEX(trc, "newborn external string",
2743
+ i - GCX_EXTERNAL_STRING);
2744
+ JS_CallTracer(trc, thing, JSTRACE_STRING);
2745
+ }
2746
+ }
2747
+
2748
+ JS_CALL_VALUE_TRACER(trc, wr->lastAtom, "lastAtom");
2749
+ JS_SET_TRACING_NAME(trc, "lastInternalResult");
2750
+ js_CallValueTracerIfGCThing(trc, wr->lastInternalResult);
2751
+ }
2752
+
2753
+ JS_FRIEND_API(void)
2754
+ js_TraceContext(JSTracer *trc, JSContext *acx)
2755
+ {
2756
+ JSArena *a;
2757
+ int64 age;
2758
+ JSStackFrame *fp, *nextChain;
2759
+ JSStackHeader *sh;
2760
+ JSTempValueRooter *tvr;
2761
+
2762
+ if (IS_GC_MARKING_TRACER(trc)) {
2763
+ /*
2764
+ * Release stackPool here, if it has been in existence for longer than
2765
+ * the limit specified by gcStackPoolLifespan.
2766
+ */
2767
+ a = acx->stackPool.current;
2768
+ if (a == acx->stackPool.first.next &&
2769
+ a->avail == a->base + sizeof(int64)) {
2770
+ age = JS_Now() - *(int64 *) a->base;
2771
+ if (age > (int64) acx->runtime->gcStackPoolLifespan * 1000)
2772
+ JS_FinishArenaPool(&acx->stackPool);
2773
+ }
2774
+
2775
+ /*
2776
+ * Clear the double free list to release all the pre-allocated doubles.
2777
+ */
2778
+ acx->doubleFreeList = NULL;
2779
+ }
2780
+
2781
+ /*
2782
+ * Iterate frame chain and dormant chains.
2783
+ *
2784
+ * (NB: see comment on this whole "dormant" thing in js_Execute.)
2785
+ */
2786
+ fp = acx->fp;
2787
+ nextChain = acx->dormantFrameChain;
2788
+ if (!fp)
2789
+ goto next_chain;
2790
+
2791
+ /* The top frame must not be dormant. */
2792
+ JS_ASSERT(!fp->dormantNext);
2793
+ for (;;) {
2794
+ do {
2795
+ js_TraceStackFrame(trc, fp);
2796
+ } while ((fp = fp->down) != NULL);
2797
+
2798
+ next_chain:
2799
+ if (!nextChain)
2800
+ break;
2801
+ fp = nextChain;
2802
+ nextChain = nextChain->dormantNext;
2803
+ }
2804
+
2805
+ /* Mark other roots-by-definition in acx. */
2806
+ if (acx->globalObject)
2807
+ JS_CALL_OBJECT_TRACER(trc, acx->globalObject, "global object");
2808
+ TraceWeakRoots(trc, &acx->weakRoots);
2809
+ if (acx->throwing) {
2810
+ JS_CALL_VALUE_TRACER(trc, acx->exception, "exception");
2811
+ } else {
2812
+ /* Avoid keeping GC-ed junk stored in JSContext.exception. */
2813
+ acx->exception = JSVAL_NULL;
2814
+ }
2815
+ #if JS_HAS_LVALUE_RETURN
2816
+ if (acx->rval2set)
2817
+ JS_CALL_VALUE_TRACER(trc, acx->rval2, "rval2");
2818
+ #endif
2819
+
2820
+ for (sh = acx->stackHeaders; sh; sh = sh->down) {
2821
+ METER(trc->context->runtime->gcStats.stackseg++);
2822
+ METER(trc->context->runtime->gcStats.segslots += sh->nslots);
2823
+ TRACE_JSVALS(trc, sh->nslots, JS_STACK_SEGMENT(sh), "stack");
2824
+ }
2825
+
2826
+ if (acx->localRootStack)
2827
+ js_TraceLocalRoots(trc, acx->localRootStack);
2828
+
2829
+ for (tvr = acx->tempValueRooters; tvr; tvr = tvr->down) {
2830
+ switch (tvr->count) {
2831
+ case JSTVU_SINGLE:
2832
+ JS_SET_TRACING_NAME(trc, "tvr->u.value");
2833
+ js_CallValueTracerIfGCThing(trc, tvr->u.value);
2834
+ break;
2835
+ case JSTVU_TRACE:
2836
+ tvr->u.trace(trc, tvr);
2837
+ break;
2838
+ case JSTVU_SPROP:
2839
+ TRACE_SCOPE_PROPERTY(trc, tvr->u.sprop);
2840
+ break;
2841
+ case JSTVU_WEAK_ROOTS:
2842
+ TraceWeakRoots(trc, tvr->u.weakRoots);
2843
+ break;
2844
+ case JSTVU_PARSE_CONTEXT:
2845
+ js_TraceParseContext(trc, tvr->u.parseContext);
2846
+ break;
2847
+ case JSTVU_SCRIPT:
2848
+ js_TraceScript(trc, tvr->u.script);
2849
+ break;
2850
+ default:
2851
+ JS_ASSERT(tvr->count >= 0);
2852
+ TRACE_JSVALS(trc, tvr->count, tvr->u.array, "tvr->u.array");
2853
+ }
2854
+ }
2855
+
2856
+ if (acx->sharpObjectMap.depth > 0)
2857
+ js_TraceSharpMap(trc, &acx->sharpObjectMap);
2858
+ }
2859
+
2860
+ void
2861
+ js_TraceRuntime(JSTracer *trc, JSBool allAtoms)
2862
+ {
2863
+ JSRuntime *rt = trc->context->runtime;
2864
+ JSContext *iter, *acx;
2865
+
2866
+ JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_traversal, trc);
2867
+ if (rt->gcLocksHash)
2868
+ JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_traversal, trc);
2869
+ js_TraceAtomState(trc, allAtoms);
2870
+ js_TraceNativeIteratorStates(trc);
2871
+ js_TraceRuntimeNumberState(trc);
2872
+
2873
+ iter = NULL;
2874
+ while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL)
2875
+ js_TraceContext(trc, acx);
2876
+
2877
+ if (rt->gcExtraRootsTraceOp)
2878
+ rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
2879
+ }
2880
+
2881
+ static void
2882
+ ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr)
2883
+ {
2884
+ JSObject *obj, *pobj;
2885
+ uint32 slot;
2886
+
2887
+ obj = ssr->obj;
2888
+ pobj = ssr->pobj;
2889
+ slot = ssr->slot;
2890
+
2891
+ while (pobj) {
2892
+ pobj = js_GetWrappedObject(cx, pobj);
2893
+ if (pobj == obj) {
2894
+ ssr->errnum = JSMSG_CYCLIC_VALUE;
2895
+ return;
2896
+ }
2897
+ pobj = JSVAL_TO_OBJECT(STOBJ_GET_SLOT(pobj, slot));
2898
+ }
2899
+
2900
+ pobj = ssr->pobj;
2901
+
2902
+ if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
2903
+ JSScope *scope, *newscope;
2904
+ JSObject *oldproto;
2905
+
2906
+ /* Check to see whether obj shares its prototype's scope. */
2907
+ scope = OBJ_SCOPE(obj);
2908
+ oldproto = STOBJ_GET_PROTO(obj);
2909
+ if (oldproto && OBJ_SCOPE(oldproto) == scope) {
2910
+ /* Either obj needs a new empty scope, or it should share pobj's. */
2911
+ if (!pobj ||
2912
+ !OBJ_IS_NATIVE(pobj) ||
2913
+ OBJ_GET_CLASS(cx, pobj) != STOBJ_GET_CLASS(oldproto)) {
2914
+ /*
2915
+ * With no proto and no scope of its own, obj is truly empty.
2916
+ *
2917
+ * If pobj is not native, obj needs its own empty scope -- it
2918
+ * should not continue to share oldproto's scope once oldproto
2919
+ * is not on obj's prototype chain. That would put properties
2920
+ * from oldproto's scope ahead of properties defined by pobj,
2921
+ * in lookup order.
2922
+ *
2923
+ * If pobj's class differs from oldproto's, we may need a new
2924
+ * scope to handle differences in private and reserved slots,
2925
+ * so we suboptimally but safely make one.
2926
+ */
2927
+ if (!js_GetMutableScope(cx, obj)) {
2928
+ ssr->errnum = JSMSG_OUT_OF_MEMORY;
2929
+ return;
2930
+ }
2931
+ } else if (OBJ_SCOPE(pobj) != scope) {
2932
+ newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
2933
+ obj->map = &newscope->map;
2934
+ js_DropObjectMap(cx, &scope->map, obj);
2935
+ JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
2936
+ }
2937
+ }
2938
+
2939
+ /*
2940
+ * Regenerate property cache shape ids for all of the scopes along the
2941
+ * old prototype chain, in case any property cache entries were filled
2942
+ * by looking up starting from obj.
2943
+ */
2944
+ while (oldproto && OBJ_IS_NATIVE(oldproto)) {
2945
+ scope = OBJ_SCOPE(oldproto);
2946
+ SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
2947
+ oldproto = STOBJ_GET_PROTO(scope->object);
2948
+ }
2949
+ }
2950
+
2951
+ /* Finally, do the deed. */
2952
+ STOBJ_SET_SLOT(obj, slot, OBJECT_TO_JSVAL(pobj));
2953
+ }
2954
+
2955
+ /*
2956
+ * The gckind flag bit GC_LOCK_HELD indicates a call from js_NewGCThing with
2957
+ * rt->gcLock already held, so the lock should be kept on return.
2958
+ */
2959
+ void
2960
+ js_GC(JSContext *cx, JSGCInvocationKind gckind)
2961
+ {
2962
+ JSRuntime *rt;
2963
+ JSBool keepAtoms;
2964
+ JSGCCallback callback;
2965
+ uintN i, type;
2966
+ JSTracer trc;
2967
+ uint32 thingSize, indexLimit;
2968
+ JSGCArenaInfo *a, **ap, *emptyArenas;
2969
+ uint8 flags, *flagp;
2970
+ JSGCThing *thing, *freeList;
2971
+ JSGCArenaList *arenaList;
2972
+ JSBool allClear;
2973
+ #ifdef JS_THREADSAFE
2974
+ uint32 requestDebit;
2975
+ JSContext *acx, *iter;
2976
+ #endif
2977
+ #ifdef JS_GCMETER
2978
+ uint32 nlivearenas, nkilledarenas, nthings;
2979
+ #endif
2980
+
2981
+ rt = cx->runtime;
2982
+ #ifdef JS_THREADSAFE
2983
+ /* Avoid deadlock. */
2984
+ JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt));
2985
+ #endif
2986
+
2987
+ if (gckind & GC_KEEP_ATOMS) {
2988
+ /*
2989
+ * The set slot request and last ditch GC kinds preserve all atoms and
2990
+ * weak roots.
2991
+ */
2992
+ keepAtoms = JS_TRUE;
2993
+ } else {
2994
+ /* Keep atoms when a suspended compile is running on another context. */
2995
+ keepAtoms = (rt->gcKeepAtoms != 0);
2996
+ JS_CLEAR_WEAK_ROOTS(&cx->weakRoots);
2997
+ }
2998
+
2999
+ /*
3000
+ * Don't collect garbage if the runtime isn't up, and cx is not the last
3001
+ * context in the runtime. The last context must force a GC, and nothing
3002
+ * should suppress that final collection or there may be shutdown leaks,
3003
+ * or runtime bloat until the next context is created.
3004
+ */
3005
+ if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT)
3006
+ return;
3007
+
3008
+ restart_at_beginning:
3009
+ /*
3010
+ * Let the API user decide to defer a GC if it wants to (unless this
3011
+ * is the last context). Invoke the callback regardless. Sample the
3012
+ * callback in case we are freely racing with a JS_SetGCCallback{,RT} on
3013
+ * another thread.
3014
+ */
3015
+ if (gckind != GC_SET_SLOT_REQUEST && (callback = rt->gcCallback)) {
3016
+ JSBool ok;
3017
+
3018
+ if (gckind & GC_LOCK_HELD)
3019
+ JS_UNLOCK_GC(rt);
3020
+ ok = callback(cx, JSGC_BEGIN);
3021
+ if (gckind & GC_LOCK_HELD)
3022
+ JS_LOCK_GC(rt);
3023
+ if (!ok && gckind != GC_LAST_CONTEXT)
3024
+ return;
3025
+ }
3026
+
3027
+ /* Lock out other GC allocator and collector invocations. */
3028
+ if (!(gckind & GC_LOCK_HELD))
3029
+ JS_LOCK_GC(rt);
3030
+
3031
+ METER(rt->gcStats.poke++);
3032
+ rt->gcPoke = JS_FALSE;
3033
+
3034
+ #ifdef JS_THREADSAFE
3035
+ JS_ASSERT(cx->thread->id == js_CurrentThreadId());
3036
+
3037
+ /* Bump gcLevel and return rather than nest on this thread. */
3038
+ if (rt->gcThread == cx->thread) {
3039
+ JS_ASSERT(rt->gcLevel > 0);
3040
+ rt->gcLevel++;
3041
+ METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel);
3042
+ if (!(gckind & GC_LOCK_HELD))
3043
+ JS_UNLOCK_GC(rt);
3044
+ return;
3045
+ }
3046
+
3047
+ /*
3048
+ * If we're in one or more requests (possibly on more than one context)
3049
+ * running on the current thread, indicate, temporarily, that all these
3050
+ * requests are inactive. If cx->thread is NULL, then cx is not using
3051
+ * the request model, and does not contribute to rt->requestCount.
3052
+ */
3053
+ requestDebit = 0;
3054
+ if (cx->thread) {
3055
+ JSCList *head, *link;
3056
+
3057
+ /*
3058
+ * Check all contexts on cx->thread->contextList for active requests,
3059
+ * counting each such context against requestDebit.
3060
+ */
3061
+ head = &cx->thread->contextList;
3062
+ for (link = head->next; link != head; link = link->next) {
3063
+ acx = CX_FROM_THREAD_LINKS(link);
3064
+ JS_ASSERT(acx->thread == cx->thread);
3065
+ if (acx->requestDepth)
3066
+ requestDebit++;
3067
+ }
3068
+ } else {
3069
+ /*
3070
+ * We assert, but check anyway, in case someone is misusing the API.
3071
+ * Avoiding the loop over all of rt's contexts is a win in the event
3072
+ * that the GC runs only on request-less contexts with null threads,
3073
+ * in a special thread such as might be used by the UI/DOM/Layout
3074
+ * "mozilla" or "main" thread in Mozilla-the-browser.
3075
+ */
3076
+ JS_ASSERT(cx->requestDepth == 0);
3077
+ if (cx->requestDepth)
3078
+ requestDebit = 1;
3079
+ }
3080
+ if (requestDebit) {
3081
+ JS_ASSERT(requestDebit <= rt->requestCount);
3082
+ rt->requestCount -= requestDebit;
3083
+ if (rt->requestCount == 0)
3084
+ JS_NOTIFY_REQUEST_DONE(rt);
3085
+ }
3086
+
3087
+ /* If another thread is already in GC, don't attempt GC; wait instead. */
3088
+ if (rt->gcLevel > 0) {
3089
+ /* Bump gcLevel to restart the current GC, so it finds new garbage. */
3090
+ rt->gcLevel++;
3091
+ METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel);
3092
+
3093
+ /* Wait for the other thread to finish, then resume our request. */
3094
+ while (rt->gcLevel > 0)
3095
+ JS_AWAIT_GC_DONE(rt);
3096
+ if (requestDebit)
3097
+ rt->requestCount += requestDebit;
3098
+ if (!(gckind & GC_LOCK_HELD))
3099
+ JS_UNLOCK_GC(rt);
3100
+ return;
3101
+ }
3102
+
3103
+ /* No other thread is in GC, so indicate that we're now in GC. */
3104
+ rt->gcLevel = 1;
3105
+ rt->gcThread = cx->thread;
3106
+
3107
+ /* Wait for all other requests to finish. */
3108
+ while (rt->requestCount > 0)
3109
+ JS_AWAIT_REQUEST_DONE(rt);
3110
+
3111
+ #else /* !JS_THREADSAFE */
3112
+
3113
+ /* Bump gcLevel and return rather than nest; the outer gc will restart. */
3114
+ rt->gcLevel++;
3115
+ METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel);
3116
+ if (rt->gcLevel > 1)
3117
+ return;
3118
+
3119
+ #endif /* !JS_THREADSAFE */
3120
+
3121
+ /*
3122
+ * Set rt->gcRunning here within the GC lock, and after waiting for any
3123
+ * active requests to end, so that new requests that try to JS_AddRoot,
3124
+ * JS_RemoveRoot, or JS_RemoveRootRT block in JS_BeginRequest waiting for
3125
+ * rt->gcLevel to drop to zero, while request-less calls to the *Root*
3126
+ * APIs block in js_AddRoot or js_RemoveRoot (see above in this file),
3127
+ * waiting for GC to finish.
3128
+ */
3129
+ rt->gcRunning = JS_TRUE;
3130
+
3131
+ if (gckind == GC_SET_SLOT_REQUEST) {
3132
+ JSSetSlotRequest *ssr;
3133
+
3134
+ while ((ssr = rt->setSlotRequests) != NULL) {
3135
+ rt->setSlotRequests = ssr->next;
3136
+ JS_UNLOCK_GC(rt);
3137
+ ssr->next = NULL;
3138
+ ProcessSetSlotRequest(cx, ssr);
3139
+ JS_LOCK_GC(rt);
3140
+ }
3141
+
3142
+ /*
3143
+ * We assume here that killing links to parent and prototype objects
3144
+ * does not create garbage (such objects typically are long-lived and
3145
+ * widely shared, e.g. global objects, Function.prototype, etc.). We
3146
+ * collect garbage only if a racing thread attempted GC and is waiting
3147
+ * for us to finish (gcLevel > 1) or if someone already poked us.
3148
+ */
3149
+ if (rt->gcLevel == 1 && !rt->gcPoke)
3150
+ goto done_running;
3151
+
3152
+ rt->gcLevel = 0;
3153
+ rt->gcPoke = JS_FALSE;
3154
+ rt->gcRunning = JS_FALSE;
3155
+ #ifdef JS_THREADSAFE
3156
+ rt->gcThread = NULL;
3157
+ rt->requestCount += requestDebit;
3158
+ #endif
3159
+ gckind = GC_LOCK_HELD;
3160
+ goto restart_at_beginning;
3161
+ }
3162
+
3163
+ JS_UNLOCK_GC(rt);
3164
+
3165
+ /* Reset malloc counter. */
3166
+ rt->gcMallocBytes = 0;
3167
+
3168
+ #ifdef JS_DUMP_SCOPE_METERS
3169
+ { extern void js_DumpScopeMeters(JSRuntime *rt);
3170
+ js_DumpScopeMeters(rt);
3171
+ }
3172
+ #endif
3173
+
3174
+ /*
3175
+ * Clear property cache weak references and disable the cache so nothing
3176
+ * can fill it during GC (this is paranoia, since scripts should not run
3177
+ * during GC).
3178
+ */
3179
+ js_DisablePropertyCache(cx);
3180
+ js_FlushPropertyCache(cx);
3181
+
3182
+ #ifdef JS_THREADSAFE
3183
+ /*
3184
+ * Set all thread local freelists to NULL. We may visit a thread's
3185
+ * freelist more than once. To avoid redundant clearing we unroll the
3186
+ * current thread's step.
3187
+ *
3188
+ * Also, in case a JSScript wrapped within an object was finalized, we
3189
+ * null acx->thread->gsnCache.script and finish the cache's hashtable.
3190
+ * Note that js_DestroyScript, called from script_finalize, will have
3191
+ * already cleared cx->thread->gsnCache above during finalization, so we
3192
+ * don't have to here.
3193
+ */
3194
+ memset(cx->thread->gcFreeLists, 0, sizeof cx->thread->gcFreeLists);
3195
+ iter = NULL;
3196
+ while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
3197
+ if (!acx->thread || acx->thread == cx->thread)
3198
+ continue;
3199
+ memset(acx->thread->gcFreeLists, 0, sizeof acx->thread->gcFreeLists);
3200
+ GSN_CACHE_CLEAR(&acx->thread->gsnCache);
3201
+ js_DisablePropertyCache(acx);
3202
+ js_FlushPropertyCache(acx);
3203
+ }
3204
+ #else
3205
+ /* The thread-unsafe case just has to clear the runtime's GSN cache. */
3206
+ GSN_CACHE_CLEAR(&rt->gsnCache);
3207
+ #endif
3208
+
3209
+ restart:
3210
+ rt->gcNumber++;
3211
+ JS_ASSERT(!rt->gcUntracedArenaStackTop);
3212
+ JS_ASSERT(rt->gcTraceLaterCount == 0);
3213
+
3214
+ /* Reset the property cache's type id generator so we can compress ids. */
3215
+ rt->shapeGen = 0;
3216
+
3217
+ /*
3218
+ * Mark phase.
3219
+ */
3220
+ JS_TRACER_INIT(&trc, cx, NULL);
3221
+ rt->gcMarkingTracer = &trc;
3222
+ JS_ASSERT(IS_GC_MARKING_TRACER(&trc));
3223
+
3224
+ for (a = rt->gcDoubleArenaList.first; a; a = a->prev)
3225
+ a->u.hasMarkedDoubles = JS_FALSE;
3226
+
3227
+ js_TraceRuntime(&trc, keepAtoms);
3228
+ js_MarkScriptFilenames(rt, keepAtoms);
3229
+
3230
+ /*
3231
+ * Mark children of things that caused too deep recursion during the above
3232
+ * tracing.
3233
+ */
3234
+ TraceDelayedChildren(&trc);
3235
+
3236
+ JS_ASSERT(!cx->insideGCMarkCallback);
3237
+ if (rt->gcCallback) {
3238
+ cx->insideGCMarkCallback = JS_TRUE;
3239
+ (void) rt->gcCallback(cx, JSGC_MARK_END);
3240
+ JS_ASSERT(cx->insideGCMarkCallback);
3241
+ cx->insideGCMarkCallback = JS_FALSE;
3242
+ }
3243
+ JS_ASSERT(rt->gcTraceLaterCount == 0);
3244
+
3245
+ rt->gcMarkingTracer = NULL;
3246
+
3247
+ /*
3248
+ * Sweep phase.
3249
+ *
3250
+ * Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set
3251
+ * so that any attempt to allocate a GC-thing from a finalizer will fail,
3252
+ * rather than nest badly and leave the unmarked newborn to be swept.
3253
+ *
3254
+ * We first sweep atom state so we can use js_IsAboutToBeFinalized on
3255
+ * JSString or jsdouble held in a hashtable to check if the hashtable
3256
+ * entry can be freed. Note that even after the entry is freed, JSObject
3257
+ * finalizers can continue to access the corresponding jsdouble* and
3258
+ * JSString* assuming that they are unique. This works since the
3259
+ * atomization API must not be called during GC.
3260
+ */
3261
+ js_SweepAtomState(cx);
3262
+
3263
+ /* Finalize iterator states before the objects they iterate over. */
3264
+ CloseNativeIterators(cx);
3265
+
3266
+ /* Finalize watch points associated with unreachable objects. */
3267
+ js_SweepWatchPoints(cx);
3268
+
3269
+ #ifdef DEBUG
3270
+ /* Save the pre-sweep count of scope-mapped properties. */
3271
+ rt->liveScopePropsPreSweep = rt->liveScopeProps;
3272
+ #endif
3273
+
3274
+ /*
3275
+ * Here we need to ensure that JSObject instances are finalized before GC-
3276
+ * allocated JSString and jsdouble instances so object's finalizer can
3277
+ * access them even if they will be freed. For that we simply finalize the
3278
+ * list containing JSObject first since the static assert at the beginning
3279
+ * of the file guarantees that JSString and jsdouble instances are
3280
+ * allocated from a different list.
3281
+ */
3282
+ emptyArenas = NULL;
3283
+ for (i = 0; i < GC_NUM_FREELISTS; i++) {
3284
+ arenaList = &rt->gcArenaList[i == 0
3285
+ ? GC_FREELIST_INDEX(sizeof(JSObject))
3286
+ : i == GC_FREELIST_INDEX(sizeof(JSObject))
3287
+ ? 0
3288
+ : i];
3289
+ ap = &arenaList->last;
3290
+ if (!(a = *ap))
3291
+ continue;
3292
+
3293
+ JS_ASSERT(arenaList->lastCount > 0);
3294
+ arenaList->freeList = NULL;
3295
+ freeList = NULL;
3296
+ thingSize = arenaList->thingSize;
3297
+ indexLimit = THINGS_PER_ARENA(thingSize);
3298
+ flagp = THING_FLAGP(a, arenaList->lastCount - 1);
3299
+ METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0));
3300
+ for (;;) {
3301
+ JS_ASSERT(a->prevUntracedPage == 0);
3302
+ JS_ASSERT(a->u.untracedThings == 0);
3303
+ allClear = JS_TRUE;
3304
+ do {
3305
+ flags = *flagp;
3306
+ if (flags & (GCF_MARK | GCF_LOCK)) {
3307
+ *flagp &= ~GCF_MARK;
3308
+ allClear = JS_FALSE;
3309
+ METER(nthings++);
3310
+ } else {
3311
+ thing = FLAGP_TO_THING(flagp, thingSize);
3312
+ if (!(flags & GCF_FINAL)) {
3313
+ /*
3314
+ * Call the finalizer with GCF_FINAL ORed into flags.
3315
+ */
3316
+ *flagp = (uint8)(flags | GCF_FINAL);
3317
+ type = flags & GCF_TYPEMASK;
3318
+ switch (type) {
3319
+ case GCX_OBJECT:
3320
+ js_FinalizeObject(cx, (JSObject *) thing);
3321
+ break;
3322
+ case GCX_DOUBLE:
3323
+ /* Do nothing. */
3324
+ break;
3325
+ #if JS_HAS_XML_SUPPORT
3326
+ case GCX_NAMESPACE:
3327
+ js_FinalizeXMLNamespace(cx,
3328
+ (JSXMLNamespace *) thing);
3329
+ break;
3330
+ case GCX_QNAME:
3331
+ js_FinalizeXMLQName(cx, (JSXMLQName *) thing);
3332
+ break;
3333
+ case GCX_XML:
3334
+ js_FinalizeXML(cx, (JSXML *) thing);
3335
+ break;
3336
+ #endif
3337
+ default:
3338
+ JS_ASSERT(type == GCX_STRING ||
3339
+ type - GCX_EXTERNAL_STRING <
3340
+ GCX_NTYPES - GCX_EXTERNAL_STRING);
3341
+ js_FinalizeStringRT(rt, (JSString *) thing,
3342
+ (intN) (type -
3343
+ GCX_EXTERNAL_STRING),
3344
+ cx);
3345
+ break;
3346
+ }
3347
+ #ifdef DEBUG
3348
+ memset(thing, JS_FREE_PATTERN, thingSize);
3349
+ #endif
3350
+ }
3351
+ thing->flagp = flagp;
3352
+ thing->next = freeList;
3353
+ freeList = thing;
3354
+ }
3355
+ } while (++flagp != THING_FLAGS_END(a));
3356
+
3357
+ if (allClear) {
3358
+ /*
3359
+ * Forget just assembled free list head for the arena and
3360
+ * add the arena itself to the destroy list.
3361
+ */
3362
+ freeList = arenaList->freeList;
3363
+ if (a == arenaList->last)
3364
+ arenaList->lastCount = (uint16) indexLimit;
3365
+ *ap = a->prev;
3366
+ a->prev = emptyArenas;
3367
+ emptyArenas = a;
3368
+ METER(nkilledarenas++);
3369
+ } else {
3370
+ arenaList->freeList = freeList;
3371
+ ap = &a->prev;
3372
+ METER(nlivearenas++);
3373
+ }
3374
+ if (!(a = *ap))
3375
+ break;
3376
+ flagp = THING_FLAGP(a, indexLimit - 1);
3377
+ }
3378
+
3379
+ /*
3380
+ * We use arenaList - &rt->gcArenaList[0], not i, as the stat index
3381
+ * due to the enumeration reorder at the beginning of the loop.
3382
+ */
3383
+ METER(UpdateArenaStats(&rt->gcStats.arenaStats[arenaList -
3384
+ &rt->gcArenaList[0]],
3385
+ nlivearenas, nkilledarenas, nthings));
3386
+ }
3387
+
3388
+ ap = &rt->gcDoubleArenaList.first;
3389
+ METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0));
3390
+ while ((a = *ap) != NULL) {
3391
+ if (!a->u.hasMarkedDoubles) {
3392
+ /* No marked double values in the arena. */
3393
+ *ap = a->prev;
3394
+ a->prev = emptyArenas;
3395
+ emptyArenas = a;
3396
+ METER(nkilledarenas++);
3397
+ } else {
3398
+ ap = &a->prev;
3399
+ #ifdef JS_GCMETER
3400
+ for (i = 0; i != DOUBLES_PER_ARENA; ++i) {
3401
+ if (IsMarkedDouble(a, index))
3402
+ METER(nthings++);
3403
+ }
3404
+ METER(nlivearenas++);
3405
+ #endif
3406
+ }
3407
+ }
3408
+ METER(UpdateArenaStats(&rt->gcStats.doubleArenaStats,
3409
+ nlivearenas, nkilledarenas, nthings));
3410
+ rt->gcDoubleArenaList.nextDoubleFlags =
3411
+ rt->gcDoubleArenaList.first
3412
+ ? DOUBLE_ARENA_BITMAP(rt->gcDoubleArenaList.first)
3413
+ : DOUBLE_BITMAP_SENTINEL;
3414
+
3415
+ /*
3416
+ * Sweep the runtime's property tree after finalizing objects, in case any
3417
+ * had watchpoints referencing tree nodes.
3418
+ */
3419
+ js_SweepScopeProperties(cx);
3420
+
3421
+ /*
3422
+ * Sweep script filenames after sweeping functions in the generic loop
3423
+ * above. In this way when a scripted function's finalizer destroys the
3424
+ * script and calls rt->destroyScriptHook, the hook can still access the
3425
+ * script's filename. See bug 323267.
3426
+ */
3427
+ js_SweepScriptFilenames(rt);
3428
+
3429
+ /*
3430
+ * Destroy arenas after we finished the sweeping sofinalizers can safely
3431
+ * use js_IsAboutToBeFinalized().
3432
+ */
3433
+ DestroyGCArenas(rt, emptyArenas);
3434
+
3435
+ if (rt->gcCallback)
3436
+ (void) rt->gcCallback(cx, JSGC_FINALIZE_END);
3437
+ #ifdef DEBUG_srcnotesize
3438
+ { extern void DumpSrcNoteSizeHist();
3439
+ DumpSrcNoteSizeHist();
3440
+ printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes);
3441
+ }
3442
+ #endif
3443
+
3444
+ #ifdef JS_SCOPE_DEPTH_METER
3445
+ { static FILE *fp;
3446
+ if (!fp)
3447
+ fp = fopen("/tmp/scopedepth.stats", "w");
3448
+
3449
+ if (fp) {
3450
+ JS_DumpBasicStats(&rt->protoLookupDepthStats, "proto-lookup depth", fp);
3451
+ JS_DumpBasicStats(&rt->scopeSearchDepthStats, "scope-search depth", fp);
3452
+ JS_DumpBasicStats(&rt->hostenvScopeDepthStats, "hostenv scope depth", fp);
3453
+ JS_DumpBasicStats(&rt->lexicalScopeDepthStats, "lexical scope depth", fp);
3454
+
3455
+ putc('\n', fp);
3456
+ fflush(fp);
3457
+ }
3458
+ }
3459
+ #endif /* JS_SCOPE_DEPTH_METER */
3460
+
3461
+ JS_LOCK_GC(rt);
3462
+
3463
+ /*
3464
+ * We want to restart GC if js_GC was called recursively or if any of the
3465
+ * finalizers called js_RemoveRoot or js_UnlockGCThingRT.
3466
+ */
3467
+ if (rt->gcLevel > 1 || rt->gcPoke) {
3468
+ rt->gcLevel = 1;
3469
+ rt->gcPoke = JS_FALSE;
3470
+ JS_UNLOCK_GC(rt);
3471
+ goto restart;
3472
+ }
3473
+
3474
+ if (!(rt->shapeGen & SHAPE_OVERFLOW_BIT)) {
3475
+ js_EnablePropertyCache(cx);
3476
+ #ifdef JS_THREADSAFE
3477
+ iter = NULL;
3478
+ while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
3479
+ if (!acx->thread || acx->thread == cx->thread)
3480
+ continue;
3481
+ js_EnablePropertyCache(acx);
3482
+ }
3483
+ #endif
3484
+ }
3485
+
3486
+ rt->gcLastBytes = rt->gcBytes;
3487
+ done_running:
3488
+ rt->gcLevel = 0;
3489
+ rt->gcRunning = JS_FALSE;
3490
+
3491
+ #ifdef JS_THREADSAFE
3492
+ /* If we were invoked during a request, pay back the temporary debit. */
3493
+ if (requestDebit)
3494
+ rt->requestCount += requestDebit;
3495
+ rt->gcThread = NULL;
3496
+ JS_NOTIFY_GC_DONE(rt);
3497
+
3498
+ /*
3499
+ * Unlock unless we have GC_LOCK_HELD which requires locked GC on return.
3500
+ */
3501
+ if (!(gckind & GC_LOCK_HELD))
3502
+ JS_UNLOCK_GC(rt);
3503
+ #endif
3504
+
3505
+ /*
3506
+ * Execute JSGC_END callback outside the lock. Again, sample the callback
3507
+ * pointer in case it changes, since we are outside of the GC vs. requests
3508
+ * interlock mechanism here.
3509
+ */
3510
+ if (gckind != GC_SET_SLOT_REQUEST && (callback = rt->gcCallback)) {
3511
+ JSWeakRoots savedWeakRoots;
3512
+ JSTempValueRooter tvr;
3513
+
3514
+ if (gckind & GC_KEEP_ATOMS) {
3515
+ /*
3516
+ * We allow JSGC_END implementation to force a full GC or allocate
3517
+ * new GC things. Thus we must protect the weak roots from garbage
3518
+ * collection and overwrites.
3519
+ */
3520
+ savedWeakRoots = cx->weakRoots;
3521
+ JS_PUSH_TEMP_ROOT_WEAK_COPY(cx, &savedWeakRoots, &tvr);
3522
+ JS_KEEP_ATOMS(rt);
3523
+ JS_UNLOCK_GC(rt);
3524
+ }
3525
+
3526
+ (void) callback(cx, JSGC_END);
3527
+
3528
+ if (gckind & GC_KEEP_ATOMS) {
3529
+ JS_LOCK_GC(rt);
3530
+ JS_UNKEEP_ATOMS(rt);
3531
+ JS_POP_TEMP_ROOT(cx, &tvr);
3532
+ } else if (gckind == GC_LAST_CONTEXT && rt->gcPoke) {
3533
+ /*
3534
+ * On shutdown iterate until JSGC_END callback stops creating
3535
+ * garbage.
3536
+ */
3537
+ goto restart_at_beginning;
3538
+ }
3539
+ }
3540
+ }
3541
+
3542
+ void
3543
+ js_UpdateMallocCounter(JSContext *cx, size_t nbytes)
3544
+ {
3545
+ uint32 *pbytes, bytes;
3546
+
3547
+ #ifdef JS_THREADSAFE
3548
+ pbytes = &cx->thread->gcMallocBytes;
3549
+ #else
3550
+ pbytes = &cx->runtime->gcMallocBytes;
3551
+ #endif
3552
+ bytes = *pbytes;
3553
+ *pbytes = ((uint32)-1 - bytes <= nbytes) ? (uint32)-1 : bytes + nbytes;
3554
+ }