Bug 774471: Store source map URLs in the ScriptSource, not on each JSScript. r=jimb

This commit is contained in:
Nick Fitzgerald 2012-08-13 12:39:57 -07:00
parent 28849e0b13
commit 38ba7ed72f
10 changed files with 107 additions and 89 deletions

View File

@ -190,6 +190,9 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
parser.freeTree(pn);
}
if (tokenStream.hasSourceMap())
ss->setSourceMap(tokenStream.releaseSourceMap());
#if JS_HAS_XML_SUPPORT
/*
* Prevent XML data theft via <script src="http://victim.com/foo.xml">.
@ -338,6 +341,9 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions
pn = fn->pn_body;
}
if (parser.tokenStream.hasSourceMap())
ss->setSourceMap(parser.tokenStream.releaseSourceMap());
if (!EmitFunctionScript(cx, &funbce, pn))
return false;

View File

@ -1246,17 +1246,16 @@ TokenStream::getAtSourceMappingURL()
* we should stop and drop everything for, though. */
return true;
int len = tokenbuf.length();
size_t sourceMapLength = tokenbuf.length();
if (sourceMap)
cx->free_(sourceMap);
sourceMap = (jschar *) cx->malloc_(sizeof(jschar) * (len + 1));
sourceMap = static_cast<jschar *>(cx->malloc_(sizeof(jschar) * (sourceMapLength + 1)));
if (!sourceMap)
return false;
for (int i = 0; i < len; i++)
sourceMap[i] = tokenbuf[i];
sourceMap[len] = '\0';
PodCopy(sourceMap, tokenbuf.begin(), sourceMapLength);
sourceMap[sourceMapLength] = '\0';
}
return true;
}

View File

@ -679,11 +679,16 @@ class TokenStream
*/
size_t endOffset(const Token &tok);
bool hasSourceMap() const {
return sourceMap != NULL;
}
/*
* Give up responsibility for managing the sourceMap filename's memory.
*/
const jschar *releaseSourceMap() {
const jschar* sm = sourceMap;
jschar *releaseSourceMap() {
JS_ASSERT(hasSourceMap());
jschar *sm = sourceMap;
sourceMap = NULL;
return sm;
}

View File

@ -8,6 +8,7 @@
#include "tests.h"
#include "jsscript.h"
#include "jsstr.h"
static JSScript *
CompileScriptForPrincipalsVersionOrigin(JSContext *cx, JS::HandleObject obj,
@ -254,3 +255,40 @@ BEGIN_TEST(testXDR_source)
return true;
}
END_TEST(testXDR_source)
BEGIN_TEST(testXDR_sourceMap)
{
const char *sourceMaps[] = {
"http://example.com/source-map.json",
"file:///var/source-map.json",
NULL
};
for (const char **sm = sourceMaps; *sm; sm++) {
JSScript *script = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__);
CHECK(script);
size_t len = strlen(*sm);
jschar *expected = js::InflateString(cx, *sm, &len);
CHECK(expected);
// The script source takes responsibility of free'ing |expected|.
script->scriptSource()->setSourceMap(expected);
script = FreezeThaw(cx, script);
CHECK(script);
CHECK(script->scriptSource());
CHECK(script->scriptSource()->hasSourceMap());
const jschar *actual = script->scriptSource()->sourceMap();
CHECK(actual);
while (*expected) {
CHECK(*actual);
CHECK(*expected == *actual);
expected++;
actual++;
}
CHECK(!*actual);
}
return true;
}
END_TEST(testXDR_sourceMap)

View File

@ -67,7 +67,6 @@ JSCompartment::JSCompartment(JSRuntime *rt)
debugModeBits(rt->debugMode ? DebugFromC : 0),
watchpointMap(NULL),
scriptCountsMap(NULL),
sourceMapMap(NULL),
debugScriptMap(NULL)
{
setGCMaxMallocBytes(rt->gcMaxMallocBytes);
@ -77,7 +76,6 @@ JSCompartment::~JSCompartment()
{
Foreground::delete_(watchpointMap);
Foreground::delete_(scriptCountsMap);
Foreground::delete_(sourceMapMap);
Foreground::delete_(debugScriptMap);
}

View File

@ -418,8 +418,6 @@ struct JSCompartment
js::ScriptCountsMap *scriptCountsMap;
js::SourceMapMap *sourceMapMap;
js::DebugScriptMap *debugScriptMap;
};

View File

@ -695,7 +695,9 @@ JS_GetScriptFilename(JSContext *cx, JSScript *script)
JS_PUBLIC_API(const jschar *)
JS_GetScriptSourceMap(JSContext *cx, JSScript *script)
{
return script->hasSourceMap ? script->getSourceMap() : NULL;
ScriptSource *source = script->scriptSource();
JS_ASSERT(source);
return source->hasSourceMap() ? source->sourceMap() : NULL;
}
JS_PUBLIC_API(unsigned)

View File

@ -908,61 +908,6 @@ JSScript::destroyScriptCounts(FreeOp *fop)
}
}
bool
JSScript::setSourceMap(JSContext *cx, jschar *sourceMap)
{
JS_ASSERT(!hasSourceMap);
/* Create compartment's sourceMapMap if necessary. */
SourceMapMap *map = compartment()->sourceMapMap;
if (!map) {
map = cx->new_<SourceMapMap>();
if (!map || !map->init()) {
cx->delete_(map);
return false;
}
compartment()->sourceMapMap = map;
}
if (!map->putNew(this, sourceMap))
return false;
hasSourceMap = true; // safe to set this; we can't fail after this point
return true;
}
jschar *
JSScript::getSourceMap() {
JS_ASSERT(hasSourceMap);
SourceMapMap *map = compartment()->sourceMapMap;
JS_ASSERT(map);
SourceMapMap::Ptr p = map->lookup(this);
JS_ASSERT(p);
return p->value;
}
jschar *
JSScript::releaseSourceMap()
{
JS_ASSERT(hasSourceMap);
SourceMapMap *map = compartment()->sourceMapMap;
JS_ASSERT(map);
SourceMapMap::Ptr p = map->lookup(this);
JS_ASSERT(p);
jschar *sourceMap = p->value;
map->remove(p);
hasSourceMap = false;
return sourceMap;
}
void
JSScript::destroySourceMap(FreeOp *fop)
{
if (hasSourceMap)
fop->free_(releaseSourceMap());
}
#ifdef JS_THREADSAFE
void
SourceCompressorThread::compressorThread(void *arg)
@ -1285,6 +1230,7 @@ ScriptSource::destroy(JSRuntime *rt)
{
JS_ASSERT(ready());
rt->free_(data.compressed);
rt->free_(sourceMap_);
#ifdef DEBUG
ready_ = false;
#endif
@ -1347,6 +1293,31 @@ ScriptSource::performXDR(XDRState<mode> *xdr)
argumentsNotIncluded_ = argumentsNotIncluded;
}
uint8_t haveSourceMap = hasSourceMap();
if (!xdr->codeUint8(&haveSourceMap))
return false;
if (haveSourceMap) {
uint32_t sourceMapLen = (mode == XDR_DECODE) ? 0 : js_strlen(sourceMap_);
if (!xdr->codeUint32(&sourceMapLen))
return false;
if (mode == XDR_DECODE) {
size_t byteLen = (sourceMapLen + 1) * sizeof(jschar);
sourceMap_ = static_cast<jschar *>(xdr->cx()->malloc_(byteLen));
if (!sourceMap_)
return false;
}
if (!xdr->codeChars(sourceMap_, sourceMapLen)) {
if (mode == XDR_DECODE) {
xdr->cx()->free_(sourceMap_);
sourceMap_ = NULL;
}
return false;
}
sourceMap_[sourceMapLen] = '\0';
}
#ifdef DEBUG
if (mode == XDR_DECODE)
ready_ = true;
@ -1355,6 +1326,21 @@ ScriptSource::performXDR(XDRState<mode> *xdr)
return true;
}
void
ScriptSource::setSourceMap(jschar *sm)
{
JS_ASSERT(!hasSourceMap());
JS_ASSERT(sm);
sourceMap_ = sm;
}
const jschar *
ScriptSource::sourceMap()
{
JS_ASSERT(hasSourceMap());
return sourceMap_;
}
/*
* Shared script filename management.
*/
@ -1749,14 +1735,6 @@ JSScript::fullyInitFromEmitter(JSContext *cx, Handle<JSScript*> script, Bytecode
}
script->nslots = script->nfixed + bce->maxStackDepth;
jschar *sourceMap = (jschar *) bce->parser->tokenStream.releaseSourceMap();
if (sourceMap) {
if (!script->setSourceMap(cx, sourceMap)) {
cx->free_(sourceMap);
return false;
}
}
if (!FinishTakingSrcNotes(cx, bce, script->notes()))
return false;
if (bce->ntrynotes != 0)
@ -1915,7 +1893,6 @@ JSScript::finalize(FreeOp *fop)
#endif
destroyScriptCounts(fop);
destroySourceMap(fop);
destroyDebugScript(fop);
scriptSource_->decref(fop->runtime());

View File

@ -302,11 +302,6 @@ typedef HashMap<JSScript *,
DefaultHasher<JSScript *>,
SystemAllocPolicy> ScriptCountsMap;
typedef HashMap<JSScript *,
jschar *,
DefaultHasher<JSScript *>,
SystemAllocPolicy> SourceMapMap;
class DebugScript
{
friend struct ::JSScript;
@ -542,8 +537,6 @@ struct JSScript : public js::gc::Cell
bool isGeneratorExp:1; /* is a generator expression */
bool hasScriptCounts:1;/* script has an entry in
JSCompartment::scriptCountsMap */
bool hasSourceMap:1; /* script has an entry in
JSCompartment::sourceMapMap */
bool hasDebugScript:1; /* script has an entry in
JSCompartment::debugScriptMap */
@ -735,11 +728,6 @@ struct JSScript : public js::gc::Cell
js::ScriptCounts releaseScriptCounts();
void destroyScriptCounts(js::FreeOp *fop);
bool setSourceMap(JSContext *cx, jschar *sourceMap);
jschar *getSourceMap();
jschar *releaseSourceMap();
void destroySourceMap(js::FreeOp *fop);
jsbytecode *main() {
return code + mainOffset;
}
@ -989,6 +977,7 @@ struct ScriptSource
uint32_t refs;
uint32_t length_;
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
@ -1004,6 +993,7 @@ struct ScriptSource
: refs(0),
length_(0),
compressedLength_(0),
sourceMap_(NULL),
sourceRetrievable_(false),
argumentsNotIncluded_(false)
#ifdef DEBUG
@ -1045,6 +1035,11 @@ struct ScriptSource
template <XDRMode mode>
bool performXDR(XDRState<mode> *xdr);
// Source maps
void setSourceMap(jschar *sm);
const jschar *sourceMap();
bool hasSourceMap() const { return sourceMap_ != NULL; }
private:
void destroy(JSRuntime *rt);
bool compressed() { return compressedLength_ != 0; }

View File

@ -25,7 +25,7 @@ namespace js {
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 128);
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 129);
class XDRBuffer {
public: