Bug 660734, part 2 - Add back a ContextAllocPolicy that calls cx->malloc_ and use it in StringBuffer so that the new string's bytes are counted by the GC (r=igor)
authorLuke Wagner <luke@mozilla.com>
Fri, 17 Jun 2011 10:14:30 -0700
changeset 71382 31e3b521775b5c4ff477848fe1650d67af84e332
parent 71381 47b578958aa4372e97c539164fbc156b43617db8
child 71383 3a70ffbb58a155394c148963ed8883ab6480f304
push idunknown
push userunknown
push dateunknown
reviewersigor
bugs660734
milestone7.0a1
Bug 660734, part 2 - Add back a ContextAllocPolicy that calls cx->malloc_ and use it in StringBuffer so that the new string's bytes are counted by the GC (r=igor)
js/src/jit-test/tests/basic/testStringBufferMallocAccounting.js
js/src/jscntxt.h
js/src/jsstrinlines.h
js/src/jsvector.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testStringBufferMallocAccounting.js
@@ -0,0 +1,13 @@
+// first build a big honkin' string
+str = "a";
+for (var i = 0; i < 20; ++i)
+    str = str + str;
+str.indexOf('a');
+
+// copying this sucker should gc
+makeFinalizeObserver();
+assertEq(finalizeCount(), 0);
+// if the assert fails, add more iterations
+for (var i = 0; i < 50; ++i)
+    str.replace(/(a)/, '$1');
+assertEq(finalizeCount(), 1);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1435,38 +1435,16 @@ struct JSContext
      * threshold when p is not null. The function takes the pointer and not
      * a boolean flag to minimize the amount of code in its inlined callers.
      */
     JS_FRIEND_API(void) checkMallocGCPressure(void *p);
 }; /* struct JSContext */
 
 namespace js {
 
-/*
- * Allocation policy that uses JSRuntime::malloc_ and friends, so that
- * memory pressure is properly accounted for. This is suitable for
- * long-lived objects owned by the JSRuntime.
- *
- * Since it doesn't hold a JSContext (those may not live long enough), it
- * can't report out-of-memory conditions itself; the caller must check for
- * OOM and take the appropriate action.
- */
-class RuntimeAllocPolicy
-{
-    JSRuntime *const runtime;
-
-  public:
-    RuntimeAllocPolicy(JSRuntime *rt) : runtime(rt) {}
-    RuntimeAllocPolicy(JSContext *cx) : runtime(cx->runtime) {}
-    void *malloc_(size_t bytes) { return runtime->malloc_(bytes); }
-    void *realloc_(void *p, size_t bytes) { return runtime->realloc_(p, bytes); }
-    void free_(void *p) { runtime->free_(p); }
-    void reportAllocOverflow() const {}
-};
-
 #ifdef JS_THREADSAFE
 # define JS_THREAD_ID(cx)       ((cx)->thread() ? (cx)->thread()->id : 0)
 #endif
 
 #if defined JS_THREADSAFE && defined DEBUG
 
 class AutoCheckRequestDepth {
     JSContext *cx;
@@ -2625,16 +2603,56 @@ class AutoShapeVector : public AutoVecto
     }
 
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 JSIdArray *
 NewIdArray(JSContext *cx, jsint length);
 
+/*
+ * Allocation policy that uses JSRuntime::malloc_ and friends, so that
+ * memory pressure is properly accounted for. This is suitable for
+ * long-lived objects owned by the JSRuntime.
+ *
+ * Since it doesn't hold a JSContext (those may not live long enough), it
+ * can't report out-of-memory conditions itself; the caller must check for
+ * OOM and take the appropriate action.
+ *
+ * FIXME bug 647103 - replace these *AllocPolicy names.
+ */
+class RuntimeAllocPolicy
+{
+    JSRuntime *const runtime;
+
+  public:
+    RuntimeAllocPolicy(JSRuntime *rt) : runtime(rt) {}
+    RuntimeAllocPolicy(JSContext *cx) : runtime(cx->runtime) {}
+    void *malloc_(size_t bytes) { return runtime->malloc_(bytes); }
+    void *realloc_(void *p, size_t bytes) { return runtime->realloc_(p, bytes); }
+    void free_(void *p) { runtime->free_(p); }
+    void reportAllocOverflow() const {}
+};
+
+/*
+ * FIXME bug 647103 - replace these *AllocPolicy names.
+ */
+class ContextAllocPolicy
+{
+    JSContext *const cx;
+
+  public:
+    ContextAllocPolicy(JSContext *cx) : cx(cx) {}
+    JSContext *context() const { return cx; }
+    void *malloc_(size_t bytes) { return cx->malloc_(bytes); }
+    void *realloc_(void *p, size_t oldBytes, size_t bytes) { return cx->realloc_(p, oldBytes, bytes); }
+    void free_(void *p) { cx->free_(p); }
+    void reportAllocOverflow() const { js_ReportAllocationOverflow(cx); }
+};
+
 } /* namespace js */
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
 #endif
 
 #endif /* jscntxt_h___ */
--- a/js/src/jsstrinlines.h
+++ b/js/src/jsstrinlines.h
@@ -74,17 +74,19 @@ CheckStringLength(JSContext *cx, size_t 
  * string length.
  *
  * Note: over-allocation is not checked for when using the infallible
  * |replaceRawBuffer|, so the implementation of |finishString| also must check
  * for over-allocation.
  */
 class StringBuffer
 {
-    typedef Vector<jschar, 32> CharBuffer;
+    /* cb's buffer is taken by the new string so use ContextAllocPolicy. */
+    typedef Vector<jschar, 32, ContextAllocPolicy> CharBuffer;
+
     CharBuffer cb;
 
     static inline bool checkLength(JSContext *cx, size_t length);
     inline bool checkLength(size_t length);
     JSContext *context() const { return cb.allocPolicy().context(); }
     jschar *extractWellSized();
 
   public:
--- a/js/src/jsvector.h
+++ b/js/src/jsvector.h
@@ -153,17 +153,17 @@ struct VectorImpl<T, N, AP, true>
     static inline void copyConstructN(T *dst, size_t n, const T &t) {
         for (T *p = dst, *end = dst + n; p != end; ++p)
             *p = t;
     }
 
     static inline bool growTo(Vector<T,N,AP> &v, size_t newcap) {
         JS_ASSERT(!v.usingInlineStorage());
         size_t bytes = sizeof(T) * newcap;
-        size_t oldBytes = sizeof(T) * v.mLength;
+        size_t oldBytes = sizeof(T) * v.mCapacity;
         T *newbuf = reinterpret_cast<T *>(v.realloc_(v.mBegin, oldBytes, bytes));
         if (!newbuf)
             return false;
         v.mBegin = newbuf;
         /* v.mLength is unchanged. */
         v.mCapacity = newcap;
         return true;
     }