Bug 825545 - Make the ScriptSource API work even if it is being compressed. r=jorendorff
authorBenjamin Peterson <benjamin@python.org>
Mon, 07 Jan 2013 18:38:38 -0600
changeset 127901 038ff90d816b2be0595d02a0404f13ffc9933845
parent 127900 80a3e4771be70fdff44cc8d6e0745f76cd4576db
child 127902 488af66ab0254c4262567f661bda69953fa10215
push idunknown
push userunknown
push dateunknown
reviewersjorendorff
bugs825545
milestone21.0a1
Bug 825545 - Make the ScriptSource API work even if it is being compressed. r=jorendorff
js/src/jsapi-tests/testSourcePolicy.cpp
js/src/jsscript.cpp
js/src/jsscript.h
--- a/js/src/jsapi-tests/testSourcePolicy.cpp
+++ b/js/src/jsapi-tests/testSourcePolicy.cpp
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "tests.h"
+#include "jsscript.h"
 
 BEGIN_TEST(testBug795104)
 {
     JS::CompileOptions opts(cx);
     opts.setSourcePolicy(JS::CompileOptions::NO_SOURCE);
     const size_t strLen = 60002;
     char *s = static_cast<char *>(JS_malloc(cx, strLen));
     CHECK(s);
@@ -16,8 +17,32 @@ BEGIN_TEST(testBug795104)
     s[strLen - 1] = '"';
     CHECK(JS::Evaluate(cx, global, opts, s, strLen, NULL));
     CHECK(JS::CompileFunction(cx, global, opts, "f", 0, NULL, s, strLen));
     JS_free(cx, s);
 
     return true;
 }
 END_TEST(testBug795104)
+
+const char *simpleSource = "var x = 4;";
+
+static void
+newScriptHook(JSContext *cx, const char *fn, unsigned lineno,
+              JSScript *script, JSFunction *fun, void *data)
+{
+    // Just need this not to fail.
+    if (!JS_StringEqualsAscii(cx, script->sourceData(cx), simpleSource, (JSBool *)data))
+        *((JSBool *)data) = JS_FALSE;
+}
+
+BEGIN_TEST(testScriptSourceReentrant)
+{
+    JS::CompileOptions opts(cx);
+    JSBool match = false;
+    JS_SetNewScriptHook(rt, newScriptHook, &match);
+    CHECK(JS::Evaluate(cx, global, opts, simpleSource, strlen(simpleSource), NULL));
+    CHECK(match);
+    JS_SetNewScriptHook(rt, NULL, NULL);
+
+    return true;
+}
+END_TEST(testScriptSourceReentrant)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -940,16 +940,23 @@ SourceCompressorThread::finish()
     if (wakeup)
         PR_DestroyCondVar(wakeup);
     if (done)
         PR_DestroyCondVar(done);
     if (lock)
         PR_DestroyLock(lock);
 }
 
+const jschar *
+SourceCompressorThread::currentChars() const
+{
+    JS_ASSERT(tok);
+    return tok->chars;
+}
+
 bool
 SourceCompressorThread::internalCompress()
 {
     JS_ASSERT(state == COMPRESSING);
     JS_ASSERT(tok);
 
     ScriptSource *ss = tok->ss;
     JS_ASSERT(!ss->ready());
@@ -1046,16 +1053,17 @@ SourceCompressorThread::compress(SourceC
         // We have reentered the compiler. (This can happen through the
         // debugger.) Complete the current compression before starting the next
         // one.
         waitOnCompression(tok);
     JS_ASSERT(state == IDLE);
     JS_ASSERT(!tok);
     stop = false;
     PR_Lock(lock);
+    sct->ss->ready_ = false;
     tok = sct;
     state = COMPRESSING;
     PR_NotifyCondVar(wakeup);
     PR_Unlock(lock);
 }
 
 void
 SourceCompressorThread::waitOnCompression(SourceCompressionToken *userTok)
@@ -1065,19 +1073,17 @@ SourceCompressorThread::waitOnCompressio
     while (state == COMPRESSING)
         PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
     JS_ASSERT(state == IDLE);
     SourceCompressionToken *saveTok = tok;
     tok = NULL;
     PR_Unlock(lock);
 
     JS_ASSERT(!saveTok->ss->ready());
-#ifdef DEBUG
     saveTok->ss->ready_ = true;
-#endif
 
     // Update memory accounting.
     if (!saveTok->oom)
         saveTok->cx->runtime->updateMallocCounter(NULL, saveTok->ss->computedSizeOfData());
 
     saveTok->ss = NULL;
     saveTok->chars = NULL;
 }
@@ -1178,21 +1184,22 @@ SourceDataCache::purge()
 {
     js_delete(map_);
     map_ = NULL;
 }
 
 JSFlatString *
 ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
 {
-    JS_ASSERT(ready());
     const jschar *chars;
 #if USE_ZLIB
     Rooted<JSStableString *> cached(cx, NULL);
-    if (compressed()) {
+    if (!ready()) {
+        chars = cx->runtime->sourceCompressorThread.currentChars();
+    } else if (compressed()) {
         cached = cx->runtime->sourceDataCache.lookup(this);
         if (!cached) {
             const size_t nbytes = sizeof(jschar) * (length_ + 1);
             jschar *decompressed = static_cast<jschar *>(cx->malloc_(nbytes));
             if (!decompressed)
                 return NULL;
             if (!DecompressString(data.compressed, compressedLength_,
                                   reinterpret_cast<unsigned char *>(decompressed), nbytes)) {
@@ -1224,19 +1231,16 @@ ScriptSource::setSourceCopy(JSContext *c
                             bool argumentsNotIncluded, SourceCompressionToken *tok)
 {
     JS_ASSERT(!hasSourceData());
     length_ = length;
     argumentsNotIncluded_ = argumentsNotIncluded;
 
 #ifdef JS_THREADSAFE
     if (tok && cx->runtime->useHelperThreads()) {
-#ifdef DEBUG
-        ready_ = false;
-#endif
         tok->ss = this;
         tok->chars = src.get();
         cx->runtime->sourceCompressorThread.compress(tok);
     } else
 #endif
     {
         if (!adjustDataSize(sizeof(jschar) * length))
             return false;
@@ -1282,19 +1286,17 @@ SourceCompressionToken::abort()
 }
 
 void
 ScriptSource::destroy(JSRuntime *rt)
 {
     JS_ASSERT(ready());
     adjustDataSize(0);
     js_free(sourceMap_);
-#ifdef DEBUG
     ready_ = false;
-#endif
     js_free(this);
 }
 
 size_t
 ScriptSource::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf)
 {
     JS_ASSERT(ready());
 
@@ -1368,20 +1370,18 @@ ScriptSource::performXDR(XDRState<mode> 
                 js_free(sourceMap_);
                 sourceMap_ = NULL;
             }
             return false;
         }
         sourceMap_[sourceMapLen] = '\0';
     }
 
-#ifdef DEBUG
     if (mode == XDR_DECODE)
         ready_ = true;
-#endif
 
     return true;
 }
 
 bool
 ScriptSource::setSourceMap(JSContext *cx, jschar *sourceMapURL, const char *filename)
 {
     JS_ASSERT(sourceMapURL);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1031,52 +1031,46 @@ struct ScriptSource
     uint32_t compressedLength_;
     jschar *sourceMap_;
 
     // True if we can call JSRuntime::sourceHook to load the source on
     // demand. If sourceRetrievable_ and hasSourceData() are false, it is not
     // possible to get source at all.
     bool sourceRetrievable_:1;
     bool argumentsNotIncluded_:1;
-#ifdef DEBUG
     bool ready_:1;
-#endif
 
   public:
     ScriptSource()
       : refs(0),
         length_(0),
         compressedLength_(0),
         sourceMap_(NULL),
         sourceRetrievable_(false),
-        argumentsNotIncluded_(false)
-#ifdef DEBUG
-       ,ready_(true)
-#endif
+        argumentsNotIncluded_(false),
+        ready_(true)
     {
         data.source = NULL;
     }
     void incref() { refs++; }
     void decref(JSRuntime *rt) {
         JS_ASSERT(refs != 0);
         if (--refs == 0)
             destroy(rt);
     }
     bool setSourceCopy(JSContext *cx,
                        StableCharPtr src,
                        uint32_t length,
                        bool argumentsNotIncluded,
                        SourceCompressionToken *tok);
     void setSource(const jschar *src, uint32_t length);
-#ifdef DEBUG
     bool ready() const { return ready_; }
-#endif
     void setSourceRetrievable() { sourceRetrievable_ = true; }
     bool sourceRetrievable() const { return sourceRetrievable_; }
-    bool hasSourceData() const { return !!data.source; }
+    bool hasSourceData() const { return !!data.source || !ready(); }
     uint32_t length() const {
         JS_ASSERT(hasSourceData());
         return length_;
     }
     bool argumentsNotIncluded() const {
         JS_ASSERT(hasSourceData());
         return argumentsNotIncluded_;
     }
@@ -1168,16 +1162,17 @@ class SourceCompressorThread
       lock(NULL),
       wakeup(NULL),
       done(NULL) {}
     void finish();
     bool init();
     void compress(SourceCompressionToken *tok);
     void waitOnCompression(SourceCompressionToken *userTok);
     void abort(SourceCompressionToken *userTok);
+    const jschar *currentChars() const;
 };
 #endif
 
 struct SourceCompressionToken
 {
     friend struct ScriptSource;
     friend class SourceCompressorThread;
   private: