--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -489,17 +489,20 @@ 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
if (!stackSpace.init())
return false;
#ifdef JS_TRACER
- InitJIT(&traceMonitor);
+ if (!InitJIT(&traceMonitor)) {
+ finish();
+ return false;
+ }
#endif
dtoaState = js_NewDtoaState();
if (!dtoaState) {
finish();
return false;
}
nativeStackBase = GetNativeStackBase();
return true;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -893,16 +893,20 @@ typedef HashMap<jsbytecode*,
/* Holds the profile data for loops. */
typedef HashMap<jsbytecode*,
LoopProfile*,
DefaultHasher<jsbytecode*>,
SystemAllocPolicy> LoopProfileMap;
class Oracle;
+typedef HashSet<JSScript *,
+ DefaultHasher<JSScript *>,
+ SystemAllocPolicy> TracedScriptSet;
+
/*
* Trace monitor. Every JSThread (if JS_THREADSAFE) or JSRuntime (if not
* JS_THREADSAFE) has an associated trace monitor that keeps track of loop
* frequencies for all JavaScript code loaded into that runtime.
*/
struct TraceMonitor {
/*
* The context currently executing JIT-compiled code on this thread, or
@@ -993,16 +997,19 @@ struct TraceMonitor {
*/
REHashMap* reFragments;
// Cached temporary typemap to avoid realloc'ing every time we create one.
// This must be used in only one place at a given time. It must be cleared
// before use.
TypeMap* cachedTempTypeMap;
+ /* Scripts with recorded fragments. */
+ TracedScriptSet tracedScripts;
+
#ifdef DEBUG
/* Fields needed for fragment/guard profiling. */
nanojit::Seq<nanojit::Fragment*>* branches;
uint32 lastFragID;
/*
* profAlloc has a lifetime which spans exactly from js_InitJIT to
* js_FinishJIT.
*/
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2057,17 +2057,17 @@ fun_finalize(JSContext *cx, JSObject *ob
return;
}
/*
* Null-check of u.i.script is required since the parser sets interpreted
* very early.
*/
if (FUN_INTERPRETED(fun) && fun->u.i.script)
- js_DestroyScript(cx, fun->u.i.script);
+ js_DestroyScriptFromGC(cx, fun->u.i.script, NULL);
}
int
JSFunction::sharpSlotBase(JSContext *cx)
{
#if JS_HAS_SHARP_VARS
JSAtom *name = js_Atomize(cx, "#array", 6, 0);
if (name) {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1746,17 +1746,17 @@ js_DestroyScriptsToGC(JSContext *cx, JST
{
JSScript **listp, *script;
for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i) {
listp = &data->scriptsToGC[i];
while ((script = *listp) != NULL) {
*listp = script->u.nextToGC;
script->u.nextToGC = NULL;
- js_DestroyScript(cx, script);
+ js_DestroyScriptFromGC(cx, script, data);
}
}
}
/*
* This function is called from js_FinishAtomState to force the finalization
* of the permanently interned strings when cx is not available.
*/
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -81,17 +81,17 @@ static const jsbytecode emptyScriptCode[
1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, 0, 0, true, false, false, false, false,
false, /* usesEval */
false, /* usesArguments */
true, /* warnedAboutTwoArgumentEval */
#ifdef JS_METHODJIT
false, /* debugMode */
#endif
const_cast<jsbytecode*>(emptyScriptCode),
- {0, NULL}, NULL, NULL, 0, 0, 0,
+ {0, jsatomid(0)}, NULL, NULL, 0, 0, 0,
0, /* nClosedArgs */
0, /* nClosedVars */
NULL, {NULL},
#ifdef CHECK_SCRIPT_OWNER
reinterpret_cast<JSThread*>(1)
#endif
};
@@ -475,17 +475,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
#endif /* JS_HAS_XDR */
static void
script_finalize(JSContext *cx, JSObject *obj)
{
JSScript *script = (JSScript *) obj->getPrivate();
if (script)
- js_DestroyScript(cx, script);
+ js_DestroyScriptFromGC(cx, script, NULL);
}
static void
script_trace(JSTracer *trc, JSObject *obj)
{
JSScript *script = (JSScript *) obj->getPrivate();
if (script)
js_TraceScript(trc, script);
@@ -1296,18 +1296,18 @@ js_CallDestroyScriptHook(JSContext *cx,
JSDestroyScriptHook hook;
hook = cx->debugHooks->destroyScriptHook;
if (hook)
hook(cx, script, cx->debugHooks->destroyScriptHookData);
}
-void
-js_DestroyScript(JSContext *cx, JSScript *script)
+static void
+DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data)
{
if (script == JSScript::emptyScript()) {
JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts);
return;
}
js_CallDestroyScriptHook(cx, script);
JS_ClearScriptTraps(cx, script);
@@ -1354,30 +1354,53 @@ js_DestroyScript(JSContext *cx, JSScript
#ifdef CHECK_SCRIPT_OWNER
JS_ASSERT(script->owner == cx->thread);
#endif
}
}
#ifdef JS_TRACER
- PurgeScriptFragments(cx, script);
+# ifdef JS_THREADSAFE
+ if (data) {
+ PurgeScriptFragments(&data->traceMonitor, script);
+ } else {
+ for (ThreadDataIter i(cx->runtime); !i.empty(); i.popFront())
+ PurgeScriptFragments(&i.threadData()->traceMonitor, script);
+ }
+# else
+ PurgeScriptFragments(JS_TRACE_MONITOR(cx), script);
+# endif
#endif
#if defined(JS_METHODJIT)
mjit::ReleaseScriptCode(cx, script);
#endif
JS_REMOVE_LINK(&script->links);
cx->free(script);
JS_RUNTIME_UNMETER(cx->runtime, liveScripts);
}
void
+js_DestroyScript(JSContext *cx, JSScript *script)
+{
+ JS_ASSERT(!cx->runtime->gcRunning);
+ DestroyScript(cx, script, JS_THREAD_DATA(cx));
+}
+
+void
+js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSThreadData *data)
+{
+ JS_ASSERT(cx->runtime->gcRunning);
+ DestroyScript(cx, script, data);
+}
+
+void
js_TraceScript(JSTracer *trc, JSScript *script)
{
JSAtomMap *map = &script->atomMap;
MarkAtomRange(trc, map->length, map->vector, "atomMap");
if (script->objectsOffset != 0) {
JSObjectArray *objarray = script->objects();
uintN i = objarray->length;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -505,19 +505,30 @@ js_SweepScriptFilenames(JSRuntime *rt);
* of any owning function (the fun parameter) or script object (null fun).
*/
extern JS_FRIEND_API(void)
js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun);
extern JS_FRIEND_API(void)
js_CallDestroyScriptHook(JSContext *cx, JSScript *script);
+/*
+ * The function must be used only outside the GC for a script that was run
+ * only on the current thread.
+ */
extern void
js_DestroyScript(JSContext *cx, JSScript *script);
+/*
+ * If data is not null, it indicates that the script could been accessed only
+ * from that thread.
+ */
+extern void
+js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSThreadData *data);
+
extern void
js_TraceScript(JSTracer *trc, JSScript *script);
extern JSBool
js_NewScriptObject(JSContext *cx, JSScript *script);
/*
* To perturb as little code as possible, we introduce a js_GetSrcNote lookup
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2746,16 +2746,17 @@ TraceMonitor::flush()
globalStates[i].globalSlots = new (alloc) SlotList(&alloc);
}
assembler = new (alloc) Assembler(*codeAlloc, alloc, alloc, core, &LogController, avmplus::AvmCore::config);
verbose_only( branches = NULL; )
PodArrayZero(vmfragments);
reFragments = new (alloc) REHashMap(alloc);
+ tracedScripts.clear();
needFlush = JS_FALSE;
}
inline bool
HasUnreachableGCThings(TreeFragment *f)
{
/*
@@ -5632,17 +5633,21 @@ RecordTree(JSContext* cx, TreeFragment*
/* Make sure the global type map didn't change on us. */
if (!CheckGlobalObjectShape(cx, tm, f->globalObj)) {
Backoff(cx, (jsbytecode*) localRootIP);
return false;
}
AUDIT(recorderStarted);
- if (tm->outOfMemory() || OverfullJITCache(tm)) {
+ if (tm->outOfMemory() ||
+ OverfullJITCache(tm) ||
+ !tm->tracedScripts.put(cx->fp()->script())) {
+ if (!OverfullJITCache(tm))
+ js_ReportOutOfMemory(cx);
Backoff(cx, (jsbytecode*) f->root->ip);
ResetJIT(cx, FR_OOM);
debug_only_print0(LC_TMTracer,
"Out of memory recording new tree, flushing cache.\n");
return false;
}
JS_ASSERT(!f->code());
@@ -7527,17 +7532,17 @@ SetMaxCodeCacheBytes(JSContext* cx, uint
JS_ASSERT(tm->codeAlloc && tm->dataAlloc && tm->traceAlloc);
if (bytes > 1 G)
bytes = 1 G;
if (bytes < 128 K)
bytes = 128 K;
tm->maxCodeCacheBytes = bytes;
}
-void
+bool
InitJIT(TraceMonitor *tm)
{
#if defined JS_JIT_SPEW
tm->profAlloc = NULL;
/* Set up debug logging. */
if (!did_we_set_up_debug_logging) {
InitJITLogController();
did_we_set_up_debug_logging = true;
@@ -7632,16 +7637,20 @@ InitJIT(TraceMonitor *tm)
#endif
#if defined NANOJIT_PPC
jitstats.archIsPPC = 1;
#endif
#if defined NANOJIT_X64
jitstats.archIsAMD64 = 1;
#endif
#endif
+
+ if (!tm->tracedScripts.init())
+ return false;
+ return true;
}
void
FinishJIT(TraceMonitor *tm)
{
JS_ASSERT(!tm->recorder);
JS_ASSERT(!tm->profile);
@@ -7743,27 +7752,30 @@ FinishJIT(TraceMonitor *tm)
tm->storage = NULL;
}
delete tm->cachedTempTypeMap;
tm->cachedTempTypeMap = NULL;
}
JS_REQUIRES_STACK void
-PurgeScriptFragments(JSContext* cx, JSScript* script)
+PurgeScriptFragments(TraceMonitor* tm, JSScript* script)
{
debug_only_printf(LC_TMTracer,
"Purging fragments for JSScript %p.\n", (void*)script);
- TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
-
/* A recorder script is being evaluated and can not be destroyed or GC-ed. */
JS_ASSERT_IF(tm->recorder,
JS_UPTRDIFF(tm->recorder->getTree()->ip, script->code) >= script->length);
+ TracedScriptSet::Ptr found = tm->tracedScripts.lookup(script);
+ if (!found)
+ return;
+ tm->tracedScripts.remove(found);
+
for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
TreeFragment** fragp = &tm->vmfragments[i];
while (TreeFragment* frag = *fragp) {
if (JS_UPTRDIFF(frag->ip, script->code) < script->length) {
/* This fragment is associated with the script. */
debug_only_printf(LC_TMTracer,
"Disconnecting TreeFragment %p "
"with ip %p, in range [%p,%p).\n",
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1671,24 +1671,24 @@ RecordTracePoint(JSContext*, uintN& inli
extern JS_REQUIRES_STACK TracePointAction
MonitorTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist,
void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits);
extern JS_REQUIRES_STACK TraceRecorder::AbortResult
AbortRecording(JSContext* cx, const char* reason);
-extern void
+extern bool
InitJIT(TraceMonitor *tm);
extern void
FinishJIT(TraceMonitor *tm);
extern void
-PurgeScriptFragments(JSContext* cx, JSScript* script);
+PurgeScriptFragments(TraceMonitor* tm, JSScript* script);
extern bool
OverfullJITCache(TraceMonitor* tm);
extern void
FlushJITCache(JSContext* cx);
extern JSObject *
deleted file mode 100644
--- a/uriloader/exthandler/tests/unit_ipc/test_encoding.js
+++ /dev/null
@@ -1,281 +0,0 @@
-
-do_get_profile();
-do_load_httpd_js();
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-// Dynamically generates a classID for our component, registers it to mask
-// the existing component, and stored the masked components classID to be
-// restored later, when we unregister.
-function registerTemporaryComponent(comp)
-{
- let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
- if (!comp.prototype.classID) {
- let uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
- comp.prototype.classID = uuidgen.generateUUID();
- }
- comp.prototype.maskedClassID = Components.ID(Cc[comp.prototype.contractID].number);
- if (!comp.prototype.factory)
- comp.prototype.factory = getFactory(comp);
- registrar.registerFactory(comp.prototype.classID, "", comp.prototype.contractID, comp.prototype.factory);
-}
-
-function unregisterTemporaryComponent(comp)
-{
- let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
- registrar.unregisterFactory(comp.prototype.classID, comp.prototype.factory);
- registrar.registerFactory(comp.prototype.maskedClassID, "", comp.prototype.contractID, null);
-}
-
-let DownloadListener = {
- init: function () {
- let obs = Services.obs;
- obs.addObserver(this, "dl-done", true);
- },
-
- observe: function (subject, topic, data) {
- this.onFinished(subject, topic, data);
- },
-
- QueryInterface: function (iid) {
- if (iid.equals(Ci.nsIObserver) ||
- iid.equals(Ci.nsISupportsWeakReference) ||
- iid.equals(Ci.nsISupports))
- return this;
-
- throw Cr.NS_ERROR_NO_INTERFACE;
- }
-}
-DownloadListener.init();
-
-function HelperAppDlg() { }
-HelperAppDlg.prototype = {
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]),
- contractID: "@mozilla.org/helperapplauncherdialog;1",
- show: function (launcher, ctx, reason) {
- launcher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToFile;
- launcher.launchWithApplication(null, false);
- },
-
- promptForSaveToFile: function (launcher, ctx, defaultFile, suggestedExtension, forcePrompt) { }
-}
-
-// Stolen from XPCOMUtils, since this handy function is not public there
-function getFactory(comp)
-{
- return {
- createInstance: function (outer, iid) {
- if (outer)
- throw Cr.NS_ERROR_NO_AGGREGATION;
- return (new comp()).QueryInterface(iid);
- }
- }
-}
-
-// Override the download-manager-ui to prevent anyone from trying to open
-// a window.
-function DownloadMgrUI() { }
-DownloadMgrUI.prototype = {
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadManagerUI]),
- contractID: "@mozilla.org/download-manager-ui;1",
- show: function (ir, aID, reason) { },
-
- visible: false,
-
- getAttention: function () { }
-}
-
-function AlertsSVC() { }
-AlertsSVC.prototype = {
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService]),
- contractID: "@mozilla.org/alerts-service;1",
- showAlertNotification: function (url, title, text, clickable, cookie, listener, name) { },
-}
-
-registerTemporaryComponent(HelperAppDlg);
-registerTemporaryComponent(DownloadMgrUI);
-registerTemporaryComponent(AlertsSVC);
-
-function initChildTestEnv()
-{
- sendCommand(' \
- const Cc = Components.classes; \
- const Ci = Components.interfaces; \
- const Cr = Components.results; \
- function WindowContext() { } \
- \
- WindowContext.prototype = { \
- getInterface: function (iid) { \
- if (iid.equals(Ci.nsIInterfaceRequestor) || \
- iid.equals(Ci.nsIURIContentListener) || \
- iid.equals(Ci.nsILoadGroup) || \
- iid.equals(Ci.nsIDocumentLoader) || \
- iid.equals(Ci.nsIDOMWindow)) \
- return this; \
- \
- throw Cr.NS_ERROR_NO_INTERFACE; \
- }, \
- \
- /* nsIURIContentListener */ \
- onStartURIOpen: function (uri) { }, \
- isPreferred: function (type, desiredtype) { return false; }, \
- \
- /* nsILoadGroup */ \
- addRequest: function (request, context) { }, \
- removeRequest: function (request, context, status) { } \
- }; \
- \
- var ioservice = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);\
- var uriloader = Cc["@mozilla.org/uriloader;1"].getService(Ci.nsIURILoader);\
- ');
-}
-
-function testFinisher(endFunc) {
- let ef = endFunc;
- return function (file) {
- ef(file);
- runNextTest();
- }
-}
-
-function runChildTestSet(set)
-{
- DownloadListener.onFinished = testFinisher(set[2]);
- sendCommand('\
- let uri = ioservice.newURI("http://localhost:4444' + set[0] + '", null, null);\
- let channel = ioservice.newChannelFromURI(uri); \
- uriloader.openURI(channel, true, new WindowContext()); \
- ');
-}
-
-var httpserver = null;
-let currentTest = 0;
-function runNextTest()
-{
- if (currentTest == tests.length) {
- httpserver.stop(do_test_finished);
- return;
- }
-
- let set = tests[currentTest++];
- runChildTestSet(set);
-}
-
-const responseBody = [0x1f, 0x8b, 0x08, 0x00, 0x16, 0x5a, 0x8a, 0x48, 0x02,
- 0x03, 0x2b, 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00, 0xc6,
- 0x35, 0xb9, 0x3b, 0x05, 0x00, 0x00, 0x00];
-
-/*
- * First test: a file with Content-Type application/x-gzip and Content-Encoding gzip
- * should not be decoded in a round-trip
- */
-function testResponse1(metadata, response) {
- response.setHeader("Content-Type", "application/x-gzip", false);
- response.setHeader("Content-Encoding", "gzip", false);
- response.setHeader("Content-Disposition", "attachment", false);
-
- var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
- bos.setOutputStream(response.bodyOutputStream);
- bos.writeByteArray(responseBody, responseBody.length);
-}
-
-function finishTest1(subject, topic, data) {
- let file = subject.QueryInterface(Ci.nsIDownload).targetFile;
- do_check_true(file.path.search("test1.gz") != 0);
- let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
- fis.init(file, -1, -1, 0);
- let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
- bis.setInputStream(fis);
- let str = bis.readByteArray(bis.available());
- do_check_true(str.length == responseBody.length);
-
- let cmp = 0;
- for (i = 0; i < str.length; i++) {
- cmp += str[i] - responseBody[i];
- if (cmp != 0) break;
- }
- do_check_true(cmp == 0);
-}
-
-/*
- * Second test: a file with Content-Type text/html and Content-Encoding gzip
- * should not be decoded in a round-trip, if its filename ends in ".gz".
- * We specify a Content-disposition header to force it to be saved as a file.
- */
-function testResponse2(metadata, response) {
- response.setHeader("Content-Type", "text/html", false);
- response.setHeader("Content-Encoding", "gzip", false);
- response.setHeader("Content-Disposition", "attachment", false);
-
- var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
- bos.setOutputStream(response.bodyOutputStream);
- bos.writeByteArray(responseBody, responseBody.length);
-}
-
-function finishTest2(subject, topic, data) {
- let file = subject.QueryInterface(Ci.nsIDownload).targetFile;
- do_check_true(file.path.search("test2.gz") != 0);
- let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
- fis.init(file, -1, -1, 0);
- let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
- bis.setInputStream(fis);
- let str = bis.readByteArray(bis.available());
- do_check_true(str.length == responseBody.length);
-
- let cmp = 0;
- for (i = 0; i < str.length; i++) {
- cmp += str[i] - responseBody[i];
- if (cmp != 0) break;
- }
- do_check_true(cmp == 0);
-}
-
-function testResponse3(metadata, response) {
- response.setHeader("Content-Type", "text/html", false);
- response.setHeader("Content-Encoding", "gzip", false);
- response.setHeader("Content-Disposition", "attachment", false);
-
- var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
- bos.setOutputStream(response.bodyOutputStream);
- bos.writeByteArray(responseBody, responseBody.length);
-}
-
-function finishTest3(subject, topic, data) {
- let file = subject.QueryInterface(Ci.nsIDownload).targetFile;
- do_check_true(file.path.search("test3.txt") != 0);
- let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
- fis.init(file, -1, -1, 0);
- let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
- bis.setInputStream(fis);
- let str = bis.readByteArray(bis.available());
- let decodedBody = [ 116, 101, 115, 116, 10 ]; // 't','e','s','t','\n'
- do_check_true(str.length == decodedBody.length);
-
- let cmp = 0;
- for (i = 0; i < str.length; i++) {
- cmp += str[i] - decodedBody[i];
- if (cmp != 0) break;
- }
- do_check_true(cmp == 0);
-}
-
-let tests = [
- [ "/test1.gz", testResponse1, finishTest1 ],
- [ "/test2.gz", testResponse2, finishTest2 ],
- [ "/test3.txt", testResponse3, finishTest3 ],
-];
-
-function run_test() {
-// do_load_child_test_harness();
- httpserver = new nsHttpServer();
- httpserver.start(4444);
- do_test_pending();
-
- initChildTestEnv();
-
- for each (set in tests)
- httpserver.registerPathHandler(set[0], set[1]);
-
- runNextTest();
-}