Bug 509857 - Get rid of DTOA_LOCK. r=jwalden.
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 22 Mar 2010 18:21:10 -0500
changeset 40339 cdcdaf01be6ce6eb346874e839b8cc4f92b2ea2b
parent 40338 3383cccf276c716330756aaee585f3d6b5cd59a1
child 40340 99f1e94c77a302c5c103410ee2e36cb2735a0e42
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs509857
milestone1.9.3a3pre
Bug 509857 - Get rid of DTOA_LOCK. r=jwalden.
js/src/dtoa.c
js/src/jsapi.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsdate.cpp
js/src/jsdtoa.cpp
js/src/jsdtoa.h
js/src/jsnum.cpp
js/src/json.cpp
js/src/jsopcode.cpp
--- a/js/src/dtoa.c
+++ b/js/src/dtoa.c
@@ -96,19 +96,20 @@
  *	define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,
  *	FLT_RADIX, FLT_ROUNDS, and DBL_MAX.
  * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n)
  *	if memory is available and otherwise does something you deem
  *	appropriate.  If MALLOC is undefined, malloc will be invoked
  *	directly -- and assumed always to succeed.  Similarly, if you
  *	want something other than the system's free() to be called to
  *	recycle memory acquired from MALLOC, #define FREE to be the
- *	name of the alternate routine.  (FREE or free is only called in
- *	pathological cases, e.g., in a dtoa call after a dtoa return in
- *	mode 3 with thousands of digits requested.)
+ *	name of the alternate routine.  (Unless you #define
+ *	NO_GLOBAL_STATE and call destroydtoa, FREE or free is only
+ *	called in pathological cases, e.g., in a dtoa call after a dtoa
+ *	return in mode 3 with thousands of digits requested.)
  * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making
  *	memory allocations from a private pool of memory when possible.
  *	When used, the private pool is PRIVATE_MEM bytes long:  2304 bytes,
  *	unless #defined to be a different length.  This default length
  *	suffices to get rid of MALLOC calls except for unusual cases,
  *	such as decimal-to-binary conversion of a very long string of
  *	digits.  The longest string dtoa can return is about 751 bytes
  *	long.  For conversions by strtod of strings of 800 digits and
@@ -159,16 +160,22 @@
  *	such that get_inexact() returns a nonzero value if the
  *	inexact bit is already set, and clear_inexact() sets the
  *	inexact bit to 0.  When SET_INEXACT is #defined, strtod
  *	also does extra computations to set the underflow and overflow
  *	flags when appropriate (i.e., when the result is tiny and
  *	inexact or when it is a numeric value rounded to +-infinity).
  * #define NO_ERRNO if strtod should not assign errno = ERANGE when
  *	the result overflows to +-Infinity or underflows to 0.
+ * #define NO_GLOBAL_STATE to avoid defining any non-const global or
+ *	static variables. Instead the necessary state is stored in an
+ *	opaque struct, DtoaState, a pointer to which must be passed to
+ *	every entry point. Two new functions are added to the API:
+ *		DtoaState *newdtoa(void);
+ *		void destroydtoa(DtoaState *);
  */
 
 #ifndef Long
 #define Long long
 #endif
 #ifndef ULong
 typedef unsigned Long ULong;
 #endif
@@ -190,22 +197,25 @@ typedef unsigned Long ULong;
 extern char *MALLOC();
 #else
 extern void *MALLOC(size_t);
 #endif
 #else
 #define MALLOC malloc
 #endif
 
+#ifndef FREE
+#define FREE free
+#endif
+
 #ifndef Omit_Private_Memory
 #ifndef PRIVATE_MEM
 #define PRIVATE_MEM 2304
 #endif
 #define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))
-static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
 #endif
 
 #undef IEEE_Arith
 #undef Avoid_Underflow
 #ifdef IEEE_MC68k
 #define IEEE_Arith
 #endif
 #ifdef IEEE_8087
@@ -474,92 +484,161 @@ extern double rnd_prod(double, double), 
 Bigint {
 	struct Bigint *next;
 	int k, maxwds, sign, wds;
 	ULong x[1];
 	};
 
  typedef struct Bigint Bigint;
 
- static Bigint *freelist[Kmax+1];
+#ifdef NO_GLOBAL_STATE
+#ifdef MULTIPLE_THREADS
+#error "cannot have both NO_GLOBAL_STATE and MULTIPLE_THREADS"
+#endif
+ struct
+DtoaState {
+#define DECLARE_GLOBAL_STATE  /* nothing */
+#else
+#define DECLARE_GLOBAL_STATE static
+#endif
+
+	DECLARE_GLOBAL_STATE Bigint *freelist[Kmax+1];
+	DECLARE_GLOBAL_STATE Bigint *p5s;
+#ifndef Omit_Private_Memory
+	DECLARE_GLOBAL_STATE double private_mem[PRIVATE_mem];
+	DECLARE_GLOBAL_STATE double *pmem_next
+#ifndef NO_GLOBAL_STATE
+	                                       = private_mem
+#endif
+	                                                    ;
+#endif
+#ifdef NO_GLOBAL_STATE
+	};
+ typedef struct DtoaState DtoaState;
+#ifdef KR_headers
+#define STATE_PARAM state,
+#define STATE_PARAM_DECL DtoaState *state;
+#else
+#define STATE_PARAM DtoaState *state,
+#endif
+#define PASS_STATE state,
+#define GET_STATE(field) (state->field)
+
+ static DtoaState *
+newdtoa(void)
+{
+	DtoaState *state = (DtoaState *) MALLOC(sizeof(DtoaState));
+	if (state) {
+		memset(state, 0, sizeof(DtoaState));
+		state->pmem_next = state->private_mem;
+		}
+	return state;
+}
+
+ static void
+destroydtoa
+#ifdef KR_headers
+	(state) STATE_PARAM_DECL
+#else
+	(DtoaState *state)
+#endif
+{
+	int i;
+	Bigint *v, *next;
+
+	for (i = 0; i <= Kmax; i++) {
+		for (v = GET_STATE(freelist)[i]; v; v = next) {
+			next = v->next;
+#ifndef Omit_Private_Memory
+			if ((double*)v < GET_STATE(private_mem) ||
+			    (double*)v >= GET_STATE(private_mem) + PRIVATE_mem)
+#endif
+				FREE((void*)v);
+			}
+		}
+	FREE((void *)state);
+}
+
+#else
+#define STATE_PARAM      /* nothing */
+#define STATE_PARAM_DECL /* nothing */
+#define PASS_STATE       /* nothing */
+#define GET_STATE(name) name
+#endif
 
  static Bigint *
 Balloc
 #ifdef KR_headers
-	(k) int k;
+	(STATE_PARAM k) STATE_PARAM_DECL int k;
 #else
-	(int k)
+	(STATE_PARAM int k)
 #endif
 {
 	int x;
 	Bigint *rv;
 #ifndef Omit_Private_Memory
 	size_t len;
 #endif
 
 	ACQUIRE_DTOA_LOCK(0);
 	/* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */
 	/* but this case seems very unlikely. */
-	if (k <= Kmax && (rv = freelist[k]))
-		freelist[k] = rv->next;
+	if (k <= Kmax && (rv = GET_STATE(freelist)[k]))
+		GET_STATE(freelist)[k] = rv->next;
 	else {
 		x = 1 << k;
 #ifdef Omit_Private_Memory
 		rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));
 #else
 		len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1)
 			/sizeof(double);
-		if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) {
-			rv = (Bigint*)pmem_next;
-			pmem_next += len;
+		if (k <= Kmax && GET_STATE(pmem_next) - GET_STATE(private_mem) + len <= PRIVATE_mem) {
+			rv = (Bigint*)GET_STATE(pmem_next);
+			GET_STATE(pmem_next) += len;
 			}
 		else
 			rv = (Bigint*)MALLOC(len*sizeof(double));
 #endif
 		rv->k = k;
 		rv->maxwds = x;
 		}
 	FREE_DTOA_LOCK(0);
 	rv->sign = rv->wds = 0;
 	return rv;
 	}
 
  static void
 Bfree
 #ifdef KR_headers
-	(v) Bigint *v;
+	(STATE_PARAM v) STATE_PARAM_DECL Bigint *v;
 #else
-	(Bigint *v)
+	(STATE_PARAM Bigint *v)
 #endif
 {
 	if (v) {
 		if (v->k > Kmax)
-#ifdef FREE
 			FREE((void*)v);
-#else
-			free((void*)v);
-#endif
 		else {
 			ACQUIRE_DTOA_LOCK(0);
-			v->next = freelist[v->k];
-			freelist[v->k] = v;
+			v->next = GET_STATE(freelist)[v->k];
+			GET_STATE(freelist)[v->k] = v;
 			FREE_DTOA_LOCK(0);
 			}
 		}
 	}
 
 #define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
 y->wds*sizeof(Long) + 2*sizeof(int))
 
  static Bigint *
 multadd
 #ifdef KR_headers
-	(b, m, a) Bigint *b; int m, a;
+	(STATE_PARAM b, m, a) STATE_PARAM_DECL Bigint *b; int m, a;
 #else
-	(Bigint *b, int m, int a)	/* multiply by m and add a */
+	(STATE_PARAM Bigint *b, int m, int a)	/* multiply by m and add a */
 #endif
 {
 	int i, wds;
 #ifdef ULLong
 	ULong *x;
 	ULLong carry, y;
 #else
 	ULong carry, *x, y;
@@ -590,62 +669,62 @@ multadd
 		carry = y >> 16;
 		*x++ = y & 0xffff;
 #endif
 #endif
 		}
 		while(++i < wds);
 	if (carry) {
 		if (wds >= b->maxwds) {
-			b1 = Balloc(b->k+1);
+			b1 = Balloc(PASS_STATE b->k+1);
 			Bcopy(b1, b);
-			Bfree(b);
+			Bfree(PASS_STATE b);
 			b = b1;
 			}
 		b->x[wds++] = (ULong) carry;
 		b->wds = wds;
 		}
 	return b;
 	}
 
  static Bigint *
 s2b
 #ifdef KR_headers
-	(s, nd0, nd, y9) CONST char *s; int nd0, nd; ULong y9;
+	(STATE_PARAM s, nd0, nd, y9) STATE_PARAM_DECL CONST char *s; int nd0, nd; ULong y9;
 #else
-	(CONST char *s, int nd0, int nd, ULong y9)
+	(STATE_PARAM CONST char *s, int nd0, int nd, ULong y9)
 #endif
 {
 	Bigint *b;
 	int i, k;
 	Long x, y;
 
 	x = (nd + 8) / 9;
 	for(k = 0, y = 1; x > y; y <<= 1, k++) ;
 #ifdef Pack_32
-	b = Balloc(k);
+	b = Balloc(PASS_STATE k);
 	b->x[0] = y9;
 	b->wds = 1;
 #else
-	b = Balloc(k+1);
+	b = Balloc(PASS_STATE k+1);
 	b->x[0] = y9 & 0xffff;
 	b->wds = (b->x[1] = y9 >> 16) ? 2 : 1;
 #endif
 
 	i = 9;
 	if (9 < nd0) {
 		s += 9;
-		do b = multadd(b, 10, *s++ - '0');
+		do b = multadd(PASS_STATE b, 10, *s++ - '0');
 			while(++i < nd0);
 		s++;
 		}
 	else
 		s += 10;
 	for(; i < nd; i++)
-		b = multadd(b, 10, *s++ - '0');
+		b = multadd(PASS_STATE b, 10, *s++ - '0');
 	return b;
 	}
 
  static int
 hi0bits
 #ifdef KR_headers
 	(x) register ULong x;
 #else
@@ -724,35 +803,35 @@ lo0bits
 		}
 	*y = x;
 	return k;
 	}
 
  static Bigint *
 i2b
 #ifdef KR_headers
-	(i) int i;
+	(STATE_PARAM i) STATE_PARAM_DECL int i;
 #else
-	(int i)
+	(STATE_PARAM int i)
 #endif
 {
 	Bigint *b;
 
-	b = Balloc(1);
+	b = Balloc(PASS_STATE 1);
 	b->x[0] = i;
 	b->wds = 1;
 	return b;
 	}
 
  static Bigint *
 mult
 #ifdef KR_headers
-	(a, b) Bigint *a, *b;
+	(STATE_PARAM a, b) STATE_PARAM_DECL Bigint *a, *b;
 #else
-	(Bigint *a, Bigint *b)
+	(STATE_PARAM Bigint *a, Bigint *b)
 #endif
 {
 	Bigint *c;
 	int k, wa, wb, wc;
 	ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
 	ULong y;
 #ifdef ULLong
 	ULLong carry, z;
@@ -769,17 +848,17 @@ mult
 		b = c;
 		}
 	k = a->k;
 	wa = a->wds;
 	wb = b->wds;
 	wc = wa + wb;
 	if (wc > a->maxwds)
 		k++;
-	c = Balloc(k);
+	c = Balloc(PASS_STATE k);
 	for(x = c->x, xa = x + wc; x < xa; x++)
 		*x = 0;
 	xa = a->x;
 	xae = xa + wa;
 	xb = b->x;
 	xbe = xb + wb;
 	xc0 = c->x;
 #ifdef ULLong
@@ -847,97 +926,95 @@ mult
 		}
 #endif
 #endif
 	for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;
 	c->wds = wc;
 	return c;
 	}
 
- static Bigint *p5s;
-
  static Bigint *
 pow5mult
 #ifdef KR_headers
-	(b, k) Bigint *b; int k;
+	(STATE_PARAM b, k) STATE_PARAM_DECL Bigint *b; int k;
 #else
-	(Bigint *b, int k)
+	(STATE_PARAM Bigint *b, int k)
 #endif
 {
 	Bigint *b1, *p5, *p51;
 	int i;
-	static int p05[3] = { 5, 25, 125 };
+	static CONST int p05[3] = { 5, 25, 125 };
 
 	if ((i = k & 3))
-		b = multadd(b, p05[i-1], 0);
+		b = multadd(PASS_STATE b, p05[i-1], 0);
 
 	if (!(k >>= 2))
 		return b;
-	if (!(p5 = p5s)) {
+	if (!(p5 = GET_STATE(p5s))) {
 		/* first time */
 #ifdef MULTIPLE_THREADS
 		ACQUIRE_DTOA_LOCK(1);
 		if (!(p5 = p5s)) {
 			p5 = p5s = i2b(625);
 			p5->next = 0;
 			}
 		FREE_DTOA_LOCK(1);
 #else
-		p5 = p5s = i2b(625);
+		p5 = GET_STATE(p5s) = i2b(PASS_STATE 625);
 		p5->next = 0;
 #endif
 		}
 	for(;;) {
 		if (k & 1) {
-			b1 = mult(b, p5);
-			Bfree(b);
+			b1 = mult(PASS_STATE b, p5);
+			Bfree(PASS_STATE b);
 			b = b1;
 			}
 		if (!(k >>= 1))
 			break;
 		if (!(p51 = p5->next)) {
 #ifdef MULTIPLE_THREADS
 			ACQUIRE_DTOA_LOCK(1);
 			if (!(p51 = p5->next)) {
 				p51 = p5->next = mult(p5,p5);
 				p51->next = 0;
 				}
 			FREE_DTOA_LOCK(1);
 #else
-			p51 = p5->next = mult(p5,p5);
+			p51 = p5->next = mult(PASS_STATE p5,p5);
 			p51->next = 0;
 #endif
 			}
 		p5 = p51;
 		}
 	return b;
 	}
 
  static Bigint *
 lshift
 #ifdef KR_headers
-	(b, k) Bigint *b; int k;
+	(STATE_PARAM b, k) STATE_PARAM_DECL Bigint *b; int k;
 #else
-	(Bigint *b, int k)
+	(STATE_PARAM Bigint *b, int k)
 #endif
 {
 	int i, k1, n, n1;
 	Bigint *b1;
 	ULong *x, *x1, *xe, z;
 
 #ifdef Pack_32
 	n = k >> 5;
 #else
 	n = k >> 4;
 #endif
 	k1 = b->k;
 	n1 = n + b->wds + 1;
 	for(i = b->maxwds; n1 > i; i <<= 1)
 		k1++;
-	b1 = Balloc(k1);
+	b1 = Balloc(PASS_STATE k1);
 	x1 = b1->x;
 	for(i = 0; i < n; i++)
 		*x1++ = 0;
 	x = b->x;
 	xe = x + b->wds;
 #ifdef Pack_32
 	if (k &= 0x1f) {
 		k1 = 32 - k;
@@ -962,17 +1039,17 @@ lshift
 		if (*x1 = z)
 			++n1;
 		}
 #endif
 	else do
 		*x1++ = *x++;
 		while(x < xe);
 	b1->wds = n1 - 1;
-	Bfree(b);
+	Bfree(PASS_STATE b);
 	return b1;
 	}
 
  static int
 cmp
 #ifdef KR_headers
 	(a, b) Bigint *a, *b;
 #else
@@ -1003,49 +1080,49 @@ cmp
 			break;
 		}
 	return 0;
 	}
 
  static Bigint *
 diff
 #ifdef KR_headers
-	(a, b) Bigint *a, *b;
+	(STATE_PARAM a, b) STATE_PARAM_DECL Bigint *a, *b;
 #else
-	(Bigint *a, Bigint *b)
+	(STATE_PARAM Bigint *a, Bigint *b)
 #endif
 {
 	Bigint *c;
 	int i, wa, wb;
 	ULong *xa, *xae, *xb, *xbe, *xc;
 #ifdef ULLong
 	ULLong borrow, y;
 #else
 	ULong borrow, y;
 #ifdef Pack_32
 	ULong z;
 #endif
 #endif
 
 	i = cmp(a,b);
 	if (!i) {
-		c = Balloc(0);
+		c = Balloc(PASS_STATE 0);
 		c->wds = 1;
 		c->x[0] = 0;
 		return c;
 		}
 	if (i < 0) {
 		c = a;
 		a = b;
 		b = c;
 		i = 1;
 		}
 	else
 		i = 0;
-	c = Balloc(a->k);
+	c = Balloc(PASS_STATE a->k);
 	c->sign = i;
 	wa = a->wds;
 	xa = a->x;
 	xae = xa + wa;
 	wb = b->wds;
 	xb = b->x;
 	xbe = xb + wb;
 	xc = c->x;
@@ -1209,19 +1286,19 @@ b2d
 #undef d1
 #endif
 	return dval(d);
 	}
 
  static Bigint *
 d2b
 #ifdef KR_headers
-	(d, e, bits) U d; int *e, *bits;
+	(STATE_PARAM d, e, bits) STATE_PARAM_DECL U d; int *e, *bits;
 #else
-	(U d, int *e, int *bits)
+	(STATE_PARAM U d, int *e, int *bits)
 #endif
 {
 	Bigint *b;
 	int de, k;
 	ULong *x, y, z;
 #ifndef Sudden_Underflow
 	int i;
 #endif
@@ -1230,19 +1307,19 @@ d2b
 	d0 = word0(d) >> 16 | word0(d) << 16;
 	d1 = word1(d) >> 16 | word1(d) << 16;
 #else
 #define d0 word0(d)
 #define d1 word1(d)
 #endif
 
 #ifdef Pack_32
-	b = Balloc(1);
+	b = Balloc(PASS_STATE 1);
 #else
-	b = Balloc(2);
+	b = Balloc(PASS_STATE 2);
 #endif
 	x = b->x;
 
 	z = d0 & Frac_mask;
 	d0 &= 0x7fffffff;	/* clear sign bit, which we ignore */
 #ifdef Sudden_Underflow
 	de = (int)(d0 >> Exp_shift);
 #ifndef IBM
@@ -1524,19 +1601,19 @@ hexnan
 		}
 	}
 #endif /*No_Hex_NaN*/
 #endif /* INFNAN_CHECK */
 
  static double
 _strtod
 #ifdef KR_headers
-	(s00, se) CONST char *s00; char **se;
+	(STATE_PARAM s00, se) STATE_PARAM_DECL CONST char *s00; char **se;
 #else
-	(CONST char *s00, char **se)
+	(STATE_PARAM CONST char *s00, char **se)
 #endif
 {
 #ifdef Avoid_Underflow
 	int scale;
 #endif
 	int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign,
 		 e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign;
 	CONST char *s, *s0, *s1;
@@ -1948,23 +2025,23 @@ hexnan
 #endif
 			}
 		}
 
 	/* Now the hard part -- adjusting rv to the correct value.*/
 
 	/* Put digits into bd: true value = bd * 10^e */
 
-	bd0 = s2b(s0, nd0, nd, y);
+	bd0 = s2b(PASS_STATE s0, nd0, nd, y);
 
 	for(;;) {
-		bd = Balloc(bd0->k);
+		bd = Balloc(PASS_STATE bd0->k);
 		Bcopy(bd, bd0);
-		bb = d2b(rv, &bbe, &bbbits);	/* rv = bb * 2^bbe */
-		bs = i2b(1);
+		bb = d2b(PASS_STATE rv, &bbe, &bbbits);	/* rv = bb * 2^bbe */
+		bs = i2b(PASS_STATE 1);
 
 		if (e >= 0) {
 			bb2 = bb5 = 0;
 			bd2 = bd5 = e;
 			}
 		else {
 			bb2 = bb5 = -e;
 			bd2 = bd5 = 0;
@@ -2010,30 +2087,30 @@ hexnan
 		if (i > bs2)
 			i = bs2;
 		if (i > 0) {
 			bb2 -= i;
 			bd2 -= i;
 			bs2 -= i;
 			}
 		if (bb5 > 0) {
-			bs = pow5mult(bs, bb5);
-			bb1 = mult(bs, bb);
-			Bfree(bb);
+			bs = pow5mult(PASS_STATE bs, bb5);
+			bb1 = mult(PASS_STATE bs, bb);
+			Bfree(PASS_STATE bb);
 			bb = bb1;
 			}
 		if (bb2 > 0)
-			bb = lshift(bb, bb2);
+			bb = lshift(PASS_STATE bb, bb2);
 		if (bd5 > 0)
-			bd = pow5mult(bd, bd5);
+			bd = pow5mult(PASS_STATE bd, bd5);
 		if (bd2 > 0)
-			bd = lshift(bd, bd2);
+			bd = lshift(PASS_STATE bd, bd2);
 		if (bs2 > 0)
-			bs = lshift(bs, bs2);
-		delta = diff(bb, bd);
+			bs = lshift(PASS_STATE bs, bs2);
+		delta = diff(PASS_STATE bb, bd);
 		dsign = delta->sign;
 		delta->sign = 0;
 		i = cmp(delta, bs);
 #ifdef Honor_FLT_ROUNDS
 		if (rounding != 1) {
 			if (i < 0) {
 				/* Error is less than an ulp */
 				if (!delta->x[0] && delta->wds <= 1) {
@@ -2055,17 +2132,17 @@ hexnan
 					 && !(word0(rv) & Frac_mask)) {
 						y = word0(rv) & Exp_mask;
 #ifdef Avoid_Underflow
 						if (!scale || y > 2*P*Exp_msk1)
 #else
 						if (y)
 #endif
 						  {
-						  delta = lshift(delta,Log2P);
+						  delta = lshift(PASS_STATE delta,Log2P);
 						  if (cmp(delta, bs) <= 0)
 							adj = -0.5;
 						  }
 						}
  apply_adj:
 #ifdef Avoid_Underflow
 					if (scale && (y = word0(rv) & Exp_mask)
 						<= 2*P*Exp_msk1)
@@ -2144,17 +2221,17 @@ hexnan
 				}
 			if (!delta->x[0] && delta->wds <= 1) {
 				/* exact result */
 #ifdef SET_INEXACT
 				inexact = 0;
 #endif
 				break;
 				}
-			delta = lshift(delta,Log2P);
+			delta = lshift(PASS_STATE delta,Log2P);
 			if (cmp(delta, bs) > 0)
 				goto drop_down;
 			break;
 			}
 		if (i == 0) {
 			/* exactly half-way between */
 			if (dsign) {
 				if ((word0(rv) & Bndry_mask1) == Bndry_mask1
@@ -2369,20 +2446,20 @@ hexnan
 				if (aadj < .4999999 || aadj > .5000001)
 					break;
 				}
 			else if (aadj < .4999999/FLT_RADIX)
 				break;
 			}
 #endif
  cont:
-		Bfree(bb);
-		Bfree(bd);
-		Bfree(bs);
-		Bfree(delta);
+		Bfree(PASS_STATE bb);
+		Bfree(PASS_STATE bd);
+		Bfree(PASS_STATE bs);
+		Bfree(PASS_STATE delta);
 		}
 #ifdef SET_INEXACT
 	if (inexact) {
 		if (!oldinexact) {
 			word0(rv0) = Exp_1 + (70 << Exp_shift);
 			word1(rv0) = 0;
 			dval(rv0) += 1.;
 			}
@@ -2405,21 +2482,21 @@ hexnan
 #ifdef SET_INEXACT
 	if (inexact && !(word0(rv) & Exp_mask)) {
 		/* set underflow bit */
 		dval(rv0) = 1e-300;
 		dval(rv0) *= dval(rv0);
 		}
 #endif
  retfree:
-	Bfree(bb);
-	Bfree(bd);
-	Bfree(bs);
-	Bfree(bd0);
-	Bfree(delta);
+	Bfree(PASS_STATE bb);
+	Bfree(PASS_STATE bd);
+	Bfree(PASS_STATE bs);
+	Bfree(PASS_STATE bd0);
+	Bfree(PASS_STATE delta);
  ret:
 	if (se)
 		*se = (char *)s;
 	return sign ? -dval(rv) : dval(rv);
 	}
 
  static int
 quorem
@@ -2534,76 +2611,77 @@ quorem
 			while(--bxe > bx && !*bxe)
 				--n;
 			b->wds = n;
 			}
 		}
 	return q;
 	}
 
-#ifndef MULTIPLE_THREADS
+#if !defined(MULTIPLE_THREADS) && !defined(NO_GLOBAL_STATE)
+#define USE_DTOA_RESULT 1
  static char *dtoa_result;
 #endif
 
  static char *
 #ifdef KR_headers
-rv_alloc(i) int i;
+rv_alloc(STATE_PARAM i) STATE_PARAM_DECL int i;
 #else
-rv_alloc(int i)
+rv_alloc(STATE_PARAM int i)
 #endif
 {
 	int j, k, *r;
 
 	j = sizeof(ULong);
 	for(k = 0;
 		sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (unsigned) i;
 		j <<= 1)
 			k++;
-	r = (int*)Balloc(k);
+	r = (int*)Balloc(PASS_STATE k);
 	*r = k;
 	return
-#ifndef MULTIPLE_THREADS
+#ifdef USE_DTOA_RESULT
 	dtoa_result =
 #endif
 		(char *)(r+1);
 	}
 
  static char *
 #ifdef KR_headers
-nrv_alloc(s, rve, n) char *s, **rve; int n;
+nrv_alloc(STATE_PARAM s, rve, n) STATE_PARAM_DECL char *s, **rve; int n;
 #else
-nrv_alloc(CONST char *s, char **rve, int n)
+nrv_alloc(STATE_PARAM CONST char *s, char **rve, int n)
 #endif
 {
 	char *rv, *t;
 
-	t = rv = rv_alloc(n);
+	t = rv = rv_alloc(PASS_STATE n);
 	while((*t = *s++)) t++;
 	if (rve)
 		*rve = t;
 	return rv;
 	}
 
 /* freedtoa(s) must be used to free values s returned by dtoa
  * when MULTIPLE_THREADS is #defined.  It should be used in all cases,
  * but for consistency with earlier versions of dtoa, it is optional
  * when MULTIPLE_THREADS is not defined.
  */
 
  static void
 #ifdef KR_headers
-freedtoa(s) char *s;
+freedtoa(STATE_PARAM s) STATE_PARAM_DECL char *s;
 #else
-freedtoa(char *s)
+freedtoa(STATE_PARAM char *s)
 #endif
 {
 	Bigint *b = (Bigint *)((int *)s - 1);
 	b->maxwds = 1 << (b->k = *(int*)b);
-	Bfree(b);
-#ifndef MULTIPLE_THREADS
+	Bfree(PASS_STATE b);
+#ifdef USE_DTOA_RESULT
 	if (s == dtoa_result)
 		dtoa_result = 0;
 #endif
 	}
 
 /* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
  *
  * Inspired by "How to Print Floating-Point Numbers Accurately" by
@@ -2636,20 +2714,20 @@ freedtoa(char *s)
  *	   "uniformly" distributed input, the probability is
  *	   something like 10^(k-15) that we must resort to the Long
  *	   calculation.
  */
 
  static char *
 dtoa
 #ifdef KR_headers
-	(d, mode, ndigits, decpt, sign, rve)
-	U d; int mode, ndigits, *decpt, *sign; char **rve;
+	(STATE_PARAM d, mode, ndigits, decpt, sign, rve)
+	STATE_PARAM_DECL U d; int mode, ndigits, *decpt, *sign; char **rve;
 #else
-	(U d, int mode, int ndigits, int *decpt, int *sign, char **rve)
+	(STATE_PARAM U d, int mode, int ndigits, int *decpt, int *sign, char **rve)
 #endif
 {
  /*	Arguments ndigits, decpt, sign are similar to those
 	of ecvt and fcvt; trailing zeros are suppressed from
 	the returned string.  If not null, *rve is set to point
 	to the end of the return value.  If d is +-Infinity or NaN,
 	then *decpt is set to 9999.
 
@@ -2700,19 +2778,19 @@ dtoa
 	int inexact, oldinexact;
 #endif
 
 #ifdef __GNUC__
 	ilim = ilim1 = 0;
 	mlo = NULL;
 #endif
 
-#ifndef MULTIPLE_THREADS
+#ifdef USE_DTOA_RESULT
 	if (dtoa_result) {
-		freedtoa(dtoa_result);
+		freedtoa(PASS_STATE dtoa_result);
 		dtoa_result = 0;
 		}
 #endif
 
 	if (word0(d) & Sign_bit) {
 		/* set sign for everything, including 0's and NaNs */
 		*sign = 1;
 		word0(d) &= ~Sign_bit;	/* clear sign bit */
@@ -2726,44 +2804,44 @@ dtoa
 #else
 	if (word0(d)  == 0x8000)
 #endif
 		{
 		/* Infinity or NaN */
 		*decpt = 9999;
 #ifdef IEEE_Arith
 		if (!word1(d) && !(word0(d) & 0xfffff))
-			return nrv_alloc("Infinity", rve, 8);
+			return nrv_alloc(PASS_STATE "Infinity", rve, 8);
 #endif
-		return nrv_alloc("NaN", rve, 3);
+		return nrv_alloc(PASS_STATE "NaN", rve, 3);
 		}
 #endif
 #ifdef IBM
 	dval(d) += 0; /* normalize */
 #endif
 	if (!dval(d)) {
 		*decpt = 1;
-		return nrv_alloc("0", rve, 1);
+		return nrv_alloc(PASS_STATE "0", rve, 1);
 		}
 
 #ifdef SET_INEXACT
 	try_quick = oldinexact = get_inexact();
 	inexact = 1;
 #endif
 #ifdef Honor_FLT_ROUNDS
 	if ((rounding = Flt_Rounds) >= 2) {
 		if (*sign)
 			rounding = rounding == 2 ? 0 : 2;
 		else
 			if (rounding != 2)
 				rounding = 0;
 		}
 #endif
 
-	b = d2b(d, &be, &bbits);
+	b = d2b(PASS_STATE d, &be, &bbits);
 #ifdef Sudden_Underflow
 	i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
 #else
 	if ((i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) {
 #endif
 		dval(d2) = dval(d);
 		word0(d2) &= Frac_mask1;
 		word0(d2) |= Exp_11;
@@ -2879,17 +2957,17 @@ dtoa
 			/* no break */
 		case 5:
 			i = ndigits + k + 1;
 			ilim = i;
 			ilim1 = i - 1;
 			if (i <= 0)
 				i = 1;
 		}
-	s = s0 = rv_alloc(i);
+	s = s0 = rv_alloc(PASS_STATE i);
 
 #ifdef Honor_FLT_ROUNDS
 	if (mode > 1 && rounding != 1)
 		leftright = 0;
 #endif
 
 	if (ilim >= 0 && ilim <= Quick_max && try_quick) {
 
@@ -3056,41 +3134,41 @@ dtoa
 #endif
 #ifdef IBM
 			1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);
 #else
 			1 + P - bbits;
 #endif
 		b2 += i;
 		s2 += i;
-		mhi = i2b(1);
+		mhi = i2b(PASS_STATE 1);
 		}
 	if (m2 > 0 && s2 > 0) {
 		i = m2 < s2 ? m2 : s2;
 		b2 -= i;
 		m2 -= i;
 		s2 -= i;
 		}
 	if (b5 > 0) {
 		if (leftright) {
 			if (m5 > 0) {
-				mhi = pow5mult(mhi, m5);
-				b1 = mult(mhi, b);
-				Bfree(b);
+				mhi = pow5mult(PASS_STATE mhi, m5);
+				b1 = mult(PASS_STATE mhi, b);
+				Bfree(PASS_STATE b);
 				b = b1;
 				}
 			if ((j = b5 - m5))
-				b = pow5mult(b, j);
+				b = pow5mult(PASS_STATE b, j);
 			}
 		else
-			b = pow5mult(b, b5);
+			b = pow5mult(PASS_STATE b, b5);
 		}
-	S = i2b(1);
+	S = i2b(PASS_STATE 1);
 	if (s5 > 0)
-		S = pow5mult(S, s5);
+		S = pow5mult(PASS_STATE S, s5);
 
 	/* Check for special case that d is a normalized power of 2. */
 
 	spec_case = 0;
 	if ((mode < 2 || leftright)
 #ifdef Honor_FLT_ROUNDS
 			&& rounding == 1
 #endif
@@ -3129,66 +3207,66 @@ dtoa
 		}
 	else if (i < 4) {
 		i += 28;
 		b2 += i;
 		m2 += i;
 		s2 += i;
 		}
 	if (b2 > 0)
-		b = lshift(b, b2);
+		b = lshift(PASS_STATE b, b2);
 	if (s2 > 0)
-		S = lshift(S, s2);
+		S = lshift(PASS_STATE S, s2);
 	if (k_check) {
 		if (cmp(b,S) < 0) {
 			k--;
-			b = multadd(b, 10, 0);	/* we botched the k estimate */
+			b = multadd(PASS_STATE b, 10, 0);	/* we botched the k estimate */
 			if (leftright)
-				mhi = multadd(mhi, 10, 0);
+				mhi = multadd(PASS_STATE mhi, 10, 0);
 			ilim = ilim1;
 			}
 		}
 	if (ilim <= 0 && (mode == 3 || mode == 5)) {
-		if (ilim < 0 || cmp(b,S = multadd(S,5,0)) < 0) {
+		if (ilim < 0 || cmp(b,S = multadd(PASS_STATE S,5,0)) < 0) {
 			/* no digits, fcvt style */
  no_digits:
 			/* MOZILLA CHANGE: Always return a non-empty string. */
 			*s++ = '0';
 			k = 0;
 			goto ret;
 			}
  one_digit:
 		*s++ = '1';
 		k++;
 		goto ret;
 		}
 	if (leftright) {
 		if (m2 > 0)
-			mhi = lshift(mhi, m2);
+			mhi = lshift(PASS_STATE mhi, m2);
 
 		/* Compute mlo -- check for special case
 		 * that d is a normalized power of 2.
 		 */
 
 		mlo = mhi;
 		if (spec_case) {
-			mhi = Balloc(mhi->k);
+			mhi = Balloc(PASS_STATE mhi->k);
 			Bcopy(mhi, mlo);
-			mhi = lshift(mhi, Log2P);
+			mhi = lshift(PASS_STATE mhi, Log2P);
 			}
 
 		for(i = 1;;i++) {
 			dig = quorem(b,S) + '0';
 			/* Do we yet have the shortest decimal string
 			 * that will round to d?
 			 */
 			j = cmp(b, mlo);
-			delta = diff(S, mhi);
+			delta = diff(PASS_STATE S, mhi);
 			j1 = delta->sign ? 1 : cmp(b, delta);
-			Bfree(delta);
+			Bfree(PASS_STATE delta);
 #ifndef ROUND_BIASED
 			if (j1 == 0 && mode != 1 && !(word1(d) & 1)
 #ifdef Honor_FLT_ROUNDS
 				&& rounding >= 1
 #endif
 								   ) {
 				if (dig == '9')
 					goto round_9_up;
@@ -3216,17 +3294,17 @@ dtoa
 #ifdef Honor_FLT_ROUNDS
 				if (mode > 1)
 				 switch(rounding) {
 				  case 0: goto accept_dig;
 				  case 2: goto keep_dig;
 				  }
 #endif /*Honor_FLT_ROUNDS*/
 				if (j1 > 0) {
-					b = lshift(b, 1);
+					b = lshift(PASS_STATE b, 1);
 					j1 = cmp(b, S);
 					if ((j1 > 0 || (j1 == 0 && dig & 1))
 					&& dig++ == '9')
 						goto round_9_up;
 					}
  accept_dig:
 				*s++ = dig;
 				goto ret;
@@ -3245,48 +3323,48 @@ dtoa
 				goto ret;
 				}
 #ifdef Honor_FLT_ROUNDS
  keep_dig:
 #endif
 			*s++ = dig;
 			if (i == ilim)
 				break;
-			b = multadd(b, 10, 0);
+			b = multadd(PASS_STATE b, 10, 0);
 			if (mlo == mhi)
-				mlo = mhi = multadd(mhi, 10, 0);
+				mlo = mhi = multadd(PASS_STATE mhi, 10, 0);
 			else {
-				mlo = multadd(mlo, 10, 0);
-				mhi = multadd(mhi, 10, 0);
+				mlo = multadd(PASS_STATE mlo, 10, 0);
+				mhi = multadd(PASS_STATE mhi, 10, 0);
 				}
 			}
 		}
 	else
 		for(i = 1;; i++) {
 			*s++ = dig = quorem(b,S) + '0';
 			if (!b->x[0] && b->wds <= 1) {
 #ifdef SET_INEXACT
 				inexact = 0;
 #endif
 				goto ret;
 				}
 			if (i >= ilim)
 				break;
-			b = multadd(b, 10, 0);
+			b = multadd(PASS_STATE b, 10, 0);
 			}
 
 	/* Round off last digit */
 
 #ifdef Honor_FLT_ROUNDS
 	switch(rounding) {
 	  case 0: goto trimzeros;
 	  case 2: goto roundoff;
 	  }
 #endif
-	b = lshift(b, 1);
+	b = lshift(PASS_STATE b, 1);
 	j = cmp(b, S);
 	if (j >= 0) {  /* ECMA compatible rounding needed by Spidermonkey */
  roundoff:
 		while(*--s == '9')
 			if (s == s0) {
 				k++;
 				*s++ = '1';
 				goto ret;
@@ -3296,35 +3374,35 @@ dtoa
 	else {
 #ifdef Honor_FLT_ROUNDS
  trimzeros:
 #endif
 		while(*--s == '0');
 		s++;
 		}
  ret:
-	Bfree(S);
+	Bfree(PASS_STATE S);
 	if (mhi) {
 		if (mlo && mlo != mhi)
-			Bfree(mlo);
-		Bfree(mhi);
+			Bfree(PASS_STATE mlo);
+		Bfree(PASS_STATE mhi);
 		}
  ret1:
 #ifdef SET_INEXACT
 	if (inexact) {
 		if (!oldinexact) {
 			word0(d) = Exp_1 + (70 << Exp_shift);
 			word1(d) = 0;
 			dval(d) += 1.;
 			}
 		}
 	else if (!oldinexact)
 		clear_inexact();
 #endif
-	Bfree(b);
+	Bfree(PASS_STATE b);
 	*s = 0;
 	*decpt = k + 1;
 	if (rve)
 		*rve = s;
 	return s0;
 	}
 #ifdef __cplusplus
 }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -558,21 +558,18 @@ JSRuntime::JSRuntime()
     JS_INIT_CLIST(&contextList);
     JS_INIT_CLIST(&trapList);
     JS_INIT_CLIST(&watchPointList);
 }
 
 bool
 JSRuntime::init(uint32 maxbytes)
 {
-    if (!js_InitDtoa() ||
-        !js_InitGC(this, maxbytes) ||
-        !js_InitAtomState(this)) {
+    if (!js_InitGC(this, maxbytes) || !js_InitAtomState(this))
         return false;
-    }
 
     deflatedStringCache = new js::DeflatedStringCache();
     if (!deflatedStringCache || !deflatedStringCache->init())
         return false;
 
 #ifdef JS_THREADSAFE
     gcLock = JS_NEW_LOCK();
     if (!gcLock)
@@ -752,17 +749,16 @@ JS_ShutDown(void)
     extern void js_DumpOpMeters();
     js_DumpOpMeters();
 #endif
 
 #ifdef JS_REPRMETER
     reprmeter::js_DumpReprMeter();
 #endif
 
-    js_FinishDtoa();
 #ifdef JS_THREADSAFE
     js_CleanupLocks();
 #endif
     PRMJ_NowShutdown();
 }
 
 JS_PUBLIC_API(void *)
 JS_GetRuntimePrivate(JSRuntime *rt)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -95,42 +95,51 @@ CallStack::contains(JSStackFrame *fp)
     for (JSStackFrame *f = start; f != stop; f = f->down) {
         if (f == fp)
             return true;
     }
     return false;
 }
 #endif
 
-void
+bool
 JSThreadData::init()
 {
 #ifdef DEBUG
     /* The data must be already zeroed. */
     for (size_t i = 0; i != sizeof(*this); ++i)
         JS_ASSERT(reinterpret_cast<uint8*>(this)[i] == 0);
 #endif
 #ifdef JS_TRACER
     InitJIT(&traceMonitor);
 #endif
+    dtoaState = js_NewDtoaState();
+    if (!dtoaState) {
+        finish();
+        return false;
+    }
+    return true;
 }
 
 void
 JSThreadData::finish()
 {
 #ifdef DEBUG
     /* All GC-related things must be already removed at this point. */
     JS_ASSERT(gcFreeLists.isEmpty());
     for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
         JS_ASSERT(!scriptsToGC[i]);
     for (size_t i = 0; i != JS_ARRAY_LENGTH(nativeEnumCache); ++i)
         JS_ASSERT(!nativeEnumCache[i]);
     JS_ASSERT(!localRootStack);
 #endif
 
+    if (dtoaState)
+        js_DestroyDtoaState(dtoaState);
+
     js_FinishGSNCache(&gsnCache);
     js_FinishPropertyCache(&propertyCache);
 #if defined JS_TRACER
     FinishJIT(&traceMonitor);
 #endif
 }
 
 void
@@ -185,17 +194,20 @@ static JSThread *
 NewThread(jsword id)
 {
     JS_ASSERT(js_CurrentThreadId() == id);
     JSThread *thread = (JSThread *) js_calloc(sizeof(JSThread));
     if (!thread)
         return NULL;
     JS_INIT_CLIST(&thread->contextList);
     thread->id = id;
-    thread->data.init();
+    if (!thread->data.init()) {
+        js_free(thread);
+        return NULL;
+    }
     return thread;
 }
 
 static void
 DestroyThread(JSThread *thread)
 {
     /* The thread must have zero contexts. */
     JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -45,16 +45,17 @@
  */
 #include <string.h>
 
 #include "jsarena.h" /* Added by JSIFY */
 #include "jsclist.h"
 #include "jslong.h"
 #include "jsatom.h"
 #include "jsdhash.h"
+#include "jsdtoa.h"
 #include "jsgc.h"
 #include "jshashtable.h"
 #include "jsinterp.h"
 #include "jsobj.h"
 #include "jspropertytree.h"
 #include "jsprvtd.h"
 #include "jspubtd.h"
 #include "jsregexp.h"
@@ -550,31 +551,34 @@ struct JSThreadData {
 
     /* Lock-free hashed lists of scripts created by eval to garbage-collect. */
     JSScript            *scriptsToGC[JS_EVAL_CACHE_SIZE];
 
 #ifdef JS_EVAL_CACHE_METERING
     JSEvalCacheMeter    evalCacheMeter;
 #endif
 
+    /* State used by dtoa.c. */
+    DtoaState           *dtoaState;
+
     /*
      * Cache of reusable JSNativeEnumerators mapped by shape identifiers (as
      * stored in scope->shape). This cache is nulled by the GC and protected
      * by gcLock.
      */
 #define NATIVE_ENUM_CACHE_LOG2  8
 #define NATIVE_ENUM_CACHE_MASK  JS_BITMASK(NATIVE_ENUM_CACHE_LOG2)
 #define NATIVE_ENUM_CACHE_SIZE  JS_BIT(NATIVE_ENUM_CACHE_LOG2)
 
 #define NATIVE_ENUM_CACHE_HASH(shape)                                         \
     ((((shape) >> NATIVE_ENUM_CACHE_LOG2) ^ (shape)) & NATIVE_ENUM_CACHE_MASK)
 
     jsuword             nativeEnumCache[NATIVE_ENUM_CACHE_SIZE];
 
-    void init();
+    bool init();
     void finish();
     void mark(JSTracer *trc);
     void purge(JSContext *cx);
     void purgeGCFreeLists();
 };
 
 #ifdef JS_THREADSAFE
 
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2170,17 +2170,17 @@ date_toSource(JSContext *cx, uintN argc,
 {
     jsdouble utctime;
     char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
     JSString *str;
 
     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
         return JS_FALSE;
 
-    numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, utctime);
+    numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf, DTOSTR_STANDARD, 0, utctime);
     if (!numStr) {
         JS_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
 
     bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
     if (!bytes) {
         JS_ReportOutOfMemory(cx);
--- a/js/src/jsdtoa.cpp
+++ b/js/src/jsdtoa.cpp
@@ -44,20 +44,17 @@
 #include "jsstdint.h"
 #include "jsdtoa.h"
 #include "jsprf.h"
 #include "jsutil.h" /* Added by JSIFY */
 #include "jsprvtd.h"
 #include "jsnum.h"
 #include "jsbit.h"
 #include "jslibmath.h"
-
-#ifdef JS_THREADSAFE
-#include "jslock.h"
-#endif
+#include "jscntxt.h"
 
 #ifdef IS_LITTLE_ENDIAN
 #define IEEE_8087
 #else
 #define IEEE_MC68k
 #endif
 
 #ifndef Long
@@ -73,78 +70,40 @@
 #define Llong JSInt64
 #endif
 
 #ifndef ULlong
 #define ULlong JSUint64
 #endif
 */
 
-#ifdef JS_THREADSAFE
-static PRLock *dtoalock;
-static JSBool _dtoainited = JS_FALSE;
-
-#define LOCK_DTOA() PR_Lock(dtoalock);
-#define UNLOCK_DTOA() PR_Unlock(dtoalock)
-#else
-#define LOCK_DTOA()
-#define UNLOCK_DTOA()
-#endif
+#define NO_GLOBAL_STATE
 #include "dtoa.c"
 
-JS_FRIEND_API(JSBool)
-js_InitDtoa()
-{
-#ifdef JS_THREADSAFE
-    if (!_dtoainited) {
-        dtoalock = PR_NewLock();
-        JS_ASSERT(dtoalock);
-        _dtoainited = JS_TRUE;
-    }
-
-    return (dtoalock != 0);
-#else
-    return JS_TRUE;
-#endif
-}
-
-JS_FRIEND_API(void)
-js_FinishDtoa()
-{
-#ifdef JS_THREADSAFE
-    if (_dtoainited) {
-        PR_DestroyLock(dtoalock);
-        dtoalock = NULL;
-        _dtoainited = JS_FALSE;
-    }
-#endif
-}
-
 /* Mapping of JSDToStrMode -> js_dtoa mode */
 static const uint8 dtoaModes[] = {
     0,   /* DTOSTR_STANDARD */
     0,   /* DTOSTR_STANDARD_EXPONENTIAL, */
     3,   /* DTOSTR_FIXED, */
     2,   /* DTOSTR_EXPONENTIAL, */
     2};  /* DTOSTR_PRECISION */
 
-JS_FRIEND_API(double)
-JS_strtod(const char *s00, char **se, int *err)
+double
+js_strtod_harder(DtoaState *state, const char *s00, char **se, int *err)
 {
     double retval;
     if (err)
         *err = 0;
-    LOCK_DTOA();
-    retval = _strtod(s00, se);
-    UNLOCK_DTOA();
+    retval = _strtod(state, s00, se);
     return retval;
 }
 
-JS_FRIEND_API(char *)
-JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double dinput)
+char *
+js_dtostr(DtoaState *state, char *buffer, size_t bufferSize, JSDToStrMode mode, int precision,
+          double dinput)
 {
     U d;
     int decPt;        /* Offset of decimal point from first digit */
     int sign;         /* Nonzero if the sign bit was set in d */
     int nDigits;      /* Number of significand digits returned by js_dtoa */
     char *numBegin;   /* Pointer to the digits returned by js_dtoa */
     char *numEnd = 0; /* Pointer past the digits returned by js_dtoa */
 
@@ -154,34 +113,30 @@ JS_dtostr(char *buffer, size_t bufferSiz
 
     /*
      * Change mode here rather than below because the buffer may not be large
      * enough to hold a large integer.
      */
     if (mode == DTOSTR_FIXED && (dinput >= 1e21 || dinput <= -1e21))
         mode = DTOSTR_STANDARD;
 
-    LOCK_DTOA();
     dval(d) = dinput;
-    numBegin = dtoa(d, dtoaModes[mode], precision, &decPt, &sign, &numEnd);
+    numBegin = dtoa(PASS_STATE d, dtoaModes[mode], precision, &decPt, &sign, &numEnd);
     if (!numBegin) {
-        UNLOCK_DTOA();
         return NULL;
     }
 
     nDigits = numEnd - numBegin;
     JS_ASSERT((size_t) nDigits <= bufferSize - 2);
     if ((size_t) nDigits > bufferSize - 2) {
-        UNLOCK_DTOA();
         return NULL;
     }
 
     memcpy(buffer + 2, numBegin, nDigits);
-    freedtoa(numBegin);
-    UNLOCK_DTOA();
+    freedtoa(PASS_STATE numBegin);
     numBegin = buffer + 2; /* +2 leaves space for sign and/or decimal point */
     numEnd = numBegin + nDigits;
     *numEnd = '\0';
 
     /* If Infinity, -Infinity, or NaN, return the string regardless of mode. */
     if (decPt != 9999) {
         JSBool exponentialNotation = JS_FALSE;
         int minNDigits = 0;  /* Min number of significant digits required */
@@ -348,18 +303,18 @@ static uint32 quorem2(Bigint *b, int32 k
 
 
 /* "-0.0000...(1073 zeros after decimal point)...0001\0" is the longest string that we could produce,
  * which occurs when printing -5e-324 in binary.  We could compute a better estimate of the size of
  * the output string and malloc fewer bytes depending on d and base, but why bother? */
 #define DTOBASESTR_BUFFER_SIZE 1078
 #define BASEDIGIT(digit) ((char)(((digit) >= 10) ? 'a' - 10 + (digit) : '0' + (digit)))
 
-JS_FRIEND_API(char *)
-JS_dtobasestr(int base, double dinput)
+char *
+js_dtobasestr(DtoaState *state, int base, double dinput)
 {
     U d;
     char *buffer;        /* The output string */
     char *p;             /* Pointer to current position in the buffer */
     char *pInt;          /* Pointer to the beginning of the integer part of the string */
     char *q;
     uint32 digit;
     U di;                /* d truncated to an integer */
@@ -381,17 +336,16 @@ JS_dtobasestr(int base, double dinput)
         }
 
         /* Check for Infinity and NaN */
         if ((word0(d) & Exp_mask) == Exp_mask) {
             strcpy(p, !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN");
             return buffer;
         }
 
-        LOCK_DTOA();
         /* Output the integer part of d with the digits in reverse order. */
         pInt = p;
         dval(di) = floor(dval(d));
         if (dval(di) <= 4294967295.0) {
             uint32 n = (uint32)dval(di);
             if (n)
                 do {
                     uint32 m = n / base;
@@ -399,33 +353,32 @@ JS_dtobasestr(int base, double dinput)
                     n = m;
                     JS_ASSERT(digit < (uint32)base);
                     *p++ = BASEDIGIT(digit);
                 } while (n);
             else *p++ = '0';
         } else {
             int e;
             int bits;  /* Number of significant bits in di; not used. */
-            Bigint *b = d2b(di, &e, &bits);
+            Bigint *b = d2b(PASS_STATE di, &e, &bits);
             if (!b)
                 goto nomem1;
-            b = lshift(b, e);
+            b = lshift(PASS_STATE b, e);
             if (!b) {
               nomem1:
-                Bfree(b);
-                UNLOCK_DTOA();
+                Bfree(PASS_STATE b);
                 js_free(buffer);
                 return NULL;
             }
             do {
                 digit = divrem(b, base);
                 JS_ASSERT(digit < (uint32)base);
                 *p++ = BASEDIGIT(digit);
             } while (b->wds);
-            Bfree(b);
+            Bfree(PASS_STATE b);
         }
         /* Reverse the digits of the integer part of d. */
         q = p-1;
         while (q > pInt) {
             char ch = *pInt;
             *pInt++ = *q;
             *q-- = ch;
         }
@@ -435,101 +388,100 @@ JS_dtobasestr(int base, double dinput)
             /* We have a fraction. */
             int e, bbits;
             int32 s2, done;
             Bigint *b, *s, *mlo, *mhi;
 
             b = s = mlo = mhi = NULL;
 
             *p++ = '.';
-            b = d2b(df, &e, &bbits);
+            b = d2b(PASS_STATE df, &e, &bbits);
             if (!b) {
               nomem2:
-                Bfree(b);
-                Bfree(s);
+                Bfree(PASS_STATE b);
+                Bfree(PASS_STATE s);
                 if (mlo != mhi)
-                    Bfree(mlo);
-                Bfree(mhi);
-                UNLOCK_DTOA();
+                    Bfree(PASS_STATE mlo);
+                Bfree(PASS_STATE mhi);
                 js_free(buffer);
                 return NULL;
             }
             JS_ASSERT(e < 0);
             /* At this point df = b * 2^e.  e must be less than zero because 0 < df < 1. */
 
             s2 = -(int32)(word0(d) >> Exp_shift1 & Exp_mask>>Exp_shift1);
 #ifndef Sudden_Underflow
             if (!s2)
                 s2 = -1;
 #endif
             s2 += Bias + P;
             /* 1/2^s2 = (nextDouble(d) - d)/2 */
             JS_ASSERT(-s2 < e);
-            mlo = i2b(1);
+            mlo = i2b(PASS_STATE 1);
             if (!mlo)
                 goto nomem2;
             mhi = mlo;
             if (!word1(d) && !(word0(d) & Bndry_mask)
 #ifndef Sudden_Underflow
                 && word0(d) & (Exp_mask & Exp_mask << 1)
 #endif
                 ) {
                 /* The special case.  Here we want to be within a quarter of the last input
                    significant digit instead of one half of it when the output string's value is less than d.  */
                 s2 += Log2P;
-                mhi = i2b(1<<Log2P);
+                mhi = i2b(PASS_STATE 1<<Log2P);
                 if (!mhi)
                     goto nomem2;
             }
-            b = lshift(b, e + s2);
+            b = lshift(PASS_STATE b, e + s2);
             if (!b)
                 goto nomem2;
-            s = i2b(1);
+            s = i2b(PASS_STATE 1);
             if (!s)
                 goto nomem2;
-            s = lshift(s, s2);
+            s = lshift(PASS_STATE s, s2);
             if (!s)
                 goto nomem2;
             /* At this point we have the following:
              *   s = 2^s2;
              *   1 > df = b/2^s2 > 0;
              *   (d - prevDouble(d))/2 = mlo/2^s2;
              *   (nextDouble(d) - d)/2 = mhi/2^s2. */
 
             done = JS_FALSE;
             do {
                 int32 j, j1;
                 Bigint *delta;
 
-                b = multadd(b, base, 0);
+                b = multadd(PASS_STATE b, base, 0);
                 if (!b)
                     goto nomem2;
                 digit = quorem2(b, s2);
                 if (mlo == mhi) {
-                    mlo = mhi = multadd(mlo, base, 0);
+                    mlo = mhi = multadd(PASS_STATE mlo, base, 0);
                     if (!mhi)
                         goto nomem2;
                 }
                 else {
-                    mlo = multadd(mlo, base, 0);
+                    mlo = multadd(PASS_STATE mlo, base, 0);
                     if (!mlo)
                         goto nomem2;
-                    mhi = multadd(mhi, base, 0);
+                    mhi = multadd(PASS_STATE mhi, base, 0);
                     if (!mhi)
                         goto nomem2;
                 }
 
                 /* Do we yet have the shortest string that will round to d? */
                 j = cmp(b, mlo);
                 /* j is b/2^s2 compared with mlo/2^s2. */
-                delta = diff(s, mhi);
+                delta = diff(PASS_STATE s, mhi);
                 if (!delta)
                     goto nomem2;
                 j1 = delta->sign ? 1 : cmp(b, delta);
-                Bfree(delta);
+                Bfree(PASS_STATE delta);
                 /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */
 
 #ifndef ROUND_BIASED
                 if (j1 == 0 && !(word1(d) & 1)) {
                     if (j > 0)
                         digit++;
                     done = JS_TRUE;
                 } else
@@ -537,36 +489,47 @@ JS_dtobasestr(int base, double dinput)
                 if (j < 0 || (j == 0
 #ifndef ROUND_BIASED
                     && !(word1(d) & 1)
 #endif
                     )) {
                     if (j1 > 0) {
                         /* Either dig or dig+1 would work here as the least significant digit.
                            Use whichever would produce an output value closer to d. */
-                        b = lshift(b, 1);
+                        b = lshift(PASS_STATE b, 1);
                         if (!b)
                             goto nomem2;
                         j1 = cmp(b, s);
                         if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output
                                      * such as 3.5 in base 3.  */
                             digit++;
                     }
                     done = JS_TRUE;
                 } else if (j1 > 0) {
                     digit++;
                     done = JS_TRUE;
                 }
                 JS_ASSERT(digit < (uint32)base);
                 *p++ = BASEDIGIT(digit);
             } while (!done);
-            Bfree(b);
-            Bfree(s);
+            Bfree(PASS_STATE b);
+            Bfree(PASS_STATE s);
             if (mlo != mhi)
-                Bfree(mlo);
-            Bfree(mhi);
+                Bfree(PASS_STATE mlo);
+            Bfree(PASS_STATE mhi);
         }
         JS_ASSERT(p < buffer + DTOBASESTR_BUFFER_SIZE);
         *p = '\0';
-        UNLOCK_DTOA();
     }
     return buffer;
 }
+
+DtoaState *
+js_NewDtoaState()
+{
+    return newdtoa();
+}
+
+void
+js_DestroyDtoaState(DtoaState *state)
+{
+    destroydtoa(state);
+}
--- a/js/src/jsdtoa.h
+++ b/js/src/jsdtoa.h
@@ -43,33 +43,40 @@
  * Public interface to portable double-precision floating point to string
  * and back conversion package.
  */
 
 #include "jscompat.h"
 
 JS_BEGIN_EXTERN_C
 
+struct DtoaState;
+
+DtoaState *
+js_NewDtoaState();
+
+void
+js_DestroyDtoaState(DtoaState *state);
+
 /*
- * JS_strtod() returns as a double-precision floating-point number
- * the  value represented by the character string pointed to by
- * s00.  The string is scanned up to  the  first  unrecognized
- * character.
- * If the value of se is not (char **)NULL,  a  pointer  to
- * the  character terminating the scan is returned in the location pointed
- * to by se.  If no number can be  formed, se is set to s00r, and
- * zero is returned.
+ * js_strtod_harder() returns as a double-precision floating-point number the
+ * value represented by the character string pointed to by s00. The string is
+ * scanned up to the first unrecognized character.
+ *
+ * If se is not NULL, *se receives a pointer to the character terminating the
+ * scan. If no number can be formed, *se receives a pointer to the first
+ * unparseable character in s00, and zero is returned.
  *
  * *err is set to zero on success; it's set to JS_DTOA_ERANGE on range
  * errors and JS_DTOA_ENOMEM on memory failure.
  */
 #define JS_DTOA_ERANGE 1
 #define JS_DTOA_ENOMEM 2
-JS_FRIEND_API(double)
-JS_strtod(const char *s00, char **se, int *err);
+double
+js_strtod_harder(DtoaState *state, const char *s00, char **se, int *err);
 
 /*
  * Modes for converting floating-point numbers to strings.
  *
  * Some of the modes can round-trip; this means that if the number is converted to
  * a string using one of these mode and then converted back to a number, the result
  * will be identical to the original number (except that, due to ECMA, -0 will get converted
  * to +0).  These round-trip modes return the minimum number of significand digits that
@@ -97,35 +104,29 @@ typedef enum JSDToStrMode {
 
 /*
  * Convert dval according to the given mode and return a pointer to the resulting ASCII string.
  * The result is held somewhere in buffer, but not necessarily at the beginning.  The size of
  * buffer is given in bufferSize, and must be at least as large as given by the above macros.
  *
  * Return NULL if out of memory.
  */
-JS_FRIEND_API(char *)
-JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double dval);
+char *
+js_dtostr(DtoaState *state, char *buffer, size_t bufferSize, JSDToStrMode mode, int precision,
+          double dval);
 
 /*
  * Convert d to a string in the given base.  The integral part of d will be printed exactly
  * in that base, regardless of how large it is, because there is no exponential notation for non-base-ten
  * numbers.  The fractional part will be rounded to as few digits as possible while still preserving
  * the round-trip property (analogous to that of printing decimal numbers).  In other words, if one were
  * to read the resulting string in via a hypothetical base-number-reading routine that rounds to the nearest
  * IEEE double (and to an even significand if there are two equally near doubles), then the result would
  * equal d (except for -0.0, which converts to "0", and NaN, which is not equal to itself).
  *
  * Return NULL if out of memory.  If the result is not NULL, it must be released via free().
  */
-JS_FRIEND_API(char *)
-JS_dtobasestr(int base, double d);
-
-/*
- * Clean up any persistent RAM allocated during the execution of DtoA
- * routines, and remove any locks that might have been created.
- */
-JS_FRIEND_API(JSBool) js_InitDtoa(void);
-JS_FRIEND_API(void) js_FinishDtoa(void);
+char *
+js_dtobasestr(DtoaState *state, int base, double d);
 
 JS_END_EXTERN_C
 
 #endif /* jsdtoa_h___ */
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -316,17 +316,18 @@ num_toSource(JSContext *cx, uintN argc, 
     char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr;
     char buf[64];
     JSString *str;
 
     if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &v))
         return JS_FALSE;
     JS_ASSERT(JSVAL_IS_NUMBER(v));
     d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
-    numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d);
+    numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, numBuf, sizeof numBuf,
+                       DTOSTR_STANDARD, 0, d);
     if (!numStr) {
         JS_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
     JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr);
     str = JS_NewStringCopyZ(cx, buf);
     if (!str)
         return JS_FALSE;
@@ -570,26 +571,28 @@ num_to(JSContext *cx, JSDToStrMode zeroA
         precision = 0.0;
         oneArgMode = zeroArgMode;
     } else {
         precision = js_ValueToNumber(cx, &vp[2]);
         if (JSVAL_IS_NULL(vp[2]))
             return JS_FALSE;
         precision = js_DoubleToInteger(precision);
         if (precision < precisionMin || precision > precisionMax) {
-            numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision);
+            numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf,
+                               DTOSTR_STANDARD, 0, precision);
             if (!numStr)
                 JS_ReportOutOfMemory(cx);
             else
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);
             return JS_FALSE;
         }
     }
 
-    numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d);
+    numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf,
+                       oneArgMode, (jsint)precision + precisionOffset, d);
     if (!numStr) {
         JS_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
     str = JS_NewStringCopyZ(cx, numStr);
     if (!str)
         return JS_FALSE;
     *vp = STRING_TO_JSVAL(str);
@@ -842,19 +845,20 @@ NumberToCString(JSContext *cx, jsdouble 
     jsint i;
     char *numStr;
 
     JS_ASSERT(bufSize >= DTOSTR_STANDARD_BUFFER_SIZE);
     if (JSDOUBLE_IS_INT(d, i)) {
         numStr = IntToCString(i, base, buf, bufSize);
     } else {
         if (base == 10)
-            numStr = JS_dtostr(buf, bufSize, DTOSTR_STANDARD, 0, d);
+            numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, bufSize,
+                               DTOSTR_STANDARD, 0, d);
         else
-            numStr = JS_dtobasestr(base, d);
+            numStr = js_dtobasestr(JS_THREAD_DATA(cx)->dtoaState, base, d);
         if (!numStr) {
             JS_ReportOutOfMemory(cx);
             return NULL;
         }
     }
     return numStr;
 }
 
@@ -910,17 +914,18 @@ js_NumberValueToCharBuffer(JSContext *cx
     /* Convert to C-string. */
     static const size_t arrSize = DTOSTR_STANDARD_BUFFER_SIZE;
     char arr[arrSize];
     const char *cstr;
     if (JSVAL_IS_INT(v)) {
         cstr = IntToCString(JSVAL_TO_INT(v), 10, arr, arrSize);
     } else {
         JS_ASSERT(JSVAL_IS_DOUBLE(v));
-        cstr = JS_dtostr(arr, arrSize, DTOSTR_STANDARD, 0, *JSVAL_TO_DOUBLE(v));
+        cstr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, arr, arrSize,
+                         DTOSTR_STANDARD, 0, *JSVAL_TO_DOUBLE(v));
     }
     if (!cstr)
         return JS_FALSE;
 
     /*
      * Inflate to jschar string.  The input C-string characters are < 127, so
      * even if jschars are UTF-8, all chars should map to one jschar.
      */
@@ -1173,17 +1178,17 @@ js_strtod(JSContext *cx, const jschar *s
     istr = cstr;
     if ((negative = (*istr == '-')) != 0 || *istr == '+')
         istr++;
     if (*istr == 'I' && !strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) {
         d = negative ? js_NegativeInfinity : js_PositiveInfinity;
         estr = istr + 8;
     } else {
         int err;
-        d = JS_strtod(cstr, &estr, &err);
+        d = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err);
         if (d == HUGE_VAL)
             d = js_PositiveInfinity;
         else if (d == -HUGE_VAL)
             d = js_NegativeInfinity;
     }
 
     i = estr - cstr;
     if (cstr != cbuf)
@@ -1291,31 +1296,31 @@ js_strtointeger(JSContext *cx, const jsc
         value = value * base + digit;
     } while (++s1 != send);
 
     if (value >= 9007199254740992.0) {
         if (base == 10) {
             /*
              * If we're accumulating a decimal number and the number is >=
              * 2^53, then the result from the repeated multiply-add above may
-             * be inaccurate.  Call JS_strtod to get the correct answer.
+             * be inaccurate.  Call js_strtod_harder to get the correct answer.
              */
             size_t i;
             size_t length = s1 - start;
             char *cstr = (char *) cx->malloc(length + 1);
             char *estr;
             int err=0;
 
             if (!cstr)
                 return JS_FALSE;
             for (i = 0; i != length; i++)
                 cstr[i] = (char)start[i];
             cstr[length] = 0;
 
-            value = JS_strtod(cstr, &estr, &err);
+            value = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err);
             if (err == JS_DTOA_ENOMEM) {
                 JS_ReportOutOfMemory(cx);
                 cx->free(cstr);
                 return JS_FALSE;
             }
             if (err == JS_DTOA_ERANGE && value == HUGE_VAL)
                 value = js_PositiveInfinity;
             cx->free(cstr);
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -477,17 +477,18 @@ Str(JSContext *cx, jsid id, JSObject *ho
         if (JSVAL_IS_DOUBLE(*vp)) {
             jsdouble d = *JSVAL_TO_DOUBLE(*vp);
             if (!JSDOUBLE_IS_FINITE(d))
                 return js_AppendLiteral(scx->cb, "null");
         }
 
         char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr;
         jsdouble d = JSVAL_IS_INT(*vp) ? jsdouble(JSVAL_TO_INT(*vp)) : *JSVAL_TO_DOUBLE(*vp);
-        numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d);
+        numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, numBuf, sizeof numBuf,
+                           DTOSTR_STANDARD, 0, d);
         if (!numStr) {
             JS_ReportOutOfMemory(cx);
             return JS_FALSE;
         }
 
         jschar dstr[DTOSTR_STANDARD_BUFFER_SIZE];
         size_t dbufSize = DTOSTR_STANDARD_BUFFER_SIZE;
         if (!js_InflateStringToBuffer(cx, numStr, strlen(numStr), dstr, &dbufSize))
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1102,17 +1102,18 @@ SprintDoubleValue(Sprinter *sp, jsval v,
         todo = SprintCString(sp,
                              JSDOUBLE_IS_NaN(d)
                              ? "0 / 0"
                              : (d < 0)
                              ? "1 / -0"
                              : "1 / 0");
         *opp = JSOP_DIV;
     } else {
-        s = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d);
+        s = js_dtostr(JS_THREAD_DATA(sp->context)->dtoaState, buf, sizeof buf,
+                      DTOSTR_STANDARD, 0, d);
         if (!s) {
             JS_ReportOutOfMemory(sp->context);
             return -1;
         }
         JS_ASSERT(strcmp(s, js_Infinity_str) &&
                   (*s != '-' ||
                    strcmp(s + 1, js_Infinity_str)) &&
                   strcmp(s, js_NaN_str));