--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -18,20 +18,20 @@
#include "frontend/TreeContext-inl.h"
using namespace js;
using namespace js::frontend;
class AutoAttachToRuntime {
JSRuntime *rt;
+ ScriptSource *ss;
public:
- ScriptSource *ss;
- AutoAttachToRuntime(JSRuntime *rt)
- : rt(rt), ss(NULL) {}
+ AutoAttachToRuntime(JSRuntime *rt, ScriptSource *ss)
+ : rt(rt), ss(ss) {}
~AutoAttachToRuntime() {
// This makes the source visible to the GC. If compilation fails, and no
// script refers to it, it will be collected.
if (ss)
ss->attachToRuntime(rt);
}
};
@@ -74,24 +74,24 @@ frontend::CompileScript(JSContext *cx, H
* The scripted callerFrame can only be given for compile-and-go scripts
* and non-zero static level requires callerFrame.
*/
JS_ASSERT_IF(callerFrame, options.compileAndGo);
JS_ASSERT_IF(staticLevel != 0, callerFrame);
if (!CheckLength(cx, length))
return NULL;
- AutoAttachToRuntime attacher(cx->runtime);
+ ScriptSource *ss = cx->new_<ScriptSource>();
+ if (!ss)
+ return NULL;
+ AutoAttachToRuntime attacher(cx->runtime, ss);
SourceCompressionToken sct(cx);
- ScriptSource *ss = NULL;
if (!cx->hasRunOption(JSOPTION_ONLY_CNG_SOURCE) || options.compileAndGo) {
- ss = ScriptSource::createFromSource(cx, chars, length, false, &sct);
- if (!ss)
+ if (!ss->setSource(cx, chars, length, false, &sct))
return NULL;
- attacher.ss = ss;
}
Parser parser(cx, options, chars, length, /* foldConstants = */ true);
if (!parser.init())
return NULL;
parser.sct = &sct;
SharedContext sc(cx, scopeChain, /* fun = */ NULL, /* funbox = */ NULL, StrictModeFromContext(cx));
@@ -239,23 +239,24 @@ frontend::CompileScript(JSContext *cx, H
// Compile a JS function body, which might appear as the value of an event
// handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
bool
frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options,
Bindings *bindings, const jschar *chars, size_t length)
{
if (!CheckLength(cx, length))
- return false;
- AutoAttachToRuntime attacher(cx->runtime);
- SourceCompressionToken sct(cx);
- ScriptSource *ss = ScriptSource::createFromSource(cx, chars, length, true, &sct);
+ return NULL;
+ ScriptSource *ss = cx->new_<ScriptSource>();
if (!ss)
return NULL;
- attacher.ss = ss;
+ AutoAttachToRuntime attacher(cx->runtime, ss);
+ SourceCompressionToken sct(cx);
+ if (!ss->setSource(cx, chars, length, true, &sct))
+ return NULL;
options.setCompileAndGo(false);
Parser parser(cx, options, chars, length, /* foldConstants = */ true);
if (!parser.init())
return false;
parser.sct = &sct;
JS_ASSERT(fun);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -601,18 +601,21 @@ js::FunctionToString(JSContext *cx, Hand
if (!out.append("function "))
return NULL;
if (fun->atom) {
if (!out.append(fun->atom))
return NULL;
}
}
bool haveSource = fun->isInterpreted();
- if (haveSource && !fun->script()->scriptSource() && !fun->script()->loadSource(cx, &haveSource))
- return NULL;
+ if (haveSource && !fun->script()->scriptSource()->hasSourceData() &&
+ !fun->script()->loadSource(cx, &haveSource))
+ {
+ return NULL;
+ }
if (haveSource) {
RootedScript script(cx, fun->script());
RootedString src(cx, fun->script()->sourceData(cx));
if (!src)
return NULL;
const jschar *chars = src->getChars(cx);
if (!chars)
return NULL;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -353,18 +353,18 @@ js::XDRScript(XDRState<mode> *xdr, Handl
ContainsDynamicNameAccess,
FunHasExtensibleScope,
ArgumentsHasVarBinding,
NeedsArgsObj,
OwnFilename,
ParentFilename,
IsGenerator,
IsGeneratorExp,
- HaveSource,
OwnSource,
+ HasSourceData,
ExplicitUseStrict
};
uint32_t length, lineno, nslots;
uint32_t natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, nClosedArgs, nClosedVars, i;
uint32_t prologLength, version;
uint32_t nTypeSets = 0;
uint32_t scriptBits = 0;
@@ -514,20 +514,20 @@ js::XDRScript(XDRState<mode> *xdr, Handl
scriptBits |= (1 << ArgumentsHasVarBinding);
if (script->analyzedArgsUsage() && script->needsArgsObj())
scriptBits |= (1 << NeedsArgsObj);
if (script->filename) {
scriptBits |= (enclosingScript && enclosingScript->filename == script->filename)
? (1 << ParentFilename)
: (1 << OwnFilename);
}
- if (script->scriptSource()) {
- scriptBits |= (1 << HaveSource);
- if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
- scriptBits |= (1 << OwnSource);
+ if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource()) {
+ scriptBits |= (1 << OwnSource);
+ if (script->scriptSource()->hasSourceData())
+ scriptBits |= (1 << HasSourceData);
}
if (script->isGenerator)
scriptBits |= (1 << IsGenerator);
if (script->isGeneratorExp)
scriptBits |= (1 << IsGeneratorExp);
JS_ASSERT(!script->compileAndGo);
JS_ASSERT(!script->hasSingletons);
@@ -568,18 +568,29 @@ js::XDRScript(XDRState<mode> *xdr, Handl
JSVersion version_ = JSVersion(version & JS_BITMASK(16));
JS_ASSERT((version_ & VersionFlags::FULL_MASK) == unsigned(version_));
// principals and originPrincipals are set with xdr->initScriptPrincipals(script) below.
// staticLevel is set below.
CompileOptions options(cx);
options.setVersion(version_)
.setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)));
+ ScriptSource *ss;
+ if (scriptBits & (1 << OwnSource)) {
+ ss = cx->new_<ScriptSource>();
+ if (!ss)
+ return NULL;
+ } else {
+ JS_ASSERT(enclosingScript);
+ ss = enclosingScript->scriptSource();
+ }
script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)),
- options, /* staticLevel = */ 0, NULL, 0, 0);
+ options, /* staticLevel = */ 0, ss, 0, 0);
+ if (scriptBits & (1 << OwnSource))
+ ss->attachToRuntime(cx->runtime);
if (!script || !JSScript::partiallyInit(cx, script,
length, nsrcnotes, natoms, nobjects,
nregexps, ntrynotes, nconsts, nClosedArgs,
nClosedVars, nTypeSets))
return JS_FALSE;
script->bindings.transfer(&bindings);
JS_ASSERT(!script->mainOffset);
@@ -629,30 +640,20 @@ js::XDRScript(XDRState<mode> *xdr, Handl
return false;
}
} else if (scriptBits & (1 << ParentFilename)) {
JS_ASSERT(enclosingScript);
if (mode == XDR_DECODE)
script->filename = enclosingScript->filename;
}
- if (scriptBits & (1 << HaveSource)) {
- ScriptSource *ss = script->scriptSource();
- if (scriptBits & (1 << OwnSource)) {
- if (!ScriptSource::performXDR<mode>(xdr, &ss))
- return false;
- } else {
- JS_ASSERT(enclosingScript);
- ss = enclosingScript->scriptSource();
- }
- if (mode == XDR_DECODE)
- script->setScriptSource(cx, ss);
- } else if (mode == XDR_DECODE) {
- script->setScriptSource(cx, NULL);
- JS_ASSERT_IF(enclosingScript, !enclosingScript->scriptSource());
+ if (scriptBits & (1 << HasSourceData)) {
+ JS_ASSERT(scriptBits & (1 << OwnSource));
+ if (!script->scriptSource()->performXDR<mode>(xdr))
+ return false;
}
if (!xdr->codeUint32(&script->sourceStart))
return false;
if (!xdr->codeUint32(&script->sourceEnd))
return false;
if (mode == XDR_DECODE) {
script->lineno = lineno;
@@ -1122,52 +1123,48 @@ SourceCompressorThread::abort(SourceComp
JS_ASSERT(userTok == tok);
stop = true;
}
#endif /* JS_THREADSAFE */
void
JSScript::setScriptSource(JSContext *cx, ScriptSource *ss)
{
+ JS_ASSERT(ss);
#ifdef JSGC_INCREMENTAL
// During IGC, we need to barrier writing to scriptSource_.
- if (ss && cx->runtime->gcIncrementalState != NO_INCREMENTAL && cx->runtime->gcIsFull)
+ if (cx->runtime->gcIncrementalState != NO_INCREMENTAL && cx->runtime->gcIsFull)
ss->mark();
#endif
scriptSource_ = ss;
}
bool
JSScript::loadSource(JSContext *cx, bool *worked)
{
- JS_ASSERT(!scriptSource_);
+ JS_ASSERT(!scriptSource_->hasSourceData());
*worked = false;
if (!cx->runtime->sourceHook)
return true;
jschar *src = NULL;
uint32_t length;
if (!cx->runtime->sourceHook(cx, this, &src, &length))
return false;
if (!src)
return true;
- ScriptSource *ss = ScriptSource::createFromSource(cx, src, length, false, NULL, true);
- if (!ss) {
- cx->free_(src);
- return false;
- }
- setScriptSource(cx, ss);
- ss->attachToRuntime(cx->runtime);
+ ScriptSource *ss = scriptSource();
+ JS_ALWAYS_TRUE(ss->setSource(cx, src, length, false, NULL, true));
*worked = true;
return true;
}
JSFixedString *
JSScript::sourceData(JSContext *cx)
{
- JS_ASSERT(scriptSource_);
+ JS_ASSERT(scriptSource_->hasSourceData());
return scriptSource_->substring(cx, sourceStart, sourceEnd);
}
JSFixedString *
SourceDataCache::lookup(ScriptSource *ss)
{
if (!map_)
return NULL;
@@ -1233,61 +1230,54 @@ ScriptSource::substring(JSContext *cx, u
chars = data.source;
}
#else
chars = data.source;
#endif
return js_NewStringCopyN(cx, chars + start, stop - start);
}
-ScriptSource *
-ScriptSource::createFromSource(JSContext *cx, const jschar *src, uint32_t length,
- bool argumentsNotIncluded, SourceCompressionToken *tok,
- bool ownSource)
+bool
+ScriptSource::setSource(JSContext *cx, const jschar *src, uint32_t length,
+ bool argumentsNotIncluded, SourceCompressionToken *tok,
+ bool ownSource)
{
- ScriptSource *ss = static_cast<ScriptSource *>(cx->runtime->malloc_(sizeof(*ss)));
- if (!ss)
- return NULL;
+ JS_ASSERT(!hasSourceData());
if (!ownSource) {
const size_t nbytes = length * sizeof(jschar);
- ss->data.compressed = static_cast<unsigned char *>(cx->runtime->malloc_(nbytes));
- if (!ss->data.compressed) {
- cx->free_(ss);
- return NULL;
- }
+ data.compressed = static_cast<unsigned char *>(cx->malloc_(nbytes));
+ if (!data.compressed)
+ return false;
}
- ss->next = NULL;
- ss->length_ = length;
- ss->compressedLength_ = 0;
- ss->marked = ss->onRuntime_ = false;
- ss->argumentsNotIncluded_ = argumentsNotIncluded;
-#ifdef DEBUG
- ss->ready_ = false;
-#endif
+ length_ = length;
+ argumentsNotIncluded_ = argumentsNotIncluded;
JS_ASSERT_IF(ownSource, !tok);
#ifdef JS_THREADSAFE
if (tok && !ownSource) {
- tok->ss = ss;
+#ifdef DEBUG
+ ready_ = false;
+#endif
+ tok->ss = this;
tok->chars = src;
cx->runtime->sourceCompressorThread.compress(tok);
} else
#endif
{
if (ownSource)
- ss->data.source = const_cast<jschar *>(src);
+ data.source = const_cast<jschar *>(src);
else
- PodCopy(ss->data.source, src, ss->length_);
+ PodCopy(data.source, src, length_);
#ifdef DEBUG
- ss->ready_ = true;
+ ready_ = true;
#endif
}
- return ss;
+ return true;
}
void
SourceCompressionToken::ensureReady()
{
#ifdef JS_THREADSAFE
cx->runtime->sourceCompressorThread.waitOnCompression(this);
#endif
@@ -1350,79 +1340,60 @@ ScriptSource::sweep(JSRuntime *rt)
*prev = next;
cur->destroy(rt);
}
}
}
template<XDRMode mode>
bool
-ScriptSource::performXDR(XDRState<mode> *xdr, ScriptSource **ssp)
+ScriptSource::performXDR(XDRState<mode> *xdr)
{
- class Cleanup {
- JSContext *cx;
- ScriptSource *ss;
- public:
- explicit Cleanup(JSContext *cx)
- : cx(cx), ss(NULL) {}
- ~Cleanup()
- {
- if (ss) {
- if (ss->data.compressed)
- cx->free_(ss->data.compressed);
- cx->free_(ss);
- }
- }
- void protect(ScriptSource *source) { ss = source; }
- void release() { ss = NULL; }
- } cleanup(xdr->cx());
- ScriptSource *ss = *ssp;
- if (mode == XDR_DECODE) {
- *ssp = static_cast<ScriptSource *>(xdr->cx()->malloc_(sizeof(ScriptSource)));
- ss = *ssp;
- if (!ss)
- return false;
- ss->marked = ss->onRuntime_ = ss->argumentsNotIncluded_ = false;
-#ifdef DEBUG
- ss->ready_ = false;
-#endif
- ss->data.compressed = NULL;
- cleanup.protect(ss);
-#ifdef JSGC_INCREMENTAL
- // See comment in ScriptSource::createFromSource.
- if (xdr->cx()->runtime->gcIncrementalState != NO_INCREMENTAL &&
- xdr->cx()->runtime->gcIsFull)
- ss->marked = true;
-#endif
- }
- if (!xdr->codeUint32(&ss->length_))
- return false;
- if (!xdr->codeUint32(&ss->compressedLength_))
+ uint8_t hasSource = hasSourceData();
+ if (!xdr->codeUint8(&hasSource))
return false;
- uint8_t argumentsNotIncluded = ss->argumentsNotIncluded_;
- if (!xdr->codeUint8(&argumentsNotIncluded))
- return false;
- ss->argumentsNotIncluded_ = argumentsNotIncluded;
+ if (hasSource) {
+ // Only set members when we know decoding cannot fail. This prevents the
+ // script source from being partially initialized.
+ uint32_t length = length_;
+ if (!xdr->codeUint32(&length))
+ return false;
- size_t byteLen = ss->compressed() ? ss->compressedLength_ : (ss->length_ * sizeof(jschar));
- if (mode == XDR_DECODE) {
- ss->data.compressed = static_cast<unsigned char *>(xdr->cx()->malloc_(byteLen));
- if (!ss->data.compressed)
+ uint32_t compressedLength = compressedLength_;
+ if (!xdr->codeUint32(&compressedLength))
+ return false;
+
+ uint8_t argumentsNotIncluded = argumentsNotIncluded_;
+ if (!xdr->codeUint8(&argumentsNotIncluded))
return false;
+
+ size_t byteLen = compressedLength ? compressedLength : (length * sizeof(jschar));
+ if (mode == XDR_DECODE) {
+ data.compressed = static_cast<unsigned char *>(xdr->cx()->malloc_(byteLen));
+ if (!data.compressed)
+ return false;
+ }
+ if (!xdr->codeBytes(data.compressed, byteLen)) {
+ if (mode == XDR_DECODE) {
+ xdr->cx()->free_(data.compressed);
+ data.compressed = NULL;
+ }
+ return false;
+ }
+ length_ = length;
+ compressedLength_ = compressedLength;
+ argumentsNotIncluded_ = argumentsNotIncluded;
}
- if (!xdr->codeBytes(ss->data.compressed, byteLen))
- return false;
- if (mode == XDR_DECODE) {
+
#ifdef DEBUG
- ss->ready_ = true;
+ if (mode == XDR_DECODE)
+ ready_ = true;
#endif
- ss->attachToRuntime(xdr->cx()->runtime);
- cleanup.release();
- }
+
return true;
}
/*
* Shared script filename management.
*/
const char *
@@ -2632,17 +2603,17 @@ JSScript::markChildren(JSTracer *trc)
if (enclosingScope_)
MarkObject(trc, &enclosingScope_, "enclosing");
if (IS_GC_MARKING_TRACER(trc)) {
if (filename)
MarkScriptFilename(trc->runtime, filename);
- if (trc->runtime->gcIsFull && scriptSource_)
+ if (trc->runtime->gcIsFull)
scriptSource_->mark();
}
bindings.trace(trc);
#ifdef JS_METHODJIT
for (int constructing = 0; constructing <= 1; constructing++) {
for (int barriers = 0; barriers <= 1; barriers++) {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -992,42 +992,62 @@ struct ScriptSource
bool marked:1;
bool onRuntime_:1;
bool argumentsNotIncluded_:1;
#ifdef DEBUG
bool ready_:1;
#endif
public:
- static ScriptSource *createFromSource(JSContext *cx,
- const jschar *src,
- uint32_t length,
- bool argumentsNotIncluded = false,
- SourceCompressionToken *tok = NULL,
- bool ownSource = false);
+ ScriptSource()
+ : next(NULL),
+ length_(0),
+ compressedLength_(0),
+ marked(false),
+ onRuntime_(false),
+ argumentsNotIncluded_(false)
+#ifdef DEBUG
+ ,ready_(true)
+#endif
+ {
+ data.source = NULL;
+ }
+ bool setSource(JSContext *cx,
+ const jschar *src,
+ uint32_t length,
+ bool argumentsNotIncluded = false,
+ SourceCompressionToken *tok = NULL,
+ bool ownSource = false);
void attachToRuntime(JSRuntime *rt);
void mark() { marked = true; }
- void destroy(JSRuntime *rt);
- uint32_t length() const { return length_; }
bool onRuntime() const { return onRuntime_; }
- bool argumentsNotIncluded() const { return argumentsNotIncluded_; }
#ifdef DEBUG
bool ready() const { return ready_; }
#endif
+ bool hasSourceData() const { return !!data.source; }
+ uint32_t length() const {
+ JS_ASSERT(hasSourceData());
+ return length_;
+ }
+ bool argumentsNotIncluded() const {
+ JS_ASSERT(hasSourceData());
+ return argumentsNotIncluded_;
+ }
JSFixedString *substring(JSContext *cx, uint32_t start, uint32_t stop);
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf);
// For the GC.
static void sweep(JSRuntime *rt);
// XDR handling
template <XDRMode mode>
- static bool performXDR(XDRState<mode> *xdr, ScriptSource **ss);
+ bool performXDR(XDRState<mode> *xdr);
private:
+ void destroy(JSRuntime *rt);
bool compressed() { return compressedLength_ != 0; }
};
#ifdef JS_THREADSAFE
/*
* Background thread to compress JS source code. This happens only while parsing
* and bytecode generation is happening in the main thread. If needed, the
* compiler waits for compression to complete before returning.
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -233,20 +233,22 @@ GlobalObject::initFunctionAndObjectClass
JS_ASSERT(proto == functionProto);
functionProto->flags |= JSFUN_PROTOTYPE;
const char *rawSource = "() {\n}";
size_t sourceLen = strlen(rawSource);
jschar *source = InflateString(cx, rawSource, &sourceLen);
if (!source)
return NULL;
- ScriptSource *ss = ScriptSource::createFromSource(cx, source, sourceLen);
- cx->free_(source);
- if (!ss)
+ ScriptSource *ss = cx->new_<ScriptSource>();
+ if (!ss) {
+ cx->free_(source);
return NULL;
+ }
+ JS_ALWAYS_TRUE(ss->setSource(cx, source, sourceLen, false, NULL, true));
CompileOptions options(cx);
options.setNoScriptRval(true)
.setVersion(JSVERSION_DEFAULT);
Rooted<JSScript*> script(cx, JSScript::Create(cx,
/* enclosingScope = */ NullPtr(),
/* savedCallerFun = */ false,
options,
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -20,17 +20,17 @@ namespace js {
* Bytecode version number. Increment the subtrahend whenever JS bytecode
* changes incompatibly.
*
* This version number is XDR'd near the front of xdr bytecode and
* aborts deserialization if there is a mismatch between the current
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 125);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 126);
class XDRBuffer {
public:
XDRBuffer(JSContext *cx)
: context(cx), base(NULL), cursor(NULL), limit(NULL) { }
JSContext *cx() const {
return context;