Bug 908699 - Allow generating parse errors and warnings when off the main thread, r=billm.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 29 Aug 2013 09:56:05 -0600
changeset 144949 f454691138040dc79d3375b4a1241494d3406843
parent 144948 630b25ba7f69c8c83a1ee4b9dffa9115601dff4b
child 144950 b1001a1e906a3742df8994042c7cb1e233bf1181
push id33109
push userbhackett@mozilla.com
push dateThu, 29 Aug 2013 15:56:15 +0000
treeherdermozilla-inbound@f45469113804 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs908699
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 908699 - Allow generating parse errors and warnings when off the main thread, r=billm.
content/xul/content/src/nsXULElement.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/Parser-inl.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/jit/AsmJS.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsexn.cpp
js/src/jsexn.h
js/src/jspubtd.h
js/src/jsworkers.cpp
js/src/jsworkers.h
js/src/shell/js.cpp
js/src/vm/StringBuffer.cpp
js/src/vm/StringBuffer.h
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2552,57 +2552,55 @@ nsXULPrototypeScript::DeserializeOutOfLi
         }
     }
     return rv;
 }
 
 class NotifyOffThreadScriptCompletedRunnable : public nsRunnable
 {
     nsRefPtr<nsIOffThreadScriptReceiver> mReceiver;
-
-    // Note: there is no need to root the script, it is protected against GC
-    // until FinishOffThreadScript is called on it.
-    JSScript *mScript;
+    void *mToken;
 
 public:
     NotifyOffThreadScriptCompletedRunnable(already_AddRefed<nsIOffThreadScriptReceiver> aReceiver,
-                                           JSScript *aScript)
-      : mReceiver(aReceiver), mScript(aScript)
+                                           void *aToken)
+      : mReceiver(aReceiver), mToken(aToken)
     {}
 
     NS_DECL_NSIRUNNABLE
 };
 
 NS_IMETHODIMP
 NotifyOffThreadScriptCompletedRunnable::Run()
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     // Note: this unroots mScript so that it is available to be collected by the
     // JS GC. The receiver needs to root the script before performing a call that
     // could GC.
     nsCOMPtr<nsIJSRuntimeService> svc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
     NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
+
     JSRuntime *rt;
     svc->GetRuntime(&rt);
     NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
-    JS::FinishOffThreadScript(rt, mScript);
+    JSScript *script = JS::FinishOffThreadScript(NULL, rt, mToken);
 
-    return mReceiver->OnScriptCompileComplete(mScript, mScript ? NS_OK : NS_ERROR_FAILURE);
+    return mReceiver->OnScriptCompileComplete(script, script ? NS_OK : NS_ERROR_FAILURE);
 }
 
 static void
-OffThreadScriptReceiverCallback(JSScript *script, void *ptr)
+OffThreadScriptReceiverCallback(void *aToken, void *aCallbackData)
 {
     // Be careful not to adjust the refcount on the receiver, as this callback
     // may be invoked off the main thread.
-    nsIOffThreadScriptReceiver* aReceiver = static_cast<nsIOffThreadScriptReceiver*>(ptr);
+    nsIOffThreadScriptReceiver* aReceiver = static_cast<nsIOffThreadScriptReceiver*>(aCallbackData);
     nsRefPtr<NotifyOffThreadScriptCompletedRunnable> notify =
         new NotifyOffThreadScriptCompletedRunnable(
-            already_AddRefed<nsIOffThreadScriptReceiver>(aReceiver), script);
+            already_AddRefed<nsIOffThreadScriptReceiver>(aReceiver), aToken);
     NS_DispatchToMainThread(notify);
 }
 
 nsresult
 nsXULPrototypeScript::Compile(const PRUnichar* aText,
                               int32_t aTextLength,
                               nsIURI* aURI,
                               uint32_t aLineNo,
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -253,17 +253,17 @@ frontend::CompileScript(ExclusiveContext
 
     // Syntax parsing may cause us to restart processing of top level
     // statements in the script. Use Maybe<> so that the parse context can be
     // reset when this occurs.
     Maybe<ParseContext<FullParseHandler> > pc;
 
     pc.construct(&parser, (GenericParseContext *) NULL, (ParseNode *) NULL, &globalsc,
                  (Directives *) NULL, staticLevel, /* bodyid = */ 0);
-    if (!pc.ref().init())
+    if (!pc.ref().init(parser.tokenStream))
         return NULL;
 
     /* If this is a direct call to eval, inherit the caller's strictness.  */
     if (evalCaller && evalCaller->strict)
         globalsc.strict = true;
 
     if (options.compileAndGo) {
         if (source) {
@@ -320,17 +320,17 @@ frontend::CompileScript(ExclusiveContext
                 // Destroying the parse context will destroy its free
                 // variables, so check if any deoptimization is needed.
                 if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref()))
                     return NULL;
 
                 pc.destroy();
                 pc.construct(&parser, (GenericParseContext *) NULL, (ParseNode *) NULL,
                              &globalsc, (Directives *) NULL, staticLevel, /* bodyid = */ 0);
-                if (!pc.ref().init())
+                if (!pc.ref().init(parser.tokenStream))
                     return NULL;
                 JS_ASSERT(parser.pc == pc.addr());
                 pn = parser.statement();
             }
             if (!pn) {
                 JS_ASSERT(!parser.hadAbortedSyntaxParse());
                 return NULL;
             }
--- a/js/src/frontend/Parser-inl.h
+++ b/js/src/frontend/Parser-inl.h
@@ -11,19 +11,19 @@
 
 #include "frontend/ParseMaps-inl.h"
 
 namespace js {
 namespace frontend {
 
 template <typename ParseHandler>
 bool
-ParseContext<ParseHandler>::init()
+ParseContext<ParseHandler>::init(TokenStream &ts)
 {
-    if (!frontend::GenerateBlockId(this, this->bodyid))
+    if (!frontend::GenerateBlockId(ts, this, this->bodyid))
         return false;
 
     return decls_.init() && lexdeps.ensureMap(sc->context);
 }
 
 template <typename ParseHandler>
 ParseContext<ParseHandler>::~ParseContext()
 {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -65,35 +65,32 @@ typedef Handle<StaticBlockObject*> Handl
         if (tokenStream.getToken() != tt) {                                                 \
             report(ParseError, false, null(), errno);                                       \
             return null();                                                                  \
         }                                                                                   \
     JS_END_MACRO
 
 template <typename ParseHandler>
 bool
-GenerateBlockId(ParseContext<ParseHandler> *pc, uint32_t &blockid)
+GenerateBlockId(TokenStream &ts, ParseContext<ParseHandler> *pc, uint32_t &blockid)
 {
     if (pc->blockidGen == JS_BIT(20)) {
-        if (!pc->sc->context->isJSContext())
-            return false;
-        JS_ReportErrorNumber(pc->sc->context->asJSContext(),
-                             js_GetErrorMessage, NULL, JSMSG_NEED_DIET, "program");
+        ts.reportError(JSMSG_NEED_DIET, "program");
         return false;
     }
     JS_ASSERT(pc->blockidGen < JS_BIT(20));
     blockid = pc->blockidGen++;
     return true;
 }
 
 template bool
-GenerateBlockId(ParseContext<SyntaxParseHandler> *pc, uint32_t &blockid);
+GenerateBlockId(TokenStream &ts, ParseContext<SyntaxParseHandler> *pc, uint32_t &blockid);
 
 template bool
-GenerateBlockId(ParseContext<FullParseHandler> *pc, uint32_t &blockid);
+GenerateBlockId(TokenStream &ts, ParseContext<FullParseHandler> *pc, uint32_t &blockid);
 
 template <typename ParseHandler>
 static void
 PushStatementPC(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, StmtType type)
 {
     stmt->blockid = pc->blockid();
     PushStatement(pc, stmt, type);
 }
@@ -620,17 +617,17 @@ Parser<ParseHandler>::parse(JSObject *ch
      *   an object lock before it finishes generating bytecode into a script
      *   protected from the GC by a root or a stack frame reference.
      */
     Directives directives(options().strictOption);
     GlobalSharedContext globalsc(context, chain, directives, options().extraWarningsOption);
     ParseContext<ParseHandler> globalpc(this, /* parent = */ NULL, ParseHandler::null(),
                                         &globalsc, /* newDirectives = */ NULL,
                                         /* staticLevel = */ 0, /* bodyid = */ 0);
-    if (!globalpc.init())
+    if (!globalpc.init(tokenStream))
         return null();
 
     Node pn = statements();
     if (pn) {
         if (!tokenStream.matchToken(TOK_EOF)) {
             report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
             return null();
         }
@@ -882,17 +879,17 @@ Parser<FullParseHandler>::standaloneFunc
                                          generatorKind);
     if (!funbox)
         return null();
     funbox->length = fun->nargs - fun->hasRest();
     handler.setFunctionBox(fn, funbox);
 
     ParseContext<FullParseHandler> funpc(this, pc, fn, funbox, newDirectives,
                                          /* staticLevel = */ 0, /* bodyid = */ 0);
-    if (!funpc.init())
+    if (!funpc.init(tokenStream))
         return null();
 
     for (unsigned i = 0; i < formals.length(); i++) {
         if (!defineArg(fn, formals[i]))
             return null();
     }
 
     ParseNode *pn = functionBody(Statement, StatementListBody);
@@ -2138,17 +2135,17 @@ Parser<FullParseHandler>::functionArgsAn
             // Move the syntax parser to the current position in the stream.
             TokenStream::Position position(keepAtoms);
             tokenStream.tell(&position);
             parser->tokenStream.seek(position, tokenStream);
 
             ParseContext<SyntaxParseHandler> funpc(parser, outerpc, SyntaxParseHandler::null(), funbox,
                                                    newDirectives, outerpc->staticLevel + 1,
                                                    outerpc->blockidGen);
-            if (!funpc.init())
+            if (!funpc.init(tokenStream))
                 return false;
 
             if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric,
                                                     fun, type, kind, newDirectives))
             {
                 if (parser->hadAbortedSyntaxParse()) {
                     // Try again with a full parse.
                     parser->clearAbortedSyntaxParse();
@@ -2170,17 +2167,17 @@ Parser<FullParseHandler>::functionArgsAn
         pn->pn_blockid = outerpc->blockid();
         PropagateTransitiveParseFlags(funbox, outerpc->sc);
         return true;
     } while (false);
 
     // Continue doing a full parse for this inner function.
     ParseContext<FullParseHandler> funpc(this, pc, pn, funbox, newDirectives,
                                          outerpc->staticLevel + 1, outerpc->blockidGen);
-    if (!funpc.init())
+    if (!funpc.init(tokenStream))
         return false;
 
     if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives))
         return false;
 
     if (!leaveFunction(pn, outerpc, kind))
         return false;
 
@@ -2209,17 +2206,17 @@ Parser<SyntaxParseHandler>::functionArgs
     // Create box for fun->object early to protect against last-ditch GC.
     FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
     if (!funbox)
         return false;
 
     // Initialize early for possible flags mutation via destructuringExpr.
     ParseContext<SyntaxParseHandler> funpc(this, pc, handler.null(), funbox, newDirectives,
                                            outerpc->staticLevel + 1, outerpc->blockidGen);
-    if (!funpc.init())
+    if (!funpc.init(tokenStream))
         return false;
 
     if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives))
         return false;
 
     if (!leaveFunction(pn, outerpc, kind))
         return false;
 
@@ -2244,17 +2241,17 @@ Parser<FullParseHandler>::standaloneLazy
                                          generatorKind);
     if (!funbox)
         return null();
     funbox->length = fun->nargs - fun->hasRest();
 
     Directives newDirectives = directives;
     ParseContext<FullParseHandler> funpc(this, /* parent = */ NULL, pn, funbox,
                                          &newDirectives, staticLevel, /* bodyid = */ 0);
-    if (!funpc.init())
+    if (!funpc.init(tokenStream))
         return null();
 
     if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement, &newDirectives)) {
         JS_ASSERT(directives == newDirectives);
         return null();
     }
 
 
@@ -2373,17 +2370,17 @@ Parser<FullParseHandler>::moduleDecl()
     ModuleBox *modulebox = newModuleBox(module, pc);
     if (!modulebox)
         return NULL;
     pn->pn_modulebox = modulebox;
 
     ParseContext<FullParseHandler> modulepc(this, pc, /* function = */ NULL, modulebox,
                                             /* newDirectives = */ NULL, pc->staticLevel + 1,
                                             pc->blockidGen);
-    if (!modulepc.init())
+    if (!modulepc.init(tokenStream))
         return NULL;
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_MODULE);
     pn->pn_body = statements();
     if (!pn->pn_body)
         return NULL;
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_MODULE);
 
     return pn;
@@ -3258,17 +3255,17 @@ Parser<ParseHandler>::pushLexicalScope(H
     PushStatementPC(pc, stmt, STMT_BLOCK);
     blockObj->initPrevBlockChainFromParser(pc->blockChain);
     FinishPushBlockScope(pc, stmt, *blockObj.get());
 
     Node pn = handler.newLexicalScope(blockbox);
     if (!pn)
         return null();
 
-    if (!GenerateBlockId(pc, stmt->blockid))
+    if (!GenerateBlockId(tokenStream, pc, stmt->blockid))
         return null();
     handler.setBlockId(pn, stmt->blockid);
     return pn;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::pushLexicalScope(StmtInfoPC *stmt)
@@ -3409,30 +3406,31 @@ Parser<ParseHandler>::letBlock(LetContex
     }
     return pnlet;
 }
 
 #endif /* JS_HAS_BLOCK_SCOPE */
 
 template <typename ParseHandler>
 static bool
-PushBlocklikeStatement(StmtInfoPC *stmt, StmtType type, ParseContext<ParseHandler> *pc)
+PushBlocklikeStatement(TokenStream &ts, StmtInfoPC *stmt, StmtType type,
+                       ParseContext<ParseHandler> *pc)
 {
     PushStatementPC(pc, stmt, type);
-    return GenerateBlockId(pc, stmt->blockid);
+    return GenerateBlockId(ts, pc, stmt->blockid);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::blockStatement()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
 
     StmtInfoPC stmtInfo(context);
-    if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, pc))
+    if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_BLOCK, pc))
         return null();
 
     Node list = statements();
     if (!list)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
     PopStatementPC(tokenStream, pc);
@@ -4338,17 +4336,17 @@ Parser<ParseHandler>::switchStatement()
         return null();
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
 
     StmtInfoPC stmtInfo(context);
     PushStatementPC(pc, &stmtInfo, STMT_SWITCH);
 
-    if (!GenerateBlockId(pc, pc->topStmt->blockid))
+    if (!GenerateBlockId(tokenStream, pc, pc->topStmt->blockid))
         return null();
 
     Node caseList = handler.newStatementList(pc->blockid(), pos());
     if (!caseList)
         return null();
 
     Node saveBlock = pc->blockNode;
     pc->blockNode = caseList;
@@ -4797,17 +4795,17 @@ Parser<ParseHandler>::tryStatement()
      *   TOK_NAME for a single identifier
      *   TOK_RB or TOK_RC for a destructuring left-hand side
      *
      * finally nodes are TOK_LC statement lists.
      */
 
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
     StmtInfoPC stmtInfo(context);
-    if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, pc))
+    if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_TRY, pc))
         return null();
     Node innerBlock = statements();
     if (!innerBlock)
         return null();
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
     PopStatementPC(tokenStream, pc);
 
     bool hasUnconditionalCatch = false;
@@ -4920,17 +4918,17 @@ Parser<ParseHandler>::tryStatement()
             tt = tokenStream.getToken(TokenStream::Operand);
         } while (tt == TOK_CATCH);
     }
 
     Node finallyBlock = null();
 
     if (tt == TOK_FINALLY) {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
-        if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, pc))
+        if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_FINALLY, pc))
             return null();
         finallyBlock = statements();
         if (!finallyBlock)
             return null();
         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
         PopStatementPC(tokenStream, pc);
     } else {
         tokenStream.ungetToken();
@@ -6121,17 +6119,17 @@ Parser<FullParseHandler>::generatorExpr(
         FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives,
                                                 LegacyGenerator);
         if (!genFunbox)
             return null();
 
         ParseContext<FullParseHandler> genpc(this, outerpc, genfn, genFunbox,
                                              /* newDirectives = */ NULL, outerpc->staticLevel + 1,
                                              outerpc->blockidGen);
-        if (!genpc.init())
+        if (!genpc.init(tokenStream))
             return null();
 
         /*
          * We assume conservatively that any deoptimization flags in pc->sc
          * come from the kid. So we propagate these flags into genfn. For code
          * simplicity we also do not detect if the flags were only set in the
          * kid and could be removed from pc->sc.
          */
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -71,17 +71,17 @@ struct GenericParseContext
         funHasReturnVoid(false),
         parsingForInit(false),
         parsingWith(parent ? parent->parsingWith : false)
     {}
 };
 
 template <typename ParseHandler>
 bool
-GenerateBlockId(ParseContext<ParseHandler> *pc, uint32_t &blockid);
+GenerateBlockId(TokenStream &ts, ParseContext<ParseHandler> *pc, uint32_t &blockid);
 
 /*
  * The struct ParseContext stores information about the current parsing context,
  * which is part of the parser state (see the field Parser::pc). The current
  * parsing context is either the global context, or the function currently being
  * parsed. When the parser encounters a function definition, it creates a new
  * ParseContext, makes it the new current context, and sets its parent to the
  * context in which it encountered the definition.
@@ -264,17 +264,17 @@ struct ParseContext : public GenericPars
         newDirectives(newDirectives),
         inDeclDestructuring(false)
     {
         prs->pc = this;
     }
 
     ~ParseContext();
 
-    bool init();
+    bool init(TokenStream &ts);
 
     unsigned blockid() { return topStmt ? topStmt->blockid : bodyid; }
 
     // True if we are at the topmost level of a entire script or function body.
     // For example, while parsing this code we would encounter f1 and f2 at
     // body level, but we would not encounter f3 or f4 at body level:
     //
     //   function f1() { function f2() { } }
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -15,26 +15,28 @@
 #include <stdio.h>
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsexn.h"
 #include "jsnum.h"
+#include "jsworkers.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "js/CharacterEncoding.h"
 #include "vm/Keywords.h"
 #include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::frontend;
 using namespace js::unicode;
 
+using mozilla::Maybe;
 using mozilla::PodAssign;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 
 struct KeywordInfo {
     const char  *chars;         // C string with keyword text
     TokenKind   tokentype;
     JSVersion   version;
@@ -559,17 +561,17 @@ TokenStream::reportStrictModeErrorNumber
         flags |= JSREPORT_WARNING;
     else
         return true;
 
     return reportCompileErrorNumberVA(offset, flags, errorNumber, args);
 }
 
 void
-CompileError::throwError()
+CompileError::throwError(JSContext *cx)
 {
     // If there's a runtime exception type associated with this error
     // number, set that as the pending exception.  For errors occuring at
     // compile time, this is very likely to be a JSEXN_SYNTAXERR.
     //
     // If an exception is thrown but not caught, the JSREPORT_EXCEPTION
     // flag will be set in report.flags.  Proper behavior for an error
     // reporter is to ignore a report with this flag for all but top-level
@@ -617,22 +619,20 @@ TokenStream::reportCompileErrorNumberVA(
 {
     bool warning = JSREPORT_IS_WARNING(flags);
 
     if (warning && options().werrorOption) {
         flags &= ~JSREPORT_WARNING;
         warning = false;
     }
 
-    if (!this->cx->isJSContext())
-        return warning;
-
-    JSContext *cx = this->cx->asJSContext();
-
-    CompileError err(cx);
+    // On the main thread, report the error immediately. When compiling off
+    // thread, save the error so that the main thread can report it later.
+    CompileError tempErr;
+    CompileError &err = cx->isJSContext() ? tempErr : cx->addPendingCompileError();
 
     err.report.flags = flags;
     err.report.errorNumber = errorNumber;
     err.report.filename = filename;
     err.report.originPrincipals = originPrincipals;
     if (offset == NoOffset) {
         err.report.lineno = 0;
         err.report.column = 0;
@@ -692,17 +692,18 @@ TokenStream::reportCompileErrorNumberVA(
         err.report.linebuf = LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars).c_str();
         if (!err.report.linebuf)
             return false;
 
         err.report.tokenptr = err.report.linebuf + windowOffset;
         err.report.uctokenptr = err.report.uclinebuf + windowOffset;
     }
 
-    err.throwError();
+    if (cx->isJSContext())
+        err.throwError(cx->asJSContext());
 
     return warning;
 }
 
 bool
 TokenStream::reportStrictModeError(unsigned errorNumber, ...)
 {
     va_list args;
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -302,27 +302,26 @@ struct Token {
 
     DecimalPoint decimalPoint() const {
         JS_ASSERT(type == TOK_NUMBER);
         return u.number.decimalPoint;
     }
 };
 
 struct CompileError {
-    JSContext *cx;
     JSErrorReport report;
     char *message;
     ErrorArgumentsType argumentsType;
-    CompileError(JSContext *cx)
-      : cx(cx), message(NULL), argumentsType(ArgumentsAreUnicode)
+    CompileError()
+      : message(NULL), argumentsType(ArgumentsAreUnicode)
     {
         mozilla::PodZero(&report);
     }
     ~CompileError();
-    void throwError();
+    void throwError(JSContext *cx);
 };
 
 // Ideally, tokenizing would be entirely independent of context.  But the
 // strict mode flag, which is in SharedContext, affects tokenizing, and
 // TokenStream needs to see it.
 //
 // This class is a tiny back-channel from TokenStream to the strict mode flag
 // that avoids exposing the rest of SharedContext to TokenStream.
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -4606,17 +4606,17 @@ ParseFunction(ModuleCompiler &m, ParseNo
     Directives directives(outerpc);
     FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator);
     if (!funbox)
         return false;
 
     Directives newDirectives = directives;
     AsmJSParseContext funpc(&m.parser(), outerpc, fn, funbox, &newDirectives,
                             outerpc->staticLevel + 1, outerpc->blockidGen);
-    if (!funpc.init())
+    if (!funpc.init(m.parser().tokenStream))
         return false;
 
     if (!m.parser().functionArgsAndBodyGeneric(fn, fun, Normal, Statement, &newDirectives))
         return false;
 
     if (tokenStream.hadError() || directives != newDirectives)
         return false;
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1744,21 +1744,17 @@ JS_updateMallocCounter(JSContext *cx, si
 {
     return cx->runtime()->updateMallocCounter(cx->zone(), nbytes);
 }
 
 JS_PUBLIC_API(char *)
 JS_strdup(JSContext *cx, const char *s)
 {
     AssertHeapIsIdle(cx);
-    size_t n = strlen(s) + 1;
-    void *p = cx->malloc_(n);
-    if (!p)
-        return NULL;
-    return (char *)js_memcpy(p, s, n);
+    return js_strdup(cx, s);
 }
 
 JS_PUBLIC_API(char *)
 JS_strdup(JSRuntime *rt, const char *s)
 {
     AssertHeapIsIdle(rt);
     size_t n = strlen(s) + 1;
     void *p = rt->malloc_(n);
@@ -4887,28 +4883,28 @@ JS_PUBLIC_API(bool)
 JS::CompileOffThread(JSContext *cx, Handle<JSObject*> obj, CompileOptions options,
                      const jschar *chars, size_t length,
                      OffThreadCompileCallback callback, void *callbackData)
 {
 #ifdef JS_WORKER_THREADS
     JS_ASSERT(CanCompileOffThread(cx, options));
     return StartOffThreadParseScript(cx, options, chars, length, obj, callback, callbackData);
 #else
-    MOZ_ASSUME_UNREACHABLE("Off thread compilation is only available with JS_ION");
+    MOZ_ASSUME_UNREACHABLE("Off thread compilation is not available.");
 #endif
 }
 
-JS_PUBLIC_API(void)
-JS::FinishOffThreadScript(JSRuntime *rt, JSScript *script)
+JS_PUBLIC_API(JSScript *)
+JS::FinishOffThreadScript(JSContext *maybecx, JSRuntime *rt, void *token)
 {
 #ifdef JS_WORKER_THREADS
     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
-    rt->workerThreadState->finishParseTaskForScript(rt, script);
+    return rt->workerThreadState->finishParseTask(maybecx, rt, token);
 #else
-    MOZ_ASSUME_UNREACHABLE("Off thread compilation is only available with JS_ION");
+    MOZ_ASSUME_UNREACHABLE("Off thread compilation is not available.");
 #endif
 }
 
 JS_PUBLIC_API(JSScript *)
 JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *objArg, JSPrincipals *principals,
                                 const jschar *chars, size_t length,
                                 const char *filename, unsigned lineno)
 {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3564,35 +3564,35 @@ Compile(JSContext *cx, JS::Handle<JSObje
 
 extern JS_PUBLIC_API(bool)
 CanCompileOffThread(JSContext *cx, const CompileOptions &options);
 
 /*
  * Off thread compilation control flow.
  *
  * After successfully triggering an off thread compile of a script, the
- * callback will eventually be invoked with the specified data and the result
- * script or NULL. The callback will be invoked while off the main thread, so
- * must ensure that its operations are thread safe. Afterwards,
- * FinishOffThreadScript must be invoked on the main thread to make the script
- * usable (correct compartment/zone); this method must be invoked even if the
- * off thread compilation produced a NULL script.
+ * callback will eventually be invoked with the specified data and a token
+ * for the compilation. The callback will be invoked while off the main thread,
+ * so must ensure that its operations are thread safe. Afterwards,
+ * FinishOffThreadScript must be invoked on the main thread to get the result
+ * script or NULL. If maybecx is specified, this method will also report any
+ * error or warnings generated during the parse.
  *
  * The characters passed in to CompileOffThread must remain live until the
  * callback is invoked, and the resulting script will be rooted until the call
  * to FinishOffThreadScript.
  */
 
 extern JS_PUBLIC_API(bool)
 CompileOffThread(JSContext *cx, Handle<JSObject*> obj, CompileOptions options,
                  const jschar *chars, size_t length,
                  OffThreadCompileCallback callback, void *callbackData);
 
-extern JS_PUBLIC_API(void)
-FinishOffThreadScript(JSRuntime *rt, JSScript *script);
+extern JS_PUBLIC_API(JSScript *)
+FinishOffThreadScript(JSContext *maybecx, JSRuntime *rt, void *token);
 
 extern JS_PUBLIC_API(JSFunction *)
 CompileFunction(JSContext *cx, JS::Handle<JSObject*> obj, CompileOptions options,
                 const char *name, unsigned nargs, const char **argnames,
                 const char *bytes, size_t length);
 
 extern JS_PUBLIC_API(JSFunction *)
 CompileFunction(JSContext *cx, JS::Handle<JSObject*> obj, CompileOptions options,
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -618,29 +618,39 @@ js::PrintError(JSContext *cx, FILE *file
         fputc('^', file);
     }
     fputc('\n', file);
     fflush(file);
     JS_free(cx, prefix);
     return true;
 }
 
+char *
+js_strdup(ExclusiveContext *cx, const char *s)
+{
+    size_t n = strlen(s) + 1;
+    void *p = cx->malloc_(n);
+    if (!p)
+        return NULL;
+    return (char *)js_memcpy(p, s, n);
+}
+
 /*
  * The arguments from ap need to be packaged up into an array and stored
  * into the report struct.
  *
  * The format string addressed by the error number may contain operands
  * identified by the format {N}, where N is a decimal digit. Each of these
  * is to be replaced by the Nth argument from the va_list. The complete
  * message is placed into reportp->ucmessage converted to a JSString.
  *
  * Returns true if the expansion succeeds (can fail if out of memory).
  */
 bool
-js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
+js_ExpandErrorArguments(ExclusiveContext *cx, JSErrorCallback callback,
                         void *userRef, const unsigned errorNumber,
                         char **messagep, JSErrorReport *reportp,
                         ErrorArgumentsType argumentsType, va_list ap)
 {
     const JSErrorFormatString *efs;
     int i;
     int argCount;
     bool messageArgsPassed = !!reportp->messageArgs;
@@ -747,17 +757,17 @@ js_ExpandErrorArguments(JSContext *cx, J
             /* Non-null messageArgs should have at least one non-null arg. */
             JS_ASSERT(!reportp->messageArgs);
             /*
              * Zero arguments: the format string (if it exists) is the
              * entire message.
              */
             if (efs->format) {
                 size_t len;
-                *messagep = JS_strdup(cx, efs->format);
+                *messagep = js_strdup(cx, efs->format);
                 if (!*messagep)
                     goto error;
                 len = strlen(*messagep);
                 reportp->ucmessage = InflateString(cx, *messagep, &len);
                 if (!reportp->ucmessage)
                     goto error;
             }
         }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -101,16 +101,18 @@ TraceCycleDetectionSet(JSTracer *trc, Ob
 
 struct AutoResolving;
 class DtoaCache;
 class ForkJoinSlice;
 class RegExpCompartment;
 class RegExpStatics;
 class ForkJoinSlice;
 
+namespace frontend { struct CompileError; }
+
 /*
  * Execution Context Overview:
  *
  * Several different structures may be used to provide a context for operations
  * on the VM. Each context is thread local, but varies in what data it can
  * access and what other threads may be running.
  *
  * - ThreadSafeContext is used by threads operating in one compartment which
@@ -370,24 +372,27 @@ class ExclusiveContext : public ThreadSa
     }
     JSCompartment *atomsCompartment() {
         return runtime_->atomsCompartment();
     }
     ScriptDataTable &scriptDataTable() {
         return runtime_->scriptDataTable();
     }
 
-#if defined(JS_ION) && defined(JS_THREADSAFE)
+#ifdef JS_WORKER_THREADS
     // Since JSRuntime::workerThreadState is necessarily initialized from the
     // main thread before the first worker thread can access it, there is no
     // possibility for a race read/writing it.
     WorkerThreadState *workerThreadState() {
         return runtime_->workerThreadState;
     }
 #endif
+
+    // Methods specific to any WorkerThread for the context.
+    frontend::CompileError &addPendingCompileError();
 };
 
 inline void
 MaybeCheckStackRoots(ExclusiveContext *cx)
 {
     MaybeCheckStackRoots(cx->maybeJSContext());
 }
 
@@ -738,17 +743,17 @@ js_ReportErrorNumberVA(JSContext *cx, un
 
 extern bool
 js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback,
                             void *userRef, const unsigned errorNumber,
                             const jschar **args);
 #endif
 
 extern bool
-js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
+js_ExpandErrorArguments(js::ExclusiveContext *cx, JSErrorCallback callback,
                         void *userRef, const unsigned errorNumber,
                         char **message, JSErrorReport *reportp,
                         js::ErrorArgumentsType argumentsType, va_list ap);
 
 namespace js {
 
 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
 extern void
@@ -804,16 +809,19 @@ js_ReportValueErrorFlags(JSContext *cx, 
                                     spindex, v, fallback, arg1, NULL))
 
 #define js_ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2)     \
     ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,          \
                                     spindex, v, fallback, arg1, arg2))
 
 extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
 
+char *
+js_strdup(js::ExclusiveContext *cx, const char *s);
+
 #ifdef JS_THREADSAFE
 # define JS_ASSERT_REQUEST_DEPTH(cx)  JS_ASSERT((cx)->runtime()->requestDepth >= 1)
 #else
 # define JS_ASSERT_REQUEST_DEPTH(cx)  ((void) 0)
 #endif
 
 /*
  * Invoke the operation callback and return false if the current execution
@@ -959,21 +967,21 @@ class AutoAssertNoException
     }
 };
 
 /*
  * FIXME bug 647103 - replace these *AllocPolicy names.
  */
 class ContextAllocPolicy
 {
-    JSContext *const cx_;
+    ThreadSafeContext *const cx_;
 
   public:
-    ContextAllocPolicy(JSContext *cx) : cx_(cx) {}
-    JSContext *context() const { return cx_; }
+    ContextAllocPolicy(ThreadSafeContext *cx) : cx_(cx) {}
+    ThreadSafeContext *context() const { return cx_; }
     void *malloc_(size_t bytes) { return cx_->malloc_(bytes); }
     void *calloc_(size_t bytes) { return cx_->calloc_(bytes); }
     void *realloc_(void *p, size_t oldBytes, size_t bytes) { return cx_->realloc_(p, oldBytes, bytes); }
     void free_(void *p) { js_free(p); }
     void reportAllocOverflow() const { js_ReportAllocationOverflow(cx_); }
 };
 
 /* Exposed intrinsics so that Ion may inline them. */
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -852,25 +852,32 @@ js_InitExceptionClasses(JSContext *cx, H
         if (!InitErrorClass(cx, global, i, errorProto))
             return NULL;
     }
 
     return errorProto;
 }
 
 const JSErrorFormatString*
-js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
+js_GetLocalizedErrorMessage(ExclusiveContext *cx, void *userRef, const char *locale,
                             const unsigned errorNumber)
 {
     const JSErrorFormatString *errorString = NULL;
 
-    if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeGetErrorMessage) {
-        errorString = cx->runtime()->localeCallbacks
-                                   ->localeGetErrorMessage(userRef, locale, errorNumber);
+    // The locale callbacks might not be thread safe, so don't call them if
+    // we're not on the main thread. When used with XPConnect,
+    // |localeGetErrorMessage| will be NULL anyways.
+    if (cx->isJSContext() &&
+        cx->asJSContext()->runtime()->localeCallbacks &&
+        cx->asJSContext()->runtime()->localeCallbacks->localeGetErrorMessage)
+    {
+        JSLocaleCallbacks *callbacks = cx->asJSContext()->runtime()->localeCallbacks;
+        errorString = callbacks->localeGetErrorMessage(userRef, locale, errorNumber);
     }
+
     if (!errorString)
         errorString = js_GetErrorMessage(userRef, locale, errorNumber);
     return errorString;
 }
 
 JS_FRIEND_API(const jschar*)
 js::GetErrorTypeName(JSRuntime* rt, int16_t exnType)
 {
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -49,17 +49,17 @@ js_ErrorToException(JSContext *cx, const
  */
 extern bool
 js_ReportUncaughtException(JSContext *cx);
 
 extern JSErrorReport *
 js_ErrorFromException(jsval exn);
 
 extern const JSErrorFormatString *
-js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
+js_GetLocalizedErrorMessage(js::ExclusiveContext *cx, void *userRef, const char *locale,
                             const unsigned errorNumber);
 
 /*
  * Make a copy of errobj parented to scope.
  *
  * cx must be in the same compartment as scope. errobj may be in a different
  * compartment, but it must be an Error object (not a wrapper of one) and it
  * must not be one of the prototype objects created by js_InitExceptionClasses
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -187,17 +187,17 @@ typedef bool                    (*JSInit
  * Generic trace operation that calls JS_CallTracer on each traceable thing
  * stored in data.
  */
 typedef void
 (* JSTraceDataOp)(JSTracer *trc, void *data);
 
 namespace JS {
 
-typedef void (*OffThreadCompileCallback)(JSScript *script, void *callbackData);
+typedef void (*OffThreadCompileCallback)(void *token, void *callbackData);
 
 namespace shadow {
 
 struct Runtime
 {
     /* Restrict zone access during Minor GC. */
     bool needsBarrier_;
 
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -168,36 +168,36 @@ js::CancelOffThreadIonCompile(JSCompartm
 static JSClass workerGlobalClass = {
     "internal-worker-global", JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub,  JS_DeletePropertyStub,
     JS_PropertyStub,  JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub,
     JS_ConvertStub,   NULL
 };
 
-ParseTask::ParseTask(Zone *zone, ExclusiveContext *cx, const CompileOptions &options,
+ParseTask::ParseTask(ExclusiveContext *cx, const CompileOptions &options,
                      const jschar *chars, size_t length, JSObject *scopeChain,
                      JS::OffThreadCompileCallback callback, void *callbackData)
-  : zone(zone), cx(cx), options(options), chars(chars), length(length),
+  : cx(cx), options(options), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(scopeChain),
-    callback(callback), callbackData(callbackData), script(NULL)
+    callback(callback), callbackData(callbackData), script(NULL), errors(cx)
 {
-    JSRuntime *rt = zone->runtimeFromMainThread();
+    JSRuntime *rt = scopeChain->runtimeFromMainThread();
 
     if (options.principals())
         JS_HoldPrincipals(options.principals());
     if (options.originPrincipals())
         JS_HoldPrincipals(options.originPrincipals());
     if (!AddObjectRoot(rt, &this->scopeChain, "ParseTask::scopeChain"))
         MOZ_CRASH();
 }
 
 ParseTask::~ParseTask()
 {
-    JSRuntime *rt = zone->runtimeFromMainThread();
+    JSRuntime *rt = scopeChain->runtimeFromMainThread();
 
     if (options.principals())
         JS_DropPrincipals(rt, options.principals());
     if (options.originPrincipals())
         JS_DropPrincipals(rt, options.originPrincipals());
     JS_RemoveObjectRootRT(rt, &scopeChain);
 
     // ParseTask takes over ownership of its input exclusive context.
@@ -258,17 +258,17 @@ js::StartOffThreadParseScript(JSContext 
         cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData *) NULL,
                                    ThreadSafeContext::Context_Exclusive));
     if (!workercx)
         return false;
 
     workercx->enterCompartment(global->compartment());
 
     ScopedJSDeletePtr<ParseTask> task(
-        cx->new_<ParseTask>(global->zone(), workercx.get(), options, chars, length,
+        cx->new_<ParseTask>(workercx.get(), options, chars, length,
                             scopeChain, callback, callbackData));
     if (!task)
         return false;
 
     workercx.forget();
 
     WorkerThreadState &state = *cx->runtime()->workerThreadState;
     JS_ASSERT(state.numThreads);
@@ -367,20 +367,18 @@ WorkerThreadState::cleanup(JSRuntime *rt
         for (size_t i = 0; i < numThreads; i++)
             threads[i].destroy();
         js_free(threads);
         threads = NULL;
         numThreads = 0;
     }
 
     // Clean up any parse tasks which haven't been finished yet.
-    while (!parseFinishedList.empty()) {
-        JSScript *script = parseFinishedList[0]->script;
-        finishParseTaskForScript(rt, script);
-    }
+    while (!parseFinishedList.empty())
+        finishParseTask(/* maybecx = */ NULL, rt, parseFinishedList[0]);
 }
 
 WorkerThreadState::~WorkerThreadState()
 {
     JS_ASSERT(!threads);
     JS_ASSERT(parseFinishedList.empty());
 
     if (workerLock)
@@ -493,72 +491,76 @@ WorkerThreadState::canStartParseTask()
 }
 
 bool
 WorkerThreadState::canStartCompressionTask()
 {
     return !compressionWorklist.empty();
 }
 
-void
-WorkerThreadState::finishParseTaskForScript(JSRuntime *rt, JSScript *script)
+JSScript *
+WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token)
 {
     ParseTask *parseTask = NULL;
 
+    // The token is a ParseTask* which should be in the finished list.
     {
         AutoLockWorkerThreadState lock(*rt->workerThreadState);
         for (size_t i = 0; i < parseFinishedList.length(); i++) {
-            if (parseFinishedList[i]->script == script) {
+            if (parseFinishedList[i] == token) {
                 parseTask = parseFinishedList[i];
                 parseFinishedList[i] = parseFinishedList.back();
                 parseFinishedList.popBack();
                 break;
             }
         }
     }
     JS_ASSERT(parseTask);
 
     // Mark the zone as no longer in use by an ExclusiveContext, and available
     // to be collected by the GC.
-    rt->clearUsedByExclusiveThread(parseTask->zone);
-
-    if (!script) {
-        // Parsing failed and there is nothing to finish, but there still may
-        // be lingering ParseTask instances holding roots which need to be
-        // cleaned up. The ParseTask which we picked might not be the right
-        // one but this is ok as finish calls will be 1:1 with calls that
-        // create a ParseTask.
-        js_delete(parseTask);
-        return;
-    }
+    rt->clearUsedByExclusiveThread(parseTask->cx->zone());
 
     // Point the prototypes of any objects in the script's compartment to refer
     // to the corresponding prototype in the new compartment. This will briefly
     // create cross compartment pointers, which will be fixed by the
     // MergeCompartments call below.
-    for (gc::CellIter iter(parseTask->zone, gc::FINALIZE_TYPE_OBJECT); !iter.done(); iter.next()) {
+    for (gc::CellIter iter(parseTask->cx->zone(), gc::FINALIZE_TYPE_OBJECT);
+         !iter.done();
+         iter.next())
+    {
         types::TypeObject *object = iter.get<types::TypeObject>();
         JSObject *proto = object->proto;
         if (!proto)
             continue;
 
         JSProtoKey key = js_IdentifyClassPrototype(proto);
         if (key == JSProto_Null)
             continue;
 
         JSObject *newProto = GetClassPrototypePure(&parseTask->scopeChain->global(), key);
         JS_ASSERT(newProto);
 
         object->proto = newProto;
     }
 
     // Move the parsed script and all its contents into the desired compartment.
-    gc::MergeCompartments(parseTask->script->compartment(), parseTask->scopeChain->compartment());
+    gc::MergeCompartments(parseTask->cx->compartment(), parseTask->scopeChain->compartment());
 
+    // If we have a context, report any error or warnings generated during the
+    // parse.
+    if (maybecx) {
+        AutoCompartment ac(maybecx, parseTask->scopeChain);
+        for (size_t i = 0; i < parseTask->errors.length(); i++)
+            parseTask->errors[i].throwError(maybecx);
+    }
+
+    JSScript *script = parseTask->script;
     js_delete(parseTask);
+    return script;
 }
 
 void
 WorkerThread::destroy()
 {
     WorkerThreadState &state = *runtime->workerThreadState;
 
     if (thread) {
@@ -667,16 +669,24 @@ WorkerThread::handleIonWorkload(WorkerTh
 
 void
 ExclusiveContext::setWorkerThread(WorkerThread *workerThread)
 {
     this->workerThread = workerThread;
     this->perThreadData = workerThread->threadData.addr();
 }
 
+frontend::CompileError &
+ExclusiveContext::addPendingCompileError()
+{
+    if (!workerThread->parseTask->errors.append(frontend::CompileError()))
+        MOZ_CRASH();
+    return workerThread->parseTask->errors.back();
+}
+
 void
 WorkerThread::handleParseWorkload(WorkerThreadState &state)
 {
     JS_ASSERT(state.isLocked());
     JS_ASSERT(state.canStartParseTask());
     JS_ASSERT(idle());
 
     parseTask = state.parseWorklist.popCopy();
@@ -686,17 +696,17 @@ WorkerThread::handleParseWorkload(Worker
         AutoUnlockWorkerThreadState unlock(runtime);
         parseTask->script = frontend::CompileScript(parseTask->cx, &parseTask->alloc,
                                                     NullPtr(), NullPtr(),
                                                     parseTask->options,
                                                     parseTask->chars, parseTask->length);
     }
 
     // The callback is invoked while we are still off the main thread.
-    parseTask->callback(parseTask->script, parseTask->callbackData);
+    parseTask->callback(parseTask, parseTask->callbackData);
 
     // FinishOffThreadScript will need to be called on the script to
     // migrate it into the correct compartment.
     state.parseFinishedList.append(parseTask);
 
     parseTask = NULL;
 
     // Notify the main thread in case it is waiting for the parse/emit to finish.
@@ -995,9 +1005,15 @@ AutoPauseWorkersForGC::AutoPauseWorkersF
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
 AutoPauseWorkersForGC::~AutoPauseWorkersForGC()
 {
 }
 
+frontend::CompileError &
+ExclusiveContext::addPendingCompileError()
+{
+    MOZ_ASSUME_UNREACHABLE("Off thread compilation not available.");
+}
+
 #endif /* JS_WORKER_THREADS */
--- a/js/src/jsworkers.h
+++ b/js/src/jsworkers.h
@@ -14,16 +14,17 @@
 #define jsworkers_h
 
 #include "mozilla/GuardObjects.h"
 #include "mozilla/PodOperations.h"
 
 #include "jscntxt.h"
 #include "jslock.h"
 
+#include "frontend/TokenStream.h"
 #include "jit/Ion.h"
 
 namespace js {
 
 struct WorkerThread;
 struct AsmJSParallelTask;
 struct ParseTask;
 namespace jit {
@@ -114,17 +115,17 @@ class WorkerThreadState
     void resetAsmJSFailureState() {
         numAsmJSFailedJobs = 0;
         asmJSFailedFunction = NULL;
     }
     void *maybeAsmJSFailedFunction() const {
         return asmJSFailedFunction;
     }
 
-    void finishParseTaskForScript(JSRuntime *rt, JSScript *script);
+    JSScript *finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token);
     bool compressionInProgress(SourceCompressionTask *task);
     SourceCompressionTask *compressionTaskForSource(ScriptSource *ss);
 
   private:
 
     /*
      * Lock protecting all mutable shared state accessed by helper threads, and
      * used by all condition variables.
@@ -361,17 +362,16 @@ struct AsmJSParallelTask
         this->mir = mir;
         this->lir = NULL;
     }
 };
 #endif
 
 struct ParseTask
 {
-    Zone *zone;
     ExclusiveContext *cx;
     CompileOptions options;
     const jschar *chars;
     size_t length;
     LifoAlloc alloc;
 
     // Rooted pointer to the scope in the target compartment which the
     // resulting script will be merged into. This is not safe to use off the
@@ -382,17 +382,21 @@ struct ParseTask
     JS::OffThreadCompileCallback callback;
     void *callbackData;
 
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
     JSScript *script;
 
-    ParseTask(Zone *zone, ExclusiveContext *cx, const CompileOptions &options,
+    // Any errors or warnings produced during compilation. These are reported
+    // when finishing the script.
+    Vector<frontend::CompileError> errors;
+
+    ParseTask(ExclusiveContext *cx, const CompileOptions &options,
               const jschar *chars, size_t length, JSObject *scopeChain,
               JS::OffThreadCompileCallback callback, void *callbackData);
 
     ~ParseTask();
 };
 
 // Compression tasks are allocated on the stack by their triggering thread,
 // which will block on the compression completing as the task goes out of scope
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3232,17 +3232,17 @@ SyntaxParse(JSContext *cx, unsigned argc
 
     args.rval().setBoolean(succeeded);
     return true;
 }
 
 #ifdef JS_THREADSAFE
 
 static void
-OffThreadCompileScriptCallback(JSScript *script, void *callbackData)
+OffThreadCompileScriptCallback(void *token, void *callbackData)
 {
     // This callback is invoked off the main thread and there isn't a good way
     // to pass the script on to the main thread. Just let the script leak.
 }
 
 static bool
 OffThreadCompileScript(JSContext *cx, unsigned argc, jsval *vp)
 {
--- a/js/src/vm/StringBuffer.cpp
+++ b/js/src/vm/StringBuffer.cpp
@@ -21,32 +21,32 @@ StringBuffer::extractWellSized()
     jschar *buf = cb.extractRawBuffer();
     if (!buf)
         return NULL;
 
     /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
     JS_ASSERT(capacity >= length);
     if (length > CharBuffer::sMaxInlineStorage && capacity - length > length / 4) {
         size_t bytes = sizeof(jschar) * (length + 1);
-        JSContext *cx = context();
+        ExclusiveContext *cx = context();
         jschar *tmp = (jschar *)cx->realloc_(buf, bytes);
         if (!tmp) {
             js_free(buf);
             return NULL;
         }
         buf = tmp;
     }
 
     return buf;
 }
 
 JSFlatString *
 StringBuffer::finishString()
 {
-    JSContext *cx = context();
+    ExclusiveContext *cx = context();
     if (cb.empty())
         return cx->names().empty;
 
     size_t length = cb.length();
     if (!JSString::validateLength(cx, length))
         return NULL;
 
     JS_STATIC_ASSERT(JSShortString::MAX_SHORT_LENGTH < CharBuffer::InlineLength);
@@ -64,17 +64,17 @@ StringBuffer::finishString()
     if (!str)
         js_free(buf);
     return str;
 }
 
 JSAtom *
 StringBuffer::finishAtom()
 {
-    JSContext *cx = context();
+    ExclusiveContext *cx = context();
 
     size_t length = cb.length();
     if (length == 0)
         return cx->names().empty;
 
     JSAtom *atom = AtomizeChars<CanGC>(cx, cb.begin(), length);
     cb.clear();
     return atom;
--- a/js/src/vm/StringBuffer.h
+++ b/js/src/vm/StringBuffer.h
@@ -28,23 +28,25 @@ namespace js {
  */
 class StringBuffer
 {
     /* cb's buffer is taken by the new string so use ContextAllocPolicy. */
     typedef Vector<jschar, 32, ContextAllocPolicy> CharBuffer;
 
     CharBuffer cb;
 
-    JSContext *context() const { return cb.allocPolicy().context(); }
+    ExclusiveContext *context() const {
+        return cb.allocPolicy().context()->asExclusiveContext();
+    }
 
     StringBuffer(const StringBuffer &other) MOZ_DELETE;
     void operator=(const StringBuffer &other) MOZ_DELETE;
 
   public:
-    explicit StringBuffer(JSContext *cx) : cb(cx) { }
+    explicit StringBuffer(ExclusiveContext *cx) : cb(cx) { }
 
     inline bool reserve(size_t len) { return cb.reserve(len); }
     inline bool resize(size_t len) { return cb.resize(len); }
     inline bool append(const jschar c) { return cb.append(c); }
     inline bool append(const jschar *chars, size_t len) { return cb.append(chars, len); }
     inline bool append(const JS::CharPtr chars, size_t len) { return cb.append(chars.get(), len); }
     inline bool append(const jschar *begin, const jschar *end) { return cb.append(begin, end); }
     inline bool append(JSString *str);
@@ -116,17 +118,17 @@ StringBuffer::append(JSString *str)
 
 inline bool
 StringBuffer::appendInflated(const char *cstr, size_t cstrlen)
 {
     size_t lengthBefore = length();
     if (!cb.growByUninitialized(cstrlen))
         return false;
     mozilla::DebugOnly<size_t> oldcstrlen = cstrlen;
-    mozilla::DebugOnly<bool> ok = InflateStringToBuffer(context(), cstr, cstrlen,
+    mozilla::DebugOnly<bool> ok = InflateStringToBuffer(NULL, cstr, cstrlen,
                                                         begin() + lengthBefore, &cstrlen);
     JS_ASSERT(ok && oldcstrlen == cstrlen);
     return true;
 }
 
 /* ES5 9.8 ToString, appending the result to the string buffer. */
 extern bool
 ValueToStringBufferSlow(JSContext *cx, const Value &v, StringBuffer &sb);