From 1671ded3c2c3976e35429ef7740171699bef7c47 Mon Sep 17 00:00:00 2001 From: Ted Mielczarek Date: Wed, 19 Oct 2011 10:01:24 -0400 Subject: [PATCH 01/42] bug 694499 - get rid of jscpucfg binary. r=jimb --- js/src/Makefile.in | 40 +--------- js/src/configure.in | 5 ++ js/src/js-config.h.in | 4 + js/src/jscpucfg.cpp | 182 ------------------------------------------ js/src/jscpucfg.h | 28 ++++++- js/src/jstypes.h | 6 +- 6 files changed, 35 insertions(+), 230 deletions(-) delete mode 100644 js/src/jscpucfg.cpp diff --git a/js/src/Makefile.in b/js/src/Makefile.in index b641d17fb27..0e8c213ae9b 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -182,7 +182,7 @@ CPPSRCS = \ # doing! INSTALLED_HEADERS = \ js-config.h \ - jsautocfg.h \ + jscpucfg.h \ $(CURDIR)/jsautokw.h \ js.msg \ jsalloc.h \ @@ -542,10 +542,6 @@ else CPPSRCS += pm_stub.cpp endif -ifeq ($(OS_ARCH),WINNT) -INSTALLED_HEADERS += jscpucfg.h -endif - EXPORTS = $(INSTALLED_HEADERS) DASH_R = -r @@ -760,12 +756,6 @@ DEFINES += -DIMPL_MFBT INCLUDES += -I$(srcdir) -GARBAGE += jscpucfg.o jsautocfg.h jsautocfg.tmp jscpucfg - -ifneq (,$(CROSS_COMPILE)$(filter-out WINNT,$(OS_ARCH))) -TARGETS += jscpucfg$(HOST_BIN_SUFFIX) -endif - ifdef JS_THREADSAFE DEFINES += -DJS_THREADSAFE endif @@ -880,34 +870,6 @@ jsdtoa.o: jsdtoa.cpp Makefile.in $(CXX) -o $@ -c $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(COMPILE_CFLAGS)) $< endif -export:: jsautocfg.h - -ifeq (,$(CROSS_COMPILE)$(GNU_CC)$(filter-out WINNT,$(OS_ARCH))) -jsautocfg.h: - $(TOUCH) $@ -else -jsautocfg.h: jscpucfg$(HOST_BIN_SUFFIX) - @$(RM) $@ jsautocfg.tmp - ./jscpucfg > jsautocfg.tmp - mv jsautocfg.tmp $@ -endif - -# jscpucfg is a strange target -# Needs to be built with the host compiler but needs to include -# the mdcpucfg for the target so it needs the appropriate target defines -ifdef HOST_NSPR_MDCPUCFG -HOST_CXX := $(HOST_CXX) -DMDCPUCFG=$(TARGET_NSPR_MDCPUCFG) -HOST_CXXFLAGS := $(patsubst -DXP_%,,$(HOST_CXXFLAGS)) -endif - -ifdef CROSS_COMPILE -# jscpucfg needs to know when it's supposed to produce a config for the target -JSCPUCFG_DEFINES = $(ACDEFINES) -endif - -jscpucfg$(HOST_BIN_SUFFIX): jscpucfg.cpp Makefile.in - $(HOST_CXX) $(HOST_CXXFLAGS) $(JSCPUCFG_DEFINES) $(DEFINES) $(NSPR_CFLAGS) $(HOST_OUTOPTION)$@ $< - # Compute the linker flags that programs linking against SpiderMonkey should # pass to get SpiderMonkey and its dependencies, beyond just the -L and -l # for the SpiderMonkey library itself. diff --git a/js/src/configure.in b/js/src/configure.in index 8d762221839..e2b932d43c0 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -2718,6 +2718,11 @@ fi MOZ_ALIGN_OF_TYPE(JS_ALIGN_OF_POINTER, void*, 2 4 8 16) MOZ_SIZE_OF_TYPE(JS_BYTES_PER_DOUBLE, double, 6 8 10 12 14) +AC_CHECK_HEADERS(endian.h) +if test "$ac_cv_header_endian_h" = yes; then + AC_DEFINE(JS_HAVE_ENDIAN_H) +fi + dnl Check for int16_t, int32_t, int64_t, int64, uint, uint_t, and uint16_t. dnl ======================================================== AC_MSG_CHECKING(for int16_t) diff --git a/js/src/js-config.h.in b/js/src/js-config.h.in index e3a5f74f52c..f3c7cbe602c 100644 --- a/js/src/js-config.h.in +++ b/js/src/js-config.h.in @@ -59,6 +59,10 @@ useable. See jstypes.h and jsstdint.h. */ #undef JS_HAVE_STDINT_H +/* Define to 1 if the header is present and + useable. See jscpucfg.h. */ +#undef JS_HAVE_ENDIAN_H + /* Define to 1 if the defines int8_t, etc. */ #undef JS_SYS_TYPES_H_DEFINES_EXACT_SIZE_TYPES diff --git a/js/src/jscpucfg.cpp b/js/src/jscpucfg.cpp deleted file mode 100644 index 028def676f5..00000000000 --- a/js/src/jscpucfg.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Roland Mainz - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Generate CPU-specific bit-size and similar #defines. - */ -#include -#include - -#if defined(CROSS_COMPILE) && !defined(FORCE_BIG_ENDIAN) && !defined(FORCE_LITTLE_ENDIAN) -#include -#endif - -/************************************************************************/ - -int main(int argc, char **argv) -{ - printf("#ifndef js_cpucfg___\n"); - printf("#define js_cpucfg___\n\n"); - - printf("/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n"); - -#ifdef CROSS_COMPILE -#if defined(__APPLE__) && !defined(FORCE_BIG_ENDIAN) && !defined(FORCE_LITTLE_ENDIAN) - /* - * Darwin NSPR uses the same MDCPUCFG (_darwin.cfg) for multiple - * processors, and determines which processor to configure for based - * on compiler predefined macros. We do the same thing here. - */ - printf("#ifdef __LITTLE_ENDIAN__\n"); - printf("#define IS_LITTLE_ENDIAN 1\n"); - printf("#undef IS_BIG_ENDIAN\n"); - printf("#else\n"); - printf("#undef IS_LITTLE_ENDIAN\n"); - printf("#define IS_BIG_ENDIAN 1\n"); - printf("#endif\n\n"); -#elif defined(IS_LITTLE_ENDIAN) || defined(FORCE_LITTLE_ENDIAN) - printf("#define IS_LITTLE_ENDIAN 1\n"); - printf("#undef IS_BIG_ENDIAN\n\n"); -#elif defined(IS_BIG_ENDIAN) || defined(FORCE_BIG_ENDIAN) - printf("#undef IS_LITTLE_ENDIAN\n"); - printf("#define IS_BIG_ENDIAN 1\n\n"); -#else -#error "Endianess not defined." -#endif - -#else - - /* - * We don't handle PDP-endian or similar orders: if a short is big-endian, - * so must int and long be big-endian for us to generate the IS_BIG_ENDIAN - * #define and the IS_LITTLE_ENDIAN #undef. - */ - { - int big_endian = 0, little_endian = 0, ntests = 0; - - if (sizeof(short) == 2) { - /* force |volatile| here to get rid of any compiler optimisations - * (var in register etc.) which may be appiled to |auto| vars - - * even those in |union|s... - * (|static| is used to get the same functionality for compilers - * which do not honor |volatile|...). - */ - volatile static union { - short i; - char c[2]; - } u; - - u.i = 0x0102; - big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02); - little_endian += (u.c[0] == 0x02 && u.c[1] == 0x01); - ntests++; - } - - if (sizeof(int) == 4) { - /* force |volatile| here ... */ - volatile static union { - int i; - char c[4]; - } u; - - u.i = 0x01020304; - big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02 && - u.c[2] == 0x03 && u.c[3] == 0x04); - little_endian += (u.c[0] == 0x04 && u.c[1] == 0x03 && - u.c[2] == 0x02 && u.c[3] == 0x01); - ntests++; - } - - if (sizeof(long) == 8) { - /* force |volatile| here ... */ - volatile static union { - long i; - char c[8]; - } u; - - /* - * Write this as portably as possible: avoid 0x0102030405060708L - * and <<= 32. - */ - u.i = 0x01020304; - u.i <<= 16, u.i <<= 16; - u.i |= 0x05060708; - big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02 && - u.c[2] == 0x03 && u.c[3] == 0x04 && - u.c[4] == 0x05 && u.c[5] == 0x06 && - u.c[6] == 0x07 && u.c[7] == 0x08); - little_endian += (u.c[0] == 0x08 && u.c[1] == 0x07 && - u.c[2] == 0x06 && u.c[3] == 0x05 && - u.c[4] == 0x04 && u.c[5] == 0x03 && - u.c[6] == 0x02 && u.c[7] == 0x01); - ntests++; - } - - if (big_endian && big_endian == ntests) { - printf("#undef IS_LITTLE_ENDIAN\n"); - printf("#define IS_BIG_ENDIAN 1\n\n"); - } else if (little_endian && little_endian == ntests) { - printf("#define IS_LITTLE_ENDIAN 1\n"); - printf("#undef IS_BIG_ENDIAN\n\n"); - } else { - fprintf(stderr, "%s: unknown byte order" - "(big_endian=%d, little_endian=%d, ntests=%d)!\n", - argv[0], big_endian, little_endian, ntests); - return EXIT_FAILURE; - } - } - -#endif /* CROSS_COMPILE */ - - // PA-RISC is the only platform we try to support on which the stack - // grows towards higher addresses. Trying to detect it here has - // historically led to portability problems, which aren't worth it - // given the near consensus on stack growth direction. - printf("#ifdef __hppa\n" - "# define JS_STACK_GROWTH_DIRECTION (1)\n" - "#else\n" - "# define JS_STACK_GROWTH_DIRECTION (-1)\n" - "#endif\n"); - - printf("#endif /* js_cpucfg___ */\n"); - - return EXIT_SUCCESS; -} - diff --git a/js/src/jscpucfg.h b/js/src/jscpucfg.h index 1ad122bad86..f40d992e26d 100644 --- a/js/src/jscpucfg.h +++ b/js/src/jscpucfg.h @@ -68,12 +68,32 @@ #define JS_BITS_PER_WORD_LOG2 5 #define JS_ALIGN_OF_POINTER 4L -#else +#elif defined(__APPLE__) +#if __LITTLE_ENDIAN__ +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#elif __BIG_ENDIAN__ +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#endif -#error "This file is supposed to be auto-generated on most platforms, but the" -#error "static version for Windows and OS2 platforms is being used." -#error "Something's probably wrong with paths/headers/dependencies/Makefiles." +#elif defined(JS_HAVE_ENDIAN_H) +#include +#if defined(__BYTE_ORDER) +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#endif +#else /* !defined(__BYTE_ORDER) */ +#error "endian.h does not define __BYTE_ORDER. Cannot determine endianness." +#endif + +#else /* !defined(HAVE_ENDIAN_H) */ +#error "Cannot determine endianness of your platform. Please add support to jscpucfg.h." #endif #ifndef JS_STACK_GROWTH_DIRECTION diff --git a/js/src/jstypes.h b/js/src/jstypes.h index 994e5bd2e1a..51571604c04 100644 --- a/js/src/jstypes.h +++ b/js/src/jstypes.h @@ -290,11 +290,7 @@ #define JS_MIN(x,y) ((x)<(y)?(x):(y)) #define JS_MAX(x,y) ((x)>(y)?(x):(y)) -#ifdef _MSC_VER -# include "jscpucfg.h" /* We can't auto-detect MSVC configuration */ -#else -# include "jsautocfg.h" /* Use auto-detected configuration */ -#endif +#include "jscpucfg.h" /* * Define JS_64BIT iff we are building in an environment with 64-bit From 8b337d3fd6b3758c9c57656a2598d6da144a2c7e Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 19 Oct 2011 16:45:12 -0500 Subject: [PATCH 02/42] Bug 695907 - Use qualified C++ definitions in js/src/frontend instead of wrapping whole .cpp files in namespace js {...}. r=luke. --HG-- extra : rebase_source : 8fca165f696344e6e269cee06871d87259386172 --- js/src/frontend/BytecodeCompiler.cpp | 4 -- js/src/frontend/BytecodeGenerator.cpp | 82 ++++++++++----------------- js/src/frontend/BytecodeGenerator.h | 3 - js/src/frontend/FoldConstants.cpp | 6 +- js/src/frontend/ParseNode.cpp | 10 +--- js/src/frontend/Parser.cpp | 12 +--- js/src/frontend/TokenStream.cpp | 16 ++---- js/src/jsreflect.cpp | 2 +- 8 files changed, 41 insertions(+), 94 deletions(-) diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index a4ae8625525..e37ec809762 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -51,8 +51,6 @@ using namespace js; using namespace js::frontend; -namespace js { - /* * Compile a top-level script. */ @@ -476,5 +474,3 @@ BytecodeCompiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipa return pn != NULL; } - -} /* namespace js */ diff --git a/js/src/frontend/BytecodeGenerator.cpp b/js/src/frontend/BytecodeGenerator.cpp index d5b1c895850..01278e6c995 100644 --- a/js/src/frontend/BytecodeGenerator.cpp +++ b/js/src/frontend/BytecodeGenerator.cpp @@ -93,9 +93,6 @@ using namespace js::frontend; extern uint8 js_opcode2extra[]; #endif -namespace js { -namespace frontend { - static JSBool NewTryNote(JSContext *cx, CodeGenerator *cg, JSTryNoteKind kind, uintN stackDepth, size_t start, size_t end); @@ -106,7 +103,8 @@ EmitIndexOp(JSContext *cx, JSOp op, uintN index, CodeGenerator *cg, JSOp *psuffi static JSBool EmitLeaveBlock(JSContext *cx, CodeGenerator *cg, JSOp op, ObjectBox *box); -} /* namespace frontend */ +static JSBool +SetSrcNoteOffset(JSContext *cx, CodeGenerator *cg, uintN index, uintN which, ptrdiff_t offset); void TreeContext::trace(JSTracer *trc) @@ -162,8 +160,6 @@ CodeGenerator::~CodeGenerator() cx->free_(spanDeps); } -namespace frontend { - static ptrdiff_t EmitCheck(JSContext *cx, CodeGenerator *cg, ptrdiff_t delta) { @@ -269,7 +265,7 @@ UpdateDecomposeLength(CodeGenerator *cg, uintN start) } ptrdiff_t -Emit1(JSContext *cx, CodeGenerator *cg, JSOp op) +frontend::Emit1(JSContext *cx, CodeGenerator *cg, JSOp op) { ptrdiff_t offset = EmitCheck(cx, cg, 1); @@ -281,7 +277,7 @@ Emit1(JSContext *cx, CodeGenerator *cg, JSOp op) } ptrdiff_t -Emit2(JSContext *cx, CodeGenerator *cg, JSOp op, jsbytecode op1) +frontend::Emit2(JSContext *cx, CodeGenerator *cg, JSOp op, jsbytecode op1) { ptrdiff_t offset = EmitCheck(cx, cg, 2); @@ -296,8 +292,8 @@ Emit2(JSContext *cx, CodeGenerator *cg, JSOp op, jsbytecode op1) } ptrdiff_t -Emit3(JSContext *cx, CodeGenerator *cg, JSOp op, jsbytecode op1, - jsbytecode op2) +frontend::Emit3(JSContext *cx, CodeGenerator *cg, JSOp op, jsbytecode op1, + jsbytecode op2) { ptrdiff_t offset = EmitCheck(cx, cg, 3); @@ -313,7 +309,7 @@ Emit3(JSContext *cx, CodeGenerator *cg, JSOp op, jsbytecode op1, } ptrdiff_t -Emit5(JSContext *cx, CodeGenerator *cg, JSOp op, uint16 op1, uint16 op2) +frontend::Emit5(JSContext *cx, CodeGenerator *cg, JSOp op, uint16 op1, uint16 op2) { ptrdiff_t offset = EmitCheck(cx, cg, 5); @@ -331,7 +327,7 @@ Emit5(JSContext *cx, CodeGenerator *cg, JSOp op, uint16 op1, uint16 op2) } ptrdiff_t -EmitN(JSContext *cx, CodeGenerator *cg, JSOp op, size_t extra) +frontend::EmitN(JSContext *cx, CodeGenerator *cg, JSOp op, size_t extra) { ptrdiff_t length = 1 + (ptrdiff_t)extra; ptrdiff_t offset = EmitCheck(cx, cg, length); @@ -558,7 +554,8 @@ AddJumpTarget(AddJumpTargetArgs *args, JumpTarget **jtp) } #ifdef DEBUG_brendan -static int AVLCheck(JumpTarget *jt) +static int +AVLCheck(JumpTarget *jt) { int lh, rh; @@ -1267,7 +1264,7 @@ GetJumpOffset(CodeGenerator *cg, jsbytecode *pc) } JSBool -SetJumpOffset(JSContext *cx, CodeGenerator *cg, jsbytecode *pc, ptrdiff_t off) +frontend::SetJumpOffset(JSContext *cx, CodeGenerator *cg, jsbytecode *pc, ptrdiff_t off) { if (!cg->spanDeps) { if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) { @@ -1282,8 +1279,6 @@ SetJumpOffset(JSContext *cx, CodeGenerator *cg, jsbytecode *pc, ptrdiff_t off) return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off); } -} /* namespace frontend */ - bool TreeContext::inStatement(StmtType type) { @@ -1344,10 +1339,8 @@ TreeContext::skipSpansGenerator(unsigned skip) return false; } -namespace frontend { - bool -SetStaticLevel(TreeContext *tc, uintN staticLevel) +frontend::SetStaticLevel(TreeContext *tc, uintN staticLevel) { /* * This is a lot simpler than error-checking every UpvarCookie::set, and @@ -1363,7 +1356,7 @@ SetStaticLevel(TreeContext *tc, uintN staticLevel) } bool -GenerateBlockId(TreeContext *tc, uint32& blockid) +frontend::GenerateBlockId(TreeContext *tc, uint32& blockid) { if (tc->blockidGen == JS_BIT(20)) { JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL, @@ -1375,7 +1368,7 @@ GenerateBlockId(TreeContext *tc, uint32& blockid) } void -PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top) +frontend::PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top) { stmt->type = type; stmt->flags = 0; @@ -1394,7 +1387,7 @@ PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top) } void -PushBlockScope(TreeContext *tc, StmtInfo *stmt, ObjectBox *blockBox, ptrdiff_t top) +frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, ObjectBox *blockBox, ptrdiff_t top) { PushStatement(tc, stmt, STMT_BLOCK, top); stmt->flags |= SIF_SCOPE; @@ -1680,7 +1673,7 @@ BackPatch(JSContext *cx, CodeGenerator *cg, ptrdiff_t last, jsbytecode *target, } void -PopStatementTC(TreeContext *tc) +frontend::PopStatementTC(TreeContext *tc) { StmtInfo *stmt = tc->topStmt; tc->topStmt = stmt->down; @@ -1693,7 +1686,7 @@ PopStatementTC(TreeContext *tc) } JSBool -PopStatementCG(JSContext *cx, CodeGenerator *cg) +frontend::PopStatementCG(JSContext *cx, CodeGenerator *cg) { StmtInfo *stmt = cg->topStmt; if (!STMT_IS_TRYING(stmt) && @@ -1707,7 +1700,7 @@ PopStatementCG(JSContext *cx, CodeGenerator *cg) } JSBool -DefineCompileTimeConstant(JSContext *cx, CodeGenerator *cg, JSAtom *atom, ParseNode *pn) +frontend::DefineCompileTimeConstant(JSContext *cx, CodeGenerator *cg, JSAtom *atom, ParseNode *pn) { /* XXX just do numbers for now */ if (pn->isKind(TOK_NUMBER)) { @@ -1718,7 +1711,7 @@ DefineCompileTimeConstant(JSContext *cx, CodeGenerator *cg, JSAtom *atom, ParseN } StmtInfo * -LexicalLookup(TreeContext *tc, JSAtom *atom, jsint *slotp, StmtInfo *stmt) +frontend::LexicalLookup(TreeContext *tc, JSAtom *atom, jsint *slotp, StmtInfo *stmt) { if (!stmt) stmt = tc->topScopeStmt; @@ -1960,16 +1953,12 @@ EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index, CodeGenerator * return bigSuffix == JSOP_NOP || Emit1(cx, cg, bigSuffix) >= 0; } -} /* namespace frontend */ - bool CodeGenerator::shouldNoteClosedName(ParseNode *pn) { return !callsEval() && pn->isDefn() && pn->isClosed(); } -namespace frontend { - /* * Adjust the slot for a block local to account for the number of variables * that share the same index space with locals. Due to the incremental code @@ -2523,8 +2512,6 @@ BindNameToSlot(JSContext *cx, CodeGenerator *cg, ParseNode *pn) return JS_TRUE; } -} /* namespace frontend */ - bool CodeGenerator::addGlobalUse(JSAtom *atom, uint32 slot, UpvarCookie *cookie) { @@ -2559,8 +2546,6 @@ CodeGenerator::addGlobalUse(JSAtom *atom, uint32 slot, UpvarCookie *cookie) return globalMap->add(p, atom, globalUseIndex); } -namespace frontend { - /* * If pn contains a useful expression, return true with *answer set to true. * If pn contains a useless expression, return true with *answer set to false. @@ -3919,7 +3904,7 @@ bad: } JSBool -EmitFunctionScript(JSContext *cx, CodeGenerator *cg, ParseNode *body) +frontend::EmitFunctionScript(JSContext *cx, CodeGenerator *cg, ParseNode *body) { /* * The decompiler has assumptions about what may occur immediately after @@ -4815,8 +4800,6 @@ EmitEndInit(JSContext *cx, CodeGenerator *cg, uint32 count) return Emit1(cx, cg, JSOP_ENDINIT) >= 0; } -} /* namespace frontend */ - bool ParseNode::getConstantValue(JSContext *cx, bool strictChecks, Value *vp) { @@ -4908,8 +4891,6 @@ ParseNode::getConstantValue(JSContext *cx, bool strictChecks, Value *vp) return false; } -namespace frontend { - static bool EmitSingletonInitialiser(JSContext *cx, CodeGenerator *cg, ParseNode *pn) { @@ -5845,7 +5826,7 @@ EmitFor(JSContext *cx, CodeGenerator *cg, ParseNode *pn, ptrdiff_t top) } JSBool -EmitTree(JSContext *cx, CodeGenerator *cg, ParseNode *pn) +frontend::EmitTree(JSContext *cx, CodeGenerator *cg, ParseNode *pn) { JSBool useful, wantval; StmtInfo stmtInfo; @@ -7439,7 +7420,7 @@ AllocSrcNote(JSContext *cx, CodeGenerator *cg) } intN -NewSrcNote(JSContext *cx, CodeGenerator *cg, SrcNoteType type) +frontend::NewSrcNote(JSContext *cx, CodeGenerator *cg, SrcNoteType type) { intN index, n; jssrcnote *sn; @@ -7487,7 +7468,7 @@ NewSrcNote(JSContext *cx, CodeGenerator *cg, SrcNoteType type) } intN -NewSrcNote2(JSContext *cx, CodeGenerator *cg, SrcNoteType type, ptrdiff_t offset) +frontend::NewSrcNote2(JSContext *cx, CodeGenerator *cg, SrcNoteType type, ptrdiff_t offset) { intN index; @@ -7500,7 +7481,7 @@ NewSrcNote2(JSContext *cx, CodeGenerator *cg, SrcNoteType type, ptrdiff_t offset } intN -NewSrcNote3(JSContext *cx, CodeGenerator *cg, SrcNoteType type, ptrdiff_t offset1, +frontend::NewSrcNote3(JSContext *cx, CodeGenerator *cg, SrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2) { intN index; @@ -7530,7 +7511,7 @@ GrowSrcNotes(JSContext *cx, CodeGenerator *cg) } jssrcnote * -AddToSrcNoteDelta(JSContext *cx, CodeGenerator *cg, jssrcnote *sn, ptrdiff_t delta) +frontend::AddToSrcNoteDelta(JSContext *cx, CodeGenerator *cg, jssrcnote *sn, ptrdiff_t delta) { ptrdiff_t base, limit, newdelta, diff; intN index; @@ -7563,7 +7544,7 @@ AddToSrcNoteDelta(JSContext *cx, CodeGenerator *cg, jssrcnote *sn, ptrdiff_t del return sn; } -JSBool +static JSBool SetSrcNoteOffset(JSContext *cx, CodeGenerator *cg, uintN index, uintN which, ptrdiff_t offset) { jssrcnote *sn; @@ -7622,7 +7603,8 @@ SetSrcNoteOffset(JSContext *cx, CodeGenerator *cg, uintN index, uintN which, ptr #define NBINS 10 static uint32 hist[NBINS]; -static void DumpSrcNoteSizeHist() +static void +DumpSrcNoteSizeHist() { static FILE *fp; int i, n; @@ -7652,7 +7634,7 @@ static void DumpSrcNoteSizeHist() * CORRESPONDING CHANGES! */ JSBool -FinishTakingSrcNotes(JSContext *cx, CodeGenerator *cg, jssrcnote *notes) +frontend::FinishTakingSrcNotes(JSContext *cx, CodeGenerator *cg, jssrcnote *notes) { uintN prologCount, mainCount, totalCount; ptrdiff_t offset, delta; @@ -7733,7 +7715,7 @@ NewTryNote(JSContext *cx, CodeGenerator *cg, JSTryNoteKind kind, uintN stackDept } void -FinishTakingTryNotes(CodeGenerator *cg, JSTryNoteArray *array) +frontend::FinishTakingTryNotes(CodeGenerator *cg, JSTryNoteArray *array) { TryNode *tryNode; JSTryNote *tn; @@ -7747,8 +7729,6 @@ FinishTakingTryNotes(CodeGenerator *cg, JSTryNoteArray *array) JS_ASSERT(tn == array->vector); } -} /* namespace frontend */ - /* * Find the index of the given object for code generator. * @@ -7827,8 +7807,6 @@ GCConstList::finish(JSConstArray *array) *dst = *src; } -} /* namespace js */ - /* * We should try to get rid of offsetBias (always 0 or 1, where 1 is * JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR and SRC_DECL. diff --git a/js/src/frontend/BytecodeGenerator.h b/js/src/frontend/BytecodeGenerator.h index d790463c300..fcf515837a4 100644 --- a/js/src/frontend/BytecodeGenerator.h +++ b/js/src/frontend/BytecodeGenerator.h @@ -1077,9 +1077,6 @@ NewSrcNote3(JSContext *cx, CodeGenerator *cg, SrcNoteType type, ptrdiff_t offset jssrcnote * AddToSrcNoteDelta(JSContext *cx, CodeGenerator *cg, jssrcnote *sn, ptrdiff_t delta); -JSBool -SetSrcNoteOffset(JSContext *cx, CodeGenerator *cg, uintN index, uintN which, ptrdiff_t offset); - /* * Finish taking source notes in cx's notePool, copying final notes to the new * stable store allocated by the caller and passed in via notes. Return false diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index c09fb379280..c260ebbfa56 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -434,10 +434,8 @@ Boolish(ParseNode *pn) } } -namespace js { - bool -FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond) +js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond) { ParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL; @@ -908,5 +906,3 @@ FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond) return true; } - -} /* namespace js */ diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 1147a25c193..d7b49c67799 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -56,8 +56,6 @@ JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses)); #undef pn_offsetof -namespace js { - void ParseNode::become(ParseNode *pn2) { @@ -465,14 +463,10 @@ NameNode::create(JSAtom *atom, TreeContext *tc) return (NameNode *)pn; } -} /* namespace js */ - const char js_argument_str[] = "argument"; const char js_variable_str[] = "variable"; const char js_unknown_str[] = "unknown"; -namespace js { - const char * Definition::kindString(Kind kind) { @@ -602,7 +596,7 @@ CloneParseTree(ParseNode *opn, TreeContext *tc) * the original tree. */ ParseNode * -CloneLeftHandSide(ParseNode *opn, TreeContext *tc) +js::CloneLeftHandSide(ParseNode *opn, TreeContext *tc) { ParseNode *pn = tc->parser->new_(opn->getKind(), opn->getOp(), opn->getArity(), opn->pn_pos); @@ -670,5 +664,3 @@ CloneLeftHandSide(ParseNode *opn, TreeContext *tc) } return pn; } - -} /* namespace js */ diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 5c1fa5e660d..28e06cf62d9 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -586,8 +586,6 @@ ReportBadParameter(JSContext *cx, TreeContext *tc, JSAtom *name, uintN errorNumb ReportStrictModeError(cx, TS(tc->parser), tc, dn, errorNumber, bytes.ptr()); } -namespace js { - /* * In strict mode code, all parameter names must be distinct, must not be * strict mode reserved keywords, and must not be 'eval' or 'arguments'. We @@ -595,7 +593,7 @@ namespace js { * function's body may turn on strict mode for the function head. */ bool -CheckStrictParameters(JSContext *cx, TreeContext *tc) +js::CheckStrictParameters(JSContext *cx, TreeContext *tc) { JS_ASSERT(tc->inFunction()); @@ -649,8 +647,6 @@ CheckStrictParameters(JSContext *cx, TreeContext *tc) return true; } -} /* namespace js */ - ParseNode * Parser::functionBody() { @@ -865,10 +861,8 @@ MakeDefIntoUse(Definition *dn, ParseNode *pn, JSAtom *atom, TreeContext *tc) return dn; } -namespace js { - bool -DefineArg(ParseNode *pn, JSAtom *atom, uintN i, TreeContext *tc) +js::DefineArg(ParseNode *pn, JSAtom *atom, uintN i, TreeContext *tc) { ParseNode *argpn, *argsbody; @@ -909,8 +903,6 @@ DefineArg(ParseNode *pn, JSAtom *atom, uintN i, TreeContext *tc) return true; } -} /* namespace js */ - /* * Parameter block types for the several Binder functions. We use a common * helper function signature in order to share code among destructuring and diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index d3687a19ef1..3bd0c2a976a 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -91,10 +91,8 @@ static const KeywordInfo keywords[] = { #undef JS_KEYWORD }; -namespace js { - const KeywordInfo * -FindKeyword(const jschar *s, size_t length) +js::FindKeyword(const jschar *s, size_t length) { JS_ASSERT(length != 0); @@ -131,7 +129,7 @@ FindKeyword(const jschar *s, size_t length) } JSBool -IsIdentifier(JSLinearString *str) +js::IsIdentifier(JSLinearString *str) { const jschar *chars = str->chars(); size_t length = str->length(); @@ -548,8 +546,8 @@ TokenStream::reportCompileErrorNumberVA(ParseNode *pn, uintN flags, uintN errorN } bool -ReportStrictModeError(JSContext *cx, TokenStream *ts, TreeContext *tc, ParseNode *pn, - uintN errorNumber, ...) +js::ReportStrictModeError(JSContext *cx, TokenStream *ts, TreeContext *tc, ParseNode *pn, + uintN errorNumber, ...) { JS_ASSERT(ts || tc); JS_ASSERT(cx == ts->getContext()); @@ -573,8 +571,8 @@ ReportStrictModeError(JSContext *cx, TokenStream *ts, TreeContext *tc, ParseNode } bool -ReportCompileErrorNumber(JSContext *cx, TokenStream *ts, ParseNode *pn, uintN flags, - uintN errorNumber, ...) +js::ReportCompileErrorNumber(JSContext *cx, TokenStream *ts, ParseNode *pn, uintN flags, + uintN errorNumber, ...) { va_list ap; @@ -2128,8 +2126,6 @@ TokenStream::getTokenInternal() return TOK_ERROR; } -} /* namespace js */ - JS_FRIEND_API(int) js_fgets(char *buf, int size, FILE *file) { diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index 397a8ad9040..47f66e45680 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -2354,7 +2354,7 @@ ASTSerializer::comprehension(ParseNode *pn, Value *dst) return false; next = next->pn_kid2; } else if (next->isKind(TOK_LC) && next->pn_count == 0) { - /* js_FoldConstants optimized away the push. */ + /* FoldConstants optimized away the push. */ NodeVector empty(cx); return builder.arrayExpression(empty, &pn->pn_pos, dst); } From 08b8a0bee5797dc4f15e6837c992d17370350b32 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Mon, 24 Oct 2011 10:51:52 -0500 Subject: [PATCH 03/42] Report an error when trying to watch an E4X property with a qualified name, rather than set a watchpoint that will not hit reliably. Bug 691746, r=jimb. --- js/src/js.msg | 1 + js/src/jsdbgapi.cpp | 5 ++++- js/src/jswatchpoint.cpp | 2 ++ js/src/tests/js1_8_5/extensions/jstests.list | 1 + js/src/tests/js1_8_5/extensions/regress-691746.js | 11 +++++++++++ 5 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 js/src/tests/js1_8_5/extensions/regress-691746.js diff --git a/js/src/js.msg b/js/src/js.msg index 107e4f96de5..f02bd06ab8e 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -370,3 +370,4 @@ MSG_DEF(JSMSG_DEBUG_BAD_LINE, 283, 0, JSEXN_TYPEERR, "invalid line numbe MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGING, 284, 0, JSEXN_ERR, "can't set breakpoint: script global is not a debuggee") MSG_DEF(JSMSG_DEBUG_COMPARTMENT_MISMATCH, 285, 2, JSEXN_TYPEERR, "{0}: descriptor .{1} property is an object in a different compartment than the target object") MSG_DEF(JSMSG_DEBUG_NOT_SCRIPT_FRAME, 286, 0, JSEXN_ERR, "stack frame is not running JavaScript code") +MSG_DEF(JSMSG_CANT_WATCH_PROP, 287, 0, JSEXN_TYPEERR, "properties whose names are objects can't be watched") diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 8ea4addfa0a..e00c25ba75f 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -297,6 +297,9 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id, AutoValueRooter idroot(cx); if (JSID_IS_INT(id)) { propid = id; + } else if (JSID_IS_OBJECT(id)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH_PROP); + return false; } else { if (!js_ValueToStringId(cx, IdToValue(id), &propid)) return false; @@ -328,7 +331,7 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id, } cx->compartment->watchpointMap = wpmap; } - return wpmap->watch(cx, obj, id, handler, closure); + return wpmap->watch(cx, obj, propid, handler, closure); } JS_PUBLIC_API(JSBool) diff --git a/js/src/jswatchpoint.cpp b/js/src/jswatchpoint.cpp index 05c2b3d94a9..9c118a9f772 100644 --- a/js/src/jswatchpoint.cpp +++ b/js/src/jswatchpoint.cpp @@ -84,6 +84,8 @@ WatchpointMap::watch(JSContext *cx, JSObject *obj, jsid id, JSWatchPointHandler handler, JSObject *closure) { JS_ASSERT(id == js_CheckForStringIndex(id)); + JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id)); + obj->setWatched(cx); Watchpoint w; w.handler = handler; diff --git a/js/src/tests/js1_8_5/extensions/jstests.list b/js/src/tests/js1_8_5/extensions/jstests.list index f3d5dd301c6..7129cc9ecc3 100644 --- a/js/src/tests/js1_8_5/extensions/jstests.list +++ b/js/src/tests/js1_8_5/extensions/jstests.list @@ -58,3 +58,4 @@ require-or(debugMode,skip) script regress-672804-2.js require-or(debugMode,skip) script regress-672804-3.js skip-if(!xulRuntime.shell) script regress-677589.js script regress-677924.js +script regress-691746.js diff --git a/js/src/tests/js1_8_5/extensions/regress-691746.js b/js/src/tests/js1_8_5/extensions/regress-691746.js new file mode 100644 index 00000000000..26f732d07d6 --- /dev/null +++ b/js/src/tests/js1_8_5/extensions/regress-691746.js @@ -0,0 +1,11 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var obj = {}; +try { + obj.watch(QName(), function () {}); +} catch (exc) { +} +gc(); + +reportCompare(0, 0, 'ok'); From 1803c53583db3db3dfbca9d7640761eeea661b8d Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Thu, 4 Aug 2011 03:32:57 -0400 Subject: [PATCH 04/42] bug 633239 - event loop responsiveness for Android r=dougt --- configure.in | 1 + embedding/android/GeckoAppShell.java | 19 ++++++++ widget/src/android/AndroidBridge.cpp | 73 ++++++++++++++++++++++++++++ widget/src/android/AndroidBridge.h | 3 ++ 4 files changed, 96 insertions(+) diff --git a/configure.in b/configure.in index f50e1b0feb9..b49880e3853 100644 --- a/configure.in +++ b/configure.in @@ -4648,6 +4648,7 @@ cairo-android) TK_LIBS='$(MOZ_CAIRO_LIBS)' MOZ_WEBGL=1 MOZ_PDF_PRINTING=1 + MOZ_INSTRUMENT_EVENT_LOOP=1 ;; esac diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index c54c4d65556..b50c3fb1c36 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -1567,4 +1567,23 @@ public class GeckoAppShell sCameraBuffer = null; } } + + + static SynchronousQueue sTracerQueue = new SynchronousQueue(); + public static void fireAndWaitForTracerEvent() { + getMainHandler().post(new Runnable() { + public void run() { + try { + sTracerQueue.put(new Date()); + } catch(InterruptedException ie) { + Log.w("GeckoAppShell", "exception firing tracer", ie); + } + } + }); + try { + sTracerQueue.take(); + } catch(InterruptedException ie) { + Log.w("GeckoAppShell", "exception firing tracer", ie); + } + } } diff --git a/widget/src/android/AndroidBridge.cpp b/widget/src/android/AndroidBridge.cpp index b74dd53afe5..5ff21448aeb 100644 --- a/widget/src/android/AndroidBridge.cpp +++ b/widget/src/android/AndroidBridge.cpp @@ -48,6 +48,7 @@ #include "nsOSHelperAppService.h" #include "nsWindow.h" #include "mozilla/Preferences.h" +#include "nsThreadUtils.h" #ifdef DEBUG #define ALOG_BRIDGE(args...) ALOG(args) @@ -146,6 +147,7 @@ AndroidBridge::Init(JNIEnv *jEnv, jScanMedia = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "scanMedia", "(Ljava/lang/String;Ljava/lang/String;)V"); jGetSystemColors = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getSystemColors", "()[I"); jGetIconForExtension = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getIconForExtension", "(Ljava/lang/String;I)[B"); + jFireAndWaitForTracerEvent = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "fireAndWaitForTracerEvent", "()V"); jCreateShortcut = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "createShortcut", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); jGetShowPasswordSetting = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getShowPasswordSetting", "()Z"); jPostToJavaThread = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "postToJavaThread", "(Z)V"); @@ -1010,6 +1012,77 @@ AndroidBridge::OpenGraphicsLibraries() } } +void +AndroidBridge::FireAndWaitForTracerEvent() { + mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, + jFireAndWaitForTracerEvent); +} + + +namespace mozilla { + class TracerRunnable : public nsRunnable{ + public: + TracerRunnable() { + mTracerLock = new Mutex("TracerRunnable"); + mTracerCondVar = new CondVar(*mTracerLock, "TracerRunnable"); + mMainThread = do_GetMainThread(); + + } + ~TracerRunnable() { + delete mTracerCondVar; + delete mTracerLock; + mTracerLock = nsnull; + mTracerCondVar = nsnull; + } + + virtual nsresult Run() { + MutexAutoLock lock(*mTracerLock); + if (!AndroidBridge::Bridge()) + return NS_OK; + + AndroidBridge::Bridge()->FireAndWaitForTracerEvent(); + mHasRun = PR_TRUE; + mTracerCondVar->Notify(); + return NS_OK; + } + + bool Fire() { + if (!mTracerLock || !mTracerCondVar) + return false; + MutexAutoLock lock(*mTracerLock); + mHasRun = PR_FALSE; + mMainThread->Dispatch(this, NS_DISPATCH_NORMAL); + while (!mHasRun) + mTracerCondVar->Wait(); + return true; + } + private: + Mutex* mTracerLock; + CondVar* mTracerCondVar; + PRBool mHasRun; + nsCOMPtr mMainThread; + + }; + nsCOMPtr sTracerRunnable; + + bool InitWidgetTracing() { + if (!sTracerRunnable) + sTracerRunnable = new TracerRunnable(); + return true; + } + + void CleanUpWidgetTracing() { + if (sTracerRunnable) + delete sTracerRunnable; + sTracerRunnable = nsnull; + } + + bool FireAndWaitForTracerEvent() { + if (sTracerRunnable) + return sTracerRunnable->Fire(); + return false; + } +} bool AndroidBridge::HasNativeBitmapAccess() { diff --git a/widget/src/android/AndroidBridge.h b/widget/src/android/AndroidBridge.h index 5eace06f99c..80be3b74511 100644 --- a/widget/src/android/AndroidBridge.h +++ b/widget/src/android/AndroidBridge.h @@ -213,6 +213,8 @@ public: bool GetShowPasswordSetting(); + void FireAndWaitForTracerEvent(); + struct AutoLocalJNIFrame { AutoLocalJNIFrame(int nEntries = 128) : mEntries(nEntries) { // Make sure there is enough space to store a local ref to the @@ -350,6 +352,7 @@ protected: jmethodID jScanMedia; jmethodID jGetSystemColors; jmethodID jGetIconForExtension; + jmethodID jFireAndWaitForTracerEvent; jmethodID jCreateShortcut; jmethodID jGetShowPasswordSetting; jmethodID jPostToJavaThread; From 425fc09a42ebb56810c09b922facdc8948035215 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Mon, 24 Oct 2011 18:13:53 +0200 Subject: [PATCH 05/42] Fixup for bug 694499 for pa-risc. r=ted --- js/src/jscpucfg.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/src/jscpucfg.h b/js/src/jscpucfg.h index f40d992e26d..ddd47bd40ce 100644 --- a/js/src/jscpucfg.h +++ b/js/src/jscpucfg.h @@ -97,7 +97,11 @@ #endif #ifndef JS_STACK_GROWTH_DIRECTION -#define JS_STACK_GROWTH_DIRECTION (-1) +#ifdef __hppa +# define JS_STACK_GROWTH_DIRECTION (1) +#else +# define JS_STACK_GROWTH_DIRECTION (-1) +#endif #endif #endif /* js_cpucfg___ */ From a765395a4aa90a74b5afd3b63d4ef317475d57f3 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Mon, 24 Oct 2011 19:11:15 +0200 Subject: [PATCH 06/42] Bug 642957 - Remove obsolete x|0 and x << 0 optimization. r=jandem --- js/src/methodjit/FastOps.cpp | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 210f383565a..08c01156468 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -187,26 +187,6 @@ mjit::Compiler::jsop_bitop(JSOp op) return; } - bool lhsIntOrDouble = !(lhs->isNotType(JSVAL_TYPE_DOUBLE) && - lhs->isNotType(JSVAL_TYPE_INT32)); - - /* Fast-path double to int conversion. */ - if (!lhs->isConstant() && rhs->isConstant() && lhsIntOrDouble && - rhs->isType(JSVAL_TYPE_INT32) && rhs->getValue().toInt32() == 0 && - (op == JSOP_BITOR || op == JSOP_LSH)) { - ensureInteger(lhs, Uses(2)); - RegisterID reg = frame.ownRegForData(lhs); - - stubcc.leave(); - OOL_STUBCALL(stub, REJOIN_FALLTHROUGH); - - frame.popn(2); - frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); - - stubcc.rejoin(Changes(1)); - return; - } - /* Convert a double RHS to integer if it's constant for the test below. */ if (rhs->isConstant() && rhs->getValue().isDouble()) rhs->convertConstantDoubleToInt32(cx); @@ -282,7 +262,7 @@ mjit::Compiler::jsop_bitop(JSOp op) masm.and32(Imm32(rhsInt), reg); else if (op == JSOP_BITXOR) masm.xor32(Imm32(rhsInt), reg); - else + else if (rhsInt != 0) masm.or32(Imm32(rhsInt), reg); } else if (frame.shouldAvoidDataRemat(rhs)) { Address rhsAddr = masm.payloadOf(frame.addressOf(rhs)); From c4f2ee1b35789b4e297cd240788c2899fb289f18 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Mon, 24 Oct 2011 13:18:50 -0400 Subject: [PATCH 07/42] [mq]: Backout changeset 78e519331f40 because of build bustage on android --- configure.in | 1 - embedding/android/GeckoAppShell.java | 19 -------- widget/src/android/AndroidBridge.cpp | 73 ---------------------------- widget/src/android/AndroidBridge.h | 3 -- 4 files changed, 96 deletions(-) diff --git a/configure.in b/configure.in index b49880e3853..f50e1b0feb9 100644 --- a/configure.in +++ b/configure.in @@ -4648,7 +4648,6 @@ cairo-android) TK_LIBS='$(MOZ_CAIRO_LIBS)' MOZ_WEBGL=1 MOZ_PDF_PRINTING=1 - MOZ_INSTRUMENT_EVENT_LOOP=1 ;; esac diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index b50c3fb1c36..c54c4d65556 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -1567,23 +1567,4 @@ public class GeckoAppShell sCameraBuffer = null; } } - - - static SynchronousQueue sTracerQueue = new SynchronousQueue(); - public static void fireAndWaitForTracerEvent() { - getMainHandler().post(new Runnable() { - public void run() { - try { - sTracerQueue.put(new Date()); - } catch(InterruptedException ie) { - Log.w("GeckoAppShell", "exception firing tracer", ie); - } - } - }); - try { - sTracerQueue.take(); - } catch(InterruptedException ie) { - Log.w("GeckoAppShell", "exception firing tracer", ie); - } - } } diff --git a/widget/src/android/AndroidBridge.cpp b/widget/src/android/AndroidBridge.cpp index 5ff21448aeb..b74dd53afe5 100644 --- a/widget/src/android/AndroidBridge.cpp +++ b/widget/src/android/AndroidBridge.cpp @@ -48,7 +48,6 @@ #include "nsOSHelperAppService.h" #include "nsWindow.h" #include "mozilla/Preferences.h" -#include "nsThreadUtils.h" #ifdef DEBUG #define ALOG_BRIDGE(args...) ALOG(args) @@ -147,7 +146,6 @@ AndroidBridge::Init(JNIEnv *jEnv, jScanMedia = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "scanMedia", "(Ljava/lang/String;Ljava/lang/String;)V"); jGetSystemColors = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getSystemColors", "()[I"); jGetIconForExtension = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getIconForExtension", "(Ljava/lang/String;I)[B"); - jFireAndWaitForTracerEvent = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "fireAndWaitForTracerEvent", "()V"); jCreateShortcut = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "createShortcut", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); jGetShowPasswordSetting = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getShowPasswordSetting", "()Z"); jPostToJavaThread = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "postToJavaThread", "(Z)V"); @@ -1012,77 +1010,6 @@ AndroidBridge::OpenGraphicsLibraries() } } -void -AndroidBridge::FireAndWaitForTracerEvent() { - mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, - jFireAndWaitForTracerEvent); -} - - -namespace mozilla { - class TracerRunnable : public nsRunnable{ - public: - TracerRunnable() { - mTracerLock = new Mutex("TracerRunnable"); - mTracerCondVar = new CondVar(*mTracerLock, "TracerRunnable"); - mMainThread = do_GetMainThread(); - - } - ~TracerRunnable() { - delete mTracerCondVar; - delete mTracerLock; - mTracerLock = nsnull; - mTracerCondVar = nsnull; - } - - virtual nsresult Run() { - MutexAutoLock lock(*mTracerLock); - if (!AndroidBridge::Bridge()) - return NS_OK; - - AndroidBridge::Bridge()->FireAndWaitForTracerEvent(); - mHasRun = PR_TRUE; - mTracerCondVar->Notify(); - return NS_OK; - } - - bool Fire() { - if (!mTracerLock || !mTracerCondVar) - return false; - MutexAutoLock lock(*mTracerLock); - mHasRun = PR_FALSE; - mMainThread->Dispatch(this, NS_DISPATCH_NORMAL); - while (!mHasRun) - mTracerCondVar->Wait(); - return true; - } - private: - Mutex* mTracerLock; - CondVar* mTracerCondVar; - PRBool mHasRun; - nsCOMPtr mMainThread; - - }; - nsCOMPtr sTracerRunnable; - - bool InitWidgetTracing() { - if (!sTracerRunnable) - sTracerRunnable = new TracerRunnable(); - return true; - } - - void CleanUpWidgetTracing() { - if (sTracerRunnable) - delete sTracerRunnable; - sTracerRunnable = nsnull; - } - - bool FireAndWaitForTracerEvent() { - if (sTracerRunnable) - return sTracerRunnable->Fire(); - return false; - } -} bool AndroidBridge::HasNativeBitmapAccess() { diff --git a/widget/src/android/AndroidBridge.h b/widget/src/android/AndroidBridge.h index 80be3b74511..5eace06f99c 100644 --- a/widget/src/android/AndroidBridge.h +++ b/widget/src/android/AndroidBridge.h @@ -213,8 +213,6 @@ public: bool GetShowPasswordSetting(); - void FireAndWaitForTracerEvent(); - struct AutoLocalJNIFrame { AutoLocalJNIFrame(int nEntries = 128) : mEntries(nEntries) { // Make sure there is enough space to store a local ref to the @@ -352,7 +350,6 @@ protected: jmethodID jScanMedia; jmethodID jGetSystemColors; jmethodID jGetIconForExtension; - jmethodID jFireAndWaitForTracerEvent; jmethodID jCreateShortcut; jmethodID jGetShowPasswordSetting; jmethodID jPostToJavaThread; From 604bf72c2b96e76eedd7ad1d4a1ed38e9b892e1e Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Thu, 6 Oct 2011 16:33:21 -0400 Subject: [PATCH 08/42] Bug 429592 part A - allow AnnotateCrashReport to be called from off the main thread in the chrome process, r?ted --HG-- extra : rebase_source : 542b5a9b0474360a4c380ac1fa978c3eeaa1810a --- toolkit/crashreporter/nsExceptionHandler.cpp | 31 ++++++++++++-------- toolkit/crashreporter/nsExceptionHandler.h | 5 ++++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp index 2ee223b5bc9..d7cdb402cde 100644 --- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -205,6 +205,7 @@ static const int kAvailableVirtualMemoryParameterLen = sizeof(kAvailableVirtualMemoryParameter)-1; // this holds additional data sent via the API +static Mutex* crashReporterAPILock; static AnnotationTable* crashReporterAPIData_Hash; static nsCString* crashReporterAPIData = nsnull; static nsCString* notesField = nsnull; @@ -617,6 +618,9 @@ nsresult SetExceptionHandler(nsILocalFile* aXREDirectory, crashReporterAPIData = new nsCString(); NS_ENSURE_TRUE(crashReporterAPIData, NS_ERROR_OUT_OF_MEMORY); + NS_ASSERTION(!crashReporterAPILock, "Shouldn't have a lock yet"); + crashReporterAPILock = new Mutex("crashReporterAPILock"); + crashReporterAPIData_Hash = new nsDataHashtable(); NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY); @@ -1037,20 +1041,17 @@ nsresult UnsetExceptionHandler() // do this here in the unlikely case that we succeeded in allocating // our strings but failed to allocate gExceptionHandler. - if (crashReporterAPIData_Hash) { - delete crashReporterAPIData_Hash; - crashReporterAPIData_Hash = nsnull; - } + delete crashReporterAPIData_Hash; + crashReporterAPIData_Hash = nsnull; - if (crashReporterAPIData) { - delete crashReporterAPIData; - crashReporterAPIData = nsnull; - } + delete crashReporterAPILock; + crashReporterAPILock = nsnull; - if (notesField) { - delete notesField; - notesField = nsnull; - } + delete crashReporterAPIData; + crashReporterAPIData = nsnull; + + delete notesField; + notesField = nsnull; if (crashReporterPath) { NS_Free(crashReporterPath); @@ -1179,6 +1180,10 @@ nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data) return rv; if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!NS_IsMainThread()) { + NS_ERROR("Cannot call AnnotateCrashReport in child processes from non-main thread."); + return NS_ERROR_FAILURE; + } PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter(); if (!reporter) { EnqueueDelayedNote(new DelayedNote(key, data)); @@ -1189,6 +1194,8 @@ nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data) return NS_OK; } + MutexAutoLock lock(*crashReporterAPILock); + rv = crashReporterAPIData_Hash->Put(key, escapedData); NS_ENSURE_SUCCESS(rv, rv); diff --git a/toolkit/crashreporter/nsExceptionHandler.h b/toolkit/crashreporter/nsExceptionHandler.h index 4cda5204bea..a8b5a9b9b74 100644 --- a/toolkit/crashreporter/nsExceptionHandler.h +++ b/toolkit/crashreporter/nsExceptionHandler.h @@ -64,7 +64,12 @@ bool GetServerURL(nsACString& aServerURL); nsresult SetServerURL(const nsACString& aServerURL); bool GetMinidumpPath(nsAString& aPath); nsresult SetMinidumpPath(const nsAString& aPath); + + +// AnnotateCrashReport may be called from any thread in a chrome process, +// but may only be called from the main thread in a content process. nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data); + nsresult AppendAppNotesToCrashReport(const nsACString& data); nsresult SetRestartArgs(int argc, char** argv); nsresult SetupExtraData(nsILocalFile* aAppDataDirectory, From 22af84cad97cb290c40896cdc7ef03e875ecae07 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 12 Oct 2011 13:52:26 -0400 Subject: [PATCH 09/42] Bug 429592 - Add a monitor thread for process hangs and crash by default if a chrome process doesn't end up back in the event loop for more than 30 seconds. By default this affects non-debug builds only. r=cjones/bent --HG-- extra : rebase_source : d75bbc917d9a07f2a5ec016d3c410ec4bcff67e9 --- modules/libpref/src/init/all.js | 8 + widget/src/windows/nsAppShell.cpp | 3 + xpcom/build/nsXPComInit.cpp | 12 +- xpcom/threads/HangMonitor.cpp | 236 ++++++++++++++++++++++++++++++ xpcom/threads/HangMonitor.h | 67 +++++++++ xpcom/threads/Makefile.in | 7 + xpcom/threads/nsThread.cpp | 24 ++- xpcom/threads/nsThread.h | 9 +- xpcom/threads/nsThreadManager.cpp | 6 +- 9 files changed, 351 insertions(+), 21 deletions(-) create mode 100644 xpcom/threads/HangMonitor.cpp create mode 100644 xpcom/threads/HangMonitor.h diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 6f73ab32c20..210affce8c9 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1470,6 +1470,14 @@ pref("editor.positioning.offset", 0); pref("dom.max_chrome_script_run_time", 20); pref("dom.max_script_run_time", 10); +// Hang monitor timeout after which we kill the browser, in seconds +// (0 is disabled) +#ifndef DEBUG +pref("hangmonitor.timeout", 30); +#else +pref("hangmonitor.timeout", 0); +#endif + #ifndef DEBUG // How long a plugin is allowed to process a synchronous IPC message // before we consider it "hung". diff --git a/widget/src/windows/nsAppShell.cpp b/widget/src/windows/nsAppShell.cpp index ac98bf8d571..cc3c06b9aea 100644 --- a/widget/src/windows/nsAppShell.cpp +++ b/widget/src/windows/nsAppShell.cpp @@ -46,6 +46,7 @@ #include "nsString.h" #include "nsIMM32Handler.h" #include "mozilla/widget/AudioSession.h" +#include "mozilla/HangMonitor.h" // For skidmark code #include @@ -342,11 +343,13 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) ::PostQuitMessage(msg.wParam); Exit(); } else { + mozilla::HangMonitor::NotifyActivity(); ::TranslateMessage(&msg); ::DispatchMessageW(&msg); } } else if (mayWait) { // Block and wait for any posted application message + mozilla::HangMonitor::Suspend(); ::WaitMessage(); } } while (!gotMessage && mayWait); diff --git a/xpcom/build/nsXPComInit.cpp b/xpcom/build/nsXPComInit.cpp index d8ad19a0d80..4150a32006c 100644 --- a/xpcom/build/nsXPComInit.cpp +++ b/xpcom/build/nsXPComInit.cpp @@ -139,6 +139,7 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **) #include "mozilla/Services.h" #include "mozilla/FunctionTimer.h" #include "mozilla/Omnijar.h" +#include "mozilla/HangMonitor.h" #include "nsChromeRegistry.h" #include "nsChromeProtocolHandler.h" @@ -530,6 +531,8 @@ NS_InitXPCOM2(nsIServiceManager* *result, mozilla::MapsMemoryReporter::Init(); + mozilla::HangMonitor::Startup(); + return NS_OK; } @@ -566,6 +569,9 @@ namespace mozilla { nsresult ShutdownXPCOM(nsIServiceManager* servMgr) { + // Make sure the hang monitor is enabled for shutdown. + HangMonitor::NotifyActivity(); + NS_ENSURE_STATE(NS_IsMainThread()); nsresult rv; @@ -623,6 +629,8 @@ ShutdownXPCOM(nsIServiceManager* servMgr) NS_ProcessPendingEvents(thread); + HangMonitor::NotifyActivity(); + // We save the "xpcom-shutdown-loaders" observers to notify after // the observerservice is gone. if (observerService) { @@ -732,7 +740,9 @@ ShutdownXPCOM(nsIServiceManager* servMgr) sExitManager = nsnull; } - mozilla::Omnijar::CleanUp(); + Omnijar::CleanUp(); + + HangMonitor::Shutdown(); NS_LogTerm(); diff --git a/xpcom/threads/HangMonitor.cpp b/xpcom/threads/HangMonitor.cpp new file mode 100644 index 00000000000..e5edece4383 --- /dev/null +++ b/xpcom/threads/HangMonitor.cpp @@ -0,0 +1,236 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Firefox. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "mozilla/HangMonitor.h" +#include "mozilla/Monitor.h" +#include "mozilla/Preferences.h" +#include "nsExceptionHandler.h" +#include "nsXULAppAPI.h" +#include "nsThreadUtils.h" + +#ifdef XP_WIN +#include +#endif + +namespace mozilla { namespace HangMonitor { + +/** + * A flag which may be set from within a debugger to disable the hang + * monitor. + */ +volatile bool gDebugDisableHangMonitor = false; + +const char kHangMonitorPrefName[] = "hangmonitor.timeout"; + +// Monitor protects gShutdown and gTimeout, but not gTimestamp which rely on +// being atomically set by the processor; synchronization doesn't really matter +// in this use case. +Monitor* gMonitor; + +// The timeout preference, in seconds. +PRInt32 gTimeout; + +PRThread* gThread; + +// Set when shutdown begins to signal the thread to exit immediately. +bool gShutdown; + +// The timestamp of the last event notification, or PR_INTERVAL_NO_WAIT if +// we're currently not processing events. +volatile PRIntervalTime gTimestamp; + +// PrefChangedFunc +int +PrefChanged(const char*, void*) +{ + PRInt32 newval = Preferences::GetInt(kHangMonitorPrefName); + MonitorAutoLock lock(*gMonitor); + if (newval != gTimeout) { + gTimeout = newval; + lock.Notify(); + } + + return 0; +} + +void +Crash() +{ + if (gDebugDisableHangMonitor) { + return; + } + +#ifdef XP_WIN + if (::IsDebuggerPresent()) { + return; + } +#endif + + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Hang"), + NS_LITERAL_CSTRING("1")); + + NS_RUNTIMEABORT("HangMonitor triggered"); +} + +void +ThreadMain(void*) +{ + MonitorAutoLock lock(*gMonitor); + + // In order to avoid issues with the hang monitor incorrectly triggering + // during a general system stop such as sleeping, the monitor thread must + // run twice to trigger hang protection. + PRIntervalTime lastTimestamp = 0; + int waitCount = 0; + + while (true) { + if (gShutdown) { + return; // Exit the thread + } + + // avoid rereading the volatile value in this loop + PRIntervalTime timestamp = gTimestamp; + + PRIntervalTime now = PR_IntervalNow(); + + if (timestamp != PR_INTERVAL_NO_WAIT && + now < timestamp) { + // 32-bit overflow, reset for another waiting period + timestamp = 1; // lowest legal PRInterval value + } + + if (timestamp != PR_INTERVAL_NO_WAIT && + timestamp == lastTimestamp && + gTimeout > 0) { + ++waitCount; + if (waitCount == 2) { + PRInt32 delay = + PRInt32(PR_IntervalToSeconds(now - timestamp)); + if (delay > gTimeout) { + MonitorAutoUnlock unlock(*gMonitor); + Crash(); + } + } + } + else { + lastTimestamp = timestamp; + waitCount = 0; + } + + PRIntervalTime timeout; + if (gTimeout <= 0) { + timeout = PR_INTERVAL_NO_TIMEOUT; + } + else { + timeout = PR_MillisecondsToInterval(gTimeout * 500); + } + lock.Wait(timeout); + } +} + +void +Startup() +{ + // The hang detector only runs in chrome processes. If you change this, + // you must also deal with the threadsafety of AnnotateCrashReport in + // non-chrome processes! + if (GeckoProcessType_Default != XRE_GetProcessType()) + return; + + NS_ASSERTION(!gMonitor, "Hang monitor already initialized"); + gMonitor = new Monitor("HangMonitor"); + + Preferences::RegisterCallback(PrefChanged, kHangMonitorPrefName, NULL); + PrefChanged(NULL, NULL); + + // Don't actually start measuring hangs until we hit the main event loop. + // This potentially misses a small class of really early startup hangs, + // but avoids dealing with some xpcshell tests and other situations which + // start XPCOM but don't ever start the event loop. + Suspend(); + + gThread = PR_CreateThread(PR_USER_THREAD, + ThreadMain, + NULL, PR_PRIORITY_LOW, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0); +} + +void +Shutdown() +{ + if (GeckoProcessType_Default != XRE_GetProcessType()) + return; + + NS_ASSERTION(gMonitor, "Hang monitor not started"); + + { // Scope the lock we're going to delete later + MonitorAutoLock lock(*gMonitor); + gShutdown = true; + lock.Notify(); + } + + // thread creation could theoretically fail + if (gThread) { + PR_JoinThread(gThread); + gThread = NULL; + } + + delete gMonitor; + gMonitor = NULL; +} + +void +NotifyActivity() +{ + NS_ASSERTION(NS_IsMainThread(), "HangMonitor::Notify called from off the main thread."); + + // This is not a locked activity because PRTimeStamp is a 32-bit quantity + // which can be read/written atomically, and we don't want to pay locking + // penalties here. + gTimestamp = PR_IntervalNow(); +} + +void +Suspend() +{ + NS_ASSERTION(NS_IsMainThread(), "HangMonitor::Suspend called from off the main thread."); + + // Because gTimestamp changes this resets the wait count. + gTimestamp = PR_INTERVAL_NO_WAIT; +} + +} } // namespace mozilla::HangMonitor diff --git a/xpcom/threads/HangMonitor.h b/xpcom/threads/HangMonitor.h new file mode 100644 index 00000000000..b6f859fe8ce --- /dev/null +++ b/xpcom/threads/HangMonitor.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Firefox. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_HangMonitor_h +#define mozilla_HangMonitor_h + +namespace mozilla { namespace HangMonitor { + +/** + * Start monitoring hangs. Should be called by the XPCOM startup process only. + */ +void Startup(); + +/** + * Stop monitoring hangs and join the thread. + */ +void Shutdown(); + +/** + * Notify the hang monitor of new activity which should reset its internal + * timer. + */ +void NotifyActivity(); + +/** + * Notify the hang monitor that the browser is now idle and no detection should + * be done. + */ +void Suspend(); + +} } // namespace mozilla::HangMonitor + +#endif // mozilla_HangMonitor_h diff --git a/xpcom/threads/Makefile.in b/xpcom/threads/Makefile.in index 82214d2ab78..915e7f23783 100644 --- a/xpcom/threads/Makefile.in +++ b/xpcom/threads/Makefile.in @@ -47,7 +47,9 @@ XPIDL_MODULE = xpcom_threads LIBRARY_NAME = xpcomthreads_s GRE_MODULE = 1 MOZILLA_INTERNAL_API = 1 +LIBXUL_LIBRARY = 1 +EXPORTS_NAMESPACES = mozilla CPPSRCS = \ nsEventQueue.cpp \ @@ -58,6 +60,7 @@ CPPSRCS = \ nsProcessCommon.cpp \ nsTimerImpl.cpp \ TimerThread.cpp \ + HangMonitor.cpp \ $(NULL) EXPORTS = \ @@ -66,6 +69,10 @@ EXPORTS = \ nsThreadUtilsInternal.h \ $(NULL) +EXPORTS_mozilla = \ + HangMonitor.h \ + $(NULL) + XPIDLSRCS = \ nsIEventTarget.idl \ nsIThread.idl \ diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 0f33f9a01b3..50214fa6dc6 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -45,6 +45,7 @@ #include "nsCOMPtr.h" #include "prlog.h" #include "nsThreadUtilsInternal.h" +#include "mozilla/HangMonitor.h" #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \ _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \ @@ -306,20 +307,7 @@ nsThread::ThreadFunc(void *arg) //----------------------------------------------------------------------------- -nsThread::nsThread() - : mLock("nsThread.mLock") - , mEvents(&mEventsRoot) - , mPriority(PRIORITY_NORMAL) - , mThread(nsnull) - , mRunningEvent(0) - , mStackSize(0) - , mShutdownContext(nsnull) - , mShutdownRequired(false) - , mEventsAreDoomed(false) -{ -} - -nsThread::nsThread(PRUint32 aStackSize) +nsThread::nsThread(MainThreadFlag aMainThread, PRUint32 aStackSize) : mLock("nsThread.mLock") , mEvents(&mEventsRoot) , mPriority(PRIORITY_NORMAL) @@ -329,6 +317,7 @@ nsThread::nsThread(PRUint32 aStackSize) , mShutdownContext(nsnull) , mShutdownRequired(false) , mEventsAreDoomed(false) + , mIsMainThread(aMainThread) { } @@ -585,6 +574,9 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result) NS_ENSURE_STATE(PR_GetCurrentThread() == mThread); + if (MAIN_THREAD == mIsMainThread && mayWait && !ShuttingDown()) + HangMonitor::Suspend(); + bool notifyGlobalObserver = (sGlobalObserver != nsnull); if (notifyGlobalObserver) sGlobalObserver->OnProcessNextEvent(this, mayWait && !ShuttingDown(), @@ -615,7 +607,7 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result) #ifdef NS_FUNCTION_TIMER char message[1024] = {'\0'}; - if (NS_IsMainThread()) { + if (MAIN_THREAD == mIsMainThread) { mozilla::FunctionTimer::ft_snprintf(message, sizeof(message), "@ Main Thread Event %p", (void*)event.get()); } @@ -628,6 +620,8 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result) if (event) { LOG(("THRD(%p) running [%p]\n", this, event.get())); + if (MAIN_THREAD == mIsMainThread) + HangMonitor::NotifyActivity(); event->Run(); } else if (mayWait) { NS_ASSERTION(ShuttingDown(), diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h index 750b33cce5f..83009a7a3be 100644 --- a/xpcom/threads/nsThread.h +++ b/xpcom/threads/nsThread.h @@ -57,8 +57,12 @@ public: NS_DECL_NSITHREADINTERNAL NS_DECL_NSISUPPORTSPRIORITY - nsThread(); - nsThread(PRUint32 aStackSize); + enum MainThreadFlag { + MAIN_THREAD, + NOT_MAIN_THREAD + }; + + nsThread(MainThreadFlag aMainThread, PRUint32 aStackSize); // Initialize this as a wrapper for a new PRThread. nsresult Init(); @@ -147,6 +151,7 @@ private: bool mShutdownPending; // Set to true when events posted to this thread will never run. bool mEventsAreDoomed; + MainThreadFlag mIsMainThread; }; //----------------------------------------------------------------------------- diff --git a/xpcom/threads/nsThreadManager.cpp b/xpcom/threads/nsThreadManager.cpp index 9524cc99b73..0cd4f737098 100644 --- a/xpcom/threads/nsThreadManager.cpp +++ b/xpcom/threads/nsThreadManager.cpp @@ -98,7 +98,7 @@ nsThreadManager::Init() mLock = new Mutex("nsThreadManager.mLock"); // Setup "main" thread - mMainThread = new nsThread(); + mMainThread = new nsThread(nsThread::MAIN_THREAD, 0); if (!mMainThread) return NS_ERROR_OUT_OF_MEMORY; @@ -224,7 +224,7 @@ nsThreadManager::GetCurrentThread() } // OK, that's fine. We'll dynamically create one :-) - nsRefPtr thread = new nsThread(); + nsRefPtr thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0); if (!thread || NS_FAILED(thread->InitCurrentThread())) return nsnull; @@ -239,7 +239,7 @@ nsThreadManager::NewThread(PRUint32 creationFlags, // No new threads during Shutdown NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); - nsThread *thr = new nsThread(stackSize); + nsThread *thr = new nsThread(nsThread::NOT_MAIN_THREAD, stackSize); if (!thr) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(thr); From 385bfc438b5eb92a166a6f77783c645117b4229e Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Mon, 24 Oct 2011 13:23:47 -0400 Subject: [PATCH 10/42] Bug 693404 - Part 1: On MacOS, add public jemalloc function to purge MADV_FREE'd pages, making our RSS reflect reality. r=khuey --HG-- extra : rebase_source : fb043fcb06963ed2d5f94e8f4659da94f43ed014 --- memory/jemalloc/jemalloc.c | 208 +++++++++++++++++++++++++++++++---- memory/jemalloc/jemalloc.h | 27 +++++ memory/jemalloc/linkedlist.h | 77 +++++++++++++ 3 files changed, 293 insertions(+), 19 deletions(-) create mode 100644 memory/jemalloc/linkedlist.h diff --git a/memory/jemalloc/jemalloc.c b/memory/jemalloc/jemalloc.c index 4783b7dc01c..7ff10e0db17 100644 --- a/memory/jemalloc/jemalloc.c +++ b/memory/jemalloc/jemalloc.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; c-basic-offset: 8; indent-tabs-mode: t -*- */ -/* vim:set softtabstop=8 shiftwidth=8: */ +/* vim:set softtabstop=8 shiftwidth=8 noet: */ /*- * Copyright (C) 2006-2008 Jason Evans . * All rights reserved. @@ -105,6 +105,29 @@ #define _pthread_self() pthread_self() #endif +/* + * On Linux, we use madvise(MADV_DONTNEED) to release memory back to the + * operating system. If we release 1MB of live pages with MADV_DONTNEED, our + * RSS will decrease by 1MB (almost) immediately. + * + * On Mac, we use madvise(MADV_FREE). Unlike MADV_DONTNEED on Linux, MADV_FREE + * on Mac doesn't cause the OS to release the specified pages immediately; the + * OS keeps them in our process until the machine comes under memory pressure. + * + * It's therefore difficult to measure the process's RSS on Mac, since, in the + * absence of memory pressure, the contribution from the heap to RSS will not + * decrease due to our madvise calls. + * + * We therefore define MALLOC_DOUBLE_PURGE on Mac. This causes jemalloc to + * track which pages have been MADV_FREE'd. You can then call + * jemalloc_purge_freed_pages(), which will force the OS to release those + * MADV_FREE'd pages, making the process's RSS reflect its true memory usage. + * + */ +#ifdef MOZ_MEMORY_DARWIN +#define MALLOC_DOUBLE_PURGE +#endif + /* * MALLOC_PRODUCTION disables assertions and statistics gathering. It also * defaults the A and J runtime options to off. These settings are appropriate @@ -354,6 +377,7 @@ __FBSDID("$FreeBSD: head/lib/libc/stdlib/malloc.c 180599 2008-07-18 19:35:44Z ja #endif #include "jemalloc.h" +#include "linkedlist.h" /* Some tools, such as /dev/dsp wrappers, LD_PRELOAD libraries that * happen to override mmap() and call dlsym() from their overridden @@ -605,6 +629,11 @@ static const bool __isthreaded = true; /******************************************************************************/ +/* MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive. */ +#if defined(MALLOC_DECOMMIT) && defined(MALLOC_DOUBLE_PURGE) +#error MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive. +#endif + /* * Mutexes based on spinlocks. We can't use normal pthread spinlocks in all * places, because they require malloc()ed memory, which causes bootstrapping @@ -807,13 +836,14 @@ struct arena_chunk_map_s { * Run address (or size) and various flags are stored together. The bit * layout looks like (assuming 32-bit system): * - * ???????? ???????? ????---- --ckdzla + * ???????? ???????? ????---- -mckdzla * * ? : Unallocated: Run address for first/last pages, unset for internal * pages. * Small: Run address. * Large: Run size for first page, unset for trailing pages. * - : Unused. + * m : MADV_FREE/MADV_DONTNEED'ed? * c : decommitted? * k : key? * d : dirty? @@ -845,8 +875,27 @@ struct arena_chunk_map_s { * -------- -------- -------- ------la */ size_t bits; -#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) + +/* Note that CHUNK_MAP_DECOMMITTED's meaning varies depending on whether + * MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are defined. + * + * If MALLOC_DECOMMIT is defined, a page which is CHUNK_MAP_DECOMMITTED must be + * re-committed with pages_commit() before it may be touched. If + * MALLOC_DECOMMIT is defined, MALLOC_DOUBLE_PURGE may not be defined. + * + * If neither MALLOC_DECOMMIT nor MALLOC_DOUBLE_PURGE is defined, pages which + * are madvised (with either MADV_DONTNEED or MADV_FREE) are marked with + * CHUNK_MAP_MADVISED. + * + * Otherwise, if MALLOC_DECOMMIT is not defined and MALLOC_DOUBLE_PURGE is + * defined, then a page which is madvised is marked as CHUNK_MAP_MADVISED. + * When it's finally freed with jemalloc_purge_freed_pages, the page is marked + * as CHUNK_MAP_DECOMMITTED. + */ +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) || defined(MALLOC_DOUBLE_PURGE) +#define CHUNK_MAP_MADVISED ((size_t)0x40U) #define CHUNK_MAP_DECOMMITTED ((size_t)0x20U) +#define CHUNK_MAP_MADVISED_OR_DECOMMITTED (CHUNK_MAP_MADVISED | CHUNK_MAP_DECOMMITTED) #endif #define CHUNK_MAP_KEY ((size_t)0x10U) #define CHUNK_MAP_DIRTY ((size_t)0x08U) @@ -866,6 +915,16 @@ struct arena_chunk_s { /* Linkage for the arena's chunks_dirty tree. */ rb_node(arena_chunk_t) link_dirty; +#ifdef MALLOC_DOUBLE_PURGE + /* If we're double-purging, we maintain a linked list of chunks which + * have pages which have been madvise(MADV_FREE)'d but not explicitly + * purged. + * + * We're currently lazy and don't remove a chunk from this list when + * all its madvised pages are recommitted. */ + LinkedList chunks_madvised_elem; +#endif + /* Number of dirty pages. */ size_t ndirty; @@ -951,6 +1010,12 @@ struct arena_s { /* Tree of dirty-page-containing chunks this arena manages. */ arena_chunk_tree_t chunks_dirty; +#ifdef MALLOC_DOUBLE_PURGE + /* Head of a linked list of MADV_FREE'd-page-containing chunks this + * arena manages. */ + LinkedList chunks_madvised; +#endif + /* * In order to avoid rapid chunk allocation/deallocation when an arena * oscillates right on the cusp of needing a new chunk, cache the most @@ -1808,7 +1873,6 @@ malloc_printf(const char *format, ...) /******************************************************************************/ -#ifdef MALLOC_DECOMMIT static inline void pages_decommit(void *addr, size_t size) { @@ -1834,7 +1898,6 @@ pages_commit(void *addr, size_t size) abort(); # endif } -#endif static bool base_pages_alloc_mmap(size_t minsize) @@ -3069,25 +3132,29 @@ arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large, } for (i = 0; i < need_pages; i++) { -#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) || defined(MALLOC_DOUBLE_PURGE) /* * Commit decommitted pages if necessary. If a decommitted * page is encountered, commit all needed adjacent decommitted * pages in one operation, in order to reduce system call * overhead. */ - if (chunk->map[run_ind + i].bits & CHUNK_MAP_DECOMMITTED) { + if (chunk->map[run_ind + i].bits & CHUNK_MAP_MADVISED_OR_DECOMMITTED) { size_t j; /* * Advance i+j to just past the index of the last page - * to commit. Clear CHUNK_MAP_DECOMMITTED along the - * way. + * to commit. Clear CHUNK_MAP_DECOMMITTED and + * CHUNK_MAP_MADVISED along the way. */ for (j = 0; i + j < need_pages && (chunk->map[run_ind + - i + j].bits & CHUNK_MAP_DECOMMITTED); j++) { - chunk->map[run_ind + i + j].bits ^= - CHUNK_MAP_DECOMMITTED; + i + j].bits & CHUNK_MAP_MADVISED_OR_DECOMMITTED); j++) { + /* DECOMMITTED and MADVISED are mutually exclusive. */ + assert(!(chunk->map[run_ind + i + j].bits & CHUNK_MAP_DECOMMITTED && + chunk->map[run_ind + i + j].bits & CHUNK_MAP_MADVISED)); + + chunk->map[run_ind + i + j].bits &= + ~CHUNK_MAP_MADVISED_OR_DECOMMITTED; } # ifdef MALLOC_DECOMMIT @@ -3204,6 +3271,8 @@ arena_chunk_init(arena_t *arena, arena_chunk_t *chunk) /* Insert the run into the runs_avail tree. */ arena_avail_tree_insert(&arena->runs_avail, &chunk->map[arena_chunk_header_npages]); + + LinkedList_Init(&chunk->chunks_madvised_elem); } static void @@ -3219,6 +3288,12 @@ arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) arena->stats.committed -= arena->spare->ndirty; #endif } + +#ifdef MALLOC_DOUBLE_PURGE + /* This is safe to do even if arena->spare is not in the list. */ + LinkedList_Remove(&arena->spare->chunks_madvised_elem); +#endif + VALGRIND_FREELIKE_BLOCK(arena->spare, 0); chunk_dealloc((void *)arena->spare, chunksize); #ifdef MALLOC_STATS @@ -3322,6 +3397,9 @@ arena_purge(arena_t *arena) * purged. */ while (arena->ndirty > (opt_dirty_max >> 1)) { +#ifdef MALLOC_DOUBLE_PURGE + bool madvised = false; +#endif chunk = arena_chunk_tree_dirty_last(&arena->chunks_dirty); assert(chunk != NULL); @@ -3329,17 +3407,23 @@ arena_purge(arena_t *arena) assert(i >= arena_chunk_header_npages); if (chunk->map[i].bits & CHUNK_MAP_DIRTY) { +#ifdef MALLOC_DECOMMIT + const size_t free_operation = CHUNK_MAP_DECOMMITTED; +#else + const size_t free_operation = CHUNK_MAP_MADVISED; +#endif assert((chunk->map[i].bits & - CHUNK_MAP_DECOMMITTED) == 0); - chunk->map[i].bits ^= CHUNK_MAP_DECOMMITTED | CHUNK_MAP_DIRTY; + CHUNK_MAP_MADVISED_OR_DECOMMITTED) == 0); + chunk->map[i].bits ^= free_operation | CHUNK_MAP_DIRTY; /* Find adjacent dirty run(s). */ - for (npages = 1; i > arena_chunk_header_npages - && (chunk->map[i - 1].bits & - CHUNK_MAP_DIRTY); npages++) { + for (npages = 1; + i > arena_chunk_header_npages && + (chunk->map[i - 1].bits & CHUNK_MAP_DIRTY); + npages++) { i--; assert((chunk->map[i].bits & - CHUNK_MAP_DECOMMITTED) == 0); - chunk->map[i].bits ^= CHUNK_MAP_DECOMMITTED | CHUNK_MAP_DIRTY; + CHUNK_MAP_MADVISED_OR_DECOMMITTED) == 0); + chunk->map[i].bits ^= free_operation | CHUNK_MAP_DIRTY; } chunk->ndirty -= npages; arena->ndirty -= npages; @@ -3361,6 +3445,9 @@ arena_purge(arena_t *arena) madvise((void *)((uintptr_t)chunk + (i << pagesize_2pow)), (npages << pagesize_2pow), MADV_FREE); +# ifdef MALLOC_DOUBLE_PURGE + madvised = true; +# endif #endif #ifdef MALLOC_STATS arena->stats.nmadvise++; @@ -3375,6 +3462,14 @@ arena_purge(arena_t *arena) arena_chunk_tree_dirty_remove(&arena->chunks_dirty, chunk); } +#ifdef MALLOC_DOUBLE_PURGE + if (madvised) { + /* The chunk might already be in the list, but this + * makes sure it's at the front. */ + LinkedList_Remove(&chunk->chunks_madvised_elem); + LinkedList_InsertHead(&arena->chunks_madvised, &chunk->chunks_madvised_elem); + } +#endif } } @@ -4562,6 +4657,9 @@ arena_new(arena_t *arena) /* Initialize chunks. */ arena_chunk_tree_dirty_new(&arena->chunks_dirty); +#ifdef MALLOC_DOUBLE_PURGE + LinkedList_Init(&arena->chunks_madvised); +#endif arena->spare = NULL; arena->ndirty = 0; @@ -6381,6 +6479,78 @@ jemalloc_stats(jemalloc_stats_t *stats) assert(stats->committed >= stats->allocated); } +#ifdef MALLOC_DOUBLE_PURGE + +/* Explicitly remove all of this chunk's MADV_FREE'd pages from memory. */ +static void +hard_purge_chunk(arena_chunk_t *chunk) +{ + /* See similar logic in arena_purge(). */ + + size_t i; + for (i = arena_chunk_header_npages; i < chunk_npages; i++) { + /* Find all adjacent pages with CHUNK_MAP_MADVISED set. */ + size_t npages; + for (npages = 0; + chunk->map[i + npages].bits & CHUNK_MAP_MADVISED && i + npages < chunk_npages; + npages++) { + /* Turn off the chunk's MADV_FREED bit and turn on its + * DECOMMITTED bit. */ + assert(!(chunk->map[i + npages].bits & CHUNK_MAP_DECOMMITTED)); + chunk->map[i + npages].bits ^= CHUNK_MAP_MADVISED_OR_DECOMMITTED; + } + + /* We could use mincore to find out which pages are actually + * present, but it's not clear that's better. */ + if (npages > 0) { + pages_decommit(((char*)chunk) + (i << pagesize_2pow), npages << pagesize_2pow); + pages_commit(((char*)chunk) + (i << pagesize_2pow), npages << pagesize_2pow); + } + i += npages; + } +} + +/* Explicitly remove all of this arena's MADV_FREE'd pages from memory. */ +static void +hard_purge_arena(arena_t *arena) +{ + malloc_spin_lock(&arena->lock); + + while (!LinkedList_IsEmpty(&arena->chunks_madvised)) { + LinkedList* next = arena->chunks_madvised.next; + arena_chunk_t *chunk = + LinkedList_Get(arena->chunks_madvised.next, + arena_chunk_t, chunks_madvised_elem); + hard_purge_chunk(chunk); + LinkedList_Remove(&chunk->chunks_madvised_elem); + } + + malloc_spin_unlock(&arena->lock); +} + +void +jemalloc_purge_freed_pages() +{ + size_t i; + for (i = 0; i < narenas; i++) { + arena_t *arena = arenas[i]; + if (arena != NULL) + hard_purge_arena(arena); + } +} + +#else /* !defined MALLOC_DOUBLE_PURGE */ + +void +jemalloc_purge_freed_pages() +{ + /* Do nothing. */ +} + +#endif /* defined MALLOC_DOUBLE_PURGE */ + + + #ifdef MOZ_MEMORY_WINDOWS void* _recalloc(void *ptr, size_t count, size_t size) diff --git a/memory/jemalloc/jemalloc.h b/memory/jemalloc/jemalloc.h index 655d78b16f4..19817f59460 100644 --- a/memory/jemalloc/jemalloc.h +++ b/memory/jemalloc/jemalloc.h @@ -80,6 +80,33 @@ size_t malloc_usable_size(const void *ptr); void jemalloc_stats(jemalloc_stats_t *stats); +/* + * On some operating systems (Mac), we use madvise(MADV_FREE) to hand pages + * back to the operating system. On Mac, the operating system doesn't take + * this memory back immediately; instead, the OS takes it back only when the + * machine is running out of physical memory. + * + * This is great from the standpoint of efficiency, but it makes measuring our + * actual RSS difficult, because pages which we've MADV_FREE'd shouldn't count + * against our RSS. + * + * This function explicitly purges any MADV_FREE'd pages from physical memory, + * causing our reported RSS match the amount of memory we're actually using. + * + * Note that this call is expensive in two ways. First, it may be slow to + * execute, because it may make a number of slow syscalls to free memory. This + * function holds the big jemalloc locks, so basically all threads are blocked + * while this function runs. + * + * This function is also expensive in that the next time we go to access a page + * which we've just explicitly decommitted, the operating system has to attach + * to it a physical page! If we hadn't run this function, the OS would have + * less work to do. + * + * If MALLOC_DOUBLE_PURGE is not defined, this function does nothing. + */ +void jemalloc_purge_freed_pages(); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/memory/jemalloc/linkedlist.h b/memory/jemalloc/linkedlist.h new file mode 100644 index 00000000000..d7553141051 --- /dev/null +++ b/memory/jemalloc/linkedlist.h @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 8; c-basic-offset: 8; indent-tabs-mode: t -*- */ +/* vim:set softtabstop=8 shiftwidth=8 noet: */ +/*- + * Copyright (C) the Mozilla Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +#ifndef linkedlist_h__ +#define linkedlist_h__ + +#include + +typedef struct LinkedList_s LinkedList; + +struct LinkedList_s { + LinkedList *next; + LinkedList *prev; +}; + +/* Convert from LinkedList* to foo*. */ +#define LinkedList_Get(e, type, prop) \ + (type*)((char*)(e) - offsetof(type, prop)) + +/* Insert |e| at the beginning of |l|. */ +void LinkedList_InsertHead(LinkedList *l, LinkedList *e) +{ + e->next = l; + e->prev = l->prev; + e->next->prev = e; + e->prev->next = e; +} + +void LinkedList_Remove(LinkedList *e) +{ + e->prev->next = e->next; + e->next->prev = e->prev; + e->next = e; + e->prev = e; +} + +bool LinkedList_IsEmpty(LinkedList *e) +{ + return e->next == e; +} + +void LinkedList_Init(LinkedList *e) +{ + e->next = e; + e->prev = e; +} + +#endif From 27940150a6019bd6c441d72a3c59a57d803184ec Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Mon, 24 Oct 2011 13:24:02 -0400 Subject: [PATCH 11/42] Bug 693404 - Part 2: On Mac, purge MADV_FREE'd pages before reading RSS. r=khuey --HG-- extra : rebase_source : c0f1f82aba30d32166e17d7d4216017475c9c09a --- .../telemetry/TelemetryHistograms.h | 3 ++ xpcom/base/nsMemoryReporterManager.cpp | 53 ++++++++++++------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index b8126905db3..d8c9a5f663a 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -84,6 +84,9 @@ HISTOGRAM(MEMORY_STORAGE_SQLITE, 1024, 512 * 1024, 50, EXPONENTIAL, "Memory used HISTOGRAM(MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED, 1024, 1024 * 1024, 50, EXPONENTIAL, "Memory used for uncompressed, in-use content images (KB)") HISTOGRAM(MEMORY_HEAP_ALLOCATED, 1024, 1024 * 1024, 50, EXPONENTIAL, "Heap memory allocated (KB)") HISTOGRAM(MEMORY_EXPLICIT, 1024, 1024 * 1024, 50, EXPONENTIAL, "Explicit memory allocations (KB)") +#if defined(XP_MACOSX) +HISTOGRAM(MEMORY_FREE_PURGED_PAGES_MS, 1, 1024, 10, EXPONENTIAL, "Time(ms) to purge MADV_FREE'd heap pages.") +#endif #if defined(XP_WIN) HISTOGRAM(EARLY_GLUESTARTUP_READ_OPS, 1, 100, 12, LINEAR, "ProcessIoCounters.ReadOperationCount before glue startup") HISTOGRAM(EARLY_GLUESTARTUP_READ_TRANSFER, 1, 50 * 1024, 12, EXPONENTIAL, "ProcessIoCounters.ReadTransferCount before glue startup (KB)") diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index d8385ccf821..87b708b9482 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -42,6 +42,27 @@ #include "nsMemoryReporterManager.h" #include "nsArrayEnumerator.h" #include "nsISimpleEnumerator.h" +#include "mozilla/Telemetry.h" + +using namespace mozilla; + +#if defined(MOZ_MEMORY) +# if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX) +# define HAVE_JEMALLOC_STATS 1 +# include "jemalloc.h" +# elif defined(XP_LINUX) +# define HAVE_JEMALLOC_STATS 1 +# include "jemalloc_types.h" +// jemalloc is directly linked into firefox-bin; libxul doesn't link +// with it. So if we tried to use jemalloc_stats directly here, it +// wouldn't be defined. Instead, we don't include the jemalloc header +// and weakly link against jemalloc_stats. +extern "C" { +extern void jemalloc_stats(jemalloc_stats_t* stats) + NS_VISIBILITY_DEFAULT __attribute__((weak)); +} +# endif // XP_LINUX +#endif // MOZ_MEMORY #if defined(XP_LINUX) || defined(XP_MACOSX) @@ -125,6 +146,20 @@ static PRInt64 GetVsize() static PRInt64 GetResident() { +#ifdef HAVE_JEMALLOC_STATS + // If we're using jemalloc on Mac, we need to instruct jemalloc to purge + // the pages it has madvise(MADV_FREE)'d before we read our RSS. The OS + // will take away MADV_FREE'd pages when there's memory pressure, so they + // shouldn't count against our RSS. + // + // Purging these pages shouldn't take more than 10ms or so, but we want to + // keep an eye on it since GetResident() is called on each Telemetry ping. + { + Telemetry::AutoTimer timer; + jemalloc_purge_freed_pages(); + } +#endif + task_basic_info ti; return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.resident_size : -1); } @@ -255,24 +290,6 @@ NS_MEMORY_REPORTER_IMPLEMENT(Resident, ** at least -- on OSX, there are sometimes other zones in use). **/ -#if defined(MOZ_MEMORY) -# if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX) -# define HAVE_JEMALLOC_STATS 1 -# include "jemalloc.h" -# elif defined(XP_LINUX) -# define HAVE_JEMALLOC_STATS 1 -# include "jemalloc_types.h" -// jemalloc is directly linked into firefox-bin; libxul doesn't link -// with it. So if we tried to use jemalloc_stats directly here, it -// wouldn't be defined. Instead, we don't include the jemalloc header -// and weakly link against jemalloc_stats. -extern "C" { -extern void jemalloc_stats(jemalloc_stats_t* stats) - NS_VISIBILITY_DEFAULT __attribute__((weak)); -} -# endif // XP_LINUX -#endif // MOZ_MEMORY - #if HAVE_JEMALLOC_STATS static PRInt64 GetHeapUnallocated() From f356e9f7f2aae68df4d36daac56d97a6d871762d Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Mon, 24 Oct 2011 14:02:16 -0400 Subject: [PATCH 12/42] Back out bug 693404 (2f7668cd67bc, 3db099ca452a) due to red. --- memory/jemalloc/jemalloc.c | 208 ++---------------- memory/jemalloc/jemalloc.h | 27 --- memory/jemalloc/linkedlist.h | 77 ------- .../telemetry/TelemetryHistograms.h | 3 - xpcom/base/nsMemoryReporterManager.cpp | 53 ++--- 5 files changed, 37 insertions(+), 331 deletions(-) delete mode 100644 memory/jemalloc/linkedlist.h diff --git a/memory/jemalloc/jemalloc.c b/memory/jemalloc/jemalloc.c index 7ff10e0db17..4783b7dc01c 100644 --- a/memory/jemalloc/jemalloc.c +++ b/memory/jemalloc/jemalloc.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; c-basic-offset: 8; indent-tabs-mode: t -*- */ -/* vim:set softtabstop=8 shiftwidth=8 noet: */ +/* vim:set softtabstop=8 shiftwidth=8: */ /*- * Copyright (C) 2006-2008 Jason Evans . * All rights reserved. @@ -105,29 +105,6 @@ #define _pthread_self() pthread_self() #endif -/* - * On Linux, we use madvise(MADV_DONTNEED) to release memory back to the - * operating system. If we release 1MB of live pages with MADV_DONTNEED, our - * RSS will decrease by 1MB (almost) immediately. - * - * On Mac, we use madvise(MADV_FREE). Unlike MADV_DONTNEED on Linux, MADV_FREE - * on Mac doesn't cause the OS to release the specified pages immediately; the - * OS keeps them in our process until the machine comes under memory pressure. - * - * It's therefore difficult to measure the process's RSS on Mac, since, in the - * absence of memory pressure, the contribution from the heap to RSS will not - * decrease due to our madvise calls. - * - * We therefore define MALLOC_DOUBLE_PURGE on Mac. This causes jemalloc to - * track which pages have been MADV_FREE'd. You can then call - * jemalloc_purge_freed_pages(), which will force the OS to release those - * MADV_FREE'd pages, making the process's RSS reflect its true memory usage. - * - */ -#ifdef MOZ_MEMORY_DARWIN -#define MALLOC_DOUBLE_PURGE -#endif - /* * MALLOC_PRODUCTION disables assertions and statistics gathering. It also * defaults the A and J runtime options to off. These settings are appropriate @@ -377,7 +354,6 @@ __FBSDID("$FreeBSD: head/lib/libc/stdlib/malloc.c 180599 2008-07-18 19:35:44Z ja #endif #include "jemalloc.h" -#include "linkedlist.h" /* Some tools, such as /dev/dsp wrappers, LD_PRELOAD libraries that * happen to override mmap() and call dlsym() from their overridden @@ -629,11 +605,6 @@ static const bool __isthreaded = true; /******************************************************************************/ -/* MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive. */ -#if defined(MALLOC_DECOMMIT) && defined(MALLOC_DOUBLE_PURGE) -#error MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive. -#endif - /* * Mutexes based on spinlocks. We can't use normal pthread spinlocks in all * places, because they require malloc()ed memory, which causes bootstrapping @@ -836,14 +807,13 @@ struct arena_chunk_map_s { * Run address (or size) and various flags are stored together. The bit * layout looks like (assuming 32-bit system): * - * ???????? ???????? ????---- -mckdzla + * ???????? ???????? ????---- --ckdzla * * ? : Unallocated: Run address for first/last pages, unset for internal * pages. * Small: Run address. * Large: Run size for first page, unset for trailing pages. * - : Unused. - * m : MADV_FREE/MADV_DONTNEED'ed? * c : decommitted? * k : key? * d : dirty? @@ -875,27 +845,8 @@ struct arena_chunk_map_s { * -------- -------- -------- ------la */ size_t bits; - -/* Note that CHUNK_MAP_DECOMMITTED's meaning varies depending on whether - * MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are defined. - * - * If MALLOC_DECOMMIT is defined, a page which is CHUNK_MAP_DECOMMITTED must be - * re-committed with pages_commit() before it may be touched. If - * MALLOC_DECOMMIT is defined, MALLOC_DOUBLE_PURGE may not be defined. - * - * If neither MALLOC_DECOMMIT nor MALLOC_DOUBLE_PURGE is defined, pages which - * are madvised (with either MADV_DONTNEED or MADV_FREE) are marked with - * CHUNK_MAP_MADVISED. - * - * Otherwise, if MALLOC_DECOMMIT is not defined and MALLOC_DOUBLE_PURGE is - * defined, then a page which is madvised is marked as CHUNK_MAP_MADVISED. - * When it's finally freed with jemalloc_purge_freed_pages, the page is marked - * as CHUNK_MAP_DECOMMITTED. - */ -#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) || defined(MALLOC_DOUBLE_PURGE) -#define CHUNK_MAP_MADVISED ((size_t)0x40U) +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) #define CHUNK_MAP_DECOMMITTED ((size_t)0x20U) -#define CHUNK_MAP_MADVISED_OR_DECOMMITTED (CHUNK_MAP_MADVISED | CHUNK_MAP_DECOMMITTED) #endif #define CHUNK_MAP_KEY ((size_t)0x10U) #define CHUNK_MAP_DIRTY ((size_t)0x08U) @@ -915,16 +866,6 @@ struct arena_chunk_s { /* Linkage for the arena's chunks_dirty tree. */ rb_node(arena_chunk_t) link_dirty; -#ifdef MALLOC_DOUBLE_PURGE - /* If we're double-purging, we maintain a linked list of chunks which - * have pages which have been madvise(MADV_FREE)'d but not explicitly - * purged. - * - * We're currently lazy and don't remove a chunk from this list when - * all its madvised pages are recommitted. */ - LinkedList chunks_madvised_elem; -#endif - /* Number of dirty pages. */ size_t ndirty; @@ -1010,12 +951,6 @@ struct arena_s { /* Tree of dirty-page-containing chunks this arena manages. */ arena_chunk_tree_t chunks_dirty; -#ifdef MALLOC_DOUBLE_PURGE - /* Head of a linked list of MADV_FREE'd-page-containing chunks this - * arena manages. */ - LinkedList chunks_madvised; -#endif - /* * In order to avoid rapid chunk allocation/deallocation when an arena * oscillates right on the cusp of needing a new chunk, cache the most @@ -1873,6 +1808,7 @@ malloc_printf(const char *format, ...) /******************************************************************************/ +#ifdef MALLOC_DECOMMIT static inline void pages_decommit(void *addr, size_t size) { @@ -1898,6 +1834,7 @@ pages_commit(void *addr, size_t size) abort(); # endif } +#endif static bool base_pages_alloc_mmap(size_t minsize) @@ -3132,29 +3069,25 @@ arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large, } for (i = 0; i < need_pages; i++) { -#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) || defined(MALLOC_DOUBLE_PURGE) +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) /* * Commit decommitted pages if necessary. If a decommitted * page is encountered, commit all needed adjacent decommitted * pages in one operation, in order to reduce system call * overhead. */ - if (chunk->map[run_ind + i].bits & CHUNK_MAP_MADVISED_OR_DECOMMITTED) { + if (chunk->map[run_ind + i].bits & CHUNK_MAP_DECOMMITTED) { size_t j; /* * Advance i+j to just past the index of the last page - * to commit. Clear CHUNK_MAP_DECOMMITTED and - * CHUNK_MAP_MADVISED along the way. + * to commit. Clear CHUNK_MAP_DECOMMITTED along the + * way. */ for (j = 0; i + j < need_pages && (chunk->map[run_ind + - i + j].bits & CHUNK_MAP_MADVISED_OR_DECOMMITTED); j++) { - /* DECOMMITTED and MADVISED are mutually exclusive. */ - assert(!(chunk->map[run_ind + i + j].bits & CHUNK_MAP_DECOMMITTED && - chunk->map[run_ind + i + j].bits & CHUNK_MAP_MADVISED)); - - chunk->map[run_ind + i + j].bits &= - ~CHUNK_MAP_MADVISED_OR_DECOMMITTED; + i + j].bits & CHUNK_MAP_DECOMMITTED); j++) { + chunk->map[run_ind + i + j].bits ^= + CHUNK_MAP_DECOMMITTED; } # ifdef MALLOC_DECOMMIT @@ -3271,8 +3204,6 @@ arena_chunk_init(arena_t *arena, arena_chunk_t *chunk) /* Insert the run into the runs_avail tree. */ arena_avail_tree_insert(&arena->runs_avail, &chunk->map[arena_chunk_header_npages]); - - LinkedList_Init(&chunk->chunks_madvised_elem); } static void @@ -3288,12 +3219,6 @@ arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) arena->stats.committed -= arena->spare->ndirty; #endif } - -#ifdef MALLOC_DOUBLE_PURGE - /* This is safe to do even if arena->spare is not in the list. */ - LinkedList_Remove(&arena->spare->chunks_madvised_elem); -#endif - VALGRIND_FREELIKE_BLOCK(arena->spare, 0); chunk_dealloc((void *)arena->spare, chunksize); #ifdef MALLOC_STATS @@ -3397,9 +3322,6 @@ arena_purge(arena_t *arena) * purged. */ while (arena->ndirty > (opt_dirty_max >> 1)) { -#ifdef MALLOC_DOUBLE_PURGE - bool madvised = false; -#endif chunk = arena_chunk_tree_dirty_last(&arena->chunks_dirty); assert(chunk != NULL); @@ -3407,23 +3329,17 @@ arena_purge(arena_t *arena) assert(i >= arena_chunk_header_npages); if (chunk->map[i].bits & CHUNK_MAP_DIRTY) { -#ifdef MALLOC_DECOMMIT - const size_t free_operation = CHUNK_MAP_DECOMMITTED; -#else - const size_t free_operation = CHUNK_MAP_MADVISED; -#endif assert((chunk->map[i].bits & - CHUNK_MAP_MADVISED_OR_DECOMMITTED) == 0); - chunk->map[i].bits ^= free_operation | CHUNK_MAP_DIRTY; + CHUNK_MAP_DECOMMITTED) == 0); + chunk->map[i].bits ^= CHUNK_MAP_DECOMMITTED | CHUNK_MAP_DIRTY; /* Find adjacent dirty run(s). */ - for (npages = 1; - i > arena_chunk_header_npages && - (chunk->map[i - 1].bits & CHUNK_MAP_DIRTY); - npages++) { + for (npages = 1; i > arena_chunk_header_npages + && (chunk->map[i - 1].bits & + CHUNK_MAP_DIRTY); npages++) { i--; assert((chunk->map[i].bits & - CHUNK_MAP_MADVISED_OR_DECOMMITTED) == 0); - chunk->map[i].bits ^= free_operation | CHUNK_MAP_DIRTY; + CHUNK_MAP_DECOMMITTED) == 0); + chunk->map[i].bits ^= CHUNK_MAP_DECOMMITTED | CHUNK_MAP_DIRTY; } chunk->ndirty -= npages; arena->ndirty -= npages; @@ -3445,9 +3361,6 @@ arena_purge(arena_t *arena) madvise((void *)((uintptr_t)chunk + (i << pagesize_2pow)), (npages << pagesize_2pow), MADV_FREE); -# ifdef MALLOC_DOUBLE_PURGE - madvised = true; -# endif #endif #ifdef MALLOC_STATS arena->stats.nmadvise++; @@ -3462,14 +3375,6 @@ arena_purge(arena_t *arena) arena_chunk_tree_dirty_remove(&arena->chunks_dirty, chunk); } -#ifdef MALLOC_DOUBLE_PURGE - if (madvised) { - /* The chunk might already be in the list, but this - * makes sure it's at the front. */ - LinkedList_Remove(&chunk->chunks_madvised_elem); - LinkedList_InsertHead(&arena->chunks_madvised, &chunk->chunks_madvised_elem); - } -#endif } } @@ -4657,9 +4562,6 @@ arena_new(arena_t *arena) /* Initialize chunks. */ arena_chunk_tree_dirty_new(&arena->chunks_dirty); -#ifdef MALLOC_DOUBLE_PURGE - LinkedList_Init(&arena->chunks_madvised); -#endif arena->spare = NULL; arena->ndirty = 0; @@ -6479,78 +6381,6 @@ jemalloc_stats(jemalloc_stats_t *stats) assert(stats->committed >= stats->allocated); } -#ifdef MALLOC_DOUBLE_PURGE - -/* Explicitly remove all of this chunk's MADV_FREE'd pages from memory. */ -static void -hard_purge_chunk(arena_chunk_t *chunk) -{ - /* See similar logic in arena_purge(). */ - - size_t i; - for (i = arena_chunk_header_npages; i < chunk_npages; i++) { - /* Find all adjacent pages with CHUNK_MAP_MADVISED set. */ - size_t npages; - for (npages = 0; - chunk->map[i + npages].bits & CHUNK_MAP_MADVISED && i + npages < chunk_npages; - npages++) { - /* Turn off the chunk's MADV_FREED bit and turn on its - * DECOMMITTED bit. */ - assert(!(chunk->map[i + npages].bits & CHUNK_MAP_DECOMMITTED)); - chunk->map[i + npages].bits ^= CHUNK_MAP_MADVISED_OR_DECOMMITTED; - } - - /* We could use mincore to find out which pages are actually - * present, but it's not clear that's better. */ - if (npages > 0) { - pages_decommit(((char*)chunk) + (i << pagesize_2pow), npages << pagesize_2pow); - pages_commit(((char*)chunk) + (i << pagesize_2pow), npages << pagesize_2pow); - } - i += npages; - } -} - -/* Explicitly remove all of this arena's MADV_FREE'd pages from memory. */ -static void -hard_purge_arena(arena_t *arena) -{ - malloc_spin_lock(&arena->lock); - - while (!LinkedList_IsEmpty(&arena->chunks_madvised)) { - LinkedList* next = arena->chunks_madvised.next; - arena_chunk_t *chunk = - LinkedList_Get(arena->chunks_madvised.next, - arena_chunk_t, chunks_madvised_elem); - hard_purge_chunk(chunk); - LinkedList_Remove(&chunk->chunks_madvised_elem); - } - - malloc_spin_unlock(&arena->lock); -} - -void -jemalloc_purge_freed_pages() -{ - size_t i; - for (i = 0; i < narenas; i++) { - arena_t *arena = arenas[i]; - if (arena != NULL) - hard_purge_arena(arena); - } -} - -#else /* !defined MALLOC_DOUBLE_PURGE */ - -void -jemalloc_purge_freed_pages() -{ - /* Do nothing. */ -} - -#endif /* defined MALLOC_DOUBLE_PURGE */ - - - #ifdef MOZ_MEMORY_WINDOWS void* _recalloc(void *ptr, size_t count, size_t size) diff --git a/memory/jemalloc/jemalloc.h b/memory/jemalloc/jemalloc.h index 19817f59460..655d78b16f4 100644 --- a/memory/jemalloc/jemalloc.h +++ b/memory/jemalloc/jemalloc.h @@ -80,33 +80,6 @@ size_t malloc_usable_size(const void *ptr); void jemalloc_stats(jemalloc_stats_t *stats); -/* - * On some operating systems (Mac), we use madvise(MADV_FREE) to hand pages - * back to the operating system. On Mac, the operating system doesn't take - * this memory back immediately; instead, the OS takes it back only when the - * machine is running out of physical memory. - * - * This is great from the standpoint of efficiency, but it makes measuring our - * actual RSS difficult, because pages which we've MADV_FREE'd shouldn't count - * against our RSS. - * - * This function explicitly purges any MADV_FREE'd pages from physical memory, - * causing our reported RSS match the amount of memory we're actually using. - * - * Note that this call is expensive in two ways. First, it may be slow to - * execute, because it may make a number of slow syscalls to free memory. This - * function holds the big jemalloc locks, so basically all threads are blocked - * while this function runs. - * - * This function is also expensive in that the next time we go to access a page - * which we've just explicitly decommitted, the operating system has to attach - * to it a physical page! If we hadn't run this function, the OS would have - * less work to do. - * - * If MALLOC_DOUBLE_PURGE is not defined, this function does nothing. - */ -void jemalloc_purge_freed_pages(); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/memory/jemalloc/linkedlist.h b/memory/jemalloc/linkedlist.h deleted file mode 100644 index d7553141051..00000000000 --- a/memory/jemalloc/linkedlist.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C; tab-width: 8; c-basic-offset: 8; indent-tabs-mode: t -*- */ -/* vim:set softtabstop=8 shiftwidth=8 noet: */ -/*- - * Copyright (C) the Mozilla Foundation. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice(s), this list of conditions and the following disclaimer as - * the first lines of this file unmodified other than the possible - * addition of one or more copyright notices. - * 2. Redistributions in binary form must reproduce the above copyright - * notice(s), this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *******************************************************************************/ - -#ifndef linkedlist_h__ -#define linkedlist_h__ - -#include - -typedef struct LinkedList_s LinkedList; - -struct LinkedList_s { - LinkedList *next; - LinkedList *prev; -}; - -/* Convert from LinkedList* to foo*. */ -#define LinkedList_Get(e, type, prop) \ - (type*)((char*)(e) - offsetof(type, prop)) - -/* Insert |e| at the beginning of |l|. */ -void LinkedList_InsertHead(LinkedList *l, LinkedList *e) -{ - e->next = l; - e->prev = l->prev; - e->next->prev = e; - e->prev->next = e; -} - -void LinkedList_Remove(LinkedList *e) -{ - e->prev->next = e->next; - e->next->prev = e->prev; - e->next = e; - e->prev = e; -} - -bool LinkedList_IsEmpty(LinkedList *e) -{ - return e->next == e; -} - -void LinkedList_Init(LinkedList *e) -{ - e->next = e; - e->prev = e; -} - -#endif diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index d8c9a5f663a..b8126905db3 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -84,9 +84,6 @@ HISTOGRAM(MEMORY_STORAGE_SQLITE, 1024, 512 * 1024, 50, EXPONENTIAL, "Memory used HISTOGRAM(MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED, 1024, 1024 * 1024, 50, EXPONENTIAL, "Memory used for uncompressed, in-use content images (KB)") HISTOGRAM(MEMORY_HEAP_ALLOCATED, 1024, 1024 * 1024, 50, EXPONENTIAL, "Heap memory allocated (KB)") HISTOGRAM(MEMORY_EXPLICIT, 1024, 1024 * 1024, 50, EXPONENTIAL, "Explicit memory allocations (KB)") -#if defined(XP_MACOSX) -HISTOGRAM(MEMORY_FREE_PURGED_PAGES_MS, 1, 1024, 10, EXPONENTIAL, "Time(ms) to purge MADV_FREE'd heap pages.") -#endif #if defined(XP_WIN) HISTOGRAM(EARLY_GLUESTARTUP_READ_OPS, 1, 100, 12, LINEAR, "ProcessIoCounters.ReadOperationCount before glue startup") HISTOGRAM(EARLY_GLUESTARTUP_READ_TRANSFER, 1, 50 * 1024, 12, EXPONENTIAL, "ProcessIoCounters.ReadTransferCount before glue startup (KB)") diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 87b708b9482..d8385ccf821 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -42,27 +42,6 @@ #include "nsMemoryReporterManager.h" #include "nsArrayEnumerator.h" #include "nsISimpleEnumerator.h" -#include "mozilla/Telemetry.h" - -using namespace mozilla; - -#if defined(MOZ_MEMORY) -# if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX) -# define HAVE_JEMALLOC_STATS 1 -# include "jemalloc.h" -# elif defined(XP_LINUX) -# define HAVE_JEMALLOC_STATS 1 -# include "jemalloc_types.h" -// jemalloc is directly linked into firefox-bin; libxul doesn't link -// with it. So if we tried to use jemalloc_stats directly here, it -// wouldn't be defined. Instead, we don't include the jemalloc header -// and weakly link against jemalloc_stats. -extern "C" { -extern void jemalloc_stats(jemalloc_stats_t* stats) - NS_VISIBILITY_DEFAULT __attribute__((weak)); -} -# endif // XP_LINUX -#endif // MOZ_MEMORY #if defined(XP_LINUX) || defined(XP_MACOSX) @@ -146,20 +125,6 @@ static PRInt64 GetVsize() static PRInt64 GetResident() { -#ifdef HAVE_JEMALLOC_STATS - // If we're using jemalloc on Mac, we need to instruct jemalloc to purge - // the pages it has madvise(MADV_FREE)'d before we read our RSS. The OS - // will take away MADV_FREE'd pages when there's memory pressure, so they - // shouldn't count against our RSS. - // - // Purging these pages shouldn't take more than 10ms or so, but we want to - // keep an eye on it since GetResident() is called on each Telemetry ping. - { - Telemetry::AutoTimer timer; - jemalloc_purge_freed_pages(); - } -#endif - task_basic_info ti; return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.resident_size : -1); } @@ -290,6 +255,24 @@ NS_MEMORY_REPORTER_IMPLEMENT(Resident, ** at least -- on OSX, there are sometimes other zones in use). **/ +#if defined(MOZ_MEMORY) +# if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX) +# define HAVE_JEMALLOC_STATS 1 +# include "jemalloc.h" +# elif defined(XP_LINUX) +# define HAVE_JEMALLOC_STATS 1 +# include "jemalloc_types.h" +// jemalloc is directly linked into firefox-bin; libxul doesn't link +// with it. So if we tried to use jemalloc_stats directly here, it +// wouldn't be defined. Instead, we don't include the jemalloc header +// and weakly link against jemalloc_stats. +extern "C" { +extern void jemalloc_stats(jemalloc_stats_t* stats) + NS_VISIBILITY_DEFAULT __attribute__((weak)); +} +# endif // XP_LINUX +#endif // MOZ_MEMORY + #if HAVE_JEMALLOC_STATS static PRInt64 GetHeapUnallocated() From 674d6ceed2b848c062ae7b52fc4c81c7c3621c0c Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Mon, 24 Oct 2011 13:23:47 -0400 Subject: [PATCH 13/42] Bug 693404 - Part 1: On MacOS, add public jemalloc function to purge MADV_FREE'd pages, making our RSS reflect reality. r=khuey --- memory/jemalloc/jemalloc.c | 210 +++++++++++++++++++++++++++++++---- memory/jemalloc/jemalloc.h | 27 +++++ memory/jemalloc/linkedlist.h | 77 +++++++++++++ 3 files changed, 295 insertions(+), 19 deletions(-) create mode 100644 memory/jemalloc/linkedlist.h diff --git a/memory/jemalloc/jemalloc.c b/memory/jemalloc/jemalloc.c index 4783b7dc01c..867226e6707 100644 --- a/memory/jemalloc/jemalloc.c +++ b/memory/jemalloc/jemalloc.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; c-basic-offset: 8; indent-tabs-mode: t -*- */ -/* vim:set softtabstop=8 shiftwidth=8: */ +/* vim:set softtabstop=8 shiftwidth=8 noet: */ /*- * Copyright (C) 2006-2008 Jason Evans . * All rights reserved. @@ -105,6 +105,29 @@ #define _pthread_self() pthread_self() #endif +/* + * On Linux, we use madvise(MADV_DONTNEED) to release memory back to the + * operating system. If we release 1MB of live pages with MADV_DONTNEED, our + * RSS will decrease by 1MB (almost) immediately. + * + * On Mac, we use madvise(MADV_FREE). Unlike MADV_DONTNEED on Linux, MADV_FREE + * on Mac doesn't cause the OS to release the specified pages immediately; the + * OS keeps them in our process until the machine comes under memory pressure. + * + * It's therefore difficult to measure the process's RSS on Mac, since, in the + * absence of memory pressure, the contribution from the heap to RSS will not + * decrease due to our madvise calls. + * + * We therefore define MALLOC_DOUBLE_PURGE on Mac. This causes jemalloc to + * track which pages have been MADV_FREE'd. You can then call + * jemalloc_purge_freed_pages(), which will force the OS to release those + * MADV_FREE'd pages, making the process's RSS reflect its true memory usage. + * + */ +#ifdef MOZ_MEMORY_DARWIN +#define MALLOC_DOUBLE_PURGE +#endif + /* * MALLOC_PRODUCTION disables assertions and statistics gathering. It also * defaults the A and J runtime options to off. These settings are appropriate @@ -354,6 +377,7 @@ __FBSDID("$FreeBSD: head/lib/libc/stdlib/malloc.c 180599 2008-07-18 19:35:44Z ja #endif #include "jemalloc.h" +#include "linkedlist.h" /* Some tools, such as /dev/dsp wrappers, LD_PRELOAD libraries that * happen to override mmap() and call dlsym() from their overridden @@ -605,6 +629,11 @@ static const bool __isthreaded = true; /******************************************************************************/ +/* MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive. */ +#if defined(MALLOC_DECOMMIT) && defined(MALLOC_DOUBLE_PURGE) +#error MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are mutually exclusive. +#endif + /* * Mutexes based on spinlocks. We can't use normal pthread spinlocks in all * places, because they require malloc()ed memory, which causes bootstrapping @@ -807,13 +836,14 @@ struct arena_chunk_map_s { * Run address (or size) and various flags are stored together. The bit * layout looks like (assuming 32-bit system): * - * ???????? ???????? ????---- --ckdzla + * ???????? ???????? ????---- -mckdzla * * ? : Unallocated: Run address for first/last pages, unset for internal * pages. * Small: Run address. * Large: Run size for first page, unset for trailing pages. * - : Unused. + * m : MADV_FREE/MADV_DONTNEED'ed? * c : decommitted? * k : key? * d : dirty? @@ -845,8 +875,27 @@ struct arena_chunk_map_s { * -------- -------- -------- ------la */ size_t bits; -#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) + +/* Note that CHUNK_MAP_DECOMMITTED's meaning varies depending on whether + * MALLOC_DECOMMIT and MALLOC_DOUBLE_PURGE are defined. + * + * If MALLOC_DECOMMIT is defined, a page which is CHUNK_MAP_DECOMMITTED must be + * re-committed with pages_commit() before it may be touched. If + * MALLOC_DECOMMIT is defined, MALLOC_DOUBLE_PURGE may not be defined. + * + * If neither MALLOC_DECOMMIT nor MALLOC_DOUBLE_PURGE is defined, pages which + * are madvised (with either MADV_DONTNEED or MADV_FREE) are marked with + * CHUNK_MAP_MADVISED. + * + * Otherwise, if MALLOC_DECOMMIT is not defined and MALLOC_DOUBLE_PURGE is + * defined, then a page which is madvised is marked as CHUNK_MAP_MADVISED. + * When it's finally freed with jemalloc_purge_freed_pages, the page is marked + * as CHUNK_MAP_DECOMMITTED. + */ +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) || defined(MALLOC_DOUBLE_PURGE) +#define CHUNK_MAP_MADVISED ((size_t)0x40U) #define CHUNK_MAP_DECOMMITTED ((size_t)0x20U) +#define CHUNK_MAP_MADVISED_OR_DECOMMITTED (CHUNK_MAP_MADVISED | CHUNK_MAP_DECOMMITTED) #endif #define CHUNK_MAP_KEY ((size_t)0x10U) #define CHUNK_MAP_DIRTY ((size_t)0x08U) @@ -866,6 +915,16 @@ struct arena_chunk_s { /* Linkage for the arena's chunks_dirty tree. */ rb_node(arena_chunk_t) link_dirty; +#ifdef MALLOC_DOUBLE_PURGE + /* If we're double-purging, we maintain a linked list of chunks which + * have pages which have been madvise(MADV_FREE)'d but not explicitly + * purged. + * + * We're currently lazy and don't remove a chunk from this list when + * all its madvised pages are recommitted. */ + LinkedList chunks_madvised_elem; +#endif + /* Number of dirty pages. */ size_t ndirty; @@ -951,6 +1010,12 @@ struct arena_s { /* Tree of dirty-page-containing chunks this arena manages. */ arena_chunk_tree_t chunks_dirty; +#ifdef MALLOC_DOUBLE_PURGE + /* Head of a linked list of MADV_FREE'd-page-containing chunks this + * arena manages. */ + LinkedList chunks_madvised; +#endif + /* * In order to avoid rapid chunk allocation/deallocation when an arena * oscillates right on the cusp of needing a new chunk, cache the most @@ -1808,7 +1873,6 @@ malloc_printf(const char *format, ...) /******************************************************************************/ -#ifdef MALLOC_DECOMMIT static inline void pages_decommit(void *addr, size_t size) { @@ -1834,7 +1898,6 @@ pages_commit(void *addr, size_t size) abort(); # endif } -#endif static bool base_pages_alloc_mmap(size_t minsize) @@ -3069,25 +3132,29 @@ arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large, } for (i = 0; i < need_pages; i++) { -#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) +#if defined(MALLOC_DECOMMIT) || defined(MALLOC_STATS) || defined(MALLOC_DOUBLE_PURGE) /* * Commit decommitted pages if necessary. If a decommitted * page is encountered, commit all needed adjacent decommitted * pages in one operation, in order to reduce system call * overhead. */ - if (chunk->map[run_ind + i].bits & CHUNK_MAP_DECOMMITTED) { + if (chunk->map[run_ind + i].bits & CHUNK_MAP_MADVISED_OR_DECOMMITTED) { size_t j; /* * Advance i+j to just past the index of the last page - * to commit. Clear CHUNK_MAP_DECOMMITTED along the - * way. + * to commit. Clear CHUNK_MAP_DECOMMITTED and + * CHUNK_MAP_MADVISED along the way. */ for (j = 0; i + j < need_pages && (chunk->map[run_ind + - i + j].bits & CHUNK_MAP_DECOMMITTED); j++) { - chunk->map[run_ind + i + j].bits ^= - CHUNK_MAP_DECOMMITTED; + i + j].bits & CHUNK_MAP_MADVISED_OR_DECOMMITTED); j++) { + /* DECOMMITTED and MADVISED are mutually exclusive. */ + assert(!(chunk->map[run_ind + i + j].bits & CHUNK_MAP_DECOMMITTED && + chunk->map[run_ind + i + j].bits & CHUNK_MAP_MADVISED)); + + chunk->map[run_ind + i + j].bits &= + ~CHUNK_MAP_MADVISED_OR_DECOMMITTED; } # ifdef MALLOC_DECOMMIT @@ -3204,6 +3271,10 @@ arena_chunk_init(arena_t *arena, arena_chunk_t *chunk) /* Insert the run into the runs_avail tree. */ arena_avail_tree_insert(&arena->runs_avail, &chunk->map[arena_chunk_header_npages]); + +#ifdef MALLOC_DOUBLE_PURGE + LinkedList_Init(&chunk->chunks_madvised_elem); +#endif } static void @@ -3219,6 +3290,12 @@ arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) arena->stats.committed -= arena->spare->ndirty; #endif } + +#ifdef MALLOC_DOUBLE_PURGE + /* This is safe to do even if arena->spare is not in the list. */ + LinkedList_Remove(&arena->spare->chunks_madvised_elem); +#endif + VALGRIND_FREELIKE_BLOCK(arena->spare, 0); chunk_dealloc((void *)arena->spare, chunksize); #ifdef MALLOC_STATS @@ -3322,6 +3399,9 @@ arena_purge(arena_t *arena) * purged. */ while (arena->ndirty > (opt_dirty_max >> 1)) { +#ifdef MALLOC_DOUBLE_PURGE + bool madvised = false; +#endif chunk = arena_chunk_tree_dirty_last(&arena->chunks_dirty); assert(chunk != NULL); @@ -3329,17 +3409,23 @@ arena_purge(arena_t *arena) assert(i >= arena_chunk_header_npages); if (chunk->map[i].bits & CHUNK_MAP_DIRTY) { +#ifdef MALLOC_DECOMMIT + const size_t free_operation = CHUNK_MAP_DECOMMITTED; +#else + const size_t free_operation = CHUNK_MAP_MADVISED; +#endif assert((chunk->map[i].bits & - CHUNK_MAP_DECOMMITTED) == 0); - chunk->map[i].bits ^= CHUNK_MAP_DECOMMITTED | CHUNK_MAP_DIRTY; + CHUNK_MAP_MADVISED_OR_DECOMMITTED) == 0); + chunk->map[i].bits ^= free_operation | CHUNK_MAP_DIRTY; /* Find adjacent dirty run(s). */ - for (npages = 1; i > arena_chunk_header_npages - && (chunk->map[i - 1].bits & - CHUNK_MAP_DIRTY); npages++) { + for (npages = 1; + i > arena_chunk_header_npages && + (chunk->map[i - 1].bits & CHUNK_MAP_DIRTY); + npages++) { i--; assert((chunk->map[i].bits & - CHUNK_MAP_DECOMMITTED) == 0); - chunk->map[i].bits ^= CHUNK_MAP_DECOMMITTED | CHUNK_MAP_DIRTY; + CHUNK_MAP_MADVISED_OR_DECOMMITTED) == 0); + chunk->map[i].bits ^= free_operation | CHUNK_MAP_DIRTY; } chunk->ndirty -= npages; arena->ndirty -= npages; @@ -3361,6 +3447,9 @@ arena_purge(arena_t *arena) madvise((void *)((uintptr_t)chunk + (i << pagesize_2pow)), (npages << pagesize_2pow), MADV_FREE); +# ifdef MALLOC_DOUBLE_PURGE + madvised = true; +# endif #endif #ifdef MALLOC_STATS arena->stats.nmadvise++; @@ -3375,6 +3464,14 @@ arena_purge(arena_t *arena) arena_chunk_tree_dirty_remove(&arena->chunks_dirty, chunk); } +#ifdef MALLOC_DOUBLE_PURGE + if (madvised) { + /* The chunk might already be in the list, but this + * makes sure it's at the front. */ + LinkedList_Remove(&chunk->chunks_madvised_elem); + LinkedList_InsertHead(&arena->chunks_madvised, &chunk->chunks_madvised_elem); + } +#endif } } @@ -4562,6 +4659,9 @@ arena_new(arena_t *arena) /* Initialize chunks. */ arena_chunk_tree_dirty_new(&arena->chunks_dirty); +#ifdef MALLOC_DOUBLE_PURGE + LinkedList_Init(&arena->chunks_madvised); +#endif arena->spare = NULL; arena->ndirty = 0; @@ -6381,6 +6481,78 @@ jemalloc_stats(jemalloc_stats_t *stats) assert(stats->committed >= stats->allocated); } +#ifdef MALLOC_DOUBLE_PURGE + +/* Explicitly remove all of this chunk's MADV_FREE'd pages from memory. */ +static void +hard_purge_chunk(arena_chunk_t *chunk) +{ + /* See similar logic in arena_purge(). */ + + size_t i; + for (i = arena_chunk_header_npages; i < chunk_npages; i++) { + /* Find all adjacent pages with CHUNK_MAP_MADVISED set. */ + size_t npages; + for (npages = 0; + chunk->map[i + npages].bits & CHUNK_MAP_MADVISED && i + npages < chunk_npages; + npages++) { + /* Turn off the chunk's MADV_FREED bit and turn on its + * DECOMMITTED bit. */ + assert(!(chunk->map[i + npages].bits & CHUNK_MAP_DECOMMITTED)); + chunk->map[i + npages].bits ^= CHUNK_MAP_MADVISED_OR_DECOMMITTED; + } + + /* We could use mincore to find out which pages are actually + * present, but it's not clear that's better. */ + if (npages > 0) { + pages_decommit(((char*)chunk) + (i << pagesize_2pow), npages << pagesize_2pow); + pages_commit(((char*)chunk) + (i << pagesize_2pow), npages << pagesize_2pow); + } + i += npages; + } +} + +/* Explicitly remove all of this arena's MADV_FREE'd pages from memory. */ +static void +hard_purge_arena(arena_t *arena) +{ + malloc_spin_lock(&arena->lock); + + while (!LinkedList_IsEmpty(&arena->chunks_madvised)) { + LinkedList* next = arena->chunks_madvised.next; + arena_chunk_t *chunk = + LinkedList_Get(arena->chunks_madvised.next, + arena_chunk_t, chunks_madvised_elem); + hard_purge_chunk(chunk); + LinkedList_Remove(&chunk->chunks_madvised_elem); + } + + malloc_spin_unlock(&arena->lock); +} + +void +jemalloc_purge_freed_pages() +{ + size_t i; + for (i = 0; i < narenas; i++) { + arena_t *arena = arenas[i]; + if (arena != NULL) + hard_purge_arena(arena); + } +} + +#else /* !defined MALLOC_DOUBLE_PURGE */ + +void +jemalloc_purge_freed_pages() +{ + /* Do nothing. */ +} + +#endif /* defined MALLOC_DOUBLE_PURGE */ + + + #ifdef MOZ_MEMORY_WINDOWS void* _recalloc(void *ptr, size_t count, size_t size) diff --git a/memory/jemalloc/jemalloc.h b/memory/jemalloc/jemalloc.h index 655d78b16f4..19817f59460 100644 --- a/memory/jemalloc/jemalloc.h +++ b/memory/jemalloc/jemalloc.h @@ -80,6 +80,33 @@ size_t malloc_usable_size(const void *ptr); void jemalloc_stats(jemalloc_stats_t *stats); +/* + * On some operating systems (Mac), we use madvise(MADV_FREE) to hand pages + * back to the operating system. On Mac, the operating system doesn't take + * this memory back immediately; instead, the OS takes it back only when the + * machine is running out of physical memory. + * + * This is great from the standpoint of efficiency, but it makes measuring our + * actual RSS difficult, because pages which we've MADV_FREE'd shouldn't count + * against our RSS. + * + * This function explicitly purges any MADV_FREE'd pages from physical memory, + * causing our reported RSS match the amount of memory we're actually using. + * + * Note that this call is expensive in two ways. First, it may be slow to + * execute, because it may make a number of slow syscalls to free memory. This + * function holds the big jemalloc locks, so basically all threads are blocked + * while this function runs. + * + * This function is also expensive in that the next time we go to access a page + * which we've just explicitly decommitted, the operating system has to attach + * to it a physical page! If we hadn't run this function, the OS would have + * less work to do. + * + * If MALLOC_DOUBLE_PURGE is not defined, this function does nothing. + */ +void jemalloc_purge_freed_pages(); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/memory/jemalloc/linkedlist.h b/memory/jemalloc/linkedlist.h new file mode 100644 index 00000000000..d7553141051 --- /dev/null +++ b/memory/jemalloc/linkedlist.h @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 8; c-basic-offset: 8; indent-tabs-mode: t -*- */ +/* vim:set softtabstop=8 shiftwidth=8 noet: */ +/*- + * Copyright (C) the Mozilla Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +#ifndef linkedlist_h__ +#define linkedlist_h__ + +#include + +typedef struct LinkedList_s LinkedList; + +struct LinkedList_s { + LinkedList *next; + LinkedList *prev; +}; + +/* Convert from LinkedList* to foo*. */ +#define LinkedList_Get(e, type, prop) \ + (type*)((char*)(e) - offsetof(type, prop)) + +/* Insert |e| at the beginning of |l|. */ +void LinkedList_InsertHead(LinkedList *l, LinkedList *e) +{ + e->next = l; + e->prev = l->prev; + e->next->prev = e; + e->prev->next = e; +} + +void LinkedList_Remove(LinkedList *e) +{ + e->prev->next = e->next; + e->next->prev = e->prev; + e->next = e; + e->prev = e; +} + +bool LinkedList_IsEmpty(LinkedList *e) +{ + return e->next == e; +} + +void LinkedList_Init(LinkedList *e) +{ + e->next = e; + e->prev = e; +} + +#endif From cb37c277f1fa026e67b31423e6dceec12d6d4036 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Mon, 24 Oct 2011 13:24:02 -0400 Subject: [PATCH 14/42] Bug 693404 - Part 2: On Mac, purge MADV_FREE'd pages before reading RSS. r=khuey --- .../telemetry/TelemetryHistograms.h | 3 ++ xpcom/base/nsMemoryReporterManager.cpp | 53 ++++++++++++------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index b8126905db3..d8c9a5f663a 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -84,6 +84,9 @@ HISTOGRAM(MEMORY_STORAGE_SQLITE, 1024, 512 * 1024, 50, EXPONENTIAL, "Memory used HISTOGRAM(MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED, 1024, 1024 * 1024, 50, EXPONENTIAL, "Memory used for uncompressed, in-use content images (KB)") HISTOGRAM(MEMORY_HEAP_ALLOCATED, 1024, 1024 * 1024, 50, EXPONENTIAL, "Heap memory allocated (KB)") HISTOGRAM(MEMORY_EXPLICIT, 1024, 1024 * 1024, 50, EXPONENTIAL, "Explicit memory allocations (KB)") +#if defined(XP_MACOSX) +HISTOGRAM(MEMORY_FREE_PURGED_PAGES_MS, 1, 1024, 10, EXPONENTIAL, "Time(ms) to purge MADV_FREE'd heap pages.") +#endif #if defined(XP_WIN) HISTOGRAM(EARLY_GLUESTARTUP_READ_OPS, 1, 100, 12, LINEAR, "ProcessIoCounters.ReadOperationCount before glue startup") HISTOGRAM(EARLY_GLUESTARTUP_READ_TRANSFER, 1, 50 * 1024, 12, EXPONENTIAL, "ProcessIoCounters.ReadTransferCount before glue startup (KB)") diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index d8385ccf821..87b708b9482 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -42,6 +42,27 @@ #include "nsMemoryReporterManager.h" #include "nsArrayEnumerator.h" #include "nsISimpleEnumerator.h" +#include "mozilla/Telemetry.h" + +using namespace mozilla; + +#if defined(MOZ_MEMORY) +# if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX) +# define HAVE_JEMALLOC_STATS 1 +# include "jemalloc.h" +# elif defined(XP_LINUX) +# define HAVE_JEMALLOC_STATS 1 +# include "jemalloc_types.h" +// jemalloc is directly linked into firefox-bin; libxul doesn't link +// with it. So if we tried to use jemalloc_stats directly here, it +// wouldn't be defined. Instead, we don't include the jemalloc header +// and weakly link against jemalloc_stats. +extern "C" { +extern void jemalloc_stats(jemalloc_stats_t* stats) + NS_VISIBILITY_DEFAULT __attribute__((weak)); +} +# endif // XP_LINUX +#endif // MOZ_MEMORY #if defined(XP_LINUX) || defined(XP_MACOSX) @@ -125,6 +146,20 @@ static PRInt64 GetVsize() static PRInt64 GetResident() { +#ifdef HAVE_JEMALLOC_STATS + // If we're using jemalloc on Mac, we need to instruct jemalloc to purge + // the pages it has madvise(MADV_FREE)'d before we read our RSS. The OS + // will take away MADV_FREE'd pages when there's memory pressure, so they + // shouldn't count against our RSS. + // + // Purging these pages shouldn't take more than 10ms or so, but we want to + // keep an eye on it since GetResident() is called on each Telemetry ping. + { + Telemetry::AutoTimer timer; + jemalloc_purge_freed_pages(); + } +#endif + task_basic_info ti; return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.resident_size : -1); } @@ -255,24 +290,6 @@ NS_MEMORY_REPORTER_IMPLEMENT(Resident, ** at least -- on OSX, there are sometimes other zones in use). **/ -#if defined(MOZ_MEMORY) -# if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX) -# define HAVE_JEMALLOC_STATS 1 -# include "jemalloc.h" -# elif defined(XP_LINUX) -# define HAVE_JEMALLOC_STATS 1 -# include "jemalloc_types.h" -// jemalloc is directly linked into firefox-bin; libxul doesn't link -// with it. So if we tried to use jemalloc_stats directly here, it -// wouldn't be defined. Instead, we don't include the jemalloc header -// and weakly link against jemalloc_stats. -extern "C" { -extern void jemalloc_stats(jemalloc_stats_t* stats) - NS_VISIBILITY_DEFAULT __attribute__((weak)); -} -# endif // XP_LINUX -#endif // MOZ_MEMORY - #if HAVE_JEMALLOC_STATS static PRInt64 GetHeapUnallocated() From 0b25fa3b45ee16dc498e13eedf81a6b19e122031 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Mon, 24 Oct 2011 11:24:29 -0700 Subject: [PATCH 15/42] Back out 564e841f1f57 (Bug 429592) for Linux64 opt orange --- modules/libpref/src/init/all.js | 8 - widget/src/windows/nsAppShell.cpp | 3 - xpcom/build/nsXPComInit.cpp | 12 +- xpcom/threads/HangMonitor.cpp | 236 ------------------------------ xpcom/threads/HangMonitor.h | 67 --------- xpcom/threads/Makefile.in | 7 - xpcom/threads/nsThread.cpp | 24 +-- xpcom/threads/nsThread.h | 9 +- xpcom/threads/nsThreadManager.cpp | 6 +- 9 files changed, 21 insertions(+), 351 deletions(-) delete mode 100644 xpcom/threads/HangMonitor.cpp delete mode 100644 xpcom/threads/HangMonitor.h diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 210affce8c9..6f73ab32c20 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1470,14 +1470,6 @@ pref("editor.positioning.offset", 0); pref("dom.max_chrome_script_run_time", 20); pref("dom.max_script_run_time", 10); -// Hang monitor timeout after which we kill the browser, in seconds -// (0 is disabled) -#ifndef DEBUG -pref("hangmonitor.timeout", 30); -#else -pref("hangmonitor.timeout", 0); -#endif - #ifndef DEBUG // How long a plugin is allowed to process a synchronous IPC message // before we consider it "hung". diff --git a/widget/src/windows/nsAppShell.cpp b/widget/src/windows/nsAppShell.cpp index cc3c06b9aea..ac98bf8d571 100644 --- a/widget/src/windows/nsAppShell.cpp +++ b/widget/src/windows/nsAppShell.cpp @@ -46,7 +46,6 @@ #include "nsString.h" #include "nsIMM32Handler.h" #include "mozilla/widget/AudioSession.h" -#include "mozilla/HangMonitor.h" // For skidmark code #include @@ -343,13 +342,11 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) ::PostQuitMessage(msg.wParam); Exit(); } else { - mozilla::HangMonitor::NotifyActivity(); ::TranslateMessage(&msg); ::DispatchMessageW(&msg); } } else if (mayWait) { // Block and wait for any posted application message - mozilla::HangMonitor::Suspend(); ::WaitMessage(); } } while (!gotMessage && mayWait); diff --git a/xpcom/build/nsXPComInit.cpp b/xpcom/build/nsXPComInit.cpp index 4150a32006c..d8ad19a0d80 100644 --- a/xpcom/build/nsXPComInit.cpp +++ b/xpcom/build/nsXPComInit.cpp @@ -139,7 +139,6 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **) #include "mozilla/Services.h" #include "mozilla/FunctionTimer.h" #include "mozilla/Omnijar.h" -#include "mozilla/HangMonitor.h" #include "nsChromeRegistry.h" #include "nsChromeProtocolHandler.h" @@ -531,8 +530,6 @@ NS_InitXPCOM2(nsIServiceManager* *result, mozilla::MapsMemoryReporter::Init(); - mozilla::HangMonitor::Startup(); - return NS_OK; } @@ -569,9 +566,6 @@ namespace mozilla { nsresult ShutdownXPCOM(nsIServiceManager* servMgr) { - // Make sure the hang monitor is enabled for shutdown. - HangMonitor::NotifyActivity(); - NS_ENSURE_STATE(NS_IsMainThread()); nsresult rv; @@ -629,8 +623,6 @@ ShutdownXPCOM(nsIServiceManager* servMgr) NS_ProcessPendingEvents(thread); - HangMonitor::NotifyActivity(); - // We save the "xpcom-shutdown-loaders" observers to notify after // the observerservice is gone. if (observerService) { @@ -740,9 +732,7 @@ ShutdownXPCOM(nsIServiceManager* servMgr) sExitManager = nsnull; } - Omnijar::CleanUp(); - - HangMonitor::Shutdown(); + mozilla::Omnijar::CleanUp(); NS_LogTerm(); diff --git a/xpcom/threads/HangMonitor.cpp b/xpcom/threads/HangMonitor.cpp deleted file mode 100644 index e5edece4383..00000000000 --- a/xpcom/threads/HangMonitor.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "mozilla/HangMonitor.h" -#include "mozilla/Monitor.h" -#include "mozilla/Preferences.h" -#include "nsExceptionHandler.h" -#include "nsXULAppAPI.h" -#include "nsThreadUtils.h" - -#ifdef XP_WIN -#include -#endif - -namespace mozilla { namespace HangMonitor { - -/** - * A flag which may be set from within a debugger to disable the hang - * monitor. - */ -volatile bool gDebugDisableHangMonitor = false; - -const char kHangMonitorPrefName[] = "hangmonitor.timeout"; - -// Monitor protects gShutdown and gTimeout, but not gTimestamp which rely on -// being atomically set by the processor; synchronization doesn't really matter -// in this use case. -Monitor* gMonitor; - -// The timeout preference, in seconds. -PRInt32 gTimeout; - -PRThread* gThread; - -// Set when shutdown begins to signal the thread to exit immediately. -bool gShutdown; - -// The timestamp of the last event notification, or PR_INTERVAL_NO_WAIT if -// we're currently not processing events. -volatile PRIntervalTime gTimestamp; - -// PrefChangedFunc -int -PrefChanged(const char*, void*) -{ - PRInt32 newval = Preferences::GetInt(kHangMonitorPrefName); - MonitorAutoLock lock(*gMonitor); - if (newval != gTimeout) { - gTimeout = newval; - lock.Notify(); - } - - return 0; -} - -void -Crash() -{ - if (gDebugDisableHangMonitor) { - return; - } - -#ifdef XP_WIN - if (::IsDebuggerPresent()) { - return; - } -#endif - - CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Hang"), - NS_LITERAL_CSTRING("1")); - - NS_RUNTIMEABORT("HangMonitor triggered"); -} - -void -ThreadMain(void*) -{ - MonitorAutoLock lock(*gMonitor); - - // In order to avoid issues with the hang monitor incorrectly triggering - // during a general system stop such as sleeping, the monitor thread must - // run twice to trigger hang protection. - PRIntervalTime lastTimestamp = 0; - int waitCount = 0; - - while (true) { - if (gShutdown) { - return; // Exit the thread - } - - // avoid rereading the volatile value in this loop - PRIntervalTime timestamp = gTimestamp; - - PRIntervalTime now = PR_IntervalNow(); - - if (timestamp != PR_INTERVAL_NO_WAIT && - now < timestamp) { - // 32-bit overflow, reset for another waiting period - timestamp = 1; // lowest legal PRInterval value - } - - if (timestamp != PR_INTERVAL_NO_WAIT && - timestamp == lastTimestamp && - gTimeout > 0) { - ++waitCount; - if (waitCount == 2) { - PRInt32 delay = - PRInt32(PR_IntervalToSeconds(now - timestamp)); - if (delay > gTimeout) { - MonitorAutoUnlock unlock(*gMonitor); - Crash(); - } - } - } - else { - lastTimestamp = timestamp; - waitCount = 0; - } - - PRIntervalTime timeout; - if (gTimeout <= 0) { - timeout = PR_INTERVAL_NO_TIMEOUT; - } - else { - timeout = PR_MillisecondsToInterval(gTimeout * 500); - } - lock.Wait(timeout); - } -} - -void -Startup() -{ - // The hang detector only runs in chrome processes. If you change this, - // you must also deal with the threadsafety of AnnotateCrashReport in - // non-chrome processes! - if (GeckoProcessType_Default != XRE_GetProcessType()) - return; - - NS_ASSERTION(!gMonitor, "Hang monitor already initialized"); - gMonitor = new Monitor("HangMonitor"); - - Preferences::RegisterCallback(PrefChanged, kHangMonitorPrefName, NULL); - PrefChanged(NULL, NULL); - - // Don't actually start measuring hangs until we hit the main event loop. - // This potentially misses a small class of really early startup hangs, - // but avoids dealing with some xpcshell tests and other situations which - // start XPCOM but don't ever start the event loop. - Suspend(); - - gThread = PR_CreateThread(PR_USER_THREAD, - ThreadMain, - NULL, PR_PRIORITY_LOW, PR_GLOBAL_THREAD, - PR_JOINABLE_THREAD, 0); -} - -void -Shutdown() -{ - if (GeckoProcessType_Default != XRE_GetProcessType()) - return; - - NS_ASSERTION(gMonitor, "Hang monitor not started"); - - { // Scope the lock we're going to delete later - MonitorAutoLock lock(*gMonitor); - gShutdown = true; - lock.Notify(); - } - - // thread creation could theoretically fail - if (gThread) { - PR_JoinThread(gThread); - gThread = NULL; - } - - delete gMonitor; - gMonitor = NULL; -} - -void -NotifyActivity() -{ - NS_ASSERTION(NS_IsMainThread(), "HangMonitor::Notify called from off the main thread."); - - // This is not a locked activity because PRTimeStamp is a 32-bit quantity - // which can be read/written atomically, and we don't want to pay locking - // penalties here. - gTimestamp = PR_IntervalNow(); -} - -void -Suspend() -{ - NS_ASSERTION(NS_IsMainThread(), "HangMonitor::Suspend called from off the main thread."); - - // Because gTimestamp changes this resets the wait count. - gTimestamp = PR_INTERVAL_NO_WAIT; -} - -} } // namespace mozilla::HangMonitor diff --git a/xpcom/threads/HangMonitor.h b/xpcom/threads/HangMonitor.h deleted file mode 100644 index b6f859fe8ce..00000000000 --- a/xpcom/threads/HangMonitor.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Firefox. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation . - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef mozilla_HangMonitor_h -#define mozilla_HangMonitor_h - -namespace mozilla { namespace HangMonitor { - -/** - * Start monitoring hangs. Should be called by the XPCOM startup process only. - */ -void Startup(); - -/** - * Stop monitoring hangs and join the thread. - */ -void Shutdown(); - -/** - * Notify the hang monitor of new activity which should reset its internal - * timer. - */ -void NotifyActivity(); - -/** - * Notify the hang monitor that the browser is now idle and no detection should - * be done. - */ -void Suspend(); - -} } // namespace mozilla::HangMonitor - -#endif // mozilla_HangMonitor_h diff --git a/xpcom/threads/Makefile.in b/xpcom/threads/Makefile.in index 915e7f23783..82214d2ab78 100644 --- a/xpcom/threads/Makefile.in +++ b/xpcom/threads/Makefile.in @@ -47,9 +47,7 @@ XPIDL_MODULE = xpcom_threads LIBRARY_NAME = xpcomthreads_s GRE_MODULE = 1 MOZILLA_INTERNAL_API = 1 -LIBXUL_LIBRARY = 1 -EXPORTS_NAMESPACES = mozilla CPPSRCS = \ nsEventQueue.cpp \ @@ -60,7 +58,6 @@ CPPSRCS = \ nsProcessCommon.cpp \ nsTimerImpl.cpp \ TimerThread.cpp \ - HangMonitor.cpp \ $(NULL) EXPORTS = \ @@ -69,10 +66,6 @@ EXPORTS = \ nsThreadUtilsInternal.h \ $(NULL) -EXPORTS_mozilla = \ - HangMonitor.h \ - $(NULL) - XPIDLSRCS = \ nsIEventTarget.idl \ nsIThread.idl \ diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 50214fa6dc6..0f33f9a01b3 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -45,7 +45,6 @@ #include "nsCOMPtr.h" #include "prlog.h" #include "nsThreadUtilsInternal.h" -#include "mozilla/HangMonitor.h" #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 || \ _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \ @@ -307,7 +306,20 @@ nsThread::ThreadFunc(void *arg) //----------------------------------------------------------------------------- -nsThread::nsThread(MainThreadFlag aMainThread, PRUint32 aStackSize) +nsThread::nsThread() + : mLock("nsThread.mLock") + , mEvents(&mEventsRoot) + , mPriority(PRIORITY_NORMAL) + , mThread(nsnull) + , mRunningEvent(0) + , mStackSize(0) + , mShutdownContext(nsnull) + , mShutdownRequired(false) + , mEventsAreDoomed(false) +{ +} + +nsThread::nsThread(PRUint32 aStackSize) : mLock("nsThread.mLock") , mEvents(&mEventsRoot) , mPriority(PRIORITY_NORMAL) @@ -317,7 +329,6 @@ nsThread::nsThread(MainThreadFlag aMainThread, PRUint32 aStackSize) , mShutdownContext(nsnull) , mShutdownRequired(false) , mEventsAreDoomed(false) - , mIsMainThread(aMainThread) { } @@ -574,9 +585,6 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result) NS_ENSURE_STATE(PR_GetCurrentThread() == mThread); - if (MAIN_THREAD == mIsMainThread && mayWait && !ShuttingDown()) - HangMonitor::Suspend(); - bool notifyGlobalObserver = (sGlobalObserver != nsnull); if (notifyGlobalObserver) sGlobalObserver->OnProcessNextEvent(this, mayWait && !ShuttingDown(), @@ -607,7 +615,7 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result) #ifdef NS_FUNCTION_TIMER char message[1024] = {'\0'}; - if (MAIN_THREAD == mIsMainThread) { + if (NS_IsMainThread()) { mozilla::FunctionTimer::ft_snprintf(message, sizeof(message), "@ Main Thread Event %p", (void*)event.get()); } @@ -620,8 +628,6 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result) if (event) { LOG(("THRD(%p) running [%p]\n", this, event.get())); - if (MAIN_THREAD == mIsMainThread) - HangMonitor::NotifyActivity(); event->Run(); } else if (mayWait) { NS_ASSERTION(ShuttingDown(), diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h index 83009a7a3be..750b33cce5f 100644 --- a/xpcom/threads/nsThread.h +++ b/xpcom/threads/nsThread.h @@ -57,12 +57,8 @@ public: NS_DECL_NSITHREADINTERNAL NS_DECL_NSISUPPORTSPRIORITY - enum MainThreadFlag { - MAIN_THREAD, - NOT_MAIN_THREAD - }; - - nsThread(MainThreadFlag aMainThread, PRUint32 aStackSize); + nsThread(); + nsThread(PRUint32 aStackSize); // Initialize this as a wrapper for a new PRThread. nsresult Init(); @@ -151,7 +147,6 @@ private: bool mShutdownPending; // Set to true when events posted to this thread will never run. bool mEventsAreDoomed; - MainThreadFlag mIsMainThread; }; //----------------------------------------------------------------------------- diff --git a/xpcom/threads/nsThreadManager.cpp b/xpcom/threads/nsThreadManager.cpp index 0cd4f737098..9524cc99b73 100644 --- a/xpcom/threads/nsThreadManager.cpp +++ b/xpcom/threads/nsThreadManager.cpp @@ -98,7 +98,7 @@ nsThreadManager::Init() mLock = new Mutex("nsThreadManager.mLock"); // Setup "main" thread - mMainThread = new nsThread(nsThread::MAIN_THREAD, 0); + mMainThread = new nsThread(); if (!mMainThread) return NS_ERROR_OUT_OF_MEMORY; @@ -224,7 +224,7 @@ nsThreadManager::GetCurrentThread() } // OK, that's fine. We'll dynamically create one :-) - nsRefPtr thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0); + nsRefPtr thread = new nsThread(); if (!thread || NS_FAILED(thread->InitCurrentThread())) return nsnull; @@ -239,7 +239,7 @@ nsThreadManager::NewThread(PRUint32 creationFlags, // No new threads during Shutdown NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); - nsThread *thr = new nsThread(nsThread::NOT_MAIN_THREAD, stackSize); + nsThread *thr = new nsThread(stackSize); if (!thr) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(thr); From bd3c94d9b742b42d03ab17bfab781195c06befc2 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Mon, 24 Oct 2011 11:25:04 -0700 Subject: [PATCH 16/42] Back out 52b481205766 (Bug 429592) for Linux64 opt orange --- toolkit/crashreporter/nsExceptionHandler.cpp | 31 ++++++++------------ toolkit/crashreporter/nsExceptionHandler.h | 5 ---- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp index d7cdb402cde..2ee223b5bc9 100644 --- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -205,7 +205,6 @@ static const int kAvailableVirtualMemoryParameterLen = sizeof(kAvailableVirtualMemoryParameter)-1; // this holds additional data sent via the API -static Mutex* crashReporterAPILock; static AnnotationTable* crashReporterAPIData_Hash; static nsCString* crashReporterAPIData = nsnull; static nsCString* notesField = nsnull; @@ -618,9 +617,6 @@ nsresult SetExceptionHandler(nsILocalFile* aXREDirectory, crashReporterAPIData = new nsCString(); NS_ENSURE_TRUE(crashReporterAPIData, NS_ERROR_OUT_OF_MEMORY); - NS_ASSERTION(!crashReporterAPILock, "Shouldn't have a lock yet"); - crashReporterAPILock = new Mutex("crashReporterAPILock"); - crashReporterAPIData_Hash = new nsDataHashtable(); NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY); @@ -1041,17 +1037,20 @@ nsresult UnsetExceptionHandler() // do this here in the unlikely case that we succeeded in allocating // our strings but failed to allocate gExceptionHandler. - delete crashReporterAPIData_Hash; - crashReporterAPIData_Hash = nsnull; + if (crashReporterAPIData_Hash) { + delete crashReporterAPIData_Hash; + crashReporterAPIData_Hash = nsnull; + } - delete crashReporterAPILock; - crashReporterAPILock = nsnull; + if (crashReporterAPIData) { + delete crashReporterAPIData; + crashReporterAPIData = nsnull; + } - delete crashReporterAPIData; - crashReporterAPIData = nsnull; - - delete notesField; - notesField = nsnull; + if (notesField) { + delete notesField; + notesField = nsnull; + } if (crashReporterPath) { NS_Free(crashReporterPath); @@ -1180,10 +1179,6 @@ nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data) return rv; if (XRE_GetProcessType() != GeckoProcessType_Default) { - if (!NS_IsMainThread()) { - NS_ERROR("Cannot call AnnotateCrashReport in child processes from non-main thread."); - return NS_ERROR_FAILURE; - } PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter(); if (!reporter) { EnqueueDelayedNote(new DelayedNote(key, data)); @@ -1194,8 +1189,6 @@ nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data) return NS_OK; } - MutexAutoLock lock(*crashReporterAPILock); - rv = crashReporterAPIData_Hash->Put(key, escapedData); NS_ENSURE_SUCCESS(rv, rv); diff --git a/toolkit/crashreporter/nsExceptionHandler.h b/toolkit/crashreporter/nsExceptionHandler.h index a8b5a9b9b74..4cda5204bea 100644 --- a/toolkit/crashreporter/nsExceptionHandler.h +++ b/toolkit/crashreporter/nsExceptionHandler.h @@ -64,12 +64,7 @@ bool GetServerURL(nsACString& aServerURL); nsresult SetServerURL(const nsACString& aServerURL); bool GetMinidumpPath(nsAString& aPath); nsresult SetMinidumpPath(const nsAString& aPath); - - -// AnnotateCrashReport may be called from any thread in a chrome process, -// but may only be called from the main thread in a content process. nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data); - nsresult AppendAppNotesToCrashReport(const nsACString& data); nsresult SetRestartArgs(int argc, char** argv); nsresult SetupExtraData(nsILocalFile* aAppDataDirectory, From 28f9023f8789c616ae7b5b0cb4553c02fe14698e Mon Sep 17 00:00:00 2001 From: Steve Workman Date: Tue, 4 Oct 2011 16:22:43 -0700 Subject: [PATCH 17/42] Bug 622232: Cancel DNS prefetches for HTML Anchor Elems after a tab is closed; r=mcmanus sr=bz --- .../html/content/src/nsHTMLAnchorElement.cpp | 17 ++- .../html/content/src/nsHTMLDNSPrefetch.cpp | 104 +++++++++++++++++- content/html/content/src/nsHTMLDNSPrefetch.h | 9 ++ netwerk/dns/nsDNSService2.cpp | 54 +++++++++ netwerk/dns/nsDNSService2.h | 1 + netwerk/dns/nsHostResolver.cpp | 44 ++++++++ netwerk/dns/nsHostResolver.h | 27 +++++ netwerk/dns/nsIDNSService.idl | 22 +++- netwerk/ipc/NeckoParent.cpp | 10 ++ netwerk/ipc/NeckoParent.h | 4 + netwerk/ipc/PNecko.ipdl | 1 + 11 files changed, 289 insertions(+), 4 deletions(-) diff --git a/content/html/content/src/nsHTMLAnchorElement.cpp b/content/html/content/src/nsHTMLAnchorElement.cpp index e99e9850924..ddd4be6f90e 100644 --- a/content/html/content/src/nsHTMLAnchorElement.cpp +++ b/content/html/content/src/nsHTMLAnchorElement.cpp @@ -131,12 +131,18 @@ public: virtual nsXPCClassInfo* GetClassInfo(); }; +// Indicates if a DNS Prefetch has been requested from this Anchor elem +#define HTML_ANCHOR_DNS_PREFETCH_REQUESTED \ + (1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET) + +// Make sure we have enough space for those bits +PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET < 32); NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor) nsHTMLAnchorElement::nsHTMLAnchorElement(already_AddRefed aNodeInfo) - : nsGenericHTMLElement(aNodeInfo), - Link(this) + : nsGenericHTMLElement(aNodeInfo) + , Link(this) { } @@ -206,6 +212,7 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, // Prefetch links if (aDocument && nsHTMLDNSPrefetch::IsAllowed(OwnerDoc())) { nsHTMLDNSPrefetch::PrefetchLow(this); + SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); } return rv; } @@ -217,6 +224,12 @@ nsHTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent) // be under a different xml:base, so forget the cached state now. Link::ResetLinkState(false); + // Cancel any DNS prefetches + if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_REQUESTED)) { + nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT); + UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); + } + nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); } diff --git a/content/html/content/src/nsHTMLDNSPrefetch.cpp b/content/html/content/src/nsHTMLDNSPrefetch.cpp index d4fd536e4ad..ee153ec3432 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.cpp +++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp @@ -192,7 +192,8 @@ nsHTMLDNSPrefetch::Prefetch(nsAString &hostname, PRUint16 flags) return NS_ERROR_NOT_AVAILABLE; nsCOMPtr tmpOutstanding; - return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), flags | nsIDNSService::RESOLVE_SPECULATE, + return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), + flags | nsIDNSService::RESOLVE_SPECULATE, sDNSListener, nsnull, getter_AddRefs(tmpOutstanding)); } @@ -214,6 +215,71 @@ nsHTMLDNSPrefetch::PrefetchHigh(nsAString &hostname) return Prefetch(hostname, 0); } +nsresult +nsHTMLDNSPrefetch::CancelPrefetch(Link *aElement, PRUint16 flags, nsresult aReason) +{ + nsAutoString hostname; + nsresult rv = aElement->GetHostname(hostname); + if (IsNeckoChild()) { + // Instead of transporting the Link object to the other process + // we are using the hostname based function here, too. Compared to the + // IPC the performance hit should be negligible. + NS_ENSURE_SUCCESS(rv,rv); + + // Forward the cancellation to the string based CancelPrefetch() + return CancelPrefetch(hostname, flags, aReason); + } + + if (!(sInitialized && sPrefetches && sDNSService && sDNSListener)) + return NS_ERROR_NOT_AVAILABLE; + + // Attempt to remove the prefetch request from the Deferrals FIFO first ... + bool found = false; + rv = sPrefetches->Remove(flags, aElement, &found); + NS_ENSURE_SUCCESS(rv, rv); + // ... If no request was found, it may have been sent to the DNS Service. + // Forward the cancellation to the string based CancelPrefetch + if (!found) + rv = CancelPrefetch(hostname, flags, aReason); + return rv; +} + +nsresult +nsHTMLDNSPrefetch::CancelPrefetch(nsAString &hostname, PRUint16 flags, nsresult aReason) +{ + // Forward this request to Necko Parent if we're a child process + if (IsNeckoChild()) { + // We need to check IsEmpty() because net_IsValidHostName() + // considers empty strings to be valid hostnames + if (!hostname.IsEmpty() && + net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) { + gNeckoChild->SendCancelHTMLDNSPrefetch(nsAutoString(hostname), flags, aReason); + } + return NS_OK; + } + + if (!(sInitialized && sDNSService && sPrefetches && sDNSListener)) + return NS_ERROR_NOT_AVAILABLE; + + // Forward cancellation to DNS service + return sDNSService->CancelAsyncResolve(NS_ConvertUTF16toUTF8(hostname), + flags | nsIDNSService::RESOLVE_SPECULATE, + sDNSListener, aReason); +} + +nsresult +nsHTMLDNSPrefetch::CancelPrefetchLow(Link *aElement, nsresult aReason) +{ + return CancelPrefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_LOW, aReason); +} + +nsresult +nsHTMLDNSPrefetch::CancelPrefetchLow(nsAString &hostname, nsresult aReason) +{ + return CancelPrefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW, aReason); +} + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTMLDNSPrefetch::nsListener, @@ -283,6 +349,42 @@ nsHTMLDNSPrefetch::nsDeferrals::Add(PRUint16 flags, Link *aElement) return NS_OK; } +nsresult +nsHTMLDNSPrefetch::nsDeferrals::Remove(PRUint16 aFlags, Link *aElement, bool *aFound) +{ + // The FIFO has no lock, so it can only be accessed on main thread + NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Remove must be on main thread"); + + // Search the deferrals FIFO for this Link elem and remove it + // Note: Element removal will leave holes in the queue. However: + // -- FIFO is flushed in SubmitQueue, so holes are temporary. + // -- holes are only created if a tab is closed before page is loaded. + bool found = false; + PRUint16 curr = mTail; + while (curr != mHead) { + nsCOMPtr content = do_QueryReferent(mEntries[curr].mElement); + if (content) { + nsCOMPtr link = do_QueryInterface(content); + if (link && (link == aElement) && (mEntries[curr].mFlags == aFlags)) { + // Null mElements will be ignored in SubmitQueue; requests won't be sent + mEntries[curr].mElement = NULL; + mEntries[curr].mFlags = 0; + found = true; + break; + } + } + curr = (curr + 1) & sMaxDeferredMask; + } + // Minor optimization: If we removed an element at the tail, increment the + // the tail end to shrink the FIFO. + if (found && (mTail != mHead)) + mTail = (mTail + 1) & sMaxDeferredMask; + + // Report "found" status back to caller + *aFound = found; + return NS_OK; +} + void nsHTMLDNSPrefetch::nsDeferrals::SubmitQueue() { diff --git a/content/html/content/src/nsHTMLDNSPrefetch.h b/content/html/content/src/nsHTMLDNSPrefetch.h index 7883ff07a4d..6c90fd085e8 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.h +++ b/content/html/content/src/nsHTMLDNSPrefetch.h @@ -87,10 +87,18 @@ public: static nsresult PrefetchHigh(nsAString &host); static nsresult PrefetchMedium(nsAString &host); static nsresult PrefetchLow(nsAString &host); + static nsresult CancelPrefetchLow(nsAString &host, nsresult aReason); + static nsresult CancelPrefetchLow(mozilla::dom::Link *aElement, nsresult aReason); private: static nsresult Prefetch(nsAString &host, PRUint16 flags); static nsresult Prefetch(mozilla::dom::Link *aElement, PRUint16 flags); + static nsresult CancelPrefetch(nsAString &hostname, + PRUint16 flags, + nsresult aReason); + static nsresult CancelPrefetch(mozilla::dom::Link *aElement, + PRUint16 flags, + nsresult aReason); public: class nsListener : public nsIDNSListener @@ -118,6 +126,7 @@ public: void Activate(); nsresult Add(PRUint16 flags, mozilla::dom::Link *aElement); + nsresult Remove(PRUint16 aFlags, mozilla::dom::Link *aElement, bool *aFound); private: ~nsDeferrals(); diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index 68ad8a526e0..8db8dd08e44 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -278,6 +278,10 @@ public: ~nsDNSAsyncRequest() {} void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); + // Returns TRUE if the DNS listener arg is the same as the member listener + // Used in Cancellations to remove DNS requests associated with a + // particular hostname and nsIDNSListener + bool EqualsAsyncListener(nsIDNSListener *aListener); nsRefPtr mResolver; nsCString mHost; // hostname we're resolving @@ -310,6 +314,12 @@ nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver, NS_RELEASE_THIS(); } +bool +nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) +{ + return (aListener == mListener); +} + NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSAsyncRequest, nsICancelable) NS_IMETHODIMP @@ -332,6 +342,7 @@ public: virtual ~nsDNSSyncRequest() {} void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); + bool EqualsAsyncListener(nsIDNSListener *aListener); bool mDone; nsresult mStatus; @@ -355,6 +366,13 @@ nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver, PR_ExitMonitor(mMonitor); } +bool +nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) +{ + // Sync request: no listener to compare + return false; +} + //----------------------------------------------------------------------------- nsDNSService::nsDNSService() @@ -584,6 +602,42 @@ nsDNSService::AsyncResolve(const nsACString &hostname, return rv; } +NS_IMETHODIMP +nsDNSService::CancelAsyncResolve(const nsACString &aHostname, + PRUint32 aFlags, + nsIDNSListener *aListener, + nsresult aReason) +{ + // grab reference to global host resolver and IDN service. beware + // simultaneous shutdown!! + nsRefPtr res; + nsCOMPtr idn; + { + MutexAutoLock lock(mLock); + + if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + + res = mResolver; + idn = mIDN; + } + if (!res) + return NS_ERROR_OFFLINE; + + nsCString hostname(aHostname); + + nsCAutoString hostACE; + if (idn && !IsASCII(aHostname)) { + if (NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE))) + hostname = hostACE; + } + + PRUint16 af = GetAFForLookup(hostname, aFlags); + + res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason); + return NS_OK; +} + NS_IMETHODIMP nsDNSService::Resolve(const nsACString &hostname, PRUint32 flags, diff --git a/netwerk/dns/nsDNSService2.h b/netwerk/dns/nsDNSService2.h index 1749b413fb0..1d33eabaaa5 100644 --- a/netwerk/dns/nsDNSService2.h +++ b/netwerk/dns/nsDNSService2.h @@ -38,6 +38,7 @@ #include "nsIIDNService.h" #include "nsIObserver.h" #include "nsHostResolver.h" +#include "nsICancelable.h" #include "nsAutoPtr.h" #include "nsString.h" #include "mozilla/Mutex.h" diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index 08fa76e2cec..a268179f22d 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -891,6 +891,50 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo NS_RELEASE(rec); } +void +nsHostResolver::CancelAsyncRequest(const char *host, + PRUint16 flags, + PRUint16 af, + nsIDNSListener *aListener, + nsresult status) + +{ + MutexAutoLock lock(mLock); + + // Lookup the host record associated with host, flags & address family + nsHostKey key = { host, flags, af }; + nsHostDBEnt *he = static_cast + (PL_DHashTableOperate(&mDB, &key, PL_DHASH_LOOKUP)); + if (he && he->rec) { + nsHostRecord* recPtr = NULL; + PRCList *node = he->rec->callbacks.next; + // Remove the first nsDNSAsyncRequest callback which matches the + // supplied listener object + while (node != &he->rec->callbacks) { + nsResolveHostCallback *callback + = static_cast(node); + if (callback && (callback->EqualsAsyncListener(aListener))) { + // Remove from the list of callbacks + PR_REMOVE_LINK(callback); + recPtr = he->rec; + callback->OnLookupComplete(this, recPtr, status); + break; + } + node = node->next; + } + + // If there are no more callbacks, remove the hash table entry + if (recPtr && PR_CLIST_IS_EMPTY(&recPtr->callbacks)) { + PL_DHashTableOperate(&mDB, (nsHostKey *)recPtr, PL_DHASH_REMOVE); + // If record is on a Queue, remove it and then deref it + if (recPtr->next != recPtr) { + PR_REMOVE_LINK(recPtr); + NS_RELEASE(recPtr); + } + } + } +} + //---------------------------------------------------------------------------- void diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h index b9e83ad5537..78ba065a53c 100644 --- a/netwerk/dns/nsHostResolver.h +++ b/netwerk/dns/nsHostResolver.h @@ -46,6 +46,7 @@ #include "mozilla/CondVar.h" #include "mozilla/Mutex.h" #include "nsISupportsImpl.h" +#include "nsIDNSListener.h" #include "nsString.h" #include "nsTArray.h" @@ -191,6 +192,20 @@ public: virtual void OnLookupComplete(nsHostResolver *resolver, nsHostRecord *record, nsresult status) = 0; + /** + * EqualsAsyncListener + * + * Determines if the listener argument matches the listener member var. + * For subclasses not implementing a member listener, should return false. + * For subclasses having a member listener, the function should check if + * they are the same. Used for cases where a pointer to an object + * implementing nsResolveHostCallback is unknown, but a pointer to + * the original listener is known. + * + * @param aListener + * nsIDNSListener object associated with the original request + */ + virtual bool EqualsAsyncListener(nsIDNSListener *aListener) = 0; }; /** @@ -244,6 +259,18 @@ public: nsResolveHostCallback *callback, nsresult status); + /** + * Cancels an async request associated with the hostname, flags, + * address family and listener. Cancels first callback found which matches + * these criteria. These parameters should correspond to the parameters + * passed to ResolveHost. If this is the last callback associated with the + * host record, it is removed from any request queues it might be on. + */ + void CancelAsyncRequest(const char *host, + PRUint16 flags, + PRUint16 af, + nsIDNSListener *aListener, + nsresult status); /** * values for the flags parameter passed to ResolveHost and DetachCallback * that may be bitwise OR'd together. diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl index 8a2a15c366f..b34327c461a 100644 --- a/netwerk/dns/nsIDNSService.idl +++ b/netwerk/dns/nsIDNSService.idl @@ -46,7 +46,7 @@ interface nsIDNSListener; /** * nsIDNSService */ -[scriptable, uuid(c1a56a45-8fa3-44e6-9f01-38c91c858cf9)] +[scriptable, uuid(F6E05CC3-8A13-463D-877F-D59B20B59724)] interface nsIDNSService : nsISupports { /** @@ -72,6 +72,26 @@ interface nsIDNSService : nsISupports in nsIDNSListener aListener, in nsIEventTarget aListenerTarget); + /** + * Attempts to cancel a previously requested async DNS lookup + * + * @param aHostName + * the hostname or IP-address-literal to resolve. + * @param aFlags + * a bitwise OR of the RESOLVE_ prefixed constants defined below. + * @param aListener + * the original listener which was to be notified about the host lookup + * result - used to match request information to requestor. + * @param aReason + * nsresult reason for the cancellation + * + * @return An object that can be used to cancel the host lookup. + */ + void cancelAsyncResolve(in AUTF8String aHostName, + in unsigned long aFlags, + in nsIDNSListener aListener, + in nsresult aReason); + /** * called to synchronously resolve a hostname. warning this method may * block the calling thread for a long period of time. it is extremely diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index 87309acecec..839b6a5df42 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -150,5 +150,15 @@ NeckoParent::RecvHTMLDNSPrefetch(const nsString& hostname, return true; } +bool +NeckoParent::RecvCancelHTMLDNSPrefetch(const nsString& hostname, + const PRUint16& flags, + const nsresult& reason) +{ + nsAutoString h(hostname); + nsHTMLDNSPrefetch::CancelPrefetch(h, flags, reason); + return true; +} + }} // mozilla::net diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index be133d3c47b..edae5c41e12 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -68,6 +68,10 @@ protected: virtual bool DeallocPWebSocket(PWebSocketParent*); virtual bool RecvHTMLDNSPrefetch(const nsString& hostname, const PRUint16& flags); + virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname, + const PRUint16& flags, + const nsresult& reason); + }; } // namespace net diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index a530c32dd68..d1a2ee77d15 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -69,6 +69,7 @@ parent: PWebSocket(PBrowser browser); HTMLDNSPrefetch(nsString hostname, PRUint16 flags); + CancelHTMLDNSPrefetch(nsString hostname, PRUint16 flags, nsresult reason); both: PHttpChannel(nullable PBrowser browser); From b6b0d97c7db5c7f12acf465fe15e8c918b10fdda Mon Sep 17 00:00:00 2001 From: Bernd Date: Mon, 24 Oct 2011 21:22:33 +0200 Subject: [PATCH 18/42] bug 695430 - stop trying to insert previously emptied framelists r=bzbarsky --- layout/tables/crashtests/695430-1.html | 23 +++++++++++++++++++++++ layout/tables/crashtests/crashtests.list | 1 + layout/tables/nsTableRowGroupFrame.cpp | 2 ++ 3 files changed, 26 insertions(+) create mode 100644 layout/tables/crashtests/695430-1.html diff --git a/layout/tables/crashtests/695430-1.html b/layout/tables/crashtests/695430-1.html new file mode 100644 index 00000000000..696fa731e97 --- /dev/null +++ b/layout/tables/crashtests/695430-1.html @@ -0,0 +1,23 @@ + + + + + +
+ + + + + + + + + + + +
+ diff --git a/layout/tables/crashtests/crashtests.list b/layout/tables/crashtests/crashtests.list index e2ab923e53a..a0f82cbcb0d 100644 --- a/layout/tables/crashtests/crashtests.list +++ b/layout/tables/crashtests/crashtests.list @@ -110,3 +110,4 @@ asserts(0-3) load 595758-1.xhtml # Bug 453871 load 595758-2.xhtml load 678447-1.html load 691824-1.xhtml +load 695430-1.html diff --git a/layout/tables/nsTableRowGroupFrame.cpp b/layout/tables/nsTableRowGroupFrame.cpp index 6f60ee27cee..4f50057e5ae 100644 --- a/layout/tables/nsTableRowGroupFrame.cpp +++ b/layout/tables/nsTableRowGroupFrame.cpp @@ -1032,6 +1032,8 @@ nsTableRowGroupFrame::UndoContinuedRow(nsPresContext* aPresContext, // will not have reflowed yet to pick up content from any overflow lines. overflows->DestroyFrame(aRow); + if (overflows->IsEmpty()) + return; // Put the overflow rows into our child list mFrames.InsertFrames(nsnull, rowBefore, *overflows); } From 6d48402fba7fe26cf7cd53498746fa45e2fd7cdd Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Thu, 22 Sep 2011 12:08:55 +0200 Subject: [PATCH 19/42] bug 684529 - remove script object. r=jorendorff --- dom/base/nsJSEnvironment.cpp | 2 +- js/src/frontend/BytecodeCompiler.cpp | 2 +- js/src/frontend/BytecodeGenerator.h | 2 +- js/src/jsapi.cpp | 34 ++++++----- js/src/jsapi.h | 2 +- js/src/jscntxtinlines.h | 4 +- js/src/jscompartment.cpp | 15 ++--- js/src/jscompartment.h | 7 ++- js/src/jsdbgapi.cpp | 3 - js/src/jsfun.cpp | 32 ++++------- js/src/jsfun.h | 3 +- js/src/jsgcmark.cpp | 4 +- js/src/jsobj.cpp | 29 ++++------ js/src/jsobj.h | 7 --- js/src/jsobjinlines.h | 7 +++ js/src/jsscript.cpp | 85 +++------------------------- js/src/jsscript.h | 49 ++++++---------- js/src/jsscriptinlines.h | 7 --- js/src/jsxdrapi.cpp | 5 +- js/src/shell/js.cpp | 7 +-- js/src/vm/Debugger.cpp | 79 +++++++++----------------- js/src/vm/Debugger.h | 48 ++++++---------- js/src/vm/GlobalObject.h | 2 +- js/xpconnect/src/nsXPConnect.cpp | 9 --- 24 files changed, 145 insertions(+), 299 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index a86c6356ef7..d420f1b8950 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -1643,7 +1643,7 @@ nsJSContext::ExecuteScript(void *aScriptObject, nsCOMPtr principal; rv = sSecurityManager->GetObjectPrincipal(mContext, - JS_GetObjectFromScript(script), + JS_GetGlobalFromScript(script), getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index e37ec809762..9a5f9d97a9a 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -72,7 +72,7 @@ BytecodeCompiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame bool inDirectivePrologue; JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT | - TCF_COMPILE_FOR_EVAL | TCF_NEED_SCRIPT_OBJECT))); + TCF_COMPILE_FOR_EVAL | TCF_NEED_SCRIPT_GLOBAL))); /* * The scripted callerFrame can only be given for compile-and-go scripts diff --git a/js/src/frontend/BytecodeGenerator.h b/js/src/frontend/BytecodeGenerator.h index fcf515837a4..464b6e29a47 100644 --- a/js/src/frontend/BytecodeGenerator.h +++ b/js/src/frontend/BytecodeGenerator.h @@ -275,7 +275,7 @@ struct StmtInfo { /* * The caller is JS_Compile*Script*. */ -#define TCF_NEED_SCRIPT_OBJECT 0x40000000 +#define TCF_NEED_SCRIPT_GLOBAL 0x40000000 /* * Flags to check for return; vs. return expr; in a function. diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index b5af4f611fd..5e216eb27ee 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1278,30 +1278,33 @@ JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target) return reinterpret_cast(call); } +namespace js { + // Declared in jscompartment.h -JSClass js_dummy_class = { +Class dummy_class = { "jdummy", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, NULL, - JSCLASS_NO_OPTIONAL_MEMBERS + JS_ConvertStub }; +} /*namespace js */ + JS_PUBLIC_API(JSCrossCompartmentCall *) JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target) { CHECK_REQUEST(cx); - - JSObject *scriptObject = target->u.object; - if (!scriptObject) { + JS_ASSERT(!target->isCachedEval); + GlobalObject *global = target->u.globalObject; + if (!global) { SwitchToCompartment sc(cx, target->compartment()); - scriptObject = JS_NewGlobalObject(cx, &js_dummy_class); - if (!scriptObject) + global = GlobalObject::create(cx, &dummy_class); + if (!global) return NULL; } - return JS_EnterCrossCompartmentCall(cx, scriptObject); + return JS_EnterCrossCompartmentCall(cx, global); } JS_PUBLIC_API(JSCrossCompartmentCall *) @@ -4582,7 +4585,7 @@ CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, JSPrincipals *p assertSameCompartment(cx, obj, principals); AutoLastFrameCheck lfc(cx); - uint32 tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT | TCF_NEED_SCRIPT_OBJECT; + uint32 tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT | TCF_NEED_SCRIPT_GLOBAL; return BytecodeCompiler::compileScript(cx, obj, NULL, principals, tcflags, chars, length, filename, lineno, version); } @@ -4759,7 +4762,7 @@ CompileFileHelper(JSContext *cx, JSObject *obj, JSPrincipals *principals, JS_ASSERT(i <= len); len = i; - uint32 tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT | TCF_NEED_SCRIPT_OBJECT; + uint32 tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT | TCF_NEED_SCRIPT_GLOBAL; script = BytecodeCompiler::compileScript(cx, obj, NULL, principals, tcflags, buf, len, filename, 1, cx->findVersion()); cx->free_(buf); @@ -4820,11 +4823,12 @@ JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, FILE *f } JS_PUBLIC_API(JSObject *) -JS_GetObjectFromScript(JSScript *script) +JS_GetGlobalFromScript(JSScript *script) { - JS_ASSERT(script->u.object); + JS_ASSERT(!script->isCachedEval); + JS_ASSERT(script->u.globalObject); - return script->u.object; + return script->u.globalObject; } static JSFunction * @@ -5018,7 +5022,7 @@ EvaluateUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, { JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); - uint32 flags = TCF_COMPILE_N_GO | TCF_NEED_SCRIPT_OBJECT; + uint32 flags = TCF_COMPILE_N_GO | TCF_NEED_SCRIPT_GLOBAL; if (!rval) flags |= TCF_NO_SCRIPT_RVAL; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 6ff1d5f4032..f004601d852 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3817,7 +3817,7 @@ JS_CompileFileHandleForPrincipalsVersion(JSContext *cx, JSObject *obj, JSVersion version); extern JS_PUBLIC_API(JSObject *) -JS_GetObjectFromScript(JSScript *script); +JS_GetGlobalFromScript(JSScript *script); extern JS_PUBLIC_API(JSFunction *) JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 3541dfc37aa..f9c36dc62ce 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -208,8 +208,8 @@ class CompartmentChecker void check(JSScript *script) { if (script) { check(script->compartment()); - if (script->u.object) - check(script->u.object); + if (!script->isCachedEval && script->u.globalObject) + check(script->u.globalObject); } } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 151bd03acde..014e0f7e456 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -278,7 +278,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp) if (vp->isObject()) { JSObject *obj = &vp->toObject(); JS_ASSERT(obj->isCrossCompartmentWrapper()); - if (global->getJSClass() != &js_dummy_class && obj->getParent() != global) { + if (global->getClass() != &dummy_class && obj->getParent() != global) { do { obj->setParent(global); obj = obj->getProto(); @@ -861,14 +861,11 @@ JSCompartment::getBreakpointSite(jsbytecode *pc) } BreakpointSite * -JSCompartment::getOrCreateBreakpointSite(JSContext *cx, JSScript *script, jsbytecode *pc, JSObject *scriptObject) +JSCompartment::getOrCreateBreakpointSite(JSContext *cx, JSScript *script, jsbytecode *pc, + GlobalObject *scriptGlobal) { JS_ASSERT(script->code <= pc); JS_ASSERT(pc < script->code + script->length); - JS_ASSERT_IF(scriptObject, scriptObject->isScript() || scriptObject->isFunction()); - JS_ASSERT_IF(scriptObject && scriptObject->isFunction(), - scriptObject->getFunctionPrivate()->script() == script); - JS_ASSERT_IF(scriptObject && scriptObject->isScript(), scriptObject->getScript() == script); BreakpointSiteMap::AddPtr p = breakpointSites.lookupForAdd(pc); if (!p) { @@ -880,10 +877,10 @@ JSCompartment::getOrCreateBreakpointSite(JSContext *cx, JSScript *script, jsbyte } BreakpointSite *site = p->value; - if (site->scriptObject) - JS_ASSERT_IF(scriptObject, site->scriptObject == scriptObject); + if (site->scriptGlobal) + JS_ASSERT_IF(scriptGlobal, site->scriptGlobal == scriptGlobal); else - site->scriptObject = scriptObject; + site->scriptGlobal = scriptGlobal; return site; } diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 475280fed26..072aeb679c8 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -293,10 +293,11 @@ struct TraceMonitor { namespace mjit { class JaegerCompartment; } -} /* Defined in jsapi.cpp */ -extern JSClass js_dummy_class; +extern Class dummy_class; + +} /* namespace js */ #ifndef JS_EVAL_CACHE_SHIFT # define JS_EVAL_CACHE_SHIFT 6 @@ -615,7 +616,7 @@ struct JS_FRIEND_API(JSCompartment) { js::BreakpointSite *getBreakpointSite(jsbytecode *pc); js::BreakpointSite *getOrCreateBreakpointSite(JSContext *cx, JSScript *script, jsbytecode *pc, - JSObject *scriptObject); + js::GlobalObject *scriptGlobal); void clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSScript *script, JSObject *handler); void clearTraps(JSContext *cx, JSScript *script); bool markTrapClosuresIteratively(JSTracer *trc); diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index e00c25ba75f..935b872ba3b 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -1049,9 +1049,6 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script) JSPrincipals *principals; nbytes = sizeof *script; - if (script->u.object) - nbytes += JS_GetObjectTotalSize(cx, script->u.object); - nbytes += script->length * sizeof script->code[0]; nbytes += script->natoms * sizeof script->atoms[0]; for (size_t i = 0; i < script->natoms; i++) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index e5495c5b24e..16edfdc769c 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1558,6 +1558,7 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) uint32 flagsword; /* word for argument count and fun->flags */ cx = xdr->cx; + JSScript *script; if (xdr->mode == JSXDR_ENCODE) { fun = (*objp)->getFunctionPrivate(); if (!fun->isInterpreted()) { @@ -1570,12 +1571,14 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) } firstword = (fun->u.i.skipmin << 2) | !!fun->atom; flagsword = (fun->nargs << 16) | fun->flags; + script = fun->script(); } else { fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL); if (!fun) return false; fun->clearParent(); fun->clearType(); + script = NULL; } AutoObjectRooter tvr(cx, fun); @@ -1587,28 +1590,20 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) if (!JS_XDRUint32(xdr, &flagsword)) return false; + if (!js_XDRScript(xdr, &script)) + return false; + if (xdr->mode == JSXDR_DECODE) { fun->nargs = flagsword >> 16; JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED); fun->flags = uint16(flagsword); fun->u.i.skipmin = uint16(firstword >> 2); - } - - /* - * Don't directly store into fun->u.i.script because we want this to happen - * at the same time as we set the script's owner. - */ - JSScript *script = fun->script(); - if (!js_XDRScript(xdr, &script)) - return false; - - if (xdr->mode == JSXDR_DECODE) { - *objp = fun; fun->setScript(script); - if (!fun->script()->typeSetFunction(cx, fun)) + if (!script->typeSetFunction(cx, fun)) return false; JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs()); js_CallNewScriptHook(cx, fun->script(), fun); + *objp = fun; } return true; @@ -1674,10 +1669,8 @@ fun_trace(JSTracer *trc, JSObject *obj) if (fun->atom) MarkString(trc, fun->atom, "atom"); - if (fun->isInterpreted() && fun->script()) { - CheckScriptOwner(fun->script(), obj); + if (fun->isInterpreted() && fun->script()) MarkScript(trc, fun->script(), "script"); - } } static void @@ -2421,19 +2414,18 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JS_ASSERT(script); JS_ASSERT(script->compartment() == fun->compartment()); JS_ASSERT(script->compartment() != cx->compartment); - JS_OPT_ASSERT(script->ownerObject == fun); cfun->u.i.script_ = NULL; JSScript *cscript = js_CloneScript(cx, script); if (!cscript) return NULL; - + cscript->u.globalObject = cfun->getGlobal(); cfun->setScript(cscript); - if (!cfun->script()->typeSetFunction(cx, cfun)) + if (!cscript->typeSetFunction(cx, cfun)) return NULL; js_CallNewScriptHook(cx, cfun->script(), cfun); - Debugger::onNewScript(cx, cfun->script(), cfun, NULL); + Debugger::onNewScript(cx, cfun->script(), NULL); } } return clone; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index d23f791932f..c0ac305bd52 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -203,10 +203,9 @@ struct JSFunction : public JSObject_Slots2 void setScript(JSScript *script) { JS_ASSERT(isInterpreted()); u.i.script_ = script; - script->setOwnerObject(this); } - JSScript * maybeScript() const { + JSScript *maybeScript() const { return isInterpreted() ? script() : NULL; } diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index 8849ee48380..c3ed16f2376 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -832,8 +832,8 @@ MarkChildren(JSTracer *trc, JSScript *script) MarkValueRange(trc, constarray->length, constarray->vector, "consts"); } - if (!script->isCachedEval && script->u.object) - MarkObject(trc, *script->u.object, "object"); + if (!script->isCachedEval && script->u.globalObject) + MarkObject(trc, *script->u.globalObject, "object"); if (IS_GC_MARKING_TRACER(trc) && script->filename) js_MarkScriptFilename(script->filename); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index be077bd2ac6..9a60882062b 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1126,7 +1126,6 @@ class EvalScriptGuard void setNewScript(JSScript *script) { /* NewScriptFromCG has already called js_CallNewScriptHook. */ JS_ASSERT(!script_ && script); - script->setOwnerObject(JS_CACHED_SCRIPT); script_ = script; script_->isActiveEval = true; } @@ -6751,25 +6750,21 @@ js_GetClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey, JS_ASSERT(protoKey < JSProto_LIMIT); if (protoKey != JSProto_Null) { - if (!scopeobj) { - if (cx->hasfp()) - scopeobj = &cx->fp()->scopeChain(); - if (!scopeobj) { - scopeobj = cx->globalObject; - if (!scopeobj) { - *protop = NULL; - return true; - } - } - } - scopeobj = scopeobj->getGlobal(); - if (scopeobj->isGlobal()) { - const Value &v = scopeobj->getReservedSlot(JSProto_LIMIT + protoKey); - if (v.isObject()) { - *protop = &v.toObject(); + GlobalObject *global; + if (scopeobj) { + global = scopeobj->getGlobal(); + } else { + global = GetCurrentGlobal(cx); + if (!global) { + *protop = NULL; return true; } } + const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey); + if (v.isObject()) { + *protop = &v.toObject(); + return true; + } } return FindClassPrototype(cx, scopeobj, protoKey, protop, clasp); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 55c031eb2e6..89500f5145d 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1184,12 +1184,6 @@ struct JSObject : js::gc::Cell { inline js::NativeIterator *getNativeIterator() const; inline void setNativeIterator(js::NativeIterator *); - /* - * Script-related getters. - */ - - inline JSScript *getScript() const; - /* * XML-related getters and setters. */ @@ -1465,7 +1459,6 @@ struct JSObject : js::gc::Cell { inline bool isCall() const { return clasp == &js::CallClass; } inline bool isDeclEnv() const { return clasp == &js::DeclEnvClass; } inline bool isRegExp() const { return clasp == &js::RegExpClass; } - inline bool isScript() const { return clasp == &js::ScriptClass; } inline bool isGenerator() const { return clasp == &js::GeneratorClass; } inline bool isIterator() const { return clasp == &js::IteratorClass; } inline bool isStopIteration() const { return clasp == &js::StopIterationClass; } diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 94d624bf121..1aa29ad8f38 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -1472,6 +1472,13 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *p return NewNativeClassInstance(cx, clasp, proto, parent, kind); } +inline GlobalObject * +GetCurrentGlobal(JSContext *cx) +{ + JSObject *scopeChain = (cx->hasfp()) ? &cx->fp()->scopeChain() : cx->globalObject; + return scopeChain ? scopeChain->getGlobal() : NULL; +} + bool FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop, Class *clasp); diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index ae296c6b149..21ed77ade9b 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -306,14 +306,6 @@ CheckScript(JSScript *script, JSScript *prev) } } -void -CheckScriptOwner(JSScript *script, JSObject *owner) -{ - JS_OPT_ASSERT(script->ownerObject == owner); - if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT) - JS_OPT_ASSERT(script->compartment() == owner->compartment()); -} - #endif /* JS_CRASH_DIAGNOSTICS */ } /* namespace js */ @@ -756,37 +748,6 @@ JSPCCounters::destroy(JSContext *cx) } } -static void -script_trace(JSTracer *trc, JSObject *obj) -{ - JSScript *script = (JSScript *) obj->getPrivate(); - if (script) { - CheckScriptOwner(script, obj); - MarkScript(trc, script, "script"); - } -} - -JS_FRIEND_DATA(Class) js::ScriptClass = { - "Script", - JSCLASS_HAS_PRIVATE | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - JS_PropertyStub, /* addProperty */ - JS_PropertyStub, /* delProperty */ - JS_PropertyStub, /* getProperty */ - JS_StrictPropertyStub, /* setProperty */ - JS_EnumerateStub, - JS_ResolveStub, - JS_ConvertStub, - NULL, /* finalize */ - NULL, /* reserved0 */ - NULL, /* checkAccess */ - NULL, /* call */ - NULL, /* construct */ - NULL, /* xdrObject */ - NULL, /* hasInstance */ - script_trace -}; - /* * Shared script filename management. */ @@ -965,7 +926,6 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom PodZero(script); #ifdef JS_CRASH_DIAGNOSTICS script->cookie1[0] = script->cookie2[0] = JS_SCRIPT_COOKIE; - script->ownerObject = JS_NEW_SCRIPT; #endif #if JS_SCRIPT_INLINE_DATA_LIMIT if (!data) @@ -1241,23 +1201,26 @@ JSScript::NewScriptFromCG(JSContext *cx, CodeGenerator *cg) return NULL; fun->setScript(script); + script->u.globalObject = fun->getParent() ? fun->getParent()->getGlobal() : NULL; } else { /* * Initialize script->object, if necessary, so that the debugger has a * valid holder object. */ - if ((cg->flags & TCF_NEED_SCRIPT_OBJECT) && !js_NewScriptObject(cx, script)) - return NULL; + if (cg->flags & TCF_NEED_SCRIPT_GLOBAL) + script->u.globalObject = GetCurrentGlobal(cx); } /* Tell the debugger about this compiled script. */ js_CallNewScriptHook(cx, script, fun); if (!cg->parent) { - JSObject *owner = fun ? fun : script->u.object; GlobalObject *compileAndGoGlobal = NULL; - if (script->compileAndGo) - compileAndGoGlobal = (owner ? owner : cg->scopeChain())->getGlobal(); - Debugger::onNewScript(cx, script, owner, compileAndGoGlobal); + if (script->compileAndGo) { + compileAndGoGlobal = script->u.globalObject; + if (!compileAndGoGlobal) + compileAndGoGlobal = cg->scopeChain()->getGlobal(); + } + Debugger::onNewScript(cx, script, compileAndGoGlobal); } return script; @@ -1288,15 +1251,6 @@ JSScript::dataSize(JSUsableSizeFun usf) return usable ? usable : dataSize(); } -void -JSScript::setOwnerObject(JSObject *owner) -{ -#ifdef JS_CRASH_DIAGNOSTICS - CheckScriptOwner(this, JS_NEW_SCRIPT); - ownerObject = owner; -#endif -} - /* * Nb: srcnotes are variable-length. This function computes the number of * srcnote *slots*, which may be greater than the number of srcnotes. @@ -1371,27 +1325,6 @@ JSScript::finalize(JSContext *cx) } } -JSObject * -js_NewScriptObject(JSContext *cx, JSScript *script) -{ - JS_ASSERT(!script->u.object); - - JSObject *obj = NewNonFunction(cx, &ScriptClass, NULL, NULL); - if (!obj) - return NULL; - obj->setPrivate(script); - script->u.object = obj; - script->setOwnerObject(obj); - - /* - * Clear the object's type/proto, to avoid entraining stuff. Once we no longer use the parent - * for security checks, then we can clear the parent, too. - */ - obj->clearType(); - - return obj; -} - namespace js { static const uint32 GSN_CACHE_THRESHOLD = 100; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index a4143311350..d18f6a07825 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -426,9 +426,6 @@ class JSPCCounters { static const uint32 JS_SCRIPT_COOKIE = 0xc00cee; -static JSObject * const JS_NEW_SCRIPT = (JSObject *)0x12345678; -static JSObject * const JS_CACHED_SCRIPT = (JSObject *)0x12341234; - struct JSScript : public js::gc::Cell { /* * Two successively less primitive ways to make a new JSScript. The first @@ -561,19 +558,20 @@ struct JSScript : public js::gc::Cell { union { /* - * A script object of class ScriptClass, to ensure the script is GC'd. + * A global object for the script. * - All scripts returned by JSAPI functions (JS_CompileScript, - * JS_CompileFile, etc.) have these objects. - * - Function scripts never have script objects; such scripts are owned - * by their function objects. + * JS_CompileFile, etc.) have a non-null globalObject. + * - A function script has a globalObject if the function comes from a + * compile-and-go script. * - Temporary scripts created by obj_eval, JS_EvaluateScript, and - * similar functions never have these objects; such scripts are - * explicitly destroyed by the code that created them. + * similar functions never have the globalObject field set; for such + * scripts the global should be extracted from the JS frame that + * execute scripts. */ - JSObject *object; + js::GlobalObject *globalObject; /* Hash table chaining for JSCompartment::evalCache. */ - JSScript *evalHashLink; + JSScript *evalHashLink; } u; uint32 *closedSlots; /* vector of closed slots; args first, then vars. */ @@ -582,14 +580,10 @@ struct JSScript : public js::gc::Cell { JSPCCounters pcCounters; #ifdef JS_CRASH_DIAGNOSTICS - JSObject *ownerObject; - /* All diagnostic fields must be multiples of Cell::CellSize. */ - uint32 cookie2[sizeof(JSObject *) == 4 ? 1 : 2]; + uint32 cookie2[Cell::CellSize / sizeof(uint32)]; #endif - void setOwnerObject(JSObject *owner); - #ifdef DEBUG /* * Unique identifier within the compartment for this script, used for @@ -638,6 +632,11 @@ struct JSScript : public js::gc::Cell { inline void clearNesting(); + /* Return creation time global or null. */ + js::GlobalObject *getGlobalObjectOrNull() const { + return isCachedEval ? NULL : u.globalObject; + } + private: bool makeTypes(JSContext *cx, JSFunction *fun); bool makeAnalysis(JSContext *cx); @@ -685,7 +684,7 @@ struct JSScript : public js::gc::Cell { /* Size of the JITScript and all sections. (This method is implemented in MethodJIT.h.) */ JS_FRIEND_API(size_t) jitDataSize(JSUsableSizeFun usf); - + #endif jsbytecode *main() { @@ -803,7 +802,7 @@ struct JSScript : public js::gc::Cell { * count-style interface.) */ bool setStepModeFlag(JSContext *cx, bool step); - + /* * Increment or decrement the single-step count. If the count is non-zero or * the flag (set by setStepModeFlag) is set, then the script is in @@ -848,9 +847,6 @@ StackDepth(JSScript *script) JS_END_MACRO -extern JSObject * -js_InitScriptClass(JSContext *cx, JSObject *obj); - extern void js_MarkScriptFilename(const char *filename); @@ -873,19 +869,11 @@ namespace js { #ifdef JS_CRASH_DIAGNOSTICS -void -CheckScriptOwner(JSScript *script, JSObject *owner); - void CheckScript(JSScript *script, JSScript *prev); #else -inline void -CheckScriptOwner(JSScript *script, JSObject *owner) -{ -} - inline void CheckScript(JSScript *script, JSScript *prev) { @@ -895,9 +883,6 @@ CheckScript(JSScript *script, JSScript *prev) } /* namespace js */ -extern JSObject * -js_NewScriptObject(JSContext *cx, JSScript *script); - /* * To perturb as little code as possible, we introduce a js_GetSrcNote lookup * cache without adding an explicit cx parameter. Thus js_GetSrcNote becomes diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index c993eaf788a..4eaa0f5db02 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -215,11 +215,4 @@ JSScript::clearNesting() } } -inline JSScript * -JSObject::getScript() const -{ - JS_ASSERT(isScript()); - return static_cast(getPrivate()); -} - #endif /* jsscriptinlines_h___ */ diff --git a/js/src/jsxdrapi.cpp b/js/src/jsxdrapi.cpp index 36e57aa3221..4f3b16649c0 100644 --- a/js/src/jsxdrapi.cpp +++ b/js/src/jsxdrapi.cpp @@ -723,10 +723,9 @@ JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) if (xdr->mode == JSXDR_DECODE) { JS_ASSERT(!script->compileAndGo); - if (!js_NewScriptObject(xdr->cx, script)) - return false; + script->u.globalObject = GetCurrentGlobal(xdr->cx); js_CallNewScriptHook(xdr->cx, script, NULL); - Debugger::onNewScript(xdr->cx, script, script->u.object, NULL); + Debugger::onNewScript(xdr->cx, script, NULL); *scriptp = script; } diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 3b8d3a6c465..cd640129326 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1562,9 +1562,7 @@ ValueToScript(JSContext *cx, jsval v, JSFunction **funp = NULL) JSObject *obj = JSVAL_TO_OBJECT(v); JSClass *clasp = JS_GET_CLASS(cx, obj); - if (clasp == Jsvalify(&ScriptClass)) { - script = (JSScript *) JS_GetPrivate(cx, obj); - } else if (clasp == Jsvalify(&GeneratorClass)) { + if (clasp == Jsvalify(&GeneratorClass)) { JSGenerator *gen = (JSGenerator *) JS_GetPrivate(cx, obj); fun = gen->floatingFrame()->fun(); script = fun->script(); @@ -1624,8 +1622,7 @@ GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp, v = argv[0]; intarg = 0; if (!JSVAL_IS_PRIMITIVE(v) && - (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&FunctionClass) || - JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&ScriptClass))) { + JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == Jsvalify(&FunctionClass)) { script = ValueToScript(cx, v); if (!script) return JS_FALSE; diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 718f844c86f..075a785e3f0 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -88,7 +88,6 @@ extern Class DebuggerScript_class; enum { JSSLOT_DEBUGSCRIPT_OWNER, - JSSLOT_DEBUGSCRIPT_HOLDER, /* PrivateValue, cross-compartment pointer */ JSSLOT_DEBUGSCRIPT_COUNT }; @@ -125,7 +124,7 @@ ReportObjectRequired(JSContext *cx) /*** Breakpoints *********************************************************************************/ BreakpointSite::BreakpointSite(JSScript *script, jsbytecode *pc) - : script(script), pc(pc), realOpcode(JSOp(*pc)), scriptObject(NULL), enabledCount(0), + : script(script), pc(pc), realOpcode(JSOp(*pc)), scriptGlobal(NULL), enabledCount(0), trapHandler(NULL), trapClosure(UndefinedValue()) { JS_ASSERT(realOpcode != JSOP_TRAP); @@ -136,11 +135,11 @@ BreakpointSite::BreakpointSite(JSScript *script, jsbytecode *pc) * Precondition: script is live, meaning either it is a non-held script that is * on the stack or a held script that hasn't been GC'd. */ -static JSObject * -ScriptScope(JSContext *cx, JSScript *script, JSObject *holder) +static GlobalObject * +ScriptGlobal(JSContext *cx, JSScript *script, GlobalObject *scriptGlobal) { - if (holder) - return holder; + if (scriptGlobal) + return scriptGlobal; /* * The referent is a non-held script. There is no direct reference from @@ -149,9 +148,9 @@ ScriptScope(JSContext *cx, JSScript *script, JSObject *holder) for (AllFramesIter i(cx->stack.space()); ; ++i) { JS_ASSERT(!i.done()); if (i.fp()->maybeScript() == script) - return &i.fp()->scopeChain(); + return i.fp()->scopeChain().getGlobal(); } - JS_NOT_REACHED("ScriptScope: live non-held script not on stack"); + JS_NOT_REACHED("ScriptGlobal: live non-held script not on stack"); } bool @@ -161,7 +160,7 @@ BreakpointSite::recompile(JSContext *cx, bool forTrap) if (script->hasJITCode()) { Maybe ac; if (!forTrap) { - ac.construct(cx, ScriptScope(cx, script, scriptObject)); + ac.construct(cx, ScriptGlobal(cx, script, scriptGlobal)); if (!ac.ref().enter()) return false; } @@ -733,7 +732,7 @@ Debugger::fireEnterFrame(JSContext *cx) } void -Debugger::fireNewScript(JSContext *cx, JSScript *script, JSObject *obj) +Debugger::fireNewScript(JSContext *cx, JSScript *script) { JSObject *hook = getHook(OnNewScript); JS_ASSERT(hook); @@ -743,7 +742,7 @@ Debugger::fireNewScript(JSContext *cx, JSScript *script, JSObject *obj) if (!ac.enter()) return; - JSObject *dsobj = wrapScript(cx, script, obj); + JSObject *dsobj = wrapScript(cx, script); if (!dsobj) { handleUncaughtException(ac, NULL, false); return; @@ -816,8 +815,7 @@ AddNewScriptRecipients(GlobalObject::DebuggerVector *src, AutoValueVector *dest) } void -Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj, - GlobalObject *compileAndGoGlobal) +Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, GlobalObject *compileAndGoGlobal) { JS_ASSERT(script->compileAndGo == !!compileAndGoGlobal); @@ -849,7 +847,7 @@ Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj, Debugger *dbg = Debugger::fromJSObject(&p->toObject()); if ((!compileAndGoGlobal || dbg->debuggees.has(compileAndGoGlobal)) && dbg->enabled && dbg->getHook(OnNewScript)) { - dbg->fireNewScript(cx, script, obj); + dbg->fireNewScript(cx, script); } } } @@ -1782,15 +1780,7 @@ static inline JSScript * GetScriptReferent(JSObject *obj) { JS_ASSERT(obj->getClass() == &DebuggerScript_class); - return (JSScript *) obj->getPrivate(); -} - -static inline JSObject * -GetScriptHolder(JSObject *obj) -{ - JS_ASSERT(obj->getClass() == &DebuggerScript_class); - Value v = obj->getReservedSlot(JSSLOT_DEBUGSCRIPT_HOLDER); - return (JSObject *) v.toPrivate(); + return static_cast(obj->getPrivate()); } static void @@ -1799,11 +1789,6 @@ DebuggerScript_trace(JSTracer *trc, JSObject *obj) if (!trc->context->runtime->gcCurrentCompartment) { if (JSScript *script = GetScriptReferent(obj)) MarkScript(trc, script, "Debugger.Script referent"); - Value v = obj->getReservedSlot(JSSLOT_DEBUGSCRIPT_HOLDER); - if (!v.isUndefined()) { - if (JSObject *obj = (JSObject *) v.toPrivate()) - MarkObject(trc, *obj, "Debugger.Script referent holder"); - } } } @@ -1821,7 +1806,7 @@ Class DebuggerScript_class = { }; JSObject * -Debugger::newDebuggerScript(JSContext *cx, JSScript *script, JSObject *holder) +Debugger::newDebuggerScript(JSContext *cx, JSScript *script) { assertSameCompartment(cx, object); @@ -1832,21 +1817,18 @@ Debugger::newDebuggerScript(JSContext *cx, JSScript *script, JSObject *holder) return NULL; scriptobj->setPrivate(script); scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_OWNER, ObjectValue(*object)); - scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_HOLDER, PrivateValue(holder)); return scriptobj; } JSObject * -Debugger::wrapScript(JSContext *cx, JSScript *script, JSObject *obj) +Debugger::wrapScript(JSContext *cx, JSScript *script) { assertSameCompartment(cx, object); JS_ASSERT(cx->compartment != script->compartment()); - JS_ASSERT_IF(obj, script->compartment() == obj->compartment()); - CellWeakMap::AddPtr p = scripts.lookupForAdd(script); if (!p) { - JSObject *scriptobj = newDebuggerScript(cx, script, obj); + JSObject *scriptobj = newDebuggerScript(cx, script); /* The allocation may have caused a GC, which can remove table entries. */ if (!scriptobj || !scripts.relookupOrAdd(p, script, scriptobj)) @@ -1857,12 +1839,6 @@ Debugger::wrapScript(JSContext *cx, JSScript *script, JSObject *obj) return p->value; } -JSObject * -Debugger::wrapFunctionScript(JSContext *cx, JSFunction *fun) -{ - return wrapScript(cx, fun->script(), fun); -} - static JSObject * DebuggerScript_check(JSContext *cx, const Value &v, const char *clsname, const char *fnname) { @@ -1879,15 +1855,14 @@ DebuggerScript_check(JSContext *cx, const Value &v, const char *clsname, const c /* * Check for Debugger.Script.prototype, which is of class DebuggerScript_class - * but whose holding object is undefined. + * but whose script is null. */ - if (thisobj->getReservedSlot(JSSLOT_DEBUGSCRIPT_HOLDER).isUndefined()) { + if (!GetScriptReferent(thisobj)) { JS_ASSERT(!GetScriptReferent(thisobj)); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, clsname, fnname, "prototype object"); return NULL; } - JS_ASSERT(GetScriptReferent(thisobj)); return thisobj; } @@ -1954,7 +1929,8 @@ DebuggerScript_getChildScripts(JSContext *cx, uintN argc, Value *vp) for (uint32 i = script->savedCallerFun ? 1 : 0; i < objects->length; i++) { JSObject *obj = objects->vector[i]; if (obj->isFunction()) { - JSObject *s = dbg->wrapFunctionScript(cx, (JSFunction *) obj); + JSFunction *fun = static_cast(obj); + JSObject *s = dbg->wrapScript(cx, fun->script()); if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s))) return false; } @@ -2257,8 +2233,8 @@ DebuggerScript_setBreakpoint(JSContext *cx, uintN argc, Value *vp) THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "setBreakpoint", args, obj, script); Debugger *dbg = Debugger::fromChildJSObject(obj); - JSObject *holder = GetScriptHolder(obj); - if (!dbg->observesScope(ScriptScope(cx, script, holder))) { + GlobalObject *scriptGlobal = script->getGlobalObjectOrNull(); + if (!dbg->observesGlobal(ScriptGlobal(cx, script, scriptGlobal))) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_DEBUGGING); return false; } @@ -2273,7 +2249,7 @@ DebuggerScript_setBreakpoint(JSContext *cx, uintN argc, Value *vp) JSCompartment *comp = script->compartment(); jsbytecode *pc = script->code + offset; - BreakpointSite *site = comp->getOrCreateBreakpointSite(cx, script, pc, holder); + BreakpointSite *site = comp->getOrCreateBreakpointSite(cx, script, pc, scriptGlobal); if (!site) return false; if (site->inc(cx)) { @@ -2628,7 +2604,7 @@ DebuggerFrame_getScript(JSContext *cx, uintN argc, Value *vp) if (fp->isFunctionFrame() && !fp->isEvalFrame()) { JSFunction *callee = fp->callee().getFunctionPrivate(); if (callee->isInterpreted()) { - scriptObject = debug->wrapFunctionScript(cx, callee); + scriptObject = debug->wrapScript(cx, callee->script()); if (!scriptObject) return false; } @@ -2638,8 +2614,7 @@ DebuggerFrame_getScript(JSContext *cx, uintN argc, Value *vp) * frames. */ JSScript *script = fp->script(); - scriptObject = debug->wrapScript(cx, script, - script->isCachedEval ? NULL : script->u.object); + scriptObject = debug->wrapScript(cx, script); if (!scriptObject) return false; } @@ -2744,7 +2719,7 @@ EvaluateInScope(JSContext *cx, JSObject *scobj, StackFrame *fp, const jschar *ch */ JSScript *script = BytecodeCompiler::compileScript(cx, scobj, fp, fp->scopeChain().principals(cx), - TCF_COMPILE_N_GO | TCF_NEED_SCRIPT_OBJECT, + TCF_COMPILE_N_GO | TCF_NEED_SCRIPT_GLOBAL, chars, length, filename, lineno, cx->findVersion(), NULL, UpvarCookie::UPVAR_LEVEL_LIMIT); @@ -3056,7 +3031,7 @@ DebuggerObject_getScript(JSContext *cx, uintN argc, Value *vp) if (!fun->isInterpreted()) return true; - JSObject *scriptObject = dbg->wrapFunctionScript(cx, fun); + JSObject *scriptObject = dbg->wrapScript(cx, fun->script()); if (!scriptObject) return false; diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 2495503e175..f8bed2db234 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -201,7 +201,7 @@ class Debugger { static void slowPathOnEnterFrame(JSContext *cx); static void slowPathOnLeaveFrame(JSContext *cx); - static void slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj, + static void slowPathOnNewScript(JSContext *cx, JSScript *script, GlobalObject *compileAndGoGlobal); static JSTrapStatus dispatchHook(JSContext *cx, js::Value *vp, Hook which); @@ -210,18 +210,16 @@ class Debugger { void fireEnterFrame(JSContext *cx); /* - * Allocate and initialize a Debugger.Script instance whose referent is |script| and - * whose holder is |obj|. If |obj| is NULL, this creates a Debugger.Script whose holder - * is null, for non-held scripts. + * Allocate and initialize a Debugger.Script instance whose referent is + * |script|. */ - JSObject *newDebuggerScript(JSContext *cx, JSScript *script, JSObject *obj); + JSObject *newDebuggerScript(JSContext *cx, JSScript *script); /* * Receive a "new script" event from the engine. A new script was compiled - * or deserialized. For eval scripts obj must be null, otherwise it must be - * a script object. + * or deserialized. */ - void fireNewScript(JSContext *cx, JSScript *script, JSObject *obj); + void fireNewScript(JSContext *cx, JSScript *script); static inline Debugger *fromLinks(JSCList *links); inline Breakpoint *firstBreakpoint() const; @@ -262,7 +260,7 @@ class Debugger { static inline void onLeaveFrame(JSContext *cx); static inline JSTrapStatus onDebuggerStatement(JSContext *cx, js::Value *vp); static inline JSTrapStatus onExceptionUnwind(JSContext *cx, js::Value *vp); - static inline void onNewScript(JSContext *cx, JSScript *script, JSObject *obj, + static inline void onNewScript(JSContext *cx, JSScript *script, GlobalObject *compileAndGoGlobal); static JSTrapStatus onTrap(JSContext *cx, Value *vp); static JSTrapStatus onSingleStep(JSContext *cx, Value *vp); @@ -271,7 +269,7 @@ class Debugger { inline bool observesEnterFrame() const; inline bool observesNewScript() const; - inline bool observesScope(JSObject *obj) const; + inline bool observesGlobal(GlobalObject *global) const; inline bool observesFrame(StackFrame *fp) const; /* @@ -331,21 +329,12 @@ class Debugger { */ bool newCompletionValue(AutoCompartment &ac, bool ok, Value val, Value *vp); - /* - * Return the Debugger.Script object for |fun|'s script, or create a new - * one if needed. The context |cx| must be in the debugger compartment; - * |fun| must be a cross-compartment wrapper referring to the JSFunction in - * a debuggee compartment. - */ - JSObject *wrapFunctionScript(JSContext *cx, JSFunction *fun); - /* * Return the Debugger.Script object for |script|, or create a new one if - * needed. The context |cx| must be in the debugger compartment; |script| must - * be a script in a debuggee compartment. |obj| is either the script holder or - * null for non-held scripts. + * needed. The context |cx| must be in the debugger compartment; |script| + * must be a script in a debuggee compartment. */ - JSObject *wrapScript(JSContext *cx, JSScript *script, JSObject *obj); + JSObject *wrapScript(JSContext *cx, JSScript *script); private: /* Prohibit copying. */ @@ -369,7 +358,7 @@ class BreakpointSite { * cached eval scripts and for JSD1 traps. It is always non-null for JSD2 * breakpoints in held scripts. */ - JSObject *scriptObject; + GlobalObject *scriptGlobal; JSCList breakpoints; /* cyclic list of all js::Breakpoints at this instruction */ size_t enabledCount; /* number of breakpoints in the list that are enabled */ @@ -383,7 +372,7 @@ class BreakpointSite { Breakpoint *firstBreakpoint() const; bool hasBreakpoint(Breakpoint *bp); bool hasTrap() const { return !!trapHandler; } - JSObject *getScriptObject() const { return scriptObject; } + GlobalObject *getScriptGlobal() const { return scriptGlobal; } bool inc(JSContext *cx); void dec(JSContext *cx); @@ -475,15 +464,15 @@ Debugger::observesNewScript() const } bool -Debugger::observesScope(JSObject *obj) const +Debugger::observesGlobal(GlobalObject *global) const { - return debuggees.has(obj->getGlobal()); + return debuggees.has(global); } bool Debugger::observesFrame(StackFrame *fp) const { - return observesScope(&fp->scopeChain()); + return observesGlobal(fp->scopeChain().getGlobal()); } void @@ -517,13 +506,12 @@ Debugger::onExceptionUnwind(JSContext *cx, js::Value *vp) } void -Debugger::onNewScript(JSContext *cx, JSScript *script, JSObject *obj, - GlobalObject *compileAndGoGlobal) +Debugger::onNewScript(JSContext *cx, JSScript *script, GlobalObject *compileAndGoGlobal) { JS_ASSERT_IF(script->compileAndGo, compileAndGoGlobal); JS_ASSERT_IF(!script->compileAndGo, !compileAndGoGlobal); if (!script->compartment()->getDebuggees().empty()) - slowPathOnNewScript(cx, script, obj, compileAndGoGlobal); + slowPathOnNewScript(cx, script, compileAndGoGlobal); } extern JSBool diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 9c4aa9d8cab..6363f1c7d24 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -373,7 +373,7 @@ js::GlobalObject * JSObject::asGlobal() { JS_ASSERT(isGlobal()); - return reinterpret_cast(this); + return static_cast(this); } #endif /* GlobalObject_h___ */ diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 462b8fffd5f..a1c34a02f1b 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -779,15 +779,6 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) if (si) { JS_snprintf(name, sizeof(name), "JS Object (%s - %s)", clazz->name, si->GetJSClass()->name); - } else if (clazz == &js::ScriptClass) { - JSScript* script = (JSScript*) xpc_GetJSPrivate(obj); - if (script->filename) { - JS_snprintf(name, sizeof(name), - "JS Object (Script - %s)", - script->filename); - } else { - JS_snprintf(name, sizeof(name), "JS Object (Script)"); - } } else if (clazz == &js::FunctionClass) { JSFunction* fun = (JSFunction*) xpc_GetJSPrivate(obj); JSString* str = JS_GetFunctionId(fun); From 308444aebee91f16c139e139f1a200f97f628913 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Mon, 24 Oct 2011 09:38:41 -0400 Subject: [PATCH 20/42] Bug 696603: Handle null being passed to BlobBuilder.append. r=sicking --- content/base/src/nsDOMBlobBuilder.cpp | 5 ++++- content/base/test/test_blobbuilder.html | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/content/base/src/nsDOMBlobBuilder.cpp b/content/base/src/nsDOMBlobBuilder.cpp index b2c4c4d0a87..c0b99c59de5 100644 --- a/content/base/src/nsDOMBlobBuilder.cpp +++ b/content/base/src/nsDOMBlobBuilder.cpp @@ -372,7 +372,10 @@ nsDOMBlobBuilder::Append(const jsval& aData, JSContext* aCx) // Is it an object? if (JSVAL_IS_OBJECT(aData)) { JSObject* obj = JSVAL_TO_OBJECT(aData); - NS_ASSERTION(obj, "Er, what?"); + if (!obj) { + // We got passed null. Just do nothing. + return NS_OK; + } // Is it a Blob? nsCOMPtr blob = do_QueryInterface( diff --git a/content/base/test/test_blobbuilder.html b/content/base/test/test_blobbuilder.html index 8ead5756841..7b767c78245 100644 --- a/content/base/test/test_blobbuilder.html +++ b/content/base/test/test_blobbuilder.html @@ -37,6 +37,9 @@ ok(false, "NOT REACHED"); ok(true, "an empty argument to append should throw"); } +blobBuilder.append(null); +// Yay we didn't crash. + blobBuilder.append("squiggle"); let blob1 = blobBuilder.getBlob(); ok(blob1 instanceof Blob, "getBlob should produce Blobs"); From 8be4fe1430842263302a01b4d834217e1e4764fb Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 24 Oct 2011 15:59:10 -0400 Subject: [PATCH 21/42] Bug 694802 - VC11 cannot compile nsWindow.cpp because it #includes ; r=bbondy --- widget/src/windows/nsWindow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index d35d14a0a7d..61ef59cecc4 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -178,7 +178,6 @@ #include "nsNativeDragTarget.h" #include // needed for WIN32_LEAN_AND_MEAN #include -#include #include #if defined(ACCESSIBILITY) From 5e7a740f3696f260df1b9a3e7b2c0ae4aed7fd14 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Mon, 24 Oct 2011 16:01:11 -0400 Subject: [PATCH 22/42] Bug 692911: Refactor some VERSION_CHANGE transaction locking stuff and disallow transaction creation before databases are completely open. r=bent --- dom/indexedDB/AsyncConnectionHelper.cpp | 4 +- dom/indexedDB/AsyncConnectionHelper.h | 5 ++ dom/indexedDB/DatabaseInfo.cpp | 3 +- dom/indexedDB/DatabaseInfo.h | 4 +- dom/indexedDB/IDBDatabase.cpp | 32 +++++++ dom/indexedDB/IDBDatabase.h | 3 + dom/indexedDB/IndexedDatabaseManager.cpp | 79 +++++++++++++--- dom/indexedDB/IndexedDatabaseManager.h | 22 +++++ dom/indexedDB/OpenDatabaseHelper.cpp | 30 ++++++- dom/indexedDB/OpenDatabaseHelper.h | 1 + dom/indexedDB/TransactionThreadPool.cpp | 16 +--- dom/indexedDB/TransactionThreadPool.h | 6 -- dom/indexedDB/test/Makefile.in | 1 + .../test/event_propagation_iframe.html | 2 +- dom/indexedDB/test/leaving_page_iframe.html | 2 +- .../test/test_autoIncrement_indexes.html | 3 + dom/indexedDB/test/test_clear.html | 2 +- .../test_create_index_with_integer_keys.html | 4 +- dom/indexedDB/test/test_cursor_mutation.html | 2 +- dom/indexedDB/test/test_getAll.html | 2 + dom/indexedDB/test/test_index_getAll.html | 4 +- .../test/test_index_getAllObjects.html | 2 +- .../test/test_index_object_cursors.html | 2 +- dom/indexedDB/test/test_indexes.html | 2 +- .../test/test_indexes_bad_values.html | 2 + dom/indexedDB/test/test_object_identity.html | 2 +- dom/indexedDB/test/test_odd_result_order.html | 2 +- dom/indexedDB/test/test_open_objectStore.html | 2 +- .../test/test_overlapping_transactions.html | 2 +- .../test/test_readonly_transactions.html | 3 + .../test/test_setVersion_exclusion.html | 90 +++++++++++++++++++ .../test/test_success_events_after_abort.html | 2 +- .../test/test_transaction_abort.html | 3 + .../test/test_transaction_lifetimes.html | 2 +- .../test_transaction_lifetimes_nested.html | 2 +- .../test/test_writer_starvation.html | 2 +- 36 files changed, 289 insertions(+), 58 deletions(-) create mode 100644 dom/indexedDB/test/test_setVersion_exclusion.html diff --git a/dom/indexedDB/AsyncConnectionHelper.cpp b/dom/indexedDB/AsyncConnectionHelper.cpp index 19e87f8c63b..df2b7b340d4 100644 --- a/dom/indexedDB/AsyncConnectionHelper.cpp +++ b/dom/indexedDB/AsyncConnectionHelper.cpp @@ -560,8 +560,8 @@ TransactionPoolEventTarget::Dispatch(nsIRunnable* aRunnable, NS_ASSERTION(aRunnable, "Null pointer!"); NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "Unsupported!"); - TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); - NS_ENSURE_TRUE(pool, NS_ERROR_FAILURE); + TransactionThreadPool* pool = TransactionThreadPool::Get(); + NS_ASSERTION(pool, "This should never be null!"); return pool->Dispatch(mTransaction, aRunnable, false, nsnull); } diff --git a/dom/indexedDB/AsyncConnectionHelper.h b/dom/indexedDB/AsyncConnectionHelper.h index b0038d48e3a..6f72a567f4a 100644 --- a/dom/indexedDB/AsyncConnectionHelper.h +++ b/dom/indexedDB/AsyncConnectionHelper.h @@ -125,6 +125,11 @@ public: static IDBTransaction* GetCurrentTransaction(); + bool HasTransaction() + { + return mTransaction; + } + nsISupports* GetSource() { return mRequest ? mRequest->Source() : nsnull; diff --git a/dom/indexedDB/DatabaseInfo.cpp b/dom/indexedDB/DatabaseInfo.cpp index f7ed1136285..0433aa75da8 100644 --- a/dom/indexedDB/DatabaseInfo.cpp +++ b/dom/indexedDB/DatabaseInfo.cpp @@ -85,7 +85,8 @@ EnumerateObjectStoreNames(const nsAString& aKey, DatabaseInfo::DatabaseInfo() : id(0), nextObjectStoreId(1), - nextIndexId(1) + nextIndexId(1), + runningVersionChange(false) { MOZ_COUNT_CTOR(DatabaseInfo); } diff --git a/dom/indexedDB/DatabaseInfo.h b/dom/indexedDB/DatabaseInfo.h index 1fdab7e3285..1f2ff398e0a 100644 --- a/dom/indexedDB/DatabaseInfo.h +++ b/dom/indexedDB/DatabaseInfo.h @@ -54,7 +54,8 @@ struct DatabaseInfo ~DatabaseInfo(); #else DatabaseInfo() - : id(0), nextObjectStoreId(1), nextIndexId(1) { } + : id(0), nextObjectStoreId(1), nextIndexId(1), runningVersionChange(false) + { } #endif static bool Get(PRUint32 aId, @@ -73,6 +74,7 @@ struct DatabaseInfo nsString filePath; PRInt64 nextObjectStoreId; PRInt64 nextIndexId; + bool runningVersionChange; nsAutoRefCnt referenceCount; }; diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 34c86d40e61..a87a325c75b 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -428,6 +428,34 @@ IDBDatabase::IsClosed() return mClosed; } +void +IDBDatabase::EnterSetVersionTransaction() +{ + DatabaseInfo* dbInfo; + if (!DatabaseInfo::Get(mDatabaseId, &dbInfo)) { + NS_ERROR("This should never fail!"); + } + + NS_ASSERTION(!dbInfo->runningVersionChange, "How did that happen?"); + dbInfo->runningVersionChange = true; +} + +void +IDBDatabase::ExitSetVersionTransaction() +{ + DatabaseInfo* dbInfo; + if (!DatabaseInfo::Get(mDatabaseId, &dbInfo)) { + NS_ERROR("This should never fail!"); + } + + NS_ASSERTION(dbInfo->runningVersionChange, "How did that happen?"); + dbInfo->runningVersionChange = false; + + IndexedDatabaseManager* manager = IndexedDatabaseManager::Get(); + NS_ASSERTION(manager, "We should always have a manager here"); + manager->UnblockSetVersionRunnable(this); +} + void IDBDatabase::OnUnlink() { @@ -710,6 +738,10 @@ IDBDatabase::Transaction(nsIVariant* aStoreNames, NS_ERROR("This should never fail!"); } + if (info->runningVersionChange) { + return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; + } + nsTArray storesToOpen; switch (type) { diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h index 5a2ab7f582e..9df3cb4f3c2 100644 --- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -135,6 +135,9 @@ public: // Whether or not the database has had Close called on it. bool IsClosed(); + void EnterSetVersionTransaction(); + void ExitSetVersionTransaction(); + private: IDBDatabase(); ~IDBDatabase(); diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index e611fa5676e..baa3784f00a 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -693,19 +693,15 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) // Now run the helper if there are no more live databases. if (runnable->mHelper && runnable->mDatabases.IsEmpty()) { - // Don't hold the callback alive longer than necessary. - nsRefPtr helper; - helper.swap(runnable->mHelper); + // At this point, all databases are closed, so no new transactions can + // be started. There may, however, still be outstanding transactions + // that have not completed. We need to wait for those before we + // dispatch the helper. - if (NS_FAILED(helper->DispatchToTransactionPool())) { - NS_WARNING("Failed to dispatch to thread pool!"); - } + TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); - // Now wait for the transaction to complete. Completing the transaction - // will be our cue to remove the SetVersionRunnable from our list and - // therefore allow other SetVersion requests to begin. - TransactionThreadPool* pool = TransactionThreadPool::Get(); - NS_ASSERTION(pool, "This should never be null!"); + nsRefPtr waitRunnable = + new WaitForTransactionsToFinishRunnable(runnable); // All other databases should be closed, so we only need to wait on this // one. @@ -714,8 +710,8 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) NS_ERROR("This should never fail!"); } - // Use the SetVersionRunnable as the callback. - if (!pool->WaitForAllDatabasesToComplete(array, runnable)) { + // Use the WaitForTransactionsToFinishRunnable as the callback. + if (!pool->WaitForAllDatabasesToComplete(array, waitRunnable)) { NS_WARNING("Failed to wait for transaction to complete!"); } } @@ -724,6 +720,27 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) } } +void +IndexedDatabaseManager::UnblockSetVersionRunnable(IDBDatabase* aDatabase) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aDatabase, "Null pointer!"); + + // Check through the list of SetVersionRunnables to find the one we're seeking. + for (PRUint32 index = 0; index < mSetVersionRunnables.Length(); index++) { + nsRefPtr& runnable = mSetVersionRunnables[index]; + + if (runnable->mRequestingDatabase->Id() == aDatabase->Id()) { + NS_ASSERTION(!runnable->mHelper, + "Why are we unblocking a runnable if the helper didn't run?"); + NS_DispatchToCurrentThread(runnable); + return; + } + } + + NS_NOTREACHED("How did we get here!"); +} + // static bool IndexedDatabaseManager::SetCurrentDatabase(IDBDatabase* aDatabase) @@ -1283,3 +1300,39 @@ IndexedDatabaseManager::SetVersionRunnable::Run() return NS_OK; } + +NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::WaitForTransactionsToFinishRunnable, + nsIRunnable) + +NS_IMETHODIMP +IndexedDatabaseManager::WaitForTransactionsToFinishRunnable::Run() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + // Don't hold the callback alive longer than necessary. + nsRefPtr helper; + helper.swap(mRunnable->mHelper); + + nsRefPtr runnable; + runnable.swap(mRunnable); + + // If the helper has a transaction, dispatch it to the transaction + // threadpool. + if (helper->HasTransaction()) { + if (NS_FAILED(helper->DispatchToTransactionPool())) { + NS_WARNING("Failed to dispatch to thread pool!"); + } + } + // Otherwise, dispatch it to the IO thread. + else { + IndexedDatabaseManager* manager = IndexedDatabaseManager::Get(); + NS_ASSERTION(manager, "We should definitely have a manager here"); + + helper->Dispatch(manager->IOThread()); + } + + // The helper is responsible for calling + // IndexedDatabaseManager::UnblockSetVersionRunnable. + + return NS_OK; +} diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index 853d239482a..7287394d233 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -204,6 +204,8 @@ private: // Called when AsyncUsageRunnable has finished its Run() method. inline void OnUsageCheckComplete(AsyncUsageRunnable* aRunnable); + void UnblockSetVersionRunnable(IDBDatabase* aDatabase); + // Responsible for waiting until all databases have been closed before running // the version change transaction. Created when // IndexedDatabaseManager::SetDatabaseVersion is called. Runs only once on the @@ -227,6 +229,26 @@ private: // Called when SetVersionRunnable has finished its Run() method. inline void OnSetVersionRunnableComplete(SetVersionRunnable* aRunnable); + + // A callback runnable used by the TransactionPool when it's safe to proceed + // with a SetVersion/DeleteDatabase/etc. + class WaitForTransactionsToFinishRunnable : public nsIRunnable + { + public: + WaitForTransactionsToFinishRunnable(SetVersionRunnable* aRunnable) + : mRunnable(aRunnable) + { + NS_ASSERTION(mRunnable, "Why don't we have a runnable?"); + NS_ASSERTION(mRunnable->mDatabases.IsEmpty(), "We're here too early!"); + } + + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + + private: + nsRefPtr mRunnable; + }; + // Maintains a list of live databases per origin. nsClassHashtable > mLiveDatabases; diff --git a/dom/indexedDB/OpenDatabaseHelper.cpp b/dom/indexedDB/OpenDatabaseHelper.cpp index 7e7559b47c8..cc909fec4d5 100644 --- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -495,10 +495,13 @@ public: NS_DECL_ISUPPORTS_INHERITED - nsresult DoDatabaseWork(mozIStorageConnection* aConnection); nsresult GetSuccessResult(JSContext* aCx, jsval* aVal); +protected: + nsresult DoDatabaseWork(mozIStorageConnection* aConnection); + nsresult Init(); + // SetVersionHelper never fires an error event at the request. It hands that // responsibility back to the OpenDatabaseHelper void OnError() { } @@ -684,6 +687,7 @@ OpenDatabaseHelper::StartSetVersion() // The SetVersionHelper is responsible for dispatching us back to the // main thread again and changing the state to eSetVersionCompleted. mState = eSetVersionPending; + return NS_OK; } @@ -711,6 +715,12 @@ OpenDatabaseHelper::Run() mState == eSetVersionCompleted, "Why are we here?"); if (mState == eSetVersionCompleted) { + // Allow transaction creation/other version change transactions to proceed + // before we fire events. Other version changes will be postd to the end + // of the event loop, and will be behind whatever the page does in + // its error/success event handlers. + mDatabase->ExitSetVersionTransaction(); + mState = eFiringEvents; } else { // Notify the request that we're done, but only if we didn't just finish @@ -865,6 +875,15 @@ OpenDatabaseHelper::NotifySetVersionFinished() return NS_DispatchToCurrentThread(this); } +void +OpenDatabaseHelper::BlockDatabase() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mDatabase, "This is going bad fast."); + + mDatabase->EnterSetVersionTransaction(); +} + void OpenDatabaseHelper::DispatchSuccessEvent() { @@ -916,6 +935,15 @@ OpenDatabaseHelper::ReleaseMainThreadObjects() NS_IMPL_ISUPPORTS_INHERITED0(SetVersionHelper, AsyncConnectionHelper); +nsresult +SetVersionHelper::Init() +{ + // Block transaction creation until we are done. + mOpenHelper->BlockDatabase(); + + return NS_OK; +} + nsresult SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { diff --git a/dom/indexedDB/OpenDatabaseHelper.h b/dom/indexedDB/OpenDatabaseHelper.h index 0d91db6b2dc..53334867ab3 100644 --- a/dom/indexedDB/OpenDatabaseHelper.h +++ b/dom/indexedDB/OpenDatabaseHelper.h @@ -82,6 +82,7 @@ public: } nsresult NotifySetVersionFinished(); + void BlockDatabase(); protected: // Methods only called on the main thread diff --git a/dom/indexedDB/TransactionThreadPool.cpp b/dom/indexedDB/TransactionThreadPool.cpp index 2bb66132b48..c78ba97ef90 100644 --- a/dom/indexedDB/TransactionThreadPool.cpp +++ b/dom/indexedDB/TransactionThreadPool.cpp @@ -238,7 +238,6 @@ TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction) #ifdef DEBUG if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) { - NS_ASSERTION(dbTransactionInfo->locked, "Should be locked!"); NS_ASSERTION(transactionCount == 1, "More transactions running than should be!"); } @@ -344,10 +343,6 @@ TransactionThreadPool::TransactionCanRun(IDBTransaction* aTransaction, PRUint32 transactionCount = transactionsInProgress.Length(); NS_ASSERTION(transactionCount, "Should never be 0!"); - if (mode == IDBTransaction::VERSION_CHANGE) { - dbTransactionInfo->lockPending = true; - } - for (PRUint32 index = 0; index < transactionCount; index++) { // See if this transaction is in out list of current transactions. const TransactionInfo& info = transactionsInProgress[index]; @@ -358,11 +353,7 @@ TransactionThreadPool::TransactionCanRun(IDBTransaction* aTransaction, } } - if (dbTransactionInfo->locked || dbTransactionInfo->lockPending) { - *aCanRun = false; - *aExistingQueue = nsnull; - return NS_OK; - } + NS_ASSERTION(mode != IDBTransaction::VERSION_CHANGE, "How did we get here?"); bool writeOverlap; nsresult rv = @@ -448,11 +439,6 @@ TransactionThreadPool::Dispatch(IDBTransaction* aTransaction, dbTransactionInfo = autoDBTransactionInfo; } - if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) { - NS_ASSERTION(!dbTransactionInfo->locked, "Already locked?!"); - dbTransactionInfo->locked = true; - } - const nsTArray& objectStoreNames = aTransaction->mObjectStoreNames; nsTArray& storesInUse = diff --git a/dom/indexedDB/TransactionThreadPool.h b/dom/indexedDB/TransactionThreadPool.h index 97d15c46917..4fdd152c4b0 100644 --- a/dom/indexedDB/TransactionThreadPool.h +++ b/dom/indexedDB/TransactionThreadPool.h @@ -123,12 +123,6 @@ protected: struct DatabaseTransactionInfo { - DatabaseTransactionInfo() - : locked(false), lockPending(false) - { } - - bool locked; - bool lockPending; nsTArray transactions; nsTArray storesReading; nsTArray storesWriting; diff --git a/dom/indexedDB/test/Makefile.in b/dom/indexedDB/test/Makefile.in index 555fbeaf93f..9d7d00f15f6 100644 --- a/dom/indexedDB/test/Makefile.in +++ b/dom/indexedDB/test/Makefile.in @@ -98,6 +98,7 @@ TEST_FILES = \ test_setVersion.html \ test_setVersion_abort.html \ test_setVersion_events.html \ + test_setVersion_exclusion.html \ test_writer_starvation.html \ third_party_iframe1.html \ third_party_iframe2.html \ diff --git a/dom/indexedDB/test/event_propagation_iframe.html b/dom/indexedDB/test/event_propagation_iframe.html index 2d1f745fad5..035fa86248a 100644 --- a/dom/indexedDB/test/event_propagation_iframe.html +++ b/dom/indexedDB/test/event_propagation_iframe.html @@ -104,7 +104,7 @@ db.onerror = errorEventCounter; db.addEventListener("error", errorEventCounter, true); - event.target.transaction.oncomplete = grabEventAndContinueHandler; + event.target.onsuccess = grabEventAndContinueHandler; db.createObjectStore("foo", { autoIncrement: true }); yield; diff --git a/dom/indexedDB/test/leaving_page_iframe.html b/dom/indexedDB/test/leaving_page_iframe.html index 43d0e00ab98..00bcff71243 100644 --- a/dom/indexedDB/test/leaving_page_iframe.html +++ b/dom/indexedDB/test/leaving_page_iframe.html @@ -12,7 +12,7 @@ function startDBWork() { } var store = db.createObjectStore("mystore"); store.add({ hello: "world" }, 42); - trans.oncomplete = madeMod; + e.target.onsuccess = madeMod; }; } diff --git a/dom/indexedDB/test/test_autoIncrement_indexes.html b/dom/indexedDB/test/test_autoIncrement_indexes.html index b24d4896c9d..951be60eef9 100644 --- a/dom/indexedDB/test/test_autoIncrement_indexes.html +++ b/dom/indexedDB/test/test_autoIncrement_indexes.html @@ -33,6 +33,9 @@ let key = event.target.result; ok(key, "Added entry"); + request.onsuccess = grabEventAndContinueHandler; + + event = yield; let objectStore = db.transaction("foo").objectStore("foo"); let first = objectStore.index("first"); diff --git a/dom/indexedDB/test/test_clear.html b/dom/indexedDB/test/test_clear.html index 648f234dafd..44f85569357 100644 --- a/dom/indexedDB/test/test_clear.html +++ b/dom/indexedDB/test/test_clear.html @@ -25,7 +25,7 @@ let db = request.result; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; let objectStore = db.createObjectStore("foo", { autoIncrement: true }); diff --git a/dom/indexedDB/test/test_create_index_with_integer_keys.html b/dom/indexedDB/test/test_create_index_with_integer_keys.html index 37c3d601656..6b058ce6b9c 100644 --- a/dom/indexedDB/test/test_create_index_with_integer_keys.html +++ b/dom/indexedDB/test/test_create_index_with_integer_keys.html @@ -23,7 +23,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; // Make object store, add data. let objectStore = db.createObjectStore("foo", { keyPath: "id" }); @@ -39,7 +39,7 @@ let db2 = event.target.result; db2.onerror = errorHandler; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; // Create index. event.target.transaction.objectStore("foo").createIndex("foo", "num"); diff --git a/dom/indexedDB/test/test_cursor_mutation.html b/dom/indexedDB/test/test_cursor_mutation.html index 7812c1f2ec9..c3d6e52a3ec 100644 --- a/dom/indexedDB/test/test_cursor_mutation.html +++ b/dom/indexedDB/test/test_cursor_mutation.html @@ -35,7 +35,7 @@ let event = yield; let db = event.target.result; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; let objectStore = db.createObjectStore("foo", { keyPath: "ss" }); objectStore.createIndex("name", "name", { unique: true }); diff --git a/dom/indexedDB/test/test_getAll.html b/dom/indexedDB/test/test_getAll.html index 3c873efdfde..7f76c438531 100644 --- a/dom/indexedDB/test/test_getAll.html +++ b/dom/indexedDB/test/test_getAll.html @@ -26,6 +26,7 @@ let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + request.onsuccess = grabEventAndContinueHandler; request = objectStore.getAll(); request.onerror = errorHandler; request.onsuccess = grabEventAndContinueHandler; @@ -46,6 +47,7 @@ } } yield; + yield; request = db.transaction("foo").objectStore("foo").getAll(); request.onerror = errorHandler; diff --git a/dom/indexedDB/test/test_index_getAll.html b/dom/indexedDB/test/test_index_getAll.html index 584bb9ff3e3..ba9fa0067a0 100644 --- a/dom/indexedDB/test/test_index_getAll.html +++ b/dom/indexedDB/test/test_index_getAll.html @@ -60,6 +60,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -78,7 +79,7 @@ } } } - event = yield; + yield; ok(true, "1"); // Now create the indexes. @@ -88,7 +89,6 @@ } is(objectStore.indexNames.length, indexData.length, "Good index count"); - continueToNextStep(); yield; ok(true, "2"); diff --git a/dom/indexedDB/test/test_index_getAllObjects.html b/dom/indexedDB/test/test_index_getAllObjects.html index a65a0536945..585a0d117ac 100644 --- a/dom/indexedDB/test/test_index_getAllObjects.html +++ b/dom/indexedDB/test/test_index_getAllObjects.html @@ -60,6 +60,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -87,7 +88,6 @@ } is(objectStore.indexNames.length, indexData.length, "Good index count"); - continueToNextStep(); yield; objectStore = db.transaction(objectStoreName) diff --git a/dom/indexedDB/test/test_index_object_cursors.html b/dom/indexedDB/test/test_index_object_cursors.html index b7091206d5d..fb8228e2ca1 100644 --- a/dom/indexedDB/test/test_index_object_cursors.html +++ b/dom/indexedDB/test/test_index_object_cursors.html @@ -37,7 +37,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; for (let objectStoreIndex in objectStoreData) { const objectStoreInfo = objectStoreData[objectStoreIndex]; diff --git a/dom/indexedDB/test/test_indexes.html b/dom/indexedDB/test/test_indexes.html index 05132a87d22..98e33701ae6 100644 --- a/dom/indexedDB/test/test_indexes.html +++ b/dom/indexedDB/test/test_indexes.html @@ -71,6 +71,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -97,7 +98,6 @@ indexData[i].options); } is(objectStore.indexNames.length, indexData.length, "Good index count"); - continueToNextStep(); yield; objectStore = db.transaction(objectStoreName) diff --git a/dom/indexedDB/test/test_indexes_bad_values.html b/dom/indexedDB/test/test_indexes_bad_values.html index e28aa6dd718..ffb92160868 100644 --- a/dom/indexedDB/test/test_indexes_bad_values.html +++ b/dom/indexedDB/test/test_indexes_bad_values.html @@ -51,6 +51,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -87,6 +88,7 @@ } } yield; + yield; objectStore = db.transaction(objectStoreName) .objectStore(objectStoreName); diff --git a/dom/indexedDB/test/test_object_identity.html b/dom/indexedDB/test/test_object_identity.html index 211725d3055..600bc396cdd 100644 --- a/dom/indexedDB/test/test_object_identity.html +++ b/dom/indexedDB/test/test_object_identity.html @@ -28,7 +28,7 @@ let index2 = objectStore2.index("bar"); ok(index1 === index2, "Got same indexes"); - transaction.oncomplete = continueToNextStep; + request.onsuccess = continueToNextStep; yield; transaction = db.transaction("foo"); diff --git a/dom/indexedDB/test/test_odd_result_order.html b/dom/indexedDB/test/test_odd_result_order.html index 2a127e6b832..1d14fcf66ce 100644 --- a/dom/indexedDB/test/test_odd_result_order.html +++ b/dom/indexedDB/test/test_odd_result_order.html @@ -29,7 +29,7 @@ autoIncrement: true }); let index = objectStore.createIndex("foo", "index"); - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; yield; objectStore = db.transaction("foo", IDBTransaction.READ_WRITE) diff --git a/dom/indexedDB/test/test_open_objectStore.html b/dom/indexedDB/test/test_open_objectStore.html index 93f09ee1e76..48ece9f2af5 100644 --- a/dom/indexedDB/test/test_open_objectStore.html +++ b/dom/indexedDB/test/test_open_objectStore.html @@ -20,6 +20,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -31,7 +32,6 @@ is(db.objectStoreNames.length, 1, "Bad objectStores list"); is(db.objectStoreNames.item(0), objectStoreName, "Bad name"); - continueToNextStep(); yield; objectStore = db.transaction(objectStoreName).objectStore(objectStoreName); diff --git a/dom/indexedDB/test/test_overlapping_transactions.html b/dom/indexedDB/test/test_overlapping_transactions.html index b59f60f3fe4..39746ce8a37 100644 --- a/dom/indexedDB/test/test_overlapping_transactions.html +++ b/dom/indexedDB/test/test_overlapping_transactions.html @@ -26,7 +26,7 @@ let db = event.target.result; is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); - event.target.transaction.oncomplete = grabEventAndContinueHandler; + event.target.onsuccess = grabEventAndContinueHandler; for (let i in objectStores) { db.createObjectStore(objectStores[i], { autoIncrement: true }); } diff --git a/dom/indexedDB/test/test_readonly_transactions.html b/dom/indexedDB/test/test_readonly_transactions.html index 5987a1c35f1..58a0a8ad9e3 100644 --- a/dom/indexedDB/test/test_readonly_transactions.html +++ b/dom/indexedDB/test/test_readonly_transactions.html @@ -22,6 +22,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -29,6 +30,8 @@ db.createObjectStore(osName, { autoIncrement: "true" }); + yield; + let key1, key2; request = db.transaction([osName], READ_WRITE) diff --git a/dom/indexedDB/test/test_setVersion_exclusion.html b/dom/indexedDB/test/test_setVersion_exclusion.html new file mode 100644 index 00000000000..96d0d322c28 --- /dev/null +++ b/dom/indexedDB/test/test_setVersion_exclusion.html @@ -0,0 +1,90 @@ + + + + Indexed Database Property Test + + + + + + + + + + + + diff --git a/dom/indexedDB/test/test_success_events_after_abort.html b/dom/indexedDB/test/test_success_events_after_abort.html index c9d75a4cb78..31a29d59d56 100644 --- a/dom/indexedDB/test/test_success_events_after_abort.html +++ b/dom/indexedDB/test/test_success_events_after_abort.html @@ -19,7 +19,7 @@ let db = event.target.result; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; let objectStore = db.createObjectStore("foo"); objectStore.add({}, 1).onerror = errorHandler; diff --git a/dom/indexedDB/test/test_transaction_abort.html b/dom/indexedDB/test/test_transaction_abort.html index 9558212a9a3..e7d6906c202 100644 --- a/dom/indexedDB/test/test_transaction_abort.html +++ b/dom/indexedDB/test/test_transaction_abort.html @@ -29,6 +29,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -157,6 +158,8 @@ ok(true, "RemoveIndex threw"); } + yield; + request = db.transaction("foo", READ_WRITE).objectStore("foo").add({}); request.onerror = errorHandler; request.onsuccess = grabEventAndContinueHandler; diff --git a/dom/indexedDB/test/test_transaction_lifetimes.html b/dom/indexedDB/test/test_transaction_lifetimes.html index c402ec8b81d..7e9e2e368bd 100644 --- a/dom/indexedDB/test/test_transaction_lifetimes.html +++ b/dom/indexedDB/test/test_transaction_lifetimes.html @@ -20,7 +20,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; db.createObjectStore("foo", { autoIncrement: true }); yield; diff --git a/dom/indexedDB/test/test_transaction_lifetimes_nested.html b/dom/indexedDB/test/test_transaction_lifetimes_nested.html index de7b1cebc74..2fde9b81dd5 100644 --- a/dom/indexedDB/test/test_transaction_lifetimes_nested.html +++ b/dom/indexedDB/test/test_transaction_lifetimes_nested.html @@ -20,7 +20,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; db.createObjectStore("foo"); yield; diff --git a/dom/indexedDB/test/test_writer_starvation.html b/dom/indexedDB/test/test_writer_starvation.html index d29bdf8bb5f..456975bcca9 100644 --- a/dom/indexedDB/test/test_writer_starvation.html +++ b/dom/indexedDB/test/test_writer_starvation.html @@ -23,6 +23,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -39,7 +40,6 @@ let key = event.target.result; ok(key, "Got a key"); - SimpleTest.executeSoon(function() { testGenerator.next(); }); yield; let continueReading = true; From b3bd86a34264b782a88767efe3736074c7b2a3ea Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 24 Oct 2011 16:08:10 -0400 Subject: [PATCH 23/42] Bug 694811 - Linking with VC11 fails with an undefined reference error to _IID_IAsyncOperation; r=bbondy --- widget/src/windows/nsDataObj.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/widget/src/windows/nsDataObj.h b/widget/src/windows/nsDataObj.h index f974b7bc62b..6ca4cd1061f 100644 --- a/widget/src/windows/nsDataObj.h +++ b/widget/src/windows/nsDataObj.h @@ -44,6 +44,7 @@ #include #endif #include +#include #include "nsCOMPtr.h" #include "nsString.h" @@ -55,6 +56,13 @@ #include "nsCOMArray.h" #include "nsITimer.h" +// The SDK shipping with VC11 has renamed IAsyncOperation to +// IDataObjectAsyncCapability. We try to detect this, and rename this in our +// code too to make sure that we pick the correct name when building. +#ifdef __IDataObjectAsyncCapability_INTERFACE_DEFINED__ +#define IAsyncOperation IDataObjectAsyncCapability +#define IID_IAsyncOperation IID_IDataObjectAsyncCapability +#else // XXX for older version of PSDK where IAsyncOperation and related stuff is not available // but thisdefine should be removed when parocles config is updated #ifndef __IAsyncOperation_INTERFACE_DEFINED__ @@ -78,6 +86,7 @@ IAsyncOperation : public IUnknown #endif #endif // __IAsyncOperation_INTERFACE_DEFINED__ +#endif // __IDataObjectAsyncCapability_INTERFACE_DEFINED__ /* * CFSTR_SHELLURL is deprecated and doesn't have a Unicode version. From 9992363283b0d81e35b2df0381866c12447c2d02 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 24 Oct 2011 17:04:16 -0400 Subject: [PATCH 24/42] Bug 694865 - VC11 cannot compile the IE profile migration code because pstore.h no longer ships with the SDK; r=bbondy --- .../migration/src/nsIEProfileMigrator.h | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/browser/components/migration/src/nsIEProfileMigrator.h b/browser/components/migration/src/nsIEProfileMigrator.h index 702a70e89a4..3e434572433 100644 --- a/browser/components/migration/src/nsIEProfileMigrator.h +++ b/browser/components/migration/src/nsIEProfileMigrator.h @@ -43,7 +43,7 @@ #include #include -#include +#include #include "nsIBrowserProfileMigrator.h" #include "nsIObserverService.h" #include "nsTArray.h" @@ -61,6 +61,79 @@ struct SignonData { char* realm; }; +// VC11 doesn't ship with pstore.h, so we go ahead and define the stuff that +// we need from that file here. +class IEnumPStoreItems : public IUnknown { +public: + virtual HRESULT STDMETHODCALLTYPE Next(DWORD celt, LPWSTR* rgelt, + DWORD* pceltFetched) = 0; + virtual HRESULT STDMETHODCALLTYPE Skip(DWORD celt) = 0; + virtual HRESULT STDMETHODCALLTYPE Reset() = 0; + virtual HRESULT STDMETHODCALLTYPE Clone(IEnumPStoreItems** ppenum) = 0; +}; + +class IEnumPStoreTypes; // not used +struct PST_PROVIDERINFO; // not used +struct PST_TYPEINFO; // not used +struct PST_PROMPTINFO; // not used +struct PST_ACCESSRULESET; // not used +typedef DWORD PST_KEY; +typedef DWORD PST_ACCESSMODE; + +class IPStore : public IUnknown { +public: + virtual HRESULT STDMETHODCALLTYPE GetInfo(PST_PROVIDERINFO** ppProperties) = 0; + virtual HRESULT STDMETHODCALLTYPE GetProvParam(DWORD dwParam, DWORD* pcbData, + BYTE** ppbData, DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE SetProvParam(DWORD dwParam, DWORD cbData, + BYTE* pbData, DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateType(PST_KEY Key, const GUID* pType, + PST_TYPEINFO* pInfo, DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(PST_KEY Key, const GUID* pType, + PST_TYPEINFO** ppInfo, DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE DeleteType(PST_KEY Key, const GUID* pType, + DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateSubtype(PST_KEY Key, const GUID* pType, + const GUID* pSubtype, PST_TYPEINFO* pInfo, + PST_ACCESSRULESET* pRules, DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE GetSubtypeInfo(PST_KEY Key, const GUID* pType, + const GUID* pSubtype, PST_TYPEINFO** ppInfo, + DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE DeleteSubtype(PST_KEY Key, const GUID* pType, + const GUID* pSubtype, DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE ReadAccessRuleset(PST_KEY Key, const GUID* pType, + const GUID* pSubtype, PST_ACCESSRULESET** ppRules, + DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE WriteAccessRuleset(PST_KEY Key, const GUID* pType, + const GUID* pSubtype, PST_ACCESSRULESET* pRules, + DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE EnumTypes(PST_KEY Key, DWORD dwFlags, IEnumPStoreTypes** ppenum) = 0; + virtual HRESULT STDMETHODCALLTYPE EnumSubtypes(PST_KEY Key, const GUID* pType, + DWORD dwFlags, IEnumPStoreTypes** ppenum) = 0; + virtual HRESULT STDMETHODCALLTYPE DeleteItem(PST_KEY Key, const GUID* pItemType, + const GUID* pItemSubtype, LPCWSTR szItemName, + PST_PROMPTINFO* pPromptInfo, DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE ReadItem(PST_KEY Key, const GUID* pItemType, + const GUID* pItemSubtype, LPCWSTR szItemName, + DWORD* pcbData, BYTE** ppbData, + PST_PROMPTINFO* pPromptInfo, DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE WriteItem(PST_KEY Key, const GUID* pItemType, + const GUID* pItemSubtype, LPCWSTR szItemName, + DWORD cbData, BYTE* pbData, + PST_PROMPTINFO* pPromptInfo, DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE OpenItem(PST_KEY Key, const GUID* pItemType, + const GUID* pItemSubtype, LPCWSTR szItemName, + PST_ACCESSMODE ModeFlags, PST_PROMPTINFO* pPromptInfo, + DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE CloseItem(PST_KEY Key, const GUID* pItemType, + const GUID* pItemSubtype, LPCWSTR szItemName, + DWORD dwFlags) = 0; + virtual HRESULT STDMETHODCALLTYPE EnumItems(PST_KEY Key, const GUID* pItemType, + const GUID* pItemSubtype, DWORD dwFlags, + IEnumPStoreItems** ppenum) = 0; +}; + + class nsIEProfileMigrator : public nsIBrowserProfileMigrator, public nsINavHistoryBatchCallback { public: From 6307c16ac5e91a69acca037dd22b06065129bc82 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Mon, 24 Oct 2011 22:05:58 +0100 Subject: [PATCH 25/42] Backout changeset 0144dca654e9 (bug 622232) for M1 permaorange --- .../html/content/src/nsHTMLAnchorElement.cpp | 17 +-- .../html/content/src/nsHTMLDNSPrefetch.cpp | 104 +----------------- content/html/content/src/nsHTMLDNSPrefetch.h | 9 -- netwerk/dns/nsDNSService2.cpp | 54 --------- netwerk/dns/nsDNSService2.h | 1 - netwerk/dns/nsHostResolver.cpp | 44 -------- netwerk/dns/nsHostResolver.h | 27 ----- netwerk/dns/nsIDNSService.idl | 22 +--- netwerk/ipc/NeckoParent.cpp | 10 -- netwerk/ipc/NeckoParent.h | 4 - netwerk/ipc/PNecko.ipdl | 1 - 11 files changed, 4 insertions(+), 289 deletions(-) diff --git a/content/html/content/src/nsHTMLAnchorElement.cpp b/content/html/content/src/nsHTMLAnchorElement.cpp index ddd4be6f90e..e99e9850924 100644 --- a/content/html/content/src/nsHTMLAnchorElement.cpp +++ b/content/html/content/src/nsHTMLAnchorElement.cpp @@ -131,18 +131,12 @@ public: virtual nsXPCClassInfo* GetClassInfo(); }; -// Indicates if a DNS Prefetch has been requested from this Anchor elem -#define HTML_ANCHOR_DNS_PREFETCH_REQUESTED \ - (1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET) - -// Make sure we have enough space for those bits -PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET < 32); NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor) nsHTMLAnchorElement::nsHTMLAnchorElement(already_AddRefed aNodeInfo) - : nsGenericHTMLElement(aNodeInfo) - , Link(this) + : nsGenericHTMLElement(aNodeInfo), + Link(this) { } @@ -212,7 +206,6 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, // Prefetch links if (aDocument && nsHTMLDNSPrefetch::IsAllowed(OwnerDoc())) { nsHTMLDNSPrefetch::PrefetchLow(this); - SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); } return rv; } @@ -224,12 +217,6 @@ nsHTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent) // be under a different xml:base, so forget the cached state now. Link::ResetLinkState(false); - // Cancel any DNS prefetches - if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_REQUESTED)) { - nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT); - UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); - } - nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); } diff --git a/content/html/content/src/nsHTMLDNSPrefetch.cpp b/content/html/content/src/nsHTMLDNSPrefetch.cpp index ee153ec3432..d4fd536e4ad 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.cpp +++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp @@ -192,8 +192,7 @@ nsHTMLDNSPrefetch::Prefetch(nsAString &hostname, PRUint16 flags) return NS_ERROR_NOT_AVAILABLE; nsCOMPtr tmpOutstanding; - return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), - flags | nsIDNSService::RESOLVE_SPECULATE, + return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), flags | nsIDNSService::RESOLVE_SPECULATE, sDNSListener, nsnull, getter_AddRefs(tmpOutstanding)); } @@ -215,71 +214,6 @@ nsHTMLDNSPrefetch::PrefetchHigh(nsAString &hostname) return Prefetch(hostname, 0); } -nsresult -nsHTMLDNSPrefetch::CancelPrefetch(Link *aElement, PRUint16 flags, nsresult aReason) -{ - nsAutoString hostname; - nsresult rv = aElement->GetHostname(hostname); - if (IsNeckoChild()) { - // Instead of transporting the Link object to the other process - // we are using the hostname based function here, too. Compared to the - // IPC the performance hit should be negligible. - NS_ENSURE_SUCCESS(rv,rv); - - // Forward the cancellation to the string based CancelPrefetch() - return CancelPrefetch(hostname, flags, aReason); - } - - if (!(sInitialized && sPrefetches && sDNSService && sDNSListener)) - return NS_ERROR_NOT_AVAILABLE; - - // Attempt to remove the prefetch request from the Deferrals FIFO first ... - bool found = false; - rv = sPrefetches->Remove(flags, aElement, &found); - NS_ENSURE_SUCCESS(rv, rv); - // ... If no request was found, it may have been sent to the DNS Service. - // Forward the cancellation to the string based CancelPrefetch - if (!found) - rv = CancelPrefetch(hostname, flags, aReason); - return rv; -} - -nsresult -nsHTMLDNSPrefetch::CancelPrefetch(nsAString &hostname, PRUint16 flags, nsresult aReason) -{ - // Forward this request to Necko Parent if we're a child process - if (IsNeckoChild()) { - // We need to check IsEmpty() because net_IsValidHostName() - // considers empty strings to be valid hostnames - if (!hostname.IsEmpty() && - net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) { - gNeckoChild->SendCancelHTMLDNSPrefetch(nsAutoString(hostname), flags, aReason); - } - return NS_OK; - } - - if (!(sInitialized && sDNSService && sPrefetches && sDNSListener)) - return NS_ERROR_NOT_AVAILABLE; - - // Forward cancellation to DNS service - return sDNSService->CancelAsyncResolve(NS_ConvertUTF16toUTF8(hostname), - flags | nsIDNSService::RESOLVE_SPECULATE, - sDNSListener, aReason); -} - -nsresult -nsHTMLDNSPrefetch::CancelPrefetchLow(Link *aElement, nsresult aReason) -{ - return CancelPrefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_LOW, aReason); -} - -nsresult -nsHTMLDNSPrefetch::CancelPrefetchLow(nsAString &hostname, nsresult aReason) -{ - return CancelPrefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW, aReason); -} - - ///////////////////////////////////////////////////////////////////////////////////////////////////////// NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTMLDNSPrefetch::nsListener, @@ -349,42 +283,6 @@ nsHTMLDNSPrefetch::nsDeferrals::Add(PRUint16 flags, Link *aElement) return NS_OK; } -nsresult -nsHTMLDNSPrefetch::nsDeferrals::Remove(PRUint16 aFlags, Link *aElement, bool *aFound) -{ - // The FIFO has no lock, so it can only be accessed on main thread - NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Remove must be on main thread"); - - // Search the deferrals FIFO for this Link elem and remove it - // Note: Element removal will leave holes in the queue. However: - // -- FIFO is flushed in SubmitQueue, so holes are temporary. - // -- holes are only created if a tab is closed before page is loaded. - bool found = false; - PRUint16 curr = mTail; - while (curr != mHead) { - nsCOMPtr content = do_QueryReferent(mEntries[curr].mElement); - if (content) { - nsCOMPtr link = do_QueryInterface(content); - if (link && (link == aElement) && (mEntries[curr].mFlags == aFlags)) { - // Null mElements will be ignored in SubmitQueue; requests won't be sent - mEntries[curr].mElement = NULL; - mEntries[curr].mFlags = 0; - found = true; - break; - } - } - curr = (curr + 1) & sMaxDeferredMask; - } - // Minor optimization: If we removed an element at the tail, increment the - // the tail end to shrink the FIFO. - if (found && (mTail != mHead)) - mTail = (mTail + 1) & sMaxDeferredMask; - - // Report "found" status back to caller - *aFound = found; - return NS_OK; -} - void nsHTMLDNSPrefetch::nsDeferrals::SubmitQueue() { diff --git a/content/html/content/src/nsHTMLDNSPrefetch.h b/content/html/content/src/nsHTMLDNSPrefetch.h index 6c90fd085e8..7883ff07a4d 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.h +++ b/content/html/content/src/nsHTMLDNSPrefetch.h @@ -87,18 +87,10 @@ public: static nsresult PrefetchHigh(nsAString &host); static nsresult PrefetchMedium(nsAString &host); static nsresult PrefetchLow(nsAString &host); - static nsresult CancelPrefetchLow(nsAString &host, nsresult aReason); - static nsresult CancelPrefetchLow(mozilla::dom::Link *aElement, nsresult aReason); private: static nsresult Prefetch(nsAString &host, PRUint16 flags); static nsresult Prefetch(mozilla::dom::Link *aElement, PRUint16 flags); - static nsresult CancelPrefetch(nsAString &hostname, - PRUint16 flags, - nsresult aReason); - static nsresult CancelPrefetch(mozilla::dom::Link *aElement, - PRUint16 flags, - nsresult aReason); public: class nsListener : public nsIDNSListener @@ -126,7 +118,6 @@ public: void Activate(); nsresult Add(PRUint16 flags, mozilla::dom::Link *aElement); - nsresult Remove(PRUint16 aFlags, mozilla::dom::Link *aElement, bool *aFound); private: ~nsDeferrals(); diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index 8db8dd08e44..68ad8a526e0 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -278,10 +278,6 @@ public: ~nsDNSAsyncRequest() {} void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); - // Returns TRUE if the DNS listener arg is the same as the member listener - // Used in Cancellations to remove DNS requests associated with a - // particular hostname and nsIDNSListener - bool EqualsAsyncListener(nsIDNSListener *aListener); nsRefPtr mResolver; nsCString mHost; // hostname we're resolving @@ -314,12 +310,6 @@ nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver, NS_RELEASE_THIS(); } -bool -nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) -{ - return (aListener == mListener); -} - NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSAsyncRequest, nsICancelable) NS_IMETHODIMP @@ -342,7 +332,6 @@ public: virtual ~nsDNSSyncRequest() {} void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); - bool EqualsAsyncListener(nsIDNSListener *aListener); bool mDone; nsresult mStatus; @@ -366,13 +355,6 @@ nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver, PR_ExitMonitor(mMonitor); } -bool -nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) -{ - // Sync request: no listener to compare - return false; -} - //----------------------------------------------------------------------------- nsDNSService::nsDNSService() @@ -602,42 +584,6 @@ nsDNSService::AsyncResolve(const nsACString &hostname, return rv; } -NS_IMETHODIMP -nsDNSService::CancelAsyncResolve(const nsACString &aHostname, - PRUint32 aFlags, - nsIDNSListener *aListener, - nsresult aReason) -{ - // grab reference to global host resolver and IDN service. beware - // simultaneous shutdown!! - nsRefPtr res; - nsCOMPtr idn; - { - MutexAutoLock lock(mLock); - - if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) - return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; - - res = mResolver; - idn = mIDN; - } - if (!res) - return NS_ERROR_OFFLINE; - - nsCString hostname(aHostname); - - nsCAutoString hostACE; - if (idn && !IsASCII(aHostname)) { - if (NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE))) - hostname = hostACE; - } - - PRUint16 af = GetAFForLookup(hostname, aFlags); - - res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason); - return NS_OK; -} - NS_IMETHODIMP nsDNSService::Resolve(const nsACString &hostname, PRUint32 flags, diff --git a/netwerk/dns/nsDNSService2.h b/netwerk/dns/nsDNSService2.h index 1d33eabaaa5..1749b413fb0 100644 --- a/netwerk/dns/nsDNSService2.h +++ b/netwerk/dns/nsDNSService2.h @@ -38,7 +38,6 @@ #include "nsIIDNService.h" #include "nsIObserver.h" #include "nsHostResolver.h" -#include "nsICancelable.h" #include "nsAutoPtr.h" #include "nsString.h" #include "mozilla/Mutex.h" diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index a268179f22d..08fa76e2cec 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -891,50 +891,6 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo NS_RELEASE(rec); } -void -nsHostResolver::CancelAsyncRequest(const char *host, - PRUint16 flags, - PRUint16 af, - nsIDNSListener *aListener, - nsresult status) - -{ - MutexAutoLock lock(mLock); - - // Lookup the host record associated with host, flags & address family - nsHostKey key = { host, flags, af }; - nsHostDBEnt *he = static_cast - (PL_DHashTableOperate(&mDB, &key, PL_DHASH_LOOKUP)); - if (he && he->rec) { - nsHostRecord* recPtr = NULL; - PRCList *node = he->rec->callbacks.next; - // Remove the first nsDNSAsyncRequest callback which matches the - // supplied listener object - while (node != &he->rec->callbacks) { - nsResolveHostCallback *callback - = static_cast(node); - if (callback && (callback->EqualsAsyncListener(aListener))) { - // Remove from the list of callbacks - PR_REMOVE_LINK(callback); - recPtr = he->rec; - callback->OnLookupComplete(this, recPtr, status); - break; - } - node = node->next; - } - - // If there are no more callbacks, remove the hash table entry - if (recPtr && PR_CLIST_IS_EMPTY(&recPtr->callbacks)) { - PL_DHashTableOperate(&mDB, (nsHostKey *)recPtr, PL_DHASH_REMOVE); - // If record is on a Queue, remove it and then deref it - if (recPtr->next != recPtr) { - PR_REMOVE_LINK(recPtr); - NS_RELEASE(recPtr); - } - } - } -} - //---------------------------------------------------------------------------- void diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h index 78ba065a53c..b9e83ad5537 100644 --- a/netwerk/dns/nsHostResolver.h +++ b/netwerk/dns/nsHostResolver.h @@ -46,7 +46,6 @@ #include "mozilla/CondVar.h" #include "mozilla/Mutex.h" #include "nsISupportsImpl.h" -#include "nsIDNSListener.h" #include "nsString.h" #include "nsTArray.h" @@ -192,20 +191,6 @@ public: virtual void OnLookupComplete(nsHostResolver *resolver, nsHostRecord *record, nsresult status) = 0; - /** - * EqualsAsyncListener - * - * Determines if the listener argument matches the listener member var. - * For subclasses not implementing a member listener, should return false. - * For subclasses having a member listener, the function should check if - * they are the same. Used for cases where a pointer to an object - * implementing nsResolveHostCallback is unknown, but a pointer to - * the original listener is known. - * - * @param aListener - * nsIDNSListener object associated with the original request - */ - virtual bool EqualsAsyncListener(nsIDNSListener *aListener) = 0; }; /** @@ -259,18 +244,6 @@ public: nsResolveHostCallback *callback, nsresult status); - /** - * Cancels an async request associated with the hostname, flags, - * address family and listener. Cancels first callback found which matches - * these criteria. These parameters should correspond to the parameters - * passed to ResolveHost. If this is the last callback associated with the - * host record, it is removed from any request queues it might be on. - */ - void CancelAsyncRequest(const char *host, - PRUint16 flags, - PRUint16 af, - nsIDNSListener *aListener, - nsresult status); /** * values for the flags parameter passed to ResolveHost and DetachCallback * that may be bitwise OR'd together. diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl index b34327c461a..8a2a15c366f 100644 --- a/netwerk/dns/nsIDNSService.idl +++ b/netwerk/dns/nsIDNSService.idl @@ -46,7 +46,7 @@ interface nsIDNSListener; /** * nsIDNSService */ -[scriptable, uuid(F6E05CC3-8A13-463D-877F-D59B20B59724)] +[scriptable, uuid(c1a56a45-8fa3-44e6-9f01-38c91c858cf9)] interface nsIDNSService : nsISupports { /** @@ -72,26 +72,6 @@ interface nsIDNSService : nsISupports in nsIDNSListener aListener, in nsIEventTarget aListenerTarget); - /** - * Attempts to cancel a previously requested async DNS lookup - * - * @param aHostName - * the hostname or IP-address-literal to resolve. - * @param aFlags - * a bitwise OR of the RESOLVE_ prefixed constants defined below. - * @param aListener - * the original listener which was to be notified about the host lookup - * result - used to match request information to requestor. - * @param aReason - * nsresult reason for the cancellation - * - * @return An object that can be used to cancel the host lookup. - */ - void cancelAsyncResolve(in AUTF8String aHostName, - in unsigned long aFlags, - in nsIDNSListener aListener, - in nsresult aReason); - /** * called to synchronously resolve a hostname. warning this method may * block the calling thread for a long period of time. it is extremely diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index 839b6a5df42..87309acecec 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -150,15 +150,5 @@ NeckoParent::RecvHTMLDNSPrefetch(const nsString& hostname, return true; } -bool -NeckoParent::RecvCancelHTMLDNSPrefetch(const nsString& hostname, - const PRUint16& flags, - const nsresult& reason) -{ - nsAutoString h(hostname); - nsHTMLDNSPrefetch::CancelPrefetch(h, flags, reason); - return true; -} - }} // mozilla::net diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index edae5c41e12..be133d3c47b 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -68,10 +68,6 @@ protected: virtual bool DeallocPWebSocket(PWebSocketParent*); virtual bool RecvHTMLDNSPrefetch(const nsString& hostname, const PRUint16& flags); - virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname, - const PRUint16& flags, - const nsresult& reason); - }; } // namespace net diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index d1a2ee77d15..a530c32dd68 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -69,7 +69,6 @@ parent: PWebSocket(PBrowser browser); HTMLDNSPrefetch(nsString hostname, PRUint16 flags); - CancelHTMLDNSPrefetch(nsString hostname, PRUint16 flags, nsresult reason); both: PHttpChannel(nullable PBrowser browser); From 9ecfddedebd7e80348eac01cfcea19ed4db04c01 Mon Sep 17 00:00:00 2001 From: Taras Glek Date: Mon, 24 Oct 2011 14:38:39 -0700 Subject: [PATCH 26/42] Bug 690585 - Report startup interruptions r=glandium --- toolkit/components/telemetry/TelemetryPing.js | 4 ++-- toolkit/components/telemetry/tests/unit/test_TelemetryPing.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 4284abc216b..05a1b5829bb 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -191,8 +191,7 @@ function getMetadata(reason) { * @return simple measurements as a dictionary. */ function getSimpleMeasurements() { - let si = Cc["@mozilla.org/toolkit/app-startup;1"]. - getService(Ci.nsIAppStartup).getStartupInfo(); + let si = Services.startup.getStartupInfo(); var ret = { // uptime in minutes @@ -206,6 +205,7 @@ function getSimpleMeasurements() { ret[field] = si[field] - si.process } } + ret.startupInterrupted = new Number(Services.startup.interrupted); ret.js = Cc["@mozilla.org/js/xpc/XPConnect;1"] .getService(Ci.nsIJSEngineTelemetryStats) diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index 95f7cb0c703..589850bd8d6 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -43,6 +43,7 @@ function telemetryObserver(aSubject, aTopic, aData) { httpserver.registerPathHandler(PATH, checkHistograms); const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); Telemetry.newHistogram(IGNORE_HISTOGRAM, 1, 2, 3, Telemetry.HISTOGRAM_BOOLEAN); + Services.startup.interrupted = true; telemetry_ping(); } @@ -70,7 +71,7 @@ function checkHistograms(request, response) { do_check_eq(request.getHeader("content-type"), "application/json; charset=UTF-8"); do_check_true(payload.simpleMeasurements.uptime >= 0) - + do_check_true(payload.simpleMeasurements.startupInterrupted === 1); // get rid of the non-deterministic field const expected_info = { reason: "test-ping", From 90dd6c5270730a39a828436384c410b5223f0252 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Mon, 24 Oct 2011 15:45:16 -0700 Subject: [PATCH 27/42] Bug 691541 - Hack to ensure sidebars are hidden properly during startup [r=mfinkle] --- mobile/chrome/content/browser.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js index b4958b17bc7..d19be73d9f2 100644 --- a/mobile/chrome/content/browser.js +++ b/mobile/chrome/content/browser.js @@ -245,8 +245,9 @@ var Browser = { let { x: x2, y: y2 } = Browser.getScrollboxPosition(Browser.pageScrollboxScroller); let [,, leftWidth, rightWidth] = Browser.computeSidebarVisibility(); - let shouldHideSidebars = Browser.controlsPosition ? Browser.controlsPosition.hideSidebars : true; - Browser.controlsPosition = { x: x1, y: y2, hideSidebars: shouldHideSidebars, + // hiddenSidebars counts how many times resizeHandler has called hideSidebars + let hiddenSidebars = Browser.controlsPosition ? Browser.controlsPosition.hiddenSidebars : 0; + Browser.controlsPosition = { x: x1, y: y2, hiddenSidebars: hiddenSidebars, leftSidebar: leftWidth, rightSidebar: rightWidth }; }, true); @@ -276,8 +277,12 @@ var Browser = { ViewableAreaObserver.update(); // Restore the previous scroll position - let restorePosition = Browser.controlsPosition || { hideSidebars: true }; - if (restorePosition.hideSidebars) { + let restorePosition = Browser.controlsPosition || { hiddenSidebars: 0 }; + + // HACK: The first time we hide the sidebars during startup might be too + // early, before layout is completed. Make sure to hide the sidebars on + // the first *two* resize events (bug 691541). + if (restorePosition.hiddenSidebars < 2) { // Since this happens early in the startup process, we need to make sure // the UI has really responded let x = {}, y = {}; @@ -285,7 +290,7 @@ var Browser = { Browser.controlsScrollboxScroller.getPosition(x, y); if (x.value > 0) { // Update the control position data so we are set correctly for the next resize - restorePosition.hideSidebars = false; + restorePosition.hiddenSidebars++; restorePosition.x = x.value; } } else { From bd59d7236620d1328b8f9b25c1241355e477c69e Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Mon, 24 Oct 2011 23:49:59 +0100 Subject: [PATCH 28/42] Backout 26ac81280f33 (bug 692911) for Moth orange on all platforms & M2 orange on win opt --- dom/indexedDB/AsyncConnectionHelper.cpp | 4 +- dom/indexedDB/AsyncConnectionHelper.h | 5 -- dom/indexedDB/DatabaseInfo.cpp | 3 +- dom/indexedDB/DatabaseInfo.h | 4 +- dom/indexedDB/IDBDatabase.cpp | 32 ------- dom/indexedDB/IDBDatabase.h | 3 - dom/indexedDB/IndexedDatabaseManager.cpp | 79 +++------------- dom/indexedDB/IndexedDatabaseManager.h | 22 ----- dom/indexedDB/OpenDatabaseHelper.cpp | 30 +------ dom/indexedDB/OpenDatabaseHelper.h | 1 - dom/indexedDB/TransactionThreadPool.cpp | 16 +++- dom/indexedDB/TransactionThreadPool.h | 6 ++ dom/indexedDB/test/Makefile.in | 1 - .../test/event_propagation_iframe.html | 2 +- dom/indexedDB/test/leaving_page_iframe.html | 2 +- .../test/test_autoIncrement_indexes.html | 3 - dom/indexedDB/test/test_clear.html | 2 +- .../test_create_index_with_integer_keys.html | 4 +- dom/indexedDB/test/test_cursor_mutation.html | 2 +- dom/indexedDB/test/test_getAll.html | 2 - dom/indexedDB/test/test_index_getAll.html | 4 +- .../test/test_index_getAllObjects.html | 2 +- .../test/test_index_object_cursors.html | 2 +- dom/indexedDB/test/test_indexes.html | 2 +- .../test/test_indexes_bad_values.html | 2 - dom/indexedDB/test/test_object_identity.html | 2 +- dom/indexedDB/test/test_odd_result_order.html | 2 +- dom/indexedDB/test/test_open_objectStore.html | 2 +- .../test/test_overlapping_transactions.html | 2 +- .../test/test_readonly_transactions.html | 3 - .../test/test_setVersion_exclusion.html | 90 ------------------- .../test/test_success_events_after_abort.html | 2 +- .../test/test_transaction_abort.html | 3 - .../test/test_transaction_lifetimes.html | 2 +- .../test_transaction_lifetimes_nested.html | 2 +- .../test/test_writer_starvation.html | 2 +- 36 files changed, 58 insertions(+), 289 deletions(-) delete mode 100644 dom/indexedDB/test/test_setVersion_exclusion.html diff --git a/dom/indexedDB/AsyncConnectionHelper.cpp b/dom/indexedDB/AsyncConnectionHelper.cpp index df2b7b340d4..19e87f8c63b 100644 --- a/dom/indexedDB/AsyncConnectionHelper.cpp +++ b/dom/indexedDB/AsyncConnectionHelper.cpp @@ -560,8 +560,8 @@ TransactionPoolEventTarget::Dispatch(nsIRunnable* aRunnable, NS_ASSERTION(aRunnable, "Null pointer!"); NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "Unsupported!"); - TransactionThreadPool* pool = TransactionThreadPool::Get(); - NS_ASSERTION(pool, "This should never be null!"); + TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); + NS_ENSURE_TRUE(pool, NS_ERROR_FAILURE); return pool->Dispatch(mTransaction, aRunnable, false, nsnull); } diff --git a/dom/indexedDB/AsyncConnectionHelper.h b/dom/indexedDB/AsyncConnectionHelper.h index 6f72a567f4a..b0038d48e3a 100644 --- a/dom/indexedDB/AsyncConnectionHelper.h +++ b/dom/indexedDB/AsyncConnectionHelper.h @@ -125,11 +125,6 @@ public: static IDBTransaction* GetCurrentTransaction(); - bool HasTransaction() - { - return mTransaction; - } - nsISupports* GetSource() { return mRequest ? mRequest->Source() : nsnull; diff --git a/dom/indexedDB/DatabaseInfo.cpp b/dom/indexedDB/DatabaseInfo.cpp index 0433aa75da8..f7ed1136285 100644 --- a/dom/indexedDB/DatabaseInfo.cpp +++ b/dom/indexedDB/DatabaseInfo.cpp @@ -85,8 +85,7 @@ EnumerateObjectStoreNames(const nsAString& aKey, DatabaseInfo::DatabaseInfo() : id(0), nextObjectStoreId(1), - nextIndexId(1), - runningVersionChange(false) + nextIndexId(1) { MOZ_COUNT_CTOR(DatabaseInfo); } diff --git a/dom/indexedDB/DatabaseInfo.h b/dom/indexedDB/DatabaseInfo.h index 1f2ff398e0a..1fdab7e3285 100644 --- a/dom/indexedDB/DatabaseInfo.h +++ b/dom/indexedDB/DatabaseInfo.h @@ -54,8 +54,7 @@ struct DatabaseInfo ~DatabaseInfo(); #else DatabaseInfo() - : id(0), nextObjectStoreId(1), nextIndexId(1), runningVersionChange(false) - { } + : id(0), nextObjectStoreId(1), nextIndexId(1) { } #endif static bool Get(PRUint32 aId, @@ -74,7 +73,6 @@ struct DatabaseInfo nsString filePath; PRInt64 nextObjectStoreId; PRInt64 nextIndexId; - bool runningVersionChange; nsAutoRefCnt referenceCount; }; diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index a87a325c75b..34c86d40e61 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -428,34 +428,6 @@ IDBDatabase::IsClosed() return mClosed; } -void -IDBDatabase::EnterSetVersionTransaction() -{ - DatabaseInfo* dbInfo; - if (!DatabaseInfo::Get(mDatabaseId, &dbInfo)) { - NS_ERROR("This should never fail!"); - } - - NS_ASSERTION(!dbInfo->runningVersionChange, "How did that happen?"); - dbInfo->runningVersionChange = true; -} - -void -IDBDatabase::ExitSetVersionTransaction() -{ - DatabaseInfo* dbInfo; - if (!DatabaseInfo::Get(mDatabaseId, &dbInfo)) { - NS_ERROR("This should never fail!"); - } - - NS_ASSERTION(dbInfo->runningVersionChange, "How did that happen?"); - dbInfo->runningVersionChange = false; - - IndexedDatabaseManager* manager = IndexedDatabaseManager::Get(); - NS_ASSERTION(manager, "We should always have a manager here"); - manager->UnblockSetVersionRunnable(this); -} - void IDBDatabase::OnUnlink() { @@ -738,10 +710,6 @@ IDBDatabase::Transaction(nsIVariant* aStoreNames, NS_ERROR("This should never fail!"); } - if (info->runningVersionChange) { - return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; - } - nsTArray storesToOpen; switch (type) { diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h index 9df3cb4f3c2..5a2ab7f582e 100644 --- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -135,9 +135,6 @@ public: // Whether or not the database has had Close called on it. bool IsClosed(); - void EnterSetVersionTransaction(); - void ExitSetVersionTransaction(); - private: IDBDatabase(); ~IDBDatabase(); diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index baa3784f00a..e611fa5676e 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -693,15 +693,19 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) // Now run the helper if there are no more live databases. if (runnable->mHelper && runnable->mDatabases.IsEmpty()) { - // At this point, all databases are closed, so no new transactions can - // be started. There may, however, still be outstanding transactions - // that have not completed. We need to wait for those before we - // dispatch the helper. + // Don't hold the callback alive longer than necessary. + nsRefPtr helper; + helper.swap(runnable->mHelper); - TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); + if (NS_FAILED(helper->DispatchToTransactionPool())) { + NS_WARNING("Failed to dispatch to thread pool!"); + } - nsRefPtr waitRunnable = - new WaitForTransactionsToFinishRunnable(runnable); + // Now wait for the transaction to complete. Completing the transaction + // will be our cue to remove the SetVersionRunnable from our list and + // therefore allow other SetVersion requests to begin. + TransactionThreadPool* pool = TransactionThreadPool::Get(); + NS_ASSERTION(pool, "This should never be null!"); // All other databases should be closed, so we only need to wait on this // one. @@ -710,8 +714,8 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) NS_ERROR("This should never fail!"); } - // Use the WaitForTransactionsToFinishRunnable as the callback. - if (!pool->WaitForAllDatabasesToComplete(array, waitRunnable)) { + // Use the SetVersionRunnable as the callback. + if (!pool->WaitForAllDatabasesToComplete(array, runnable)) { NS_WARNING("Failed to wait for transaction to complete!"); } } @@ -720,27 +724,6 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) } } -void -IndexedDatabaseManager::UnblockSetVersionRunnable(IDBDatabase* aDatabase) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aDatabase, "Null pointer!"); - - // Check through the list of SetVersionRunnables to find the one we're seeking. - for (PRUint32 index = 0; index < mSetVersionRunnables.Length(); index++) { - nsRefPtr& runnable = mSetVersionRunnables[index]; - - if (runnable->mRequestingDatabase->Id() == aDatabase->Id()) { - NS_ASSERTION(!runnable->mHelper, - "Why are we unblocking a runnable if the helper didn't run?"); - NS_DispatchToCurrentThread(runnable); - return; - } - } - - NS_NOTREACHED("How did we get here!"); -} - // static bool IndexedDatabaseManager::SetCurrentDatabase(IDBDatabase* aDatabase) @@ -1300,39 +1283,3 @@ IndexedDatabaseManager::SetVersionRunnable::Run() return NS_OK; } - -NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::WaitForTransactionsToFinishRunnable, - nsIRunnable) - -NS_IMETHODIMP -IndexedDatabaseManager::WaitForTransactionsToFinishRunnable::Run() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - // Don't hold the callback alive longer than necessary. - nsRefPtr helper; - helper.swap(mRunnable->mHelper); - - nsRefPtr runnable; - runnable.swap(mRunnable); - - // If the helper has a transaction, dispatch it to the transaction - // threadpool. - if (helper->HasTransaction()) { - if (NS_FAILED(helper->DispatchToTransactionPool())) { - NS_WARNING("Failed to dispatch to thread pool!"); - } - } - // Otherwise, dispatch it to the IO thread. - else { - IndexedDatabaseManager* manager = IndexedDatabaseManager::Get(); - NS_ASSERTION(manager, "We should definitely have a manager here"); - - helper->Dispatch(manager->IOThread()); - } - - // The helper is responsible for calling - // IndexedDatabaseManager::UnblockSetVersionRunnable. - - return NS_OK; -} diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index 7287394d233..853d239482a 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -204,8 +204,6 @@ private: // Called when AsyncUsageRunnable has finished its Run() method. inline void OnUsageCheckComplete(AsyncUsageRunnable* aRunnable); - void UnblockSetVersionRunnable(IDBDatabase* aDatabase); - // Responsible for waiting until all databases have been closed before running // the version change transaction. Created when // IndexedDatabaseManager::SetDatabaseVersion is called. Runs only once on the @@ -229,26 +227,6 @@ private: // Called when SetVersionRunnable has finished its Run() method. inline void OnSetVersionRunnableComplete(SetVersionRunnable* aRunnable); - - // A callback runnable used by the TransactionPool when it's safe to proceed - // with a SetVersion/DeleteDatabase/etc. - class WaitForTransactionsToFinishRunnable : public nsIRunnable - { - public: - WaitForTransactionsToFinishRunnable(SetVersionRunnable* aRunnable) - : mRunnable(aRunnable) - { - NS_ASSERTION(mRunnable, "Why don't we have a runnable?"); - NS_ASSERTION(mRunnable->mDatabases.IsEmpty(), "We're here too early!"); - } - - NS_DECL_ISUPPORTS - NS_DECL_NSIRUNNABLE - - private: - nsRefPtr mRunnable; - }; - // Maintains a list of live databases per origin. nsClassHashtable > mLiveDatabases; diff --git a/dom/indexedDB/OpenDatabaseHelper.cpp b/dom/indexedDB/OpenDatabaseHelper.cpp index cc909fec4d5..7e7559b47c8 100644 --- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -495,13 +495,10 @@ public: NS_DECL_ISUPPORTS_INHERITED + nsresult DoDatabaseWork(mozIStorageConnection* aConnection); nsresult GetSuccessResult(JSContext* aCx, jsval* aVal); -protected: - nsresult DoDatabaseWork(mozIStorageConnection* aConnection); - nsresult Init(); - // SetVersionHelper never fires an error event at the request. It hands that // responsibility back to the OpenDatabaseHelper void OnError() { } @@ -687,7 +684,6 @@ OpenDatabaseHelper::StartSetVersion() // The SetVersionHelper is responsible for dispatching us back to the // main thread again and changing the state to eSetVersionCompleted. mState = eSetVersionPending; - return NS_OK; } @@ -715,12 +711,6 @@ OpenDatabaseHelper::Run() mState == eSetVersionCompleted, "Why are we here?"); if (mState == eSetVersionCompleted) { - // Allow transaction creation/other version change transactions to proceed - // before we fire events. Other version changes will be postd to the end - // of the event loop, and will be behind whatever the page does in - // its error/success event handlers. - mDatabase->ExitSetVersionTransaction(); - mState = eFiringEvents; } else { // Notify the request that we're done, but only if we didn't just finish @@ -875,15 +865,6 @@ OpenDatabaseHelper::NotifySetVersionFinished() return NS_DispatchToCurrentThread(this); } -void -OpenDatabaseHelper::BlockDatabase() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mDatabase, "This is going bad fast."); - - mDatabase->EnterSetVersionTransaction(); -} - void OpenDatabaseHelper::DispatchSuccessEvent() { @@ -935,15 +916,6 @@ OpenDatabaseHelper::ReleaseMainThreadObjects() NS_IMPL_ISUPPORTS_INHERITED0(SetVersionHelper, AsyncConnectionHelper); -nsresult -SetVersionHelper::Init() -{ - // Block transaction creation until we are done. - mOpenHelper->BlockDatabase(); - - return NS_OK; -} - nsresult SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { diff --git a/dom/indexedDB/OpenDatabaseHelper.h b/dom/indexedDB/OpenDatabaseHelper.h index 53334867ab3..0d91db6b2dc 100644 --- a/dom/indexedDB/OpenDatabaseHelper.h +++ b/dom/indexedDB/OpenDatabaseHelper.h @@ -82,7 +82,6 @@ public: } nsresult NotifySetVersionFinished(); - void BlockDatabase(); protected: // Methods only called on the main thread diff --git a/dom/indexedDB/TransactionThreadPool.cpp b/dom/indexedDB/TransactionThreadPool.cpp index c78ba97ef90..2bb66132b48 100644 --- a/dom/indexedDB/TransactionThreadPool.cpp +++ b/dom/indexedDB/TransactionThreadPool.cpp @@ -238,6 +238,7 @@ TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction) #ifdef DEBUG if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) { + NS_ASSERTION(dbTransactionInfo->locked, "Should be locked!"); NS_ASSERTION(transactionCount == 1, "More transactions running than should be!"); } @@ -343,6 +344,10 @@ TransactionThreadPool::TransactionCanRun(IDBTransaction* aTransaction, PRUint32 transactionCount = transactionsInProgress.Length(); NS_ASSERTION(transactionCount, "Should never be 0!"); + if (mode == IDBTransaction::VERSION_CHANGE) { + dbTransactionInfo->lockPending = true; + } + for (PRUint32 index = 0; index < transactionCount; index++) { // See if this transaction is in out list of current transactions. const TransactionInfo& info = transactionsInProgress[index]; @@ -353,7 +358,11 @@ TransactionThreadPool::TransactionCanRun(IDBTransaction* aTransaction, } } - NS_ASSERTION(mode != IDBTransaction::VERSION_CHANGE, "How did we get here?"); + if (dbTransactionInfo->locked || dbTransactionInfo->lockPending) { + *aCanRun = false; + *aExistingQueue = nsnull; + return NS_OK; + } bool writeOverlap; nsresult rv = @@ -439,6 +448,11 @@ TransactionThreadPool::Dispatch(IDBTransaction* aTransaction, dbTransactionInfo = autoDBTransactionInfo; } + if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) { + NS_ASSERTION(!dbTransactionInfo->locked, "Already locked?!"); + dbTransactionInfo->locked = true; + } + const nsTArray& objectStoreNames = aTransaction->mObjectStoreNames; nsTArray& storesInUse = diff --git a/dom/indexedDB/TransactionThreadPool.h b/dom/indexedDB/TransactionThreadPool.h index 4fdd152c4b0..97d15c46917 100644 --- a/dom/indexedDB/TransactionThreadPool.h +++ b/dom/indexedDB/TransactionThreadPool.h @@ -123,6 +123,12 @@ protected: struct DatabaseTransactionInfo { + DatabaseTransactionInfo() + : locked(false), lockPending(false) + { } + + bool locked; + bool lockPending; nsTArray transactions; nsTArray storesReading; nsTArray storesWriting; diff --git a/dom/indexedDB/test/Makefile.in b/dom/indexedDB/test/Makefile.in index 9d7d00f15f6..555fbeaf93f 100644 --- a/dom/indexedDB/test/Makefile.in +++ b/dom/indexedDB/test/Makefile.in @@ -98,7 +98,6 @@ TEST_FILES = \ test_setVersion.html \ test_setVersion_abort.html \ test_setVersion_events.html \ - test_setVersion_exclusion.html \ test_writer_starvation.html \ third_party_iframe1.html \ third_party_iframe2.html \ diff --git a/dom/indexedDB/test/event_propagation_iframe.html b/dom/indexedDB/test/event_propagation_iframe.html index 035fa86248a..2d1f745fad5 100644 --- a/dom/indexedDB/test/event_propagation_iframe.html +++ b/dom/indexedDB/test/event_propagation_iframe.html @@ -104,7 +104,7 @@ db.onerror = errorEventCounter; db.addEventListener("error", errorEventCounter, true); - event.target.onsuccess = grabEventAndContinueHandler; + event.target.transaction.oncomplete = grabEventAndContinueHandler; db.createObjectStore("foo", { autoIncrement: true }); yield; diff --git a/dom/indexedDB/test/leaving_page_iframe.html b/dom/indexedDB/test/leaving_page_iframe.html index 00bcff71243..43d0e00ab98 100644 --- a/dom/indexedDB/test/leaving_page_iframe.html +++ b/dom/indexedDB/test/leaving_page_iframe.html @@ -12,7 +12,7 @@ function startDBWork() { } var store = db.createObjectStore("mystore"); store.add({ hello: "world" }, 42); - e.target.onsuccess = madeMod; + trans.oncomplete = madeMod; }; } diff --git a/dom/indexedDB/test/test_autoIncrement_indexes.html b/dom/indexedDB/test/test_autoIncrement_indexes.html index 951be60eef9..b24d4896c9d 100644 --- a/dom/indexedDB/test/test_autoIncrement_indexes.html +++ b/dom/indexedDB/test/test_autoIncrement_indexes.html @@ -33,9 +33,6 @@ let key = event.target.result; ok(key, "Added entry"); - request.onsuccess = grabEventAndContinueHandler; - - event = yield; let objectStore = db.transaction("foo").objectStore("foo"); let first = objectStore.index("first"); diff --git a/dom/indexedDB/test/test_clear.html b/dom/indexedDB/test/test_clear.html index 44f85569357..648f234dafd 100644 --- a/dom/indexedDB/test/test_clear.html +++ b/dom/indexedDB/test/test_clear.html @@ -25,7 +25,7 @@ let db = request.result; - event.target.onsuccess = continueToNextStep; + event.target.transaction.oncomplete = continueToNextStep; let objectStore = db.createObjectStore("foo", { autoIncrement: true }); diff --git a/dom/indexedDB/test/test_create_index_with_integer_keys.html b/dom/indexedDB/test/test_create_index_with_integer_keys.html index 6b058ce6b9c..37c3d601656 100644 --- a/dom/indexedDB/test/test_create_index_with_integer_keys.html +++ b/dom/indexedDB/test/test_create_index_with_integer_keys.html @@ -23,7 +23,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.onsuccess = continueToNextStep; + event.target.transaction.oncomplete = continueToNextStep; // Make object store, add data. let objectStore = db.createObjectStore("foo", { keyPath: "id" }); @@ -39,7 +39,7 @@ let db2 = event.target.result; db2.onerror = errorHandler; - event.target.onsuccess = continueToNextStep; + event.target.transaction.oncomplete = continueToNextStep; // Create index. event.target.transaction.objectStore("foo").createIndex("foo", "num"); diff --git a/dom/indexedDB/test/test_cursor_mutation.html b/dom/indexedDB/test/test_cursor_mutation.html index c3d6e52a3ec..7812c1f2ec9 100644 --- a/dom/indexedDB/test/test_cursor_mutation.html +++ b/dom/indexedDB/test/test_cursor_mutation.html @@ -35,7 +35,7 @@ let event = yield; let db = event.target.result; - event.target.onsuccess = continueToNextStep; + event.target.transaction.oncomplete = continueToNextStep; let objectStore = db.createObjectStore("foo", { keyPath: "ss" }); objectStore.createIndex("name", "name", { unique: true }); diff --git a/dom/indexedDB/test/test_getAll.html b/dom/indexedDB/test/test_getAll.html index 7f76c438531..3c873efdfde 100644 --- a/dom/indexedDB/test/test_getAll.html +++ b/dom/indexedDB/test/test_getAll.html @@ -26,7 +26,6 @@ let objectStore = db.createObjectStore("foo", { autoIncrement: true }); - request.onsuccess = grabEventAndContinueHandler; request = objectStore.getAll(); request.onerror = errorHandler; request.onsuccess = grabEventAndContinueHandler; @@ -47,7 +46,6 @@ } } yield; - yield; request = db.transaction("foo").objectStore("foo").getAll(); request.onerror = errorHandler; diff --git a/dom/indexedDB/test/test_index_getAll.html b/dom/indexedDB/test/test_index_getAll.html index ba9fa0067a0..584bb9ff3e3 100644 --- a/dom/indexedDB/test/test_index_getAll.html +++ b/dom/indexedDB/test/test_index_getAll.html @@ -60,7 +60,6 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -79,7 +78,7 @@ } } } - yield; + event = yield; ok(true, "1"); // Now create the indexes. @@ -89,6 +88,7 @@ } is(objectStore.indexNames.length, indexData.length, "Good index count"); + continueToNextStep(); yield; ok(true, "2"); diff --git a/dom/indexedDB/test/test_index_getAllObjects.html b/dom/indexedDB/test/test_index_getAllObjects.html index 585a0d117ac..a65a0536945 100644 --- a/dom/indexedDB/test/test_index_getAllObjects.html +++ b/dom/indexedDB/test/test_index_getAllObjects.html @@ -60,7 +60,6 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -88,6 +87,7 @@ } is(objectStore.indexNames.length, indexData.length, "Good index count"); + continueToNextStep(); yield; objectStore = db.transaction(objectStoreName) diff --git a/dom/indexedDB/test/test_index_object_cursors.html b/dom/indexedDB/test/test_index_object_cursors.html index fb8228e2ca1..b7091206d5d 100644 --- a/dom/indexedDB/test/test_index_object_cursors.html +++ b/dom/indexedDB/test/test_index_object_cursors.html @@ -37,7 +37,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.onsuccess = continueToNextStep; + event.target.transaction.oncomplete = continueToNextStep; for (let objectStoreIndex in objectStoreData) { const objectStoreInfo = objectStoreData[objectStoreIndex]; diff --git a/dom/indexedDB/test/test_indexes.html b/dom/indexedDB/test/test_indexes.html index 98e33701ae6..05132a87d22 100644 --- a/dom/indexedDB/test/test_indexes.html +++ b/dom/indexedDB/test/test_indexes.html @@ -71,7 +71,6 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -98,6 +97,7 @@ indexData[i].options); } is(objectStore.indexNames.length, indexData.length, "Good index count"); + continueToNextStep(); yield; objectStore = db.transaction(objectStoreName) diff --git a/dom/indexedDB/test/test_indexes_bad_values.html b/dom/indexedDB/test/test_indexes_bad_values.html index ffb92160868..e28aa6dd718 100644 --- a/dom/indexedDB/test/test_indexes_bad_values.html +++ b/dom/indexedDB/test/test_indexes_bad_values.html @@ -51,7 +51,6 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -88,7 +87,6 @@ } } yield; - yield; objectStore = db.transaction(objectStoreName) .objectStore(objectStoreName); diff --git a/dom/indexedDB/test/test_object_identity.html b/dom/indexedDB/test/test_object_identity.html index 600bc396cdd..211725d3055 100644 --- a/dom/indexedDB/test/test_object_identity.html +++ b/dom/indexedDB/test/test_object_identity.html @@ -28,7 +28,7 @@ let index2 = objectStore2.index("bar"); ok(index1 === index2, "Got same indexes"); - request.onsuccess = continueToNextStep; + transaction.oncomplete = continueToNextStep; yield; transaction = db.transaction("foo"); diff --git a/dom/indexedDB/test/test_odd_result_order.html b/dom/indexedDB/test/test_odd_result_order.html index 1d14fcf66ce..2a127e6b832 100644 --- a/dom/indexedDB/test/test_odd_result_order.html +++ b/dom/indexedDB/test/test_odd_result_order.html @@ -29,7 +29,7 @@ autoIncrement: true }); let index = objectStore.createIndex("foo", "index"); - event.target.onsuccess = continueToNextStep; + event.target.transaction.oncomplete = continueToNextStep; yield; objectStore = db.transaction("foo", IDBTransaction.READ_WRITE) diff --git a/dom/indexedDB/test/test_open_objectStore.html b/dom/indexedDB/test/test_open_objectStore.html index 48ece9f2af5..93f09ee1e76 100644 --- a/dom/indexedDB/test/test_open_objectStore.html +++ b/dom/indexedDB/test/test_open_objectStore.html @@ -20,7 +20,6 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -32,6 +31,7 @@ is(db.objectStoreNames.length, 1, "Bad objectStores list"); is(db.objectStoreNames.item(0), objectStoreName, "Bad name"); + continueToNextStep(); yield; objectStore = db.transaction(objectStoreName).objectStore(objectStoreName); diff --git a/dom/indexedDB/test/test_overlapping_transactions.html b/dom/indexedDB/test/test_overlapping_transactions.html index 39746ce8a37..b59f60f3fe4 100644 --- a/dom/indexedDB/test/test_overlapping_transactions.html +++ b/dom/indexedDB/test/test_overlapping_transactions.html @@ -26,7 +26,7 @@ let db = event.target.result; is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); - event.target.onsuccess = grabEventAndContinueHandler; + event.target.transaction.oncomplete = grabEventAndContinueHandler; for (let i in objectStores) { db.createObjectStore(objectStores[i], { autoIncrement: true }); } diff --git a/dom/indexedDB/test/test_readonly_transactions.html b/dom/indexedDB/test/test_readonly_transactions.html index 58a0a8ad9e3..5987a1c35f1 100644 --- a/dom/indexedDB/test/test_readonly_transactions.html +++ b/dom/indexedDB/test/test_readonly_transactions.html @@ -22,7 +22,6 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -30,8 +29,6 @@ db.createObjectStore(osName, { autoIncrement: "true" }); - yield; - let key1, key2; request = db.transaction([osName], READ_WRITE) diff --git a/dom/indexedDB/test/test_setVersion_exclusion.html b/dom/indexedDB/test/test_setVersion_exclusion.html deleted file mode 100644 index 96d0d322c28..00000000000 --- a/dom/indexedDB/test/test_setVersion_exclusion.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - Indexed Database Property Test - - - - - - - - - - - - diff --git a/dom/indexedDB/test/test_success_events_after_abort.html b/dom/indexedDB/test/test_success_events_after_abort.html index 31a29d59d56..c9d75a4cb78 100644 --- a/dom/indexedDB/test/test_success_events_after_abort.html +++ b/dom/indexedDB/test/test_success_events_after_abort.html @@ -19,7 +19,7 @@ let db = event.target.result; - event.target.onsuccess = continueToNextStep; + event.target.transaction.oncomplete = continueToNextStep; let objectStore = db.createObjectStore("foo"); objectStore.add({}, 1).onerror = errorHandler; diff --git a/dom/indexedDB/test/test_transaction_abort.html b/dom/indexedDB/test/test_transaction_abort.html index e7d6906c202..9558212a9a3 100644 --- a/dom/indexedDB/test/test_transaction_abort.html +++ b/dom/indexedDB/test/test_transaction_abort.html @@ -29,7 +29,6 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -158,8 +157,6 @@ ok(true, "RemoveIndex threw"); } - yield; - request = db.transaction("foo", READ_WRITE).objectStore("foo").add({}); request.onerror = errorHandler; request.onsuccess = grabEventAndContinueHandler; diff --git a/dom/indexedDB/test/test_transaction_lifetimes.html b/dom/indexedDB/test/test_transaction_lifetimes.html index 7e9e2e368bd..c402ec8b81d 100644 --- a/dom/indexedDB/test/test_transaction_lifetimes.html +++ b/dom/indexedDB/test/test_transaction_lifetimes.html @@ -20,7 +20,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.onsuccess = continueToNextStep; + event.target.transaction.oncomplete = continueToNextStep; db.createObjectStore("foo", { autoIncrement: true }); yield; diff --git a/dom/indexedDB/test/test_transaction_lifetimes_nested.html b/dom/indexedDB/test/test_transaction_lifetimes_nested.html index 2fde9b81dd5..de7b1cebc74 100644 --- a/dom/indexedDB/test/test_transaction_lifetimes_nested.html +++ b/dom/indexedDB/test/test_transaction_lifetimes_nested.html @@ -20,7 +20,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.onsuccess = continueToNextStep; + event.target.transaction.oncomplete = continueToNextStep; db.createObjectStore("foo"); yield; diff --git a/dom/indexedDB/test/test_writer_starvation.html b/dom/indexedDB/test/test_writer_starvation.html index 456975bcca9..d29bdf8bb5f 100644 --- a/dom/indexedDB/test/test_writer_starvation.html +++ b/dom/indexedDB/test/test_writer_starvation.html @@ -23,7 +23,6 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -40,6 +39,7 @@ let key = event.target.result; ok(key, "Got a key"); + SimpleTest.executeSoon(function() { testGenerator.next(); }); yield; let continueReading = true; From 9fbcea508f6e9ba5393f27279b1cd11700beec5c Mon Sep 17 00:00:00 2001 From: Taras Glek Date: Mon, 24 Oct 2011 16:04:04 -0700 Subject: [PATCH 29/42] Include enabled addons + persona in telemetry r=Mossop --- toolkit/components/telemetry/TelemetryPing.js | 55 ++++++++++++++++++- .../tests/unit/test_TelemetryPing.js | 53 ++++++++++++------ toolkit/mozapps/extensions/XPIProvider.jsm | 3 + 3 files changed, 93 insertions(+), 18 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 05a1b5829bb..5eca67c92c3 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -40,6 +40,7 @@ const Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/LightweightThemeManager.jsm"); // When modifying the payload in incompatible ways, please bump this version number const PAYLOAD_VERSION = 1; @@ -230,6 +231,54 @@ TelemetryPing.prototype = { h.add(val); }, + /** + * Descriptive metadata + * + * @param reason + * The reason for the telemetry ping, this will be included in the + * returned metadata, + * @return The metadata as a JS object + */ + getMetadata: function getMetadata(reason) { + let ai = Services.appinfo; + let ret = { + reason: reason, + OS: ai.OS, + appID: ai.ID, + appVersion: ai.version, + appName: ai.name, + appBuildID: ai.appBuildID, + platformBuildID: ai.platformBuildID, + }; + + // sysinfo fields are not always available, get what we can. + let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); + let fields = ["cpucount", "memsize", "arch", "version", "device", "manufacturer", "hardware"]; + for each (let field in fields) { + let value; + try { + value = sysInfo.getProperty(field); + } catch (e) { + continue + } + if (field == "memsize") { + // Send RAM size in megabytes. Rounding because sysinfo doesn't + // always provide RAM in multiples of 1024. + value = Math.round(value / 1024 / 1024) + } + ret[field] = value + } + + let theme = LightweightThemeManager.currentTheme; + if (theme) + ret.persona = theme.id; + + if (this._addons) + ret.addons = this._addons; + + return ret; + }, + /** * Pull values from about:memory into corresponding histograms */ @@ -296,10 +345,11 @@ TelemetryPing.prototype = { this.gatherMemory(); let payload = { ver: PAYLOAD_VERSION, - info: getMetadata(reason), + info: this.getMetadata(reason), simpleMeasurements: getSimpleMeasurements(), histograms: getHistograms() }; + let isTestPing = (reason == "test-ping"); // Generate a unique id once per session so the server can cope with duplicate submissions. // Use a deterministic url for testing. @@ -405,6 +455,9 @@ TelemetryPing.prototype = { var server = this._server; switch (aTopic) { + case "Add-ons": + this._addons = aData; + break; case "profile-after-change": this.setup(); break; diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index 589850bd8d6..4ba4d31e3a7 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -10,6 +10,7 @@ do_load_httpd_js(); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/LightweightThemeManager.jsm"); const PATH = "/submit/telemetry/test-ping"; const SERVER = "http://localhost:4444"; @@ -21,6 +22,7 @@ const BinaryInputStream = Components.Constructor( "setInputStream"); var httpserver = new nsHttpServer(); +var gFinished = false; function telemetry_ping () { const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver); @@ -47,27 +49,12 @@ function telemetryObserver(aSubject, aTopic, aData) { telemetry_ping(); } -function run_test() { - createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - Services.obs.addObserver(nonexistentServerObserver, "telemetry-test-xhr-complete", false); - telemetry_ping(); - // spin the event loop - do_test_pending(); -} - -function readBytesFromInputStream(inputStream, count) { - if (!count) { - count = inputStream.available(); - } - return new BinaryInputStream(inputStream).readBytes(count); -} - function checkHistograms(request, response) { // do not need the http server anymore httpserver.stop(do_test_finished); - let s = request.bodyInputStream + let s = request.bodyInputStream; let payload = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON) - .decode(readBytesFromInputStream(s)) + .decodeFromStream(s, s.available()); do_check_eq(request.getHeader("content-type"), "application/json; charset=UTF-8"); do_check_true(payload.simpleMeasurements.uptime >= 0) @@ -103,6 +90,7 @@ function checkHistograms(request, response) { let tc = payload.histograms[TELEMETRY_SUCCESS] do_check_eq(uneval(tc), uneval(expected_tc)); + gFinished = true; } // copied from toolkit/mozapps/extensions/test/xpcshell/head_addons.js @@ -153,3 +141,34 @@ function createAppInfo(id, name, version, platformVersion) { registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo", XULAPPINFO_CONTRACTID, XULAppInfoFactory); } + +function dummyTheme(id) { + return { + id: id, + name: Math.random().toString(), + headerURL: "http://lwttest.invalid/a.png", + footerURL: "http://lwttest.invalid/b.png", + textcolor: Math.random().toString(), + accentcolor: Math.random().toString() + }; +} + +function run_test() { + // Addon manager needs a profile directory + do_get_profile(); + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); + // try to make LightweightThemeManager do stuff + let gInternalManager = Cc["@mozilla.org/addons/integration;1"] + .getService(Ci.nsIObserver) + .QueryInterface(Ci.nsITimerCallback); + + gInternalManager.observe(null, "addons-startup", null); + LightweightThemeManager.currentTheme = dummyTheme("1234"); + + Services.obs.addObserver(nonexistentServerObserver, "telemetry-test-xhr-complete", false); + telemetry_ping(); + // spin the event loop + do_test_pending(); + // ensure that test runs to completion + do_register_cleanup(function () do_check_true(gFinished)) + } diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index ad6816e5421..e0a632cae36 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -1762,6 +1762,9 @@ var XPIProvider = { Services.appinfo.annotateCrashReport("Add-ons", data); } catch (e) { } + + const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver); + TelemetryPing.observe(null, "Add-ons", data); }, /** From 618657cc852725b66bd919da0e7f46b738f907fc Mon Sep 17 00:00:00 2001 From: Jesse Ruderman Date: Mon, 24 Oct 2011 16:21:09 -0700 Subject: [PATCH 30/42] Bug 675553 followup: remove tautologous assertions. r=ehsan --- js/xpconnect/tests/components/native/xpctest_attributes.cpp | 2 -- layout/generic/nsHTMLReflowState.cpp | 2 -- layout/style/nsCSSParser.cpp | 6 ------ layout/style/nsCSSScanner.h | 2 -- 4 files changed, 12 deletions(-) diff --git a/js/xpconnect/tests/components/native/xpctest_attributes.cpp b/js/xpconnect/tests/components/native/xpctest_attributes.cpp index 38b2261d5f7..55a807e55ee 100644 --- a/js/xpconnect/tests/components/native/xpctest_attributes.cpp +++ b/js/xpconnect/tests/components/native/xpctest_attributes.cpp @@ -118,8 +118,6 @@ NS_IMETHODIMP xpcTestObjectReadWrite :: GetBooleanProperty(bool *aBooleanPropert return NS_OK; } NS_IMETHODIMP xpcTestObjectReadWrite :: SetBooleanProperty(bool aBooleanProperty) { - NS_ENSURE_TRUE(aBooleanProperty == true || aBooleanProperty == false, - NS_ERROR_INVALID_ARG); boolProperty = aBooleanProperty; return NS_OK; } diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index a42f8c61ba3..59bf434d088 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -140,8 +140,6 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, NS_PRECONDITION((aContainingBlockWidth == -1) == (aContainingBlockHeight == -1), "cb width and height should only be non-default together"); - NS_PRECONDITION(aInit == true || aInit == false, - "aInit out of range for bool"); NS_PRECONDITION(!mFlags.mSpecialHeightReflow || !NS_SUBTREE_DIRTY(aFrame), "frame should be clean when getting special height reflow"); diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 72ae5902a2c..bb9252bf8a6 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -587,7 +587,6 @@ protected: bool aIsRepeating); void SetParsingCompoundProperty(bool aBool) { - NS_ASSERTION(aBool == true || aBool == false, "bad bool value"); mParsingCompoundProperty = aBool; } bool IsParsingCompoundProperty(void) const { @@ -792,7 +791,6 @@ CSSParserImpl::SetStyleSheet(nsCSSStyleSheet* aSheet) nsresult CSSParserImpl::SetQuirkMode(bool aQuirkMode) { - NS_ASSERTION(aQuirkMode == true || aQuirkMode == false, "bad bool value"); mNavQuirkMode = aQuirkMode; return NS_OK; } @@ -800,8 +798,6 @@ CSSParserImpl::SetQuirkMode(bool aQuirkMode) nsresult CSSParserImpl::SetSVGMode(bool aSVGMode) { - NS_ASSERTION(aSVGMode == true || aSVGMode == false, - "bad bool value"); mScanner.SetSVGMode(aSVGMode); return NS_OK; } @@ -1157,8 +1153,6 @@ CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer, InitScanner(aBuffer, aURI, aLineNumber, aURI, nsnull); AssertInitialState(); - NS_ASSERTION(aHTMLMode == true || aHTMLMode == false, - "invalid bool"); mHTMLMediaMode = aHTMLMode; // XXXldb We need to make the scanner not skip CSS comments! (Or diff --git a/layout/style/nsCSSScanner.h b/layout/style/nsCSSScanner.h index 291887d1263..6871c10b887 100644 --- a/layout/style/nsCSSScanner.h +++ b/layout/style/nsCSSScanner.h @@ -147,8 +147,6 @@ class nsCSSScanner { // Set whether or not we are processing SVG void SetSVGMode(bool aSVGMode) { - NS_ASSERTION(aSVGMode == true || aSVGMode == false, - "bad bool value"); mSVGMode = aSVGMode; } bool IsSVGMode() const { From d26cae0aed1c5671614a8d1e234b29407f67de5e Mon Sep 17 00:00:00 2001 From: Geoff Lankow Date: Tue, 25 Oct 2011 01:39:45 +0100 Subject: [PATCH 31/42] Bug 693714 - Bootstrapped addons have uninstall & install called on Firefox update; r=dtownsend --- toolkit/mozapps/extensions/XPIProvider.jsm | 13 +++++++-- .../test/xpcshell/test_bug659772.js | 28 ++++++++++++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index e0a632cae36..16482e1f2f1 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -2553,6 +2553,7 @@ var XPIProvider = { LOG("New add-on " + aId + " installed in " + aInstallLocation.name); let newAddon = null; + let sameVersion = false; // Check the updated manifests lists for the install location, If there // is no manifest for the add-on ID then newAddon will be undefined if (aInstallLocation.name in aManifests) @@ -2621,10 +2622,11 @@ var XPIProvider = { // Some properties should only be migrated if the add-on hasn't changed. // The version property isn't a perfect check for this but covers the // vast majority of cases. - if (aMigrateData.version == newAddon.version && - "targetApplications" in aMigrateData) { + if (aMigrateData.version == newAddon.version) { LOG("Migrating compatibility info"); - newAddon.applyCompatibilityUpdate(aMigrateData, true); + sameVersion = true; + if ("targetApplications" in aMigrateData) + newAddon.applyCompatibilityUpdate(aMigrateData, true); } // Since the DB schema has changed make sure softDisabled is correct @@ -2700,6 +2702,11 @@ var XPIProvider = { let oldBootstrap = oldBootstrappedAddons[newAddon.id]; XPIProvider.bootstrappedAddons[newAddon.id] = oldBootstrap; + // If the old version is the same as the new version, don't call + // uninstall and install methods. + if (sameVersion) + return false; + installReason = Services.vc.compare(oldBootstrap.version, newAddon.version) < 0 ? BOOTSTRAP_REASONS.ADDON_UPGRADE : BOOTSTRAP_REASONS.ADDON_DOWNGRADE; diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug659772.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug659772.js index 61b34ba62f9..f591509fdc4 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_bug659772.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug659772.js @@ -100,8 +100,11 @@ function run_test_1() { do_check_false(a4.isActive); do_check_false(isExtensionInAddonsList(profileDir, addon4.id)); - // Prepare the add-on update - installAllFiles([do_get_addon("test_bug659772")], function() { + // Prepare the add-on update, and a bootstrapped addon (bug 693714) + installAllFiles([ + do_get_addon("test_bug659772"), + do_get_addon("test_bootstrap1_1") + ], function() { shutdownManager(); // Make it look like the next time the app is started it has a new DB schema @@ -142,6 +145,9 @@ function run_test_1() { converter.close(); stream.close(); + Services.prefs.clearUserPref("bootstraptest.install_reason"); + Services.prefs.clearUserPref("bootstraptest.uninstall_reason"); + startupManager(false); AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", @@ -179,6 +185,10 @@ function run_test_1() { do_check_false(a4.isActive); do_check_false(isExtensionInAddonsList(profileDir, addon4.id)); + // Check that install and uninstall haven't been called on the bootstrapped adddon + do_check_false(Services.prefs.prefHasUserValue("bootstraptest.install_reason")); + do_check_false(Services.prefs.prefHasUserValue("bootstraptest.uninstall_reason")); + a1.uninstall(); a2.uninstall(); a3.uninstall(); @@ -235,8 +245,11 @@ function run_test_2() { do_check_false(a4.isActive); do_check_false(isExtensionInAddonsList(profileDir, addon4.id)); - // Prepare the add-on update - installAllFiles([do_get_addon("test_bug659772")], function() { + // Prepare the add-on update, and a bootstrapped addon (bug 693714) + installAllFiles([ + do_get_addon("test_bug659772"), + do_get_addon("test_bootstrap1_1") + ], function() { shutdownManager(); // Make it look like the next time the app is started it has a new DB schema @@ -277,6 +290,9 @@ function run_test_2() { converter.close(); stream.close(); + Services.prefs.clearUserPref("bootstraptest.install_reason"); + Services.prefs.clearUserPref("bootstraptest.uninstall_reason"); + gAppInfo.version = "2"; startupManager(true); @@ -315,6 +331,10 @@ function run_test_2() { do_check_true(a4.isActive); do_check_true(isExtensionInAddonsList(profileDir, addon4.id)); + // Check that install and uninstall haven't been called on the bootstrapped adddon + do_check_false(Services.prefs.prefHasUserValue("bootstraptest.install_reason")); + do_check_false(Services.prefs.prefHasUserValue("bootstraptest.uninstall_reason")); + a1.uninstall(); a2.uninstall(); a3.uninstall(); From 31aee7f540a0273c13b93c580a8b692a956a1eea Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 24 Oct 2011 17:50:47 -0700 Subject: [PATCH 32/42] Bug 696690 - Memory reporter for the startup cache. r=tglek. --- modules/libjar/nsZipArchive.cpp | 13 +++++++++++++ modules/libjar/nsZipArchive.h | 8 ++++++++ startupcache/StartupCache.cpp | 32 +++++++++++++++++++++++++++++++- startupcache/StartupCache.h | 6 ++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/modules/libjar/nsZipArchive.cpp b/modules/libjar/nsZipArchive.cpp index 68d362ca73d..3c39efd6b5a 100644 --- a/modules/libjar/nsZipArchive.cpp +++ b/modules/libjar/nsZipArchive.cpp @@ -234,6 +234,11 @@ nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry, return NS_OK; } +PRInt64 nsZipHandle::SizeOfMapping() +{ + return mLen; +} + nsZipHandle::~nsZipHandle() { if (mMap) { @@ -775,6 +780,14 @@ MOZ_WIN_MEM_TRY_BEGIN MOZ_WIN_MEM_TRY_CATCH(return nsnull) } +//--------------------------------------------- +// nsZipArchive::SizeOfMapping +//--------------------------------------------- +PRInt64 nsZipArchive::SizeOfMapping() +{ + return mFd ? mFd->SizeOfMapping() : 0; +} + //------------------------------------------ // nsZipArchive constructor and destructor //------------------------------------------ diff --git a/modules/libjar/nsZipArchive.h b/modules/libjar/nsZipArchive.h index 871811a0ded..6c273dc22b5 100644 --- a/modules/libjar/nsZipArchive.h +++ b/modules/libjar/nsZipArchive.h @@ -221,6 +221,12 @@ public: */ const PRUint8* GetData(nsZipItem* aItem); + /** + * Gets the amount of memory taken up by the archive's mapping. + * @return the size + */ + PRInt64 SizeOfMapping(); + private: //--- private members --- @@ -382,6 +388,8 @@ public: NS_METHOD_(nsrefcnt) AddRef(void); NS_METHOD_(nsrefcnt) Release(void); + PRInt64 SizeOfMapping(); + protected: const PRUint8 * mFileData; /* pointer to mmaped file */ PRUint32 mLen; /* length of file and memory mapped area */ diff --git a/startupcache/StartupCache.cpp b/startupcache/StartupCache.cpp index f36bdd77830..7c6f392bbde 100644 --- a/startupcache/StartupCache.cpp +++ b/startupcache/StartupCache.cpp @@ -49,6 +49,7 @@ #include "nsIClassInfo.h" #include "nsIFile.h" #include "nsILocalFile.h" +#include "nsIMemoryReporter.h" #include "nsIObserver.h" #include "nsIObserverService.h" #include "nsIOutputStream.h" @@ -81,6 +82,22 @@ #define SC_WORDSIZE "8" #endif +static PRInt64 +GetStartupCacheSize() +{ + mozilla::scache::StartupCache* sc = mozilla::scache::StartupCache::GetSingleton(); + return sc ? sc->SizeOfMapping() : 0; +} + +NS_MEMORY_REPORTER_IMPLEMENT(StartupCache, + "explicit/startup-cache", + KIND_NONHEAP, + nsIMemoryReporter::UNITS_BYTES, + GetStartupCacheSize, + "Memory used to hold the startup cache. This " + "memory is backed by a file and is likely to be " + "swapped out shortly after start-up."); + namespace mozilla { namespace scache { @@ -120,7 +137,8 @@ StartupCache* StartupCache::gStartupCache; bool StartupCache::gShutdownInitiated; StartupCache::StartupCache() - : mArchive(NULL), mStartupWriteInitiated(false), mWriteThread(NULL) {} + : mArchive(NULL), mStartupWriteInitiated(false), mWriteThread(NULL), + mMemoryReporter(nsnull) { } StartupCache::~StartupCache() { @@ -134,6 +152,8 @@ StartupCache::~StartupCache() WaitOnWriteThread(); WriteToDisk(); gStartupCache = nsnull; + (void)::NS_UnregisterMemoryReporter(mMemoryReporter); + mMemoryReporter = nsnull; } nsresult @@ -206,6 +226,10 @@ StartupCache::Init() NS_WARNING("Failed to load startupcache file correctly, removing!"); InvalidateCache(); } + + mMemoryReporter = new NS_MEMORY_REPORTER_NAME(StartupCache); + (void)::NS_RegisterMemoryReporter(mMemoryReporter); + return NS_OK; } @@ -308,6 +332,12 @@ StartupCache::PutBuffer(const char* id, const char* inbuf, PRUint32 len) return ResetStartupWriteTimer(); } +PRInt64 +StartupCache::SizeOfMapping() +{ + return mArchive ? mArchive->SizeOfMapping() : 0; +} + struct CacheWriteHolder { nsCOMPtr writer; diff --git a/startupcache/StartupCache.h b/startupcache/StartupCache.h index 5f16c85546e..d737d43c1b4 100644 --- a/startupcache/StartupCache.h +++ b/startupcache/StartupCache.h @@ -95,6 +95,8 @@ * provide some convenience in writing out data. */ +class nsIMemoryReporter; + namespace mozilla { namespace scache { @@ -148,6 +150,8 @@ public: static StartupCache* GetSingleton(); static void DeleteSingleton(); + PRInt64 SizeOfMapping(); + private: StartupCache(); ~StartupCache(); @@ -178,6 +182,8 @@ private: #ifdef DEBUG nsTHashtable mWriteObjectMap; #endif + + nsIMemoryReporter* mMemoryReporter; }; // This debug outputstream attempts to detect if clients are writing multiple From ec22ec53026f516407e0a855e9eb202dea01feb9 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Tue, 25 Oct 2011 02:02:36 +0100 Subject: [PATCH 33/42] Bug 690204 - Migrate jsarray.cpp to CallArgs; r=waldo Except for array_splice and array_concat which have other work outstanding. --- js/src/jsarray.cpp | 446 ++++++++++++++++++++++++--------------------- 1 file changed, 235 insertions(+), 211 deletions(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 64b734cef54..b9dc0dfb4eb 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1517,9 +1517,9 @@ array_toSource(JSContext *cx, uintN argc, Value *vp) for (jsuint index = 0; index < length; index++) { JSBool hole; - Value tmp; + Value elt; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, index, &hole, &tmp)) { + !GetElement(cx, obj, index, &hole, &elt)) { return false; } @@ -1528,7 +1528,7 @@ array_toSource(JSContext *cx, uintN argc, Value *vp) if (hole) { str = cx->runtime->emptyString; } else { - str = js_ValueToSource(cx, tmp); + str = js_ValueToSource(cx, elt); if (!str) return false; } @@ -1607,7 +1607,7 @@ class AutoArrayCycleDetector static JSBool array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, - JSString *sepstr, Value *rval) + JSString *sepstr, CallArgs &args) { static const jschar comma = ','; const jschar *sep; @@ -1627,7 +1627,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, return false; if (detector.foundCycle()) { - rval->setString(cx->runtime->atomState.emptyAtom); + args.rval().setString(cx->runtime->atomState.emptyAtom); return true; } @@ -1656,19 +1656,20 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, return false; JSBool hole; - if (!GetElement(cx, obj, index, &hole, rval)) + Value elt; + if (!GetElement(cx, obj, index, &hole, &elt)) return false; - if (!hole && !rval->isNullOrUndefined()) { + if (!hole && !elt.isNullOrUndefined()) { if (locale) { - JSObject *robj = ToObject(cx, rval); + JSObject *robj = ToObject(cx, &elt); if (!robj) return false; jsid id = ATOM_TO_JSID(cx->runtime->atomState.toLocaleStringAtom); - if (!robj->callMethod(cx, id, 0, NULL, rval)) + if (!robj->callMethod(cx, id, 0, NULL, &elt)) return false; } - if (!ValueToStringBuffer(cx, *rval, sb)) + if (!ValueToStringBuffer(cx, elt, sb)) return false; } @@ -1682,7 +1683,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, JSString *str = sb.finishString(); if (!str) return false; - rval->setString(str); + args.rval().setString(str); return true; } @@ -1692,11 +1693,12 @@ array_toString(JSContext *cx, uintN argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; - Value &join = vp[0]; + Value join = args.calleev(); if (!obj->getProperty(cx, cx->runtime->atomState.joinAtom, &join)) return false; @@ -1704,22 +1706,22 @@ array_toString(JSContext *cx, uintN argc, Value *vp) JSString *str = obj_toStringHelper(cx, obj); if (!str) return false; - vp->setString(str); + args.rval().setString(str); return true; } LeaveTrace(cx); - InvokeArgsGuard args; - if (!cx->stack.pushInvokeArgs(cx, 0, &args)) + InvokeArgsGuard ag; + if (!cx->stack.pushInvokeArgs(cx, 0, &ag)) return false; - args.calleev() = join; - args.thisv().setObject(*obj); + ag.calleev() = join; + ag.thisv().setObject(*obj); /* Do the call. */ - if (!Invoke(cx, args)) + if (!Invoke(cx, ag)) return false; - *vp = args.rval(); + args.rval() = ag.rval(); return true; } @@ -1728,7 +1730,8 @@ array_toLocaleString(JSContext *cx, uintN argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; @@ -1736,7 +1739,7 @@ array_toLocaleString(JSContext *cx, uintN argc, Value *vp) * Passing comma here as the separator. Need a way to get a * locale-specific version. */ - return array_toString_sub(cx, obj, JS_TRUE, NULL, vp); + return array_toString_sub(cx, obj, JS_TRUE, NULL, args); } static inline bool @@ -1871,32 +1874,33 @@ array_join(JSContext *cx, uintN argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); + CallArgs args = CallArgsFromVp(argc, vp); JSString *str; - if (argc == 0 || vp[2].isUndefined()) { + if (args.length() == 0 || args[0].isUndefined()) { str = NULL; } else { - str = js_ValueToString(cx, vp[2]); + str = js_ValueToString(cx, args[0]); if (!str) return JS_FALSE; - vp[2].setString(str); + args[0].setString(str); } - JSObject *obj = ToObject(cx, &vp[1]); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; - return array_toString_sub(cx, obj, JS_FALSE, str, vp); + return array_toString_sub(cx, obj, JS_FALSE, str, args); } static JSBool array_reverse(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; jsuint len; if (!js_GetLengthProperty(cx, obj, &len)) return false; - vp->setObject(*obj); do { if (!obj->isDenseArray()) @@ -1905,8 +1909,10 @@ array_reverse(JSContext *cx, uintN argc, Value *vp) break; /* An empty array or an array with no elements is already reversed. */ - if (len == 0 || obj->getDenseArrayCapacity() == 0) + if (len == 0 || obj->getDenseArrayCapacity() == 0) { + args.rval().setObject(*obj); return true; + } /* * It's actually surprisingly complicated to reverse an array due to the @@ -1949,21 +1955,22 @@ array_reverse(JSContext *cx, uintN argc, Value *vp) * array has trailing holes (and thus the original array began with * holes). */ + args.rval().setObject(*obj); return true; } while (false); - AutoValueRooter tvr(cx); + Value lowval, hival; for (jsuint i = 0, half = len / 2; i < half; i++) { JSBool hole, hole2; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, i, &hole, tvr.addr()) || - !GetElement(cx, obj, len - i - 1, &hole2, vp) || - !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.value()) || - !SetOrDeleteArrayElement(cx, obj, i, hole2, *vp)) { + !GetElement(cx, obj, i, &hole, &lowval) || + !GetElement(cx, obj, len - i - 1, &hole2, &hival) || + !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, lowval) || + !SetOrDeleteArrayElement(cx, obj, i, hole2, hival)) { return false; } } - vp->setObject(*obj); + args.rval().setObject(*obj); return true; } @@ -2145,21 +2152,21 @@ sort_compare(void *arg, const void *a, const void *b, int *result) if (!JS_CHECK_OPERATION_LIMIT(cx)) return JS_FALSE; - InvokeArgsGuard &args = ca->args; - if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, 2, &args)) + InvokeArgsGuard &ag = ca->args; + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 2, &ag)) return JS_FALSE; - args.calleeHasBeenReset(); - args.calleev() = ca->fval; - args.thisv() = UndefinedValue(); - args[0] = *av; - args[1] = *bv; + ag.calleeHasBeenReset(); + ag.calleev() = ca->fval; + ag.thisv() = UndefinedValue(); + ag[0] = *av; + ag[1] = *bv; - if (!Invoke(cx, args)) + if (!Invoke(cx, ag)) return JS_FALSE; jsdouble cmp; - if (!ToNumber(cx, args.rval(), &cmp)) + if (!ToNumber(cx, ag.rval(), &cmp)) return JS_FALSE; /* Clamp cmp to -1, 0, 1. */ @@ -2200,26 +2207,26 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) jsuint len, newlen, i, undefs; size_t elemsize; JSString *str; - - Value *argv = JS_ARGV(cx, vp); + + CallArgs args = CallArgsFromVp(argc, vp); Value fval; - if (argc > 0 && !argv[0].isUndefined()) { - if (argv[0].isPrimitive()) { + if (args.length() > 0 && !args[0].isUndefined()) { + if (args[0].isPrimitive()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG); return false; } - fval = argv[0]; /* non-default compare function */ + fval = args[0]; /* non-default compare function */ } else { fval.setNull(); } - JSObject *obj = ToObject(cx, &vp[1]); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; if (!js_GetLengthProperty(cx, obj, &len)) return false; if (len == 0) { - vp->setObject(*obj); + args.rval().setObject(*obj); return true; } @@ -2298,7 +2305,7 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) } if (newlen == 0) { - vp->setObject(*obj); + args.rval().setObject(*obj); return true; /* The array has only holes and undefs. */ } @@ -2429,52 +2436,49 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) if (!JS_CHECK_OPERATION_LIMIT(cx) || DeleteArrayElement(cx, obj, --len, true) < 0) return false; } - vp->setObject(*obj); + args.rval().setObject(*obj); return true; } /* * Perl-inspired push, pop, shift, unshift, and splice methods. */ -static JSBool -array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) +static bool +array_push_slowly(JSContext *cx, JSObject *obj, CallArgs &args) { jsuint length; if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - if (!InitArrayElements(cx, obj, length, argc, argv, true)) - return JS_FALSE; + return false; + if (!InitArrayElements(cx, obj, length, args.length(), args.array(), true)) + return false; /* Per ECMA-262, return the new array length. */ - jsdouble newlength = length + jsdouble(argc); - rval->setNumber(newlength); + jsdouble newlength = length + jsdouble(args.length()); + args.rval().setNumber(newlength); return js_SetLengthProperty(cx, obj, newlength); } -static JSBool -array_push1_dense(JSContext* cx, JSObject* obj, const Value &v, Value *rval) +static bool +array_push1_dense(JSContext* cx, JSObject* obj, CallArgs &args) { + JS_ASSERT(args.length() == 1); + uint32 length = obj->getArrayLength(); - do { - JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, 1); - if (result != JSObject::ED_OK) { - if (result == JSObject::ED_FAILED) - return false; - JS_ASSERT(result == JSObject::ED_SPARSE); - break; - } + JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, 1); + if (result != JSObject::ED_OK) { + if (result == JSObject::ED_FAILED) + return false; + JS_ASSERT(result == JSObject::ED_SPARSE); + if (!obj->makeDenseArraySlow(cx)) + return false; + return array_push_slowly(cx, obj, args); + } - obj->setDenseArrayLength(length + 1); - obj->setDenseArrayElementWithType(cx, length, v); - rval->setNumber(obj->getArrayLength()); - return true; - } while (false); - - if (!obj->makeDenseArraySlow(cx)) - return false; - Value tmp = v; - return array_push_slowly(cx, obj, 1, &tmp, rval); + obj->setDenseArrayLength(length + 1); + obj->setDenseArrayElementWithType(cx, length, args[0]); + args.rval().setNumber(obj->getArrayLength()); + return true; } JS_ALWAYS_INLINE JSBool @@ -2529,71 +2533,81 @@ JS_DEFINE_CALLINFO_3(extern, BOOL_FAIL, js_NewbornArrayPush_tn, CONTEXT, OBJECT, JSBool js::array_push(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; /* Insist on one argument and obj of the expected class. */ - if (argc != 1 || !obj->isDenseArray()) - return array_push_slowly(cx, obj, argc, vp + 2, vp); + if (args.length() != 1 || !obj->isDenseArray()) + return array_push_slowly(cx, obj, args); - return array_push1_dense(cx, obj, vp[2], vp); + return array_push1_dense(cx, obj, args); } static JSBool -array_pop_slowly(JSContext *cx, JSObject* obj, Value *vp) +array_pop_slowly(JSContext *cx, JSObject* obj, CallArgs &args) { jsuint index; - JSBool hole; - if (!js_GetLengthProperty(cx, obj, &index)) - return JS_FALSE; - if (index == 0) { - vp->setUndefined(); - } else { - index--; + return false; - /* Get the to-be-deleted property's value into vp. */ - if (!GetElement(cx, obj, index, &hole, vp)) - return JS_FALSE; - if (!hole && DeleteArrayElement(cx, obj, index, true) < 0) - return JS_FALSE; + if (index == 0) { + args.rval().setUndefined(); + return js_SetLengthProperty(cx, obj, index); } + + index--; + + JSBool hole; + Value elt; + if (!GetElement(cx, obj, index, &hole, &elt)) + return false; + + if (!hole && DeleteArrayElement(cx, obj, index, true) < 0) + return false; + + args.rval() = elt; return js_SetLengthProperty(cx, obj, index); } static JSBool -array_pop_dense(JSContext *cx, JSObject* obj, Value *vp) +array_pop_dense(JSContext *cx, JSObject* obj, CallArgs &args) { - jsuint index; - JSBool hole; - - index = obj->getArrayLength(); + jsuint index = obj->getArrayLength(); if (index == 0) { - vp->setUndefined(); + args.rval().setUndefined(); return JS_TRUE; } + index--; - if (!GetElement(cx, obj, index, &hole, vp)) - return JS_FALSE; - if (!hole && DeleteArrayElement(cx, obj, index, true) < 0) + + JSBool hole; + Value elt; + if (!GetElement(cx, obj, index, &hole, &elt)) return JS_FALSE; + if (!hole && DeleteArrayElement(cx, obj, index, true) < 0) + return JS_FALSE; if (cx->typeInferenceEnabled() && obj->getDenseArrayInitializedLength() > index) obj->setDenseArrayInitializedLength(index); + obj->setArrayLength(cx, index); + + args.rval() = elt; return JS_TRUE; } JSBool js::array_pop(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; if (obj->isDenseArray()) - return array_pop_dense(cx, obj, vp); - return array_pop_slowly(cx, obj, vp); + return array_pop_dense(cx, obj, args); + return array_pop_slowly(cx, obj, args); } #ifdef JS_METHODJIT @@ -2617,7 +2631,8 @@ mjit::stubs::ArrayShift(VMFrame &f) JSBool js::array_shift(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return JS_FALSE; @@ -2626,16 +2641,16 @@ js::array_shift(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; if (length == 0) { - vp->setUndefined(); + args.rval().setUndefined(); } else { length--; if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) && length < obj->getDenseArrayCapacity() && 0 < obj->getDenseArrayInitializedLength()) { - *vp = obj->getDenseArrayElement(0); - if (vp->isMagic(JS_ARRAY_HOLE)) - vp->setUndefined(); + args.rval() = obj->getDenseArrayElement(0); + if (args.rval().isMagic(JS_ARRAY_HOLE)) + args.rval().setUndefined(); obj->moveDenseArrayElements(0, 1, length); if (cx->typeInferenceEnabled()) obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1); @@ -2647,9 +2662,8 @@ js::array_shift(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } - /* Get the to-be-deleted property's value into vp ASAP. */ JSBool hole; - if (!GetElement(cx, obj, 0, &hole, vp)) + if (!GetElement(cx, obj, 0, &hole, &args.rval())) return JS_FALSE; /* Slide down the array above the first element. */ @@ -2672,11 +2686,8 @@ js::array_shift(JSContext *cx, uintN argc, Value *vp) static JSBool array_unshift(JSContext *cx, uintN argc, Value *vp) { - Value *argv; - JSBool hole; - jsdouble last, newlen; - - JSObject *obj = ToObject(cx, &vp[1]); + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; @@ -2684,10 +2695,9 @@ array_unshift(JSContext *cx, uintN argc, Value *vp) if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; - newlen = length; - if (argc > 0) { - /* Slide up the array to make room for argc at the bottom. */ - argv = JS_ARGV(cx, vp); + jsdouble newlen = length; + if (args.length() > 0) { + /* Slide up the array to make room for all args at the bottom. */ if (length > 0) { bool optimized = false; do { @@ -2695,25 +2705,26 @@ array_unshift(JSContext *cx, uintN argc, Value *vp) break; if (js_PrototypeHasIndexedProperties(cx, obj)) break; - JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, argc); + JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, args.length()); if (result != JSObject::ED_OK) { if (result == JSObject::ED_FAILED) return false; JS_ASSERT(result == JSObject::ED_SPARSE); break; } - obj->moveDenseArrayElements(argc, 0, length); - for (uint32 i = 0; i < argc; i++) + obj->moveDenseArrayElements(args.length(), 0, length); + for (uint32 i = 0; i < args.length(); i++) obj->setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE)); optimized = true; } while (false); if (!optimized) { - last = length; - jsdouble upperIndex = last + argc; + jsdouble last = length; + jsdouble upperIndex = last + args.length(); AutoValueRooter tvr(cx); do { --last, --upperIndex; + JSBool hole; if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, obj, last, &hole, tvr.addr()) || !SetOrDeleteArrayElement(cx, obj, upperIndex, hole, tvr.value())) { @@ -2723,17 +2734,17 @@ array_unshift(JSContext *cx, uintN argc, Value *vp) } } - /* Copy from argv to the bottom of the array. */ - if (!InitArrayElements(cx, obj, 0, argc, argv, true)) + /* Copy from args to the bottom of the array. */ + if (!InitArrayElements(cx, obj, 0, args.length(), args.array(), true)) return JS_FALSE; - newlen += argc; + newlen += args.length(); } if (!js_SetLengthProperty(cx, obj, newlen)) return JS_FALSE; /* Follow Perl by returning the new array length. */ - vp->setNumber(newlen); + args.rval().setNumber(newlen); return JS_TRUE; } @@ -3102,14 +3113,13 @@ js::array_concat(JSContext *cx, uintN argc, Value *vp) static JSBool array_slice(JSContext *cx, uintN argc, Value *vp) { - Value *argv; JSObject *nobj; jsuint length, begin, end, slot; JSBool hole; - argv = JS_ARGV(cx, vp); + CallArgs args = CallArgsFromVp(argc, vp); - JSObject *obj = ToObject(cx, &vp[1]); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; @@ -3118,9 +3128,9 @@ array_slice(JSContext *cx, uintN argc, Value *vp) begin = 0; end = length; - if (argc > 0) { + if (args.length() > 0) { jsdouble d; - if (!ToInteger(cx, argv[0], &d)) + if (!ToInteger(cx, args[0], &d)) return false; if (d < 0) { d += length; @@ -3131,8 +3141,8 @@ array_slice(JSContext *cx, uintN argc, Value *vp) } begin = (jsuint)d; - if (argc > 1 && !argv[1].isUndefined()) { - if (!ToInteger(cx, argv[1], &d)) + if (args.length() > 1 && !args[1].isUndefined()) { + if (!ToInteger(cx, args[1], &d)) return false; if (d < 0) { d += length; @@ -3156,16 +3166,14 @@ array_slice(JSContext *cx, uintN argc, Value *vp) TryReuseArrayType(obj, nobj); if (!obj->isPackedDenseArray()) nobj->markDenseArrayNotPacked(cx); - vp->setObject(*nobj); + args.rval().setObject(*nobj); return JS_TRUE; } - /* Create a new Array object and root it using *vp. */ nobj = NewDenseAllocatedArray(cx, end - begin); if (!nobj) return JS_FALSE; TryReuseArrayType(obj, nobj); - vp->setObject(*nobj); AutoValueRooter tvr(cx); for (slot = begin; slot < end; slot++) { @@ -3177,18 +3185,24 @@ array_slice(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; } + args.rval().setObject(*nobj); return JS_TRUE; } +enum IndexOfKind { + IndexOf, + LastIndexOf +}; + static JSBool -array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) +array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args) { jsuint length, i, stop; Value tosearch; jsint direction; JSBool hole; - JSObject *obj = ToObject(cx, &vp[1]); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; if (!js_GetLengthProperty(cx, obj, &length)) @@ -3196,26 +3210,26 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) if (length == 0) goto not_found; - if (argc <= 1) { - i = isLast ? length - 1 : 0; - tosearch = (argc != 0) ? vp[2] : UndefinedValue(); + if (args.length() <= 1) { + i = (mode == LastIndexOf) ? length - 1 : 0; + tosearch = (args.length() != 0) ? args[0] : UndefinedValue(); } else { jsdouble start; - tosearch = vp[2]; - if (!ToInteger(cx, vp[3], &start)) + tosearch = args[0]; + if (!ToInteger(cx, args[1], &start)) return false; if (start < 0) { start += length; if (start < 0) { - if (isLast) + if (mode == LastIndexOf) goto not_found; i = 0; } else { i = (jsuint)start; } } else if (start >= length) { - if (!isLast) + if (mode == IndexOf) goto not_found; i = length - 1; } else { @@ -3223,7 +3237,7 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) } } - if (isLast) { + if (mode == LastIndexOf) { stop = 0; direction = -1; } else { @@ -3232,16 +3246,17 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) } for (;;) { + Value elt; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, (jsuint)i, &hole, vp)) { + !GetElement(cx, obj, (jsuint)i, &hole, &elt)) { return JS_FALSE; } if (!hole) { JSBool equal; - if (!StrictlyEqual(cx, *vp, tosearch, &equal)) + if (!StrictlyEqual(cx, elt, tosearch, &equal)) return JS_FALSE; if (equal) { - vp->setNumber(i); + args.rval().setNumber(i); return JS_TRUE; } } @@ -3251,20 +3266,22 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) } not_found: - vp->setInt32(-1); + args.rval().setInt32(-1); return JS_TRUE; } static JSBool array_indexOf(JSContext *cx, uintN argc, Value *vp) { - return array_indexOfHelper(cx, JS_FALSE, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_indexOfHelper(cx, IndexOf, args); } static JSBool array_lastIndexOf(JSContext *cx, uintN argc, Value *vp) { - return array_indexOfHelper(cx, JS_TRUE, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_indexOfHelper(cx, LastIndexOf, args); } /* Order is important; extras that take a predicate funarg must follow MAP. */ @@ -3281,9 +3298,9 @@ typedef enum ArrayExtraMode { #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT) static JSBool -array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) +array_extra(JSContext *cx, ArrayExtraMode mode, CallArgs &args) { - JSObject *obj = ToObject(cx, &vp[1]); + JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; @@ -3295,12 +3312,11 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) * First, get or compute our callee, so that we error out consistently * when passed a non-callable object. */ - if (argc == 0) { - js_ReportMissingArg(cx, *vp, 0); + if (args.length() == 0) { + js_ReportMissingArg(cx, args.calleev(), 0); return JS_FALSE; } - Value *argv = vp + 2; - JSObject *callable = js_ValueToCallableObject(cx, &argv[0], JSV2F_SEARCH_STACK); + JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); if (!callable) return JS_FALSE; @@ -3323,17 +3339,17 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) start = length - 1, end = -1, step = -1; /* FALL THROUGH */ case REDUCE: - if (length == 0 && argc == 1) { + if (length == 0 && args.length() == 1) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE); return JS_FALSE; } - if (argc >= 2) { - *vp = argv[1]; + if (args.length() >= 2) { + args.rval() = args[1]; } else { JSBool hole; do { - if (!GetElement(cx, obj, start, &hole, vp)) + if (!GetElement(cx, obj, start, &hole, &args.rval())) return JS_FALSE; start += step; } while (hole && start != end); @@ -3355,29 +3371,29 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) if (!newtype) return JS_FALSE; newarr->setType(newtype); - vp->setObject(*newarr); + args.rval().setObject(*newarr); break; case SOME: - vp->setBoolean(false); + args.rval().setBoolean(false); break; case EVERY: - vp->setBoolean(true); + args.rval().setBoolean(true); break; case FOREACH: - vp->setUndefined(); + args.rval().setUndefined(); break; } if (length == 0) return JS_TRUE; - Value thisv = (argc > 1 && !REDUCE_MODE(mode)) ? argv[1] : UndefinedValue(); + Value thisv = (args.length() > 1 && !REDUCE_MODE(mode)) ? args[1] : UndefinedValue(); /* * For all but REDUCE, we call with 3 args (value, index, array). REDUCE * requires 4 args (accum, value, index, array). */ - argc = 3 + REDUCE_MODE(mode); + uintN agArgc = 3 + REDUCE_MODE(mode); MUST_FLOW_THROUGH("out"); JSBool ok = JS_TRUE; @@ -3385,7 +3401,7 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) Value objv = ObjectValue(*obj); AutoValueRooter tvr(cx); - InvokeArgsGuard args; + InvokeArgsGuard ag; for (jsuint i = start; i != end; i += step) { JSBool hole; ok = JS_CHECK_OPERATION_LIMIT(cx) && @@ -3395,29 +3411,29 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) if (hole) continue; - if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, argc, &args)) + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, agArgc, &ag)) return false; /* * Push callable and 'this', then args. We must do this for every * iteration around the loop since Invoke clobbers its arguments. */ - args.calleeHasBeenReset(); - args.calleev() = ObjectValue(*callable); - args.thisv() = thisv; + ag.calleeHasBeenReset(); + ag.calleev() = ObjectValue(*callable); + ag.thisv() = thisv; uintN argi = 0; if (REDUCE_MODE(mode)) - args[argi++] = *vp; - args[argi++] = tvr.value(); - args[argi++] = Int32Value(i); - args[argi] = objv; + ag[argi++] = args.rval(); + ag[argi++] = tvr.value(); + ag[argi++] = Int32Value(i); + ag[argi] = objv; /* Do the call. */ - ok = Invoke(cx, args); + ok = Invoke(cx, ag); if (!ok) break; - const Value &rval = args.rval(); + const Value &rval = ag.rval(); if (mode > MAP) cond = js_ValueToBoolean(rval); @@ -3431,7 +3447,7 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) break; case REDUCE: case REDUCE_RIGHT: - *vp = rval; + args.rval() = rval; break; case MAP: if (!ok) @@ -3452,13 +3468,13 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) break; case SOME: if (cond) { - vp->setBoolean(true); + args.rval().setBoolean(true); goto out; } break; case EVERY: if (!cond) { - vp->setBoolean(false); + args.rval().setBoolean(false); goto out; } break; @@ -3474,43 +3490,50 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) static JSBool array_forEach(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, FOREACH, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_extra(cx, FOREACH, args); } static JSBool array_map(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, MAP, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_extra(cx, MAP, args); } static JSBool array_reduce(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, REDUCE, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_extra(cx, REDUCE, args); } static JSBool array_reduceRight(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, REDUCE_RIGHT, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_extra(cx, REDUCE_RIGHT, args); } static JSBool array_filter(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, FILTER, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_extra(cx, FILTER, args); } static JSBool array_some(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, SOME, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_extra(cx, SOME, args); } static JSBool array_every(JSContext *cx, uintN argc, Value *vp) { - return array_extra(cx, EVERY, argc, vp); + CallArgs args = CallArgsFromVp(argc, vp); + return array_extra(cx, EVERY, args); } static JSBool @@ -3569,33 +3592,34 @@ static JSFunctionSpec array_static_methods[] = { JSBool js_Array(JSContext *cx, uintN argc, Value *vp) { + CallArgs args = CallArgsFromVp(argc, vp); TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Array); if (!type) return JS_FALSE; - if (argc != 1 || !vp[2].isNumber()) { - if (!InitArrayTypes(cx, type, vp + 2, argc)) + if (args.length() != 1 || !args[0].isNumber()) { + if (!InitArrayTypes(cx, type, args.array(), args.length())) return false; - JSObject *obj = (argc == 0) + JSObject *obj = (args.length() == 0) ? NewDenseEmptyArray(cx) - : NewDenseCopiedArray(cx, argc, vp + 2); + : NewDenseCopiedArray(cx, args.length(), args.array()); if (!obj) return false; obj->setType(type); - vp->setObject(*obj); + args.rval().setObject(*obj); return true; } uint32 length; - if (vp[2].isInt32()) { - int32_t i = vp[2].toInt32(); + if (args[0].isInt32()) { + int32 i = args[0].toInt32(); if (i < 0) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); return false; } length = uint32(i); } else { - jsdouble d = vp[2].toDouble(); + jsdouble d = args[0].toDouble(); length = js_DoubleToECMAUint32(d); if (d != jsdouble(length)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); @@ -3613,7 +3637,7 @@ js_Array(JSContext *cx, uintN argc, Value *vp) if (obj->getArrayLength() > INT32_MAX) obj->setArrayLength(cx, obj->getArrayLength()); - vp->setObject(*obj); + args.rval().setObject(*obj); return true; } @@ -3775,11 +3799,11 @@ NewSlowEmptyArray(JSContext *cx) JSBool js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp) { - uintN i; + CallArgs args = CallArgsFromVp(argc, vp); JSObject *array; - for (i = 0; i < argc; i++) { - Value arg = JS_ARGV(cx, vp)[i]; + for (uintN i = 0; i < args.length(); i++) { + Value arg = args[i]; char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, NULL); if (!bytes) @@ -3801,7 +3825,7 @@ js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp) cx->free_(bytes); } - JS_SET_RVAL(cx, vp, JSVAL_VOID); + args.rval().setUndefined(); return true; } #endif From b4239526ca2331235ce6fbc9485f1518e83870cf Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 24 Oct 2011 18:13:23 -0700 Subject: [PATCH 34/42] Bug 696690 - Maemo bustage fix for a394d649cf90. r=red. --- startupcache/StartupCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/startupcache/StartupCache.cpp b/startupcache/StartupCache.cpp index 7c6f392bbde..285264e6e86 100644 --- a/startupcache/StartupCache.cpp +++ b/startupcache/StartupCache.cpp @@ -96,7 +96,7 @@ NS_MEMORY_REPORTER_IMPLEMENT(StartupCache, GetStartupCacheSize, "Memory used to hold the startup cache. This " "memory is backed by a file and is likely to be " - "swapped out shortly after start-up."); + "swapped out shortly after start-up.") namespace mozilla { namespace scache { From 1f309402a937c6367838a66c88e7ce28fc2d0539 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 24 Oct 2011 19:22:27 -0700 Subject: [PATCH 35/42] Bug 696941 - Make Token::u::reflags private and hidden behind accessors. r=cdleary --- js/src/frontend/Parser.cpp | 2 +- js/src/frontend/TokenStream.cpp | 32 ++++++++++++++++++-------------- js/src/frontend/TokenStream.h | 23 ++++++++++++++++++++--- js/src/jsprvtd.h | 11 +++++++---- 4 files changed, 46 insertions(+), 22 deletions(-) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 28e06cf62d9..b3a19513f9b 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -7637,7 +7637,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) const jschar *chars = tokenStream.getTokenbuf().begin(); size_t length = tokenStream.getTokenbuf().length(); - RegExpFlag flags = RegExpFlag(tokenStream.currentToken().t_reflags); + RegExpFlag flags = tokenStream.currentToken().regExpFlags(); RegExpStatics *res = context->regExpStatics(); RegExpObject *reobj; diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 3bd0c2a976a..bc00f20414f 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -1960,10 +1960,9 @@ TokenStream::getTokenInternal() * Look for a regexp. */ if (flags & TSF_OPERAND) { - uintN reflags, length; - JSBool inCharClass = JS_FALSE; - tokenbuf.clear(); + + bool inCharClass = false; for (;;) { c = getChar(); if (c == '\\') { @@ -1971,9 +1970,9 @@ TokenStream::getTokenInternal() goto error; c = getChar(); } else if (c == '[') { - inCharClass = JS_TRUE; + inCharClass = true; } else if (c == ']') { - inCharClass = JS_FALSE; + inCharClass = false; } else if (c == '/' && !inCharClass) { /* For compat with IE, allow unescaped / in char classes. */ break; @@ -1987,31 +1986,36 @@ TokenStream::getTokenInternal() if (!tokenbuf.append(c)) goto error; } - for (reflags = 0, length = tokenbuf.length() + 1; ; length++) { + + RegExpFlag reflags = NoFlags; + uintN length = tokenbuf.length() + 1; + while (true) { c = peekChar(); - if (c == 'g' && !(reflags & JSREG_GLOB)) - reflags |= JSREG_GLOB; + if (c == 'g' && !(reflags & GlobalFlag)) + reflags = RegExpFlag(reflags | GlobalFlag); else if (c == 'i' && !(reflags & IgnoreCaseFlag)) - reflags |= IgnoreCaseFlag; + reflags = RegExpFlag(reflags | IgnoreCaseFlag); else if (c == 'm' && !(reflags & MultilineFlag)) - reflags |= MultilineFlag; + reflags = RegExpFlag(reflags | MultilineFlag); else if (c == 'y' && !(reflags & StickyFlag)) - reflags |= StickyFlag; + reflags = RegExpFlag(reflags | StickyFlag); else break; getChar(); + length++; } + c = peekChar(); if (JS7_ISLET(c)) { - char buf[2] = { '\0' }; + char buf[2] = { '\0', '\0' }; tp->pos.begin.index += length + 1; - buf[0] = (char)c; + buf[0] = char(c); ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR, JSMSG_BAD_REGEXP_FLAG, buf); (void) getChar(); goto error; } - tp->t_reflags = reflags; + tp->setRegExpFlags(reflags); tt = TOK_REGEXP; break; } diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 4fb8b91aaf5..27d52d090a3 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -265,18 +265,25 @@ struct Token { JSAtom *atom; /* potentially-numeric atom */ } n; } s; - uintN reflags; /* regexp flags, use tokenbuf to access - regexp chars */ class { /* pair for XML PI */ friend struct Token; JSAtom *data; /* auxiliary atom table entry */ PropertyName *target; /* main atom table entry */ } xmlpi; jsdouble dval; /* floating point number */ + private: + friend struct Token; + RegExpFlag reflags; /* regexp flags, use tokenbuf to access + regexp chars */ } u; /* Mutators */ + /* + * FIXME: Init type early enough such that all mutators can assert + * type-safety. See bug 697000. + */ + void setName(JSOp op, PropertyName *name) { JS_ASSERT(op == JSOP_NAME); u.s.op = op; @@ -294,6 +301,11 @@ struct Token { u.xmlpi.data = data; } + void setRegExpFlags(js::RegExpFlag flags) { + JS_ASSERT((flags & AllFlags) == flags); + u.reflags = flags; + } + /* Type-safe accessors */ PropertyName *name() const { @@ -320,10 +332,15 @@ struct Token { JS_ASSERT(type == TOK_XMLPI); return u.xmlpi.data; } + + js::RegExpFlag regExpFlags() const { + JS_ASSERT(type == TOK_REGEXP); + JS_ASSERT((u.reflags & AllFlags) == u.reflags); + return u.reflags; + } }; #define t_op u.s.op -#define t_reflags u.reflags #define t_dval u.dval enum TokenStreamFlags diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index b15fc9a1918..6dbd4a98652 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -129,10 +129,13 @@ class MatchPairs; enum RegExpFlag { - IgnoreCaseFlag = JS_BIT(0), - GlobalFlag = JS_BIT(1), - MultilineFlag = JS_BIT(2), - StickyFlag = JS_BIT(3) + IgnoreCaseFlag = 0x01, + GlobalFlag = 0x02, + MultilineFlag = 0x04, + StickyFlag = 0x08, + + NoFlags = 0x00, + AllFlags = 0x0f }; enum RegExpExecType From 8bbd8be9b78a1b7650369b96eb1b2928595438fc Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 24 Oct 2011 19:24:06 -0700 Subject: [PATCH 36/42] Bug 696941 - Privatize Token::u::dval. r=cdleary --- js/src/frontend/Parser.cpp | 10 +++++----- js/src/frontend/TokenStream.cpp | 9 ++++----- js/src/frontend/TokenStream.h | 30 ++++++++++++++++++++++++------ 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index b3a19513f9b..3ef90bffaf2 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -7240,7 +7240,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) pn3 = NullaryNode::create(tc); if (!pn3) return NULL; - pn3->pn_dval = tokenStream.currentToken().t_dval; + pn3->pn_dval = tokenStream.currentToken().number(); if (!js_ValueToAtom(context, DoubleValue(pn3->pn_dval), &atom)) return NULL; break; @@ -7266,7 +7266,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) pn3 = NullaryNode::create(tc); if (!pn3) return NULL; - pn3->pn_dval = tokenStream.currentToken().t_dval; + pn3->pn_dval = tokenStream.currentToken().number(); if (!js_ValueToAtom(context, DoubleValue(pn3->pn_dval), &atom)) return NULL; } else { @@ -7412,7 +7412,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) pn = UnaryNode::create(tc); if (!pn) return NULL; - pn->pn_num = (jsint) tokenStream.currentToken().t_dval; + pn->pn_num = tokenStream.currentToken().sharpNumber(); tt = tokenStream.getToken(TSF_OPERAND); pn->pn_kid = primaryExpr(tt, JS_FALSE); if (!pn->pn_kid) @@ -7436,7 +7436,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) return NULL; if (!tc->ensureSharpSlots()) return NULL; - pn->pn_num = (jsint) tokenStream.currentToken().t_dval; + pn->pn_num = tokenStream.currentToken().sharpNumber(); break; #endif /* JS_HAS_SHARP_VARS */ @@ -7667,7 +7667,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) if (!pn) return NULL; pn->setOp(JSOP_DOUBLE); - pn->pn_dval = tokenStream.currentToken().t_dval; + pn->pn_dval = tokenStream.currentToken().number(); break; case TOK_PRIMARY: diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index bc00f20414f..652a71ab132 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -1713,7 +1713,7 @@ TokenStream::getTokenInternal() if (!js_strtod(cx, numStart, userbuf.addressOfNextRawChar(), &dummy, &dval)) goto error; } - tp->t_dval = dval; + tp->setNumber(dval); tt = TOK_NUMBER; goto out; } @@ -1799,7 +1799,7 @@ TokenStream::getTokenInternal() const jschar *dummy; if (!GetPrefixInteger(cx, numStart, userbuf.addressOfNextRawChar(), radix, &dummy, &dval)) goto error; - tp->t_dval = dval; + tp->setNumber(dval); tt = TOK_NUMBER; goto out; } @@ -2071,9 +2071,8 @@ TokenStream::getTokenInternal() goto error; } } - tp->t_dval = (jsdouble) n; - if (cx->hasStrictOption() && - (c == '=' || c == '#')) { + tp->setSharpNumber(uint16(n)); + if (cx->hasStrictOption() && (c == '=' || c == '#')) { char buf[20]; JS_snprintf(buf, sizeof buf, "#%u%c", n, c); if (!ReportCompileErrorNumber(cx, this, NULL, JSREPORT_WARNING | JSREPORT_STRICT, diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 27d52d090a3..47d5758f70d 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -265,14 +265,15 @@ struct Token { JSAtom *atom; /* potentially-numeric atom */ } n; } s; - class { /* pair for XML PI */ - friend struct Token; + + private: + friend struct Token; + struct { /* pair for XML PI */ JSAtom *data; /* auxiliary atom table entry */ PropertyName *target; /* main atom table entry */ } xmlpi; - jsdouble dval; /* floating point number */ - private: - friend struct Token; + uint16 sharpNumber; /* sharp variable number: #1# or #1= */ + jsdouble number; /* floating point number */ RegExpFlag reflags; /* regexp flags, use tokenbuf to access regexp chars */ } u; @@ -306,6 +307,14 @@ struct Token { u.reflags = flags; } + void setSharpNumber(uint16 sharpNum) { + u.sharpNumber = sharpNum; + } + + void setNumber(jsdouble n) { + u.number = n; + } + /* Type-safe accessors */ PropertyName *name() const { @@ -338,10 +347,19 @@ struct Token { JS_ASSERT((u.reflags & AllFlags) == u.reflags); return u.reflags; } + + uint16 sharpNumber() const { + JS_ASSERT(type == TOK_DEFSHARP || type == TOK_USESHARP); + return u.sharpNumber; + } + + jsdouble number() const { + JS_ASSERT(type == TOK_NUMBER); + return u.number; + } }; #define t_op u.s.op -#define t_dval u.dval enum TokenStreamFlags { From cd84b9121d4e8cfda53637290ffe45ea8c9c2091 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 24 Oct 2011 20:46:00 -0700 Subject: [PATCH 37/42] Always discard methodjit code on GC, remove JM+TM integration, bug 685358. r=dvander --- js/src/jscompartment.cpp | 148 ++------ js/src/jscompartment.h | 2 +- js/src/jsgc.cpp | 36 +- js/src/jsgcmark.cpp | 7 - js/src/jsinfer.cpp | 15 - js/src/jsinterp.cpp | 66 +--- js/src/jsinterp.h | 7 +- js/src/jstracer.cpp | 19 +- js/src/methodjit/Compiler.cpp | 236 ++---------- js/src/methodjit/Compiler.h | 16 - js/src/methodjit/FastArithmetic.cpp | 2 +- js/src/methodjit/FrameState.cpp | 14 +- js/src/methodjit/InvokeHelpers.cpp | 565 ++-------------------------- js/src/methodjit/MethodJIT.cpp | 51 +-- js/src/methodjit/MethodJIT.h | 19 - js/src/methodjit/MonoIC.cpp | 163 -------- js/src/methodjit/MonoIC.h | 27 -- js/src/methodjit/PolyIC.cpp | 52 --- js/src/methodjit/PolyIC.h | 3 - js/src/methodjit/StubCalls.h | 5 - 20 files changed, 126 insertions(+), 1327 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 014e0f7e456..3db31a519c3 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -412,42 +412,6 @@ JSCompartment::wrap(JSContext *cx, AutoIdVector &props) return true; } -#if defined JS_METHODJIT && defined JS_MONOIC -/* - * Check if the pool containing the code for jit should be destroyed, per the - * heuristics in JSCompartment::sweep. - */ -static inline bool -ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit, - uint32 releaseInterval, uint32 &counter) -{ - JSC::ExecutablePool *pool = jit->code.m_executablePool; - if (pool->m_gcNumber != cx->runtime->gcNumber) { - /* - * The m_destroy flag may have been set in a previous GC for a pool which had - * references we did not remove (e.g. from the compartment's ExecutableAllocator) - * and is still around. Forget we tried to destroy it in such cases. - */ - pool->m_destroy = false; - pool->m_gcNumber = cx->runtime->gcNumber; - if (--counter == 0) { - pool->m_destroy = true; - counter = releaseInterval; - } - } - return pool->m_destroy; -} - -static inline void -ScriptTryDestroyCode(JSContext *cx, JSScript *script, bool normal, - uint32 releaseInterval, uint32 &counter) -{ - mjit::JITScript *jit = normal ? script->jitNormal : script->jitCtor; - if (jit && ScriptPoolDestroyed(cx, jit, releaseInterval, counter)) - mjit::ReleaseScriptCode(cx, script, !normal); -} -#endif // JS_METHODJIT && JS_MONOIC - /* * This method marks pointers that cross compartment boundaries. It should be * called only for per-compartment GCs, since full GCs naturally follow pointers @@ -492,7 +456,7 @@ JSCompartment::markTypes(JSTracer *trc) } void -JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) +JSCompartment::sweep(JSContext *cx, bool releaseTypes) { /* Remove dead wrappers from the table. */ for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { @@ -531,83 +495,27 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) traceMonitor()->sweep(cx); #endif + /* + * Kick all frames on the stack into the interpreter, and release all JIT + * code in the compartment. + */ #ifdef JS_METHODJIT - /* - * Purge PICs in the compartment, along with native call stubs for - * compartments which do not have such stubs on the stack. PICs can - * reference shapes and type data, and native call stubs are disassociated - * from the PIC or MIC they were generated for. - */ - bool canPurgeNativeCalls = true; - VMFrame *f = hasJaegerCompartment() ? jaegerCompartment()->activeFrame() : NULL; - for (; f; f = f->previous) { - if (f->stubRejoin) - canPurgeNativeCalls = false; - } - for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { - JSScript *script = i.get(); - if (script->hasJITCode()) { -#ifdef JS_POLYIC - mjit::ic::PurgePICs(cx, script); -#endif - if (canPurgeNativeCalls) { - if (script->jitNormal) - script->jitNormal->purgeNativeCallStubs(); - if (script->jitCtor) - script->jitCtor->purgeNativeCallStubs(); - } - } - } -#endif - - bool discardScripts = !active && (releaseInterval != 0 || hasDebugModeCodeToDrop); - -#if defined JS_METHODJIT && defined JS_MONOIC - - /* - * The release interval is the frequency with which we should try to destroy - * executable pools by releasing all JIT code in them, zero to never destroy pools. - * Initialize counter so that the first pool will be destroyed, and eventually drive - * the amount of JIT code in never-used compartments to zero. Don't discard anything - * for compartments which currently have active stack frames. - */ - uint32 counter = 1; - if (discardScripts) - hasDebugModeCodeToDrop = false; + mjit::ClearAllFrames(this); for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get(); - if (script->hasJITCode()) { - mjit::ic::SweepCallICs(cx, script, discardScripts); - if (discardScripts) { - ScriptTryDestroyCode(cx, script, true, releaseInterval, counter); - ScriptTryDestroyCode(cx, script, false, releaseInterval, counter); - } - } - } + mjit::ReleaseScriptCode(cx, script); -#endif - -#ifdef JS_METHODJIT - if (types.inferenceEnabled) - mjit::ClearAllFrames(this); -#endif - - if (activeAnalysis) { /* - * Analysis information is in use, so don't clear the analysis pool. - * jitcode still needs to be released, if this is a shape-regenerating - * GC then shape numbers baked into the code may change. + * Use counts for scripts are reset on GC. After discarding code we + * need to let it warm back up to get information like which opcodes + * are setting array holes or accessing getter properties. */ -#ifdef JS_METHODJIT - if (types.inferenceEnabled) { - for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { - JSScript *script = i.get(); - mjit::ReleaseScriptCode(cx, script); - } - } + script->resetUseCount(); + } #endif - } else { + + if (!activeAnalysis) { /* * Clear the analysis pool, but don't release its data yet. While * sweeping types any live data will be allocated into the pool. @@ -615,6 +523,13 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize()); oldAlloc.steal(&typeLifoAlloc); + /* + * Periodically release observed types for all scripts. This is safe to + * do when there are no frames for the compartment on the stack. + */ + if (active) + releaseTypes = false; + /* * Sweep analysis information and everything depending on it from the * compartment, including all remaining mjit code if inference is @@ -626,12 +541,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) if (script->types) { types::TypeScript::Sweep(cx, script); - /* - * On each 1/8 lifetime, release observed types for all scripts. - * This is always safe to do when there are no frames for the - * compartment on the stack. - */ - if (discardScripts) { + if (releaseTypes) { script->types->destroy(); script->types = NULL; script->typesPurged = true; @@ -684,20 +594,6 @@ JSCompartment::purge(JSContext *cx) if (hasTraceMonitor()) traceMonitor()->needFlush = JS_TRUE; #endif - -#if defined JS_METHODJIT && defined JS_MONOIC - /* - * MICs do not refer to data which can be GC'ed and do not generate stubs - * which might need to be discarded, but are sensitive to shape regeneration. - */ - if (cx->runtime->gcRegenShapes) { - for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { - JSScript *script = i.get(); - if (script->hasJITCode()) - mjit::ic::PurgeMICs(cx, script); - } - } -#endif } MathCache * diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 072aeb679c8..52403eab665 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -534,7 +534,7 @@ struct JS_FRIEND_API(JSCompartment) { bool wrap(JSContext *cx, js::AutoIdVector &props); void markTypes(JSTracer *trc); - void sweep(JSContext *cx, uint32 releaseInterval); + void sweep(JSContext *cx, bool releaseTypes); void purge(JSContext *cx); void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index b8d0293b24b..037e4d93b3f 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -730,12 +730,8 @@ js_GCThingIsMarked(void *thing, uintN color = BLACK) return reinterpret_cast(thing)->isMarked(color); } -/* - * 1/8 life for JIT code. After this number of microseconds have passed, 1/8 of all - * JIT code is discarded in inactive compartments, regardless of how often that - * code runs. - */ -static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 60 * 1000 * 1000; +/* Lifetime for type sets attached to scripts containing observed types. */ +static const int64 JIT_SCRIPT_RELEASE_TYPES_INTERVAL = 60 * 1000 * 1000; JSBool js_InitGC(JSRuntime *rt, uint32 maxbytes) @@ -777,7 +773,7 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) */ rt->setGCLastBytes(8192, GC_NORMAL); - rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_EIGHTH_LIFETIME; + rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL; return true; } @@ -2281,27 +2277,19 @@ GCHelperThread::doSweep() #endif /* JS_THREADSAFE */ -static uint32 -ComputeJitReleaseInterval(JSContext *cx) +static bool +ReleaseObservedTypes(JSContext *cx) { JSRuntime *rt = cx->runtime; - /* - * Figure out how much JIT code should be released from inactive compartments. - * If multiple eighth-lives have passed, compound the release interval linearly; - * if enough time has passed, all inactive JIT code will be released. - */ - uint32 releaseInterval = 0; + + bool releaseTypes = false; int64 now = PRMJ_Now(); if (now >= rt->gcJitReleaseTime) { - releaseInterval = 8; - while (now >= rt->gcJitReleaseTime) { - if (--releaseInterval == 1) - rt->gcJitReleaseTime = now; - rt->gcJitReleaseTime += JIT_SCRIPT_EIGHTH_LIFETIME; - } + releaseTypes = true; + rt->gcJitReleaseTime = now + JIT_SCRIPT_RELEASE_TYPES_INTERVAL; } - return releaseInterval; + return releaseTypes; } static void @@ -2452,9 +2440,9 @@ SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind) if (!rt->gcCurrentCompartment) Debugger::sweepAll(cx); - uint32 releaseInterval = rt->gcCurrentCompartment ? 0 : ComputeJitReleaseInterval(cx); + bool releaseTypes = !rt->gcCurrentCompartment && ReleaseObservedTypes(cx); for (GCCompartmentsIter c(rt); !c.done(); c.next()) - c->sweep(cx, releaseInterval); + c->sweep(cx, releaseTypes); { gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_OBJECT); diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index c3ed16f2376..1d480f524f7 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -842,13 +842,6 @@ MarkChildren(JSTracer *trc, JSScript *script) if (script->types) script->types->trace(trc); - -#ifdef JS_METHODJIT - if (script->jitNormal) - script->jitNormal->trace(trc); - if (script->jitCtor) - script->jitCtor->trace(trc); -#endif } void diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 264ee65049c..d8f5b94dcb5 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -6086,21 +6086,6 @@ TypeScript::Sweep(JSContext *cx, JSScript *script) * cannot alias the most recent one, and future activations will overwrite * activeCall on creation. */ - - /* - * Method JIT code depends on the type inference data which is about to - * be purged, so purge the jitcode as well. - */ -#ifdef JS_METHODJIT - mjit::ReleaseScriptCode(cx, script); - - /* - * Use counts for scripts are reset on GC. After discarding code we need to - * let it warm back up to get information like which opcodes are setting - * array holes or accessing getter properties. - */ - script->resetUseCount(); -#endif } void diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 4f69a4feb55..91a132470b5 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1802,30 +1802,12 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) goto error; \ JS_END_MACRO -#if defined(JS_TRACER) && defined(JS_METHODJIT) -# define LEAVE_ON_SAFE_POINT() \ - do { \ - JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \ - JS_ASSERT_IF(leaveOnSafePoint, !TRACE_PROFILER(cx)); \ - JS_ASSERT_IF(leaveOnSafePoint, interpMode != JSINTERP_NORMAL); \ - if (leaveOnSafePoint && !regs.fp()->hasImacropc() && \ - script->maybeNativeCodeForPC(regs.fp()->isConstructing(), regs.pc)) { \ - JS_ASSERT(!TRACE_RECORDER(cx)); \ - interpReturnOK = true; \ - goto leave_on_safe_point; \ - } \ - } while (0) -#else -# define LEAVE_ON_SAFE_POINT() /* nop */ -#endif - #define BRANCH(n) \ JS_BEGIN_MACRO \ regs.pc += (n); \ op = (JSOp) *regs.pc; \ if ((n) <= 0) \ goto check_backedge; \ - LEAVE_ON_SAFE_POINT(); \ DO_OP(); \ JS_END_MACRO @@ -1861,13 +1843,6 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) Value *argv = regs.fp()->maybeFormalArgs(); CHECK_INTERRUPT_HANDLER(); -#if defined(JS_TRACER) && defined(JS_METHODJIT) - bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT); -# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false)) -#else -# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0) -#endif - if (!entryFrame) entryFrame = regs.fp(); @@ -2050,17 +2025,8 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) LoopProfile *prof = TRACE_PROFILER(cx); JS_ASSERT(!TRACE_RECORDER(cx)); LoopProfile::ProfileAction act = prof->profileOperation(cx, op); - switch (act) { - case LoopProfile::ProfComplete: - if (interpMode != JSINTERP_NORMAL) { - leaveOnSafePoint = true; - LEAVE_ON_SAFE_POINT(); - } - break; - default: - moreInterrupts = true; - break; - } + if (act != LoopProfile::ProfComplete) + moreInterrupts = true; } #endif if (TraceRecorder* tr = TRACE_RECORDER(cx)) { @@ -2068,23 +2034,6 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) AbortableRecordingStatus status = tr->monitorRecording(op); JS_ASSERT_IF(cx->isExceptionPending(), status == ARECORD_ERROR); - if (interpMode != JSINTERP_NORMAL) { - JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT); - switch (status) { - case ARECORD_IMACRO_ABORTED: - case ARECORD_ABORTED: - case ARECORD_COMPLETED: - case ARECORD_STOP: -#ifdef JS_METHODJIT - leaveOnSafePoint = true; - LEAVE_ON_SAFE_POINT(); -#endif - break; - default: - break; - } - } - switch (status) { case ARECORD_CONTINUE: moreInterrupts = true; @@ -2093,7 +2042,6 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) case ARECORD_IMACRO_ABORTED: atoms = rt->atomState.commonAtomsStart(); op = JSOp(*regs.pc); - CLEAR_LEAVE_ON_TRACE_POINT(); if (status == ARECORD_IMACRO) DO_OP(); /* keep interrupting for op. */ break; @@ -2138,7 +2086,7 @@ END_EMPTY_CASES BEGIN_CASE(JSOP_TRACE) BEGIN_CASE(JSOP_NOTRACE) - LEAVE_ON_SAFE_POINT(); + /* No-op */ END_CASE(JSOP_TRACE) check_backedge: @@ -2155,7 +2103,6 @@ check_backedge: JS_ASSERT(!TRACE_PROFILER(cx)); MONITOR_BRANCH_TRACEVIS; ENABLE_INTERRUPTS(); - CLEAR_LEAVE_ON_TRACE_POINT(); } JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR); RESTORE_INTERP_VARS_CHECK_EXCEPTION(); @@ -2280,7 +2227,6 @@ BEGIN_CASE(JSOP_STOP) if (js_CodeSpec[*imacpc].format & JOF_DECOMPOSE) regs.pc += GetDecomposeLength(imacpc, js_CodeSpec[*imacpc].length); regs.fp()->clearImacropc(); - LEAVE_ON_SAFE_POINT(); atoms = script->atoms; op = JSOp(*regs.pc); DO_OP(); @@ -5349,12 +5295,6 @@ END_VARLEN_CASE BEGIN_CASE(JSOP_EXCEPTION) PUSH_COPY(cx->getPendingException()); cx->clearPendingException(); -#if defined(JS_TRACER) && defined(JS_METHODJIT) - if (interpMode == JSINTERP_PROFILE) { - leaveOnSafePoint = true; - LEAVE_ON_SAFE_POINT(); - } -#endif CHECK_BRANCH(); END_CASE(JSOP_EXCEPTION) diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 5767b1cd9c0..0ba6462afa5 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -215,10 +215,9 @@ enum InterpMode { JSINTERP_NORMAL = 0, /* interpreter is running normally */ JSINTERP_RECORD = 1, /* interpreter has been started to record/run traces */ - JSINTERP_SAFEPOINT = 2, /* interpreter should leave on a method JIT safe point */ - JSINTERP_PROFILE = 3, /* interpreter should profile a loop */ - JSINTERP_REJOIN = 4, /* as normal, but the frame has already started */ - JSINTERP_SKIP_TRAP = 5 /* as REJOIN, but skip trap at first opcode */ + JSINTERP_PROFILE = 2, /* interpreter should profile a loop */ + JSINTERP_REJOIN = 3, /* as normal, but the frame has already started */ + JSINTERP_SKIP_TRAP = 4 /* as REJOIN, but skip trap at first opcode */ }; /* diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 881cf4e5e25..fb0e4bb946f 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -1319,14 +1319,8 @@ static void Unblacklist(JSScript *script, jsbytecode *pc) { JS_ASSERT(*pc == JSOP_NOTRACE || *pc == JSOP_TRACE); - if (*pc == JSOP_NOTRACE) { + if (*pc == JSOP_NOTRACE) *pc = JSOP_TRACE; - -#ifdef JS_METHODJIT - /* This code takes care of unblacklisting in the method JIT. */ - js::mjit::ResetTraceHint(script, pc, GET_UINT16(pc), false); -#endif - } } #ifdef JS_METHODJIT @@ -2688,17 +2682,6 @@ TraceMonitor::flush() flushEpoch++; -#ifdef JS_METHODJIT - if (loopProfiles) { - for (LoopProfileMap::Enum e(*loopProfiles); !e.empty(); e.popFront()) { - jsbytecode *pc = e.front().key; - LoopProfile *prof = e.front().value; - /* This code takes care of resetting all methodjit state. */ - js::mjit::ResetTraceHint(prof->entryScript, pc, GET_UINT16(pc), true); - } - } -#endif - frameCache->reset(); dataAlloc->reset(); traceAlloc->reset(); diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 6abcde21033..92d3e4b17c0 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -112,7 +112,6 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript, bool isConstructi setGlobalNames(CompilerAllocPolicy(cx, *thisFromCtor())), callICs(CompilerAllocPolicy(cx, *thisFromCtor())), equalityICs(CompilerAllocPolicy(cx, *thisFromCtor())), - traceICs(CompilerAllocPolicy(cx, *thisFromCtor())), #endif #if defined JS_POLYIC pics(CompilerAllocPolicy(cx, *thisFromCtor())), @@ -127,14 +126,8 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript, bool isConstructi jumpTables(CompilerAllocPolicy(cx, *thisFromCtor())), jumpTableOffsets(CompilerAllocPolicy(cx, *thisFromCtor())), loopEntries(CompilerAllocPolicy(cx, *thisFromCtor())), - rootedObjects(CompilerAllocPolicy(cx, *thisFromCtor())), stubcc(cx, *thisFromCtor(), frame), debugMode_(cx->compartment->debugMode()), -#if defined JS_TRACER - addTraceHints(cx->traceJitEnabled), -#else - addTraceHints(false), -#endif inlining_(false), hasGlobalReallocation(false), oomInVector(false), @@ -143,10 +136,6 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript, bool isConstructi applyTricks(NoApplyTricks), pcLengths(NULL) { - /* :FIXME: bug 637856 disabling traceJit if inference is enabled */ - if (cx->typeInferenceEnabled()) - addTraceHints = false; - /* Once a script starts getting really hot we will inline calls in it. */ if (!debugMode() && cx->typeInferenceEnabled() && globalObj && (outerScript->getUseCount() >= USES_BEFORE_INLINING || @@ -760,10 +749,9 @@ mjit::Compiler::generatePrologue() /* * Set locals to undefined, as in initCallFrameLatePrologue. * Skip locals which aren't closed and are known to be defined before used, - * :FIXME: bug 604541: write undefined if we might be using the tracer, so it works. */ for (uint32 i = 0; i < script->nfixed; i++) { - if (analysis->localHasUseBeforeDef(i) || addTraceHints) { + if (analysis->localHasUseBeforeDef(i)) { Address local(JSFrameReg, sizeof(StackFrame) + i * sizeof(Value)); masm.storeValue(UndefinedValue(), local); } @@ -968,13 +956,11 @@ mjit::Compiler::finishThisUp(JITScript **jitp) sizeof(NativeMapEntry) * nNmapLive + sizeof(InlineFrame) * inlineFrames.length() + sizeof(CallSite) * callSites.length() + - sizeof(JSObject *) * rootedObjects.length() + #if defined JS_MONOIC sizeof(ic::GetGlobalNameIC) * getGlobalNames.length() + sizeof(ic::SetGlobalNameIC) * setGlobalNames.length() + sizeof(ic::CallICInfo) * callICs.length() + sizeof(ic::EqualityICInfo) * equalityICs.length() + - sizeof(ic::TraceICInfo) * traceICs.length() + #endif #if defined JS_POLYIC sizeof(ic::PICInfo) * pics.length() + @@ -1100,13 +1086,6 @@ mjit::Compiler::finishThisUp(JITScript **jitp) stubCode.patch(from.loopPatch.codePatch, result + codeOffset); } - /* Build the list of objects rooted by the script. */ - JSObject **jitRooted = (JSObject **)cursor; - jit->nRootedObjects = rootedObjects.length(); - cursor += sizeof(JSObject *) * jit->nRootedObjects; - for (size_t i = 0; i < jit->nRootedObjects; i++) - jitRooted[i] = rootedObjects[i]; - #if defined JS_MONOIC JS_INIT_CLIST(&jit->callers); @@ -1249,43 +1228,6 @@ mjit::Compiler::finishThisUp(JITScript **jitp) stubCode.patch(equalityICs[i].addrLabel, &jitEqualityICs[i]); } - - ic::TraceICInfo *jitTraceICs = (ic::TraceICInfo *)cursor; - jit->nTraceICs = traceICs.length(); - cursor += sizeof(ic::TraceICInfo) * jit->nTraceICs; - for (size_t i = 0; i < jit->nTraceICs; i++) { - jitTraceICs[i].initialized = traceICs[i].initialized; - if (!traceICs[i].initialized) - continue; - - if (traceICs[i].fastTrampoline) { - jitTraceICs[i].fastTarget = stubCode.locationOf(traceICs[i].trampolineStart); - } else { - uint32 offs = uint32(traceICs[i].jumpTarget - script->code); - JS_ASSERT(jumpMap[offs].isSet()); - jitTraceICs[i].fastTarget = fullCode.locationOf(jumpMap[offs]); - } - jitTraceICs[i].slowTarget = stubCode.locationOf(traceICs[i].trampolineStart); - - jitTraceICs[i].traceHint = fullCode.locationOf(traceICs[i].traceHint); - jitTraceICs[i].stubEntry = stubCode.locationOf(traceICs[i].stubEntry); - jitTraceICs[i].traceData = NULL; -#ifdef DEBUG - jitTraceICs[i].jumpTargetPC = traceICs[i].jumpTarget; -#endif - - jitTraceICs[i].hasSlowTraceHint = traceICs[i].slowTraceHint.isSet(); - if (traceICs[i].slowTraceHint.isSet()) - jitTraceICs[i].slowTraceHint = stubCode.locationOf(traceICs[i].slowTraceHint.get()); -#ifdef JS_TRACER - uint32 hotloop = GetHotloop(cx); - uint32 prevCount = cx->compartment->backEdgeCount(traceICs[i].jumpTarget); - jitTraceICs[i].loopCounterStart = hotloop; - jitTraceICs[i].loopCounter = hotloop < prevCount ? 1 : hotloop - prevCount; -#endif - - stubCode.patch(traceICs[i].addrLabel, &jitTraceICs[i]); - } #endif /* JS_MONOIC */ for (size_t i = 0; i < callPatches.length(); i++) { @@ -4715,12 +4657,6 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom) if (!obj) return false; - /* - * Root the proto, since JS_ClearScope might overwrite the global object's - * copy. - */ - rootedObjects.append(obj); - /* Force into a register because getprop won't expect a constant. */ RegisterID reg = frame.allocReg(); @@ -6838,150 +6774,50 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slow, bool *tramp consistent = frame.consistentRegisters(target); } - if (!addTraceHints || target >= PC || - (JSOp(*target) != JSOP_TRACE && JSOp(*target) != JSOP_NOTRACE) -#ifdef JS_MONOIC - || GET_UINT16(target) == BAD_TRACEIC_INDEX -#endif - ) - { - if (!lvtarget || lvtarget->synced()) { - JS_ASSERT(consistent); - if (!jumpInScript(j, target)) - return false; - if (slow && !stubcc.jumpInScript(*slow, target)) - return false; - } else { - if (consistent) { - if (!jumpInScript(j, target)) - return false; - } else { - /* - * Make a trampoline to issue remaining loads for the register - * state at target. - */ - Label start = stubcc.masm.label(); - stubcc.linkExitDirect(j, start); - frame.prepareForJump(target, stubcc.masm, false); - if (!stubcc.jumpInScript(stubcc.masm.jump(), target)) - return false; - if (trampoline) - *trampoline = true; - if (pcLengths) { - /* - * This is OOL code but will usually be executed, so track - * it in the CODE_LENGTH for the opcode. - */ - uint32 offset = ssa.frameLength(a->inlineIndex) + PC - script->code; - size_t length = stubcc.masm.size() - stubcc.masm.distanceOf(start); - pcLengths[offset].codeLength += length; - } - } - - if (slow) { - slow->linkTo(stubcc.masm.label(), &stubcc.masm); - frame.prepareForJump(target, stubcc.masm, true); - if (!stubcc.jumpInScript(stubcc.masm.jump(), target)) - return false; - } - } - - if (target < PC) - return finishLoop(target); - return true; - } - - /* The trampoline should not be specified if we need to generate a trace IC. */ - JS_ASSERT(!trampoline); - -#ifndef JS_TRACER - JS_NOT_REACHED("Bad addTraceHints"); - return false; -#else - -# if JS_MONOIC - TraceGenInfo ic; - - ic.initialized = true; - ic.stubEntry = stubcc.masm.label(); - ic.traceHint = j; - if (slow) - ic.slowTraceHint = *slow; - - uint16 index = GET_UINT16(target); - if (traceICs.length() <= index) - if (!traceICs.resize(index+1)) + if (!lvtarget || lvtarget->synced()) { + JS_ASSERT(consistent); + if (!jumpInScript(j, target)) return false; -# endif - - Label traceStart = stubcc.masm.label(); - - stubcc.linkExitDirect(j, traceStart); - if (slow) - slow->linkTo(traceStart, &stubcc.masm); - -# if JS_MONOIC - ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); - - Jump nonzero = stubcc.masm.branchSub32(Assembler::NonZero, Imm32(1), - Address(Registers::ArgReg1, - offsetof(TraceICInfo, loopCounter))); -# endif - - /* Save and restore compiler-tracked PC, so cx->regs is right in InvokeTracer. */ - { - jsbytecode* pc = PC; - PC = target; - - OOL_STUBCALL(stubs::InvokeTracer, REJOIN_NONE); - - PC = pc; - } - - Jump no = stubcc.masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg, - Registers::ReturnReg); - if (!cx->typeInferenceEnabled()) - stubcc.masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg); - stubcc.masm.jump(Registers::ReturnReg); - no.linkTo(stubcc.masm.label(), &stubcc.masm); - -#ifdef JS_MONOIC - nonzero.linkTo(stubcc.masm.label(), &stubcc.masm); - - ic.jumpTarget = target; - ic.fastTrampoline = !consistent; - ic.trampolineStart = stubcc.masm.label(); - - traceICs[index] = ic; -#endif - - /* - * Jump past the tracer call if the trace has been blacklisted. We still make - * a trace IC in such cases, in case it is un-blacklisted later. - */ - if (JSOp(*target) == JSOP_NOTRACE) { + if (slow && !stubcc.jumpInScript(*slow, target)) + return false; + } else { if (consistent) { if (!jumpInScript(j, target)) return false; } else { - stubcc.linkExitDirect(j, stubcc.masm.label()); + /* + * Make a trampoline to issue remaining loads for the register + * state at target. + */ + Label start = stubcc.masm.label(); + stubcc.linkExitDirect(j, start); + frame.prepareForJump(target, stubcc.masm, false); + if (!stubcc.jumpInScript(stubcc.masm.jump(), target)) + return false; + if (trampoline) + *trampoline = true; + if (pcLengths) { + /* + * This is OOL code but will usually be executed, so track + * it in the CODE_LENGTH for the opcode. + */ + uint32 offset = ssa.frameLength(a->inlineIndex) + PC - script->code; + size_t length = stubcc.masm.size() - stubcc.masm.distanceOf(start); + pcLengths[offset].codeLength += length; + } } - if (slow) + + if (slow) { slow->linkTo(stubcc.masm.label(), &stubcc.masm); + frame.prepareForJump(target, stubcc.masm, true); + if (!stubcc.jumpInScript(stubcc.masm.jump(), target)) + return false; + } } - /* - * Reload any registers needed at the head of the loop. Note that we didn't - * need to do syncing before calling InvokeTracer, as state is always synced - * on backwards jumps. - */ - frame.prepareForJump(target, stubcc.masm, true); - - if (!stubcc.jumpInScript(stubcc.masm.jump(), target)) - return false; -#endif - - return finishLoop(target); + if (target < PC) + return finishLoop(target); + return true; } void diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 67664ce11c2..2b81037d986 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -125,19 +125,6 @@ class Compiler : public BaseCompiler Assembler::Condition cond; JSC::MacroAssembler::RegisterID tempReg; }; - - struct TraceGenInfo { - bool initialized; - Label stubEntry; - DataLabelPtr addrLabel; - jsbytecode *jumpTarget; - bool fastTrampoline; - Label trampolineStart; - Jump traceHint; - MaybeJump slowTraceHint; - - TraceGenInfo() : initialized(false) {} - }; /* InlineFrameAssembler wants to see this. */ public: @@ -449,7 +436,6 @@ private: js::Vector setGlobalNames; js::Vector callICs; js::Vector equalityICs; - js::Vector traceICs; #endif #if defined JS_POLYIC js::Vector pics; @@ -464,7 +450,6 @@ private: js::Vector jumpTables; js::Vector jumpTableOffsets; js::Vector loopEntries; - js::Vector rootedObjects; StubCompiler stubcc; Label invokeLabel; Label arityLabel; @@ -475,7 +460,6 @@ private: Jump argsCheckJump; #endif bool debugMode_; - bool addTraceHints; bool inlining_; bool hasGlobalReallocation; bool oomInVector; // True if we have OOM'd appending to a vector. diff --git a/js/src/methodjit/FastArithmetic.cpp b/js/src/methodjit/FastArithmetic.cpp index cf2786b9e5f..27f88956403 100644 --- a/js/src/methodjit/FastArithmetic.cpp +++ b/js/src/methodjit/FastArithmetic.cpp @@ -1162,7 +1162,7 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, ic.stubEntry = stubEntry; ic.stub = stub; - bool useIC = (!addTraceHints || target >= PC) && !a->parent; + bool useIC = !a->parent; /* Call the IC stub, which may generate a fast path. */ if (useIC) { diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index c0bd80da60d..6b21c72e5ca 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -1325,11 +1325,7 @@ FrameState::sync(Assembler &masm, Uses uses) const Registers avail(freeRegs.freeMask & Registers::AvailRegs); Registers temp(Registers::TempAnyRegs); - FrameEntry *bottom = (cx->typeInferenceEnabled() || cx->compartment->debugMode()) - ? entries - : a->sp - uses.nuses; - - for (FrameEntry *fe = a->sp - 1; fe >= bottom; fe--) { + for (FrameEntry *fe = a->sp - 1; fe >= entries; fe--) { if (!fe->isTracked()) continue; @@ -1379,7 +1375,7 @@ FrameState::sync(Assembler &masm, Uses uses) const /* Fall back to a slower sync algorithm if load required. */ if ((!fe->type.synced() && backing->type.inMemory()) || (!fe->data.synced() && backing->data.inMemory())) { - syncFancy(masm, avail, fe, bottom); + syncFancy(masm, avail, fe, entries); return; } #endif @@ -1460,11 +1456,7 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore) uint32 maxvisits = tracker.nentries; - FrameEntry *bottom = (cx->typeInferenceEnabled() || cx->compartment->debugMode()) - ? entries - : a->sp - uses.nuses; - - for (FrameEntry *fe = a->sp - 1; fe >= bottom && maxvisits; fe--) { + for (FrameEntry *fe = a->sp - 1; fe >= entries && maxvisits; fe--) { if (!fe->isTracked()) continue; diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index 2cc029abf9f..34c8a5fdfdb 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -533,21 +533,7 @@ js_InternalThrow(VMFrame &f) // or SplatApplyArgs threw an exception. RemoveOrphanedNative(cx, f.fp()); - // It's possible that from within RunTracer(), Interpret() returned with - // an error and finished the frame (i.e., called ScriptEpilogue), but has - // not yet performed an inline return. - // - // In this case, RunTracer() has no choice but to propagate the error - // up to the method JIT, and thus to this function. But ScriptEpilogue() - // has already been called. Detect this, and avoid double-finishing the - // frame. See HandleErrorInExcessFrame() and bug 624100. - if (f.fp()->finishedInInterpreter()) { - // If it's the last frame, just propagate the failure up again. - if (f.fp() == f.entryfp) - return NULL; - - InlineReturn(f); - } + JS_ASSERT(!f.fp()->finishedInInterpreter()); // Make sure sp is up to date. JS_ASSERT(&cx->regs() == &f.regs); @@ -616,50 +602,46 @@ js_InternalThrow(VMFrame &f) StackFrame *fp = cx->fp(); JSScript *script = fp->script(); - if (cx->typeInferenceEnabled() || !fp->jit()) { - /* - * Fall back to EnterMethodJIT and finish the frame in the interpreter. - * With type inference enabled, we may wipe out all JIT code on the - * stack without patching ncode values to jump to the interpreter, and - * thus can only enter JIT code via EnterMethodJIT (which overwrites - * its entry frame's ncode). See ClearAllFrames. - */ - cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished); - - if (!script->ensureRanAnalysis(cx)) { - js_ReportOutOfMemory(cx); - return NULL; - } - - analyze::AutoEnterAnalysis enter(cx); - - cx->regs().pc = pc; - cx->regs().sp = fp->base() + script->analysis()->getCode(pc).stackDepth; - - /* - * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go - * back into the interpreter with a pending exception. This will cause - * it to immediately rethrow. - */ - if (cx->isExceptionPending()) { - JS_ASSERT(js_GetOpcode(cx, script, pc) == JSOP_ENTERBLOCK); - JSObject *obj = script->getObject(GET_SLOTNO(pc)); - Value *vp = cx->regs().sp + OBJ_BLOCK_COUNT(cx, obj); - SetValueRangeToUndefined(cx->regs().sp, vp); - cx->regs().sp = vp; - JS_ASSERT(js_GetOpcode(cx, script, pc + JSOP_ENTERBLOCK_LENGTH) == JSOP_EXCEPTION); - cx->regs().sp[0] = cx->getPendingException(); - cx->clearPendingException(); - cx->regs().sp++; - cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH; - } - - *f.oldregs = f.regs; + /* + * Fall back to EnterMethodJIT and finish the frame in the interpreter. + * With type inference enabled, we may wipe out all JIT code on the + * stack without patching ncode values to jump to the interpreter, and + * thus can only enter JIT code via EnterMethodJIT (which overwrites + * its entry frame's ncode). See ClearAllFrames. + */ + cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished); + if (!script->ensureRanAnalysis(cx)) { + js_ReportOutOfMemory(cx); return NULL; } - return script->nativeCodeForPC(fp->isConstructing(), pc); + analyze::AutoEnterAnalysis enter(cx); + + cx->regs().pc = pc; + cx->regs().sp = fp->base() + script->analysis()->getCode(pc).stackDepth; + + /* + * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go + * back into the interpreter with a pending exception. This will cause + * it to immediately rethrow. + */ + if (cx->isExceptionPending()) { + JS_ASSERT(js_GetOpcode(cx, script, pc) == JSOP_ENTERBLOCK); + JSObject *obj = script->getObject(GET_SLOTNO(pc)); + Value *vp = cx->regs().sp + OBJ_BLOCK_COUNT(cx, obj); + SetValueRangeToUndefined(cx->regs().sp, vp); + cx->regs().sp = vp; + JS_ASSERT(js_GetOpcode(cx, script, pc + JSOP_ENTERBLOCK_LENGTH) == JSOP_EXCEPTION); + cx->regs().sp[0] = cx->getPendingException(); + cx->clearPendingException(); + cx->regs().sp++; + cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH; + } + + *f.oldregs = f.regs; + + return NULL; } void JS_FASTCALL @@ -701,481 +683,8 @@ stubs::ScriptProbeOnlyEpilogue(VMFrame &f) Probes::exitJSFun(f.cx, f.fp()->fun(), f.fp()->script()); } -#ifdef JS_TRACER - -/* - * Called when an error is in progress and the topmost frame could not handle - * it. This will unwind to a given frame, or find and align to an exception - * handler in the process. - */ -static inline bool -HandleErrorInExcessFrame(VMFrame &f, StackFrame *stopFp, bool searchedTopmostFrame = true) -{ - JSContext *cx = f.cx; - - /* - * Callers of this called either Interpret() or JaegerShot(), which would - * have searched for exception handlers already. If we see stopFp, just - * return false. Otherwise, pop the frame, since it's guaranteed useless. - * - * Note that this also guarantees ScriptEpilogue() has been called. - */ - StackFrame *fp = cx->fp(); - if (searchedTopmostFrame) { - /* - * This is a special case meaning that fp->finishedInInterpreter() is - * true. If so, and fp == stopFp, our only choice is to propagate this - * error up, back to the method JIT, and then to js_InternalThrow, - * where this becomes a special case. See the comment there and bug - * 624100. - */ - if (fp == stopFp) - return false; - - /* - * Otherwise, the protocol here (like Invoke) is to assume that the - * execution mode finished the frame, and to just pop it. - */ - InlineReturn(f); - } - - /* Remove the bottom frame. */ - bool returnOK = false; - for (;;) { - fp = cx->fp(); - - /* Clear imacros. */ - if (fp->hasImacropc()) { - cx->regs().pc = fp->imacropc(); - fp->clearImacropc(); - } - JS_ASSERT(!fp->hasImacropc()); - - /* If there's an exception and a handler, set the pc and leave. */ - if (cx->isExceptionPending()) { - jsbytecode *pc = FindExceptionHandler(cx); - if (pc) { - cx->regs().pc = pc; - returnOK = true; - break; - } - } - - /* Don't unwind if this was the entry frame. */ - if (fp == stopFp) - break; - - /* Unwind and return. */ - returnOK &= UnwindScope(cx, 0, returnOK || cx->isExceptionPending()); - returnOK = ScriptEpilogue(cx, fp, returnOK); - InlineReturn(f); - } - - JS_ASSERT(&f.regs == &cx->regs()); - JS_ASSERT_IF(!returnOK, cx->fp() == stopFp); - - return returnOK; -} - -/* Returns whether the current PC has method JIT'd code. */ -static inline void * -AtSafePoint(JSContext *cx) -{ - StackFrame *fp = cx->fp(); - if (fp->hasImacropc()) - return NULL; - - JSScript *script = fp->script(); - return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs().pc); -} - -/* - * Interprets until either a safe point is reached that has method JIT'd - * code, or the current frame tries to return. - */ -static inline JSBool -PartialInterpret(VMFrame &f) -{ - JSContext *cx = f.cx; - StackFrame *fp = cx->fp(); - -#ifdef DEBUG - JSScript *script = fp->script(); - JS_ASSERT(!fp->finishedInInterpreter()); - JS_ASSERT(fp->hasImacropc() || - !script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs().pc)); -#endif - - JSBool ok = JS_TRUE; - ok = Interpret(cx, fp, JSINTERP_SAFEPOINT); - - return ok; -} - JS_STATIC_ASSERT(JSOP_NOP == 0); -/* - * Returns whether the current PC would return, or if the frame has already - * been completed. This distinction avoids re-entering the interpreter or JIT - * to complete a JSOP_RETURN. Instead, that edge case is handled in - * HandleFinishedFrame. We could consider reducing complexity, and making this - * function return only "finishedInInterpreter", and always using the full VM - * machinery to fully finish frames. - */ -static inline bool -FrameIsFinished(JSContext *cx) -{ - JSOp op = JSOp(*cx->regs().pc); - return (op == JSOP_RETURN || - op == JSOP_RETRVAL || - op == JSOP_STOP) - ? true - : cx->fp()->finishedInInterpreter(); -} - -/* - * Given a frame that is about to return, make sure its return value and - * activation objects are fixed up. Then, pop the frame and advance the - * current PC. Note that while we could enter the JIT at this point, the - * logic would still be necessary for the interpreter, so it's easier - * (and faster) to finish frames in C++ even if at a safe point here. - */ -static bool -HandleFinishedFrame(VMFrame &f, StackFrame *entryFrame) -{ - JSContext *cx = f.cx; - - JS_ASSERT(FrameIsFinished(cx)); - - /* - * This is the most difficult and complicated piece of the tracer - * integration, and historically has been very buggy. The problem is that - * although this frame has to be popped (see RemoveExcessFrames), it may - * be at a JSOP_RETURN opcode, and it might not have ever been executed. - * That is, fp->rval may not be set to the top of the stack, and if it - * has, the stack has already been decremented. Note that fp->rval is not - * the only problem: the epilogue may never have been executed. - * - * Here are the edge cases and whether the frame has been exited cleanly: - * 1. No: A trace exited directly before a RETURN op, and the - * interpreter never ran. - * 2. Yes: The interpreter exited cleanly. - * 3. No: The interpreter exited on a safe point. LEAVE_ON_SAFE_POINT - * is not used in between JSOP_RETURN and advancing the PC, - * therefore, it cannot have been run if at a safe point. - * 4. No: Somewhere in the RunTracer call tree, we removed a frame, - * and we returned to a JSOP_RETURN opcode. Note carefully - * that in this situation, FrameIsFinished() returns true! - * 5. Yes: The function exited in the method JIT, during - * FinishExcessFrames() However, in this case, we'll never enter - * HandleFinishedFrame(): we always immediately pop JIT'd frames. - * - * Since the only scenario where this fixup is NOT needed is a normal exit - * from the interpreter, we can cleanly check for this scenario by checking - * a bit it sets in the frame. - */ - bool returnOK = true; - if (!cx->fp()->finishedInInterpreter()) { - if (JSOp(*cx->regs().pc) == JSOP_RETURN) - cx->fp()->setReturnValue(f.regs.sp[-1]); - - returnOK = ScriptEpilogue(cx, cx->fp(), true); - } - - if (cx->fp() != entryFrame) { - InlineReturn(f); - } - - return returnOK; -} - -/* - * Given a frame newer than the entry frame, try to finish it. If it's at a - * return position, pop the frame. If it's at a safe point, execute it in - * Jaeger code. Otherwise, try to interpret until a safe point. - * - * While this function is guaranteed to make progress, it may not actually - * finish or pop the current frame. It can either: - * 1) Finalize a finished frame, or - * 2) Finish and finalize the frame in the Method JIT, or - * 3) Interpret, which can: - * a) Propagate an error, or - * b) Finish the frame, but not finalize it, or - * c) Abruptly leave at any point in the frame, or in a newer frame - * pushed by a call, that has method JIT'd code. - */ -static bool -EvaluateExcessFrame(VMFrame &f, StackFrame *entryFrame) -{ - JSContext *cx = f.cx; - StackFrame *fp = cx->fp(); - - /* - * A "finished" frame is when the interpreter rested on a STOP, - * RETURN, RETRVAL, etc. We check for finished frames BEFORE looking - * for a safe point. If the frame was finished, we could have already - * called ScriptEpilogue(), and entering the JIT could call it twice. - */ - if (!fp->hasImacropc() && FrameIsFinished(cx)) - return HandleFinishedFrame(f, entryFrame); - - if (void *ncode = AtSafePoint(cx)) { - if (!JaegerShotAtSafePoint(cx, ncode, false)) - return false; - InlineReturn(f); - return true; - } - - return PartialInterpret(f); -} - -/* - * Evaluate frames newer than the entry frame until all are gone. This will - * always leave f.regs.fp == entryFrame. - */ -static bool -FinishExcessFrames(VMFrame &f, StackFrame *entryFrame) -{ - JSContext *cx = f.cx; - - while (cx->fp() != entryFrame || entryFrame->hasImacropc()) { - if (!EvaluateExcessFrame(f, entryFrame)) { - if (!HandleErrorInExcessFrame(f, entryFrame)) - return false; - } - } - - return true; -} - -#if defined JS_MONOIC -static void -UpdateTraceHintSingle(Repatcher &repatcher, JSC::CodeLocationJump jump, JSC::CodeLocationLabel target) -{ - /* - * Hack: The value that will be patched is before the executable address, - * so to get protection right, just unprotect the general region around - * the jump. - */ - repatcher.relink(jump, target); - - JaegerSpew(JSpew_PICs, "relinking trace hint %p to %p\n", - jump.executableAddress(), target.executableAddress()); -} - -static void -DisableTraceHint(JITScript *jit, ic::TraceICInfo &ic) -{ - Repatcher repatcher(jit); - UpdateTraceHintSingle(repatcher, ic.traceHint, ic.fastTarget); - - if (ic.hasSlowTraceHint) - UpdateTraceHintSingle(repatcher, ic.slowTraceHint, ic.slowTarget); -} - -static void -ResetTraceHintAt(JSScript *script, js::mjit::JITScript *jit, - jsbytecode *pc, uint16_t index, bool full) -{ - if (index >= jit->nTraceICs) - return; - ic::TraceICInfo &ic = jit->traceICs()[index]; - if (!ic.initialized) - return; - - JS_ASSERT(ic.jumpTargetPC == pc); - - JaegerSpew(JSpew_PICs, "Enabling trace IC %u in script %p\n", index, - static_cast(script)); - - Repatcher repatcher(jit); - - UpdateTraceHintSingle(repatcher, ic.traceHint, ic.stubEntry); - - if (ic.hasSlowTraceHint) - UpdateTraceHintSingle(repatcher, ic.slowTraceHint, ic.stubEntry); - - if (full) { - ic.traceData = NULL; - ic.loopCounterStart = 1; - ic.loopCounter = ic.loopCounterStart; - } -} -#endif - -void -js::mjit::ResetTraceHint(JSScript *script, jsbytecode *pc, uint16_t index, bool full) -{ -#if JS_MONOIC - if (script->jitNormal) - ResetTraceHintAt(script, script->jitNormal, pc, index, full); - - if (script->jitCtor) - ResetTraceHintAt(script, script->jitCtor, pc, index, full); -#endif -} - -#if JS_MONOIC -void * -RunTracer(VMFrame &f, ic::TraceICInfo &ic) -#else -void * -RunTracer(VMFrame &f) -#endif -{ - JSContext *cx = f.cx; - StackFrame *entryFrame = f.fp(); - TracePointAction tpa; - - /* :TODO: nuke PIC? */ - if (!cx->traceJitEnabled) - return NULL; - - /* - * Force initialization of the entry frame's scope chain and return value, - * if necessary. The tracer can query the scope chain without needing to - * check the HAS_SCOPECHAIN flag, and the frame is guaranteed to have the - * correct return value stored if we trace/interpret through to the end - * of the frame. - */ - entryFrame->scopeChain(); - entryFrame->returnValue(); - - bool blacklist; - void **traceData; - uintN *traceEpoch; - uint32 *loopCounter; - uint32 hits; -#if JS_MONOIC - traceData = &ic.traceData; - traceEpoch = &ic.traceEpoch; - loopCounter = &ic.loopCounter; - *loopCounter = 1; - hits = ic.loopCounterStart; -#else - traceData = NULL; - traceEpoch = NULL; - loopCounter = NULL; - hits = 1; -#endif - - { - /* - * While the tracer is running, redirect the regs to a local variable here. - * If the tracer exits during an inlined frame, it will synthesize those - * frames, point f.regs.fp at them and then enter the interpreter. If the - * interpreter pops the frames it will not be reflected here as a local - * set of regs is used by the interpreter, and f->regs end up pointing at - * garbage, confusing the recompiler. - */ - FrameRegs regs = f.regs; - PreserveRegsGuard regsGuard(cx, regs); - - tpa = MonitorTracePoint(f.cx, &blacklist, traceData, traceEpoch, - loopCounter, hits); - JS_ASSERT(!TRACE_RECORDER(cx)); - } - -#if JS_MONOIC - ic.loopCounterStart = *loopCounter; - if (blacklist) - DisableTraceHint(entryFrame->jit(), ic); -#endif - - // Even though ExecuteTree() bypasses the interpreter, it should propagate - // error failures correctly. - JS_ASSERT_IF(cx->isExceptionPending(), tpa == TPA_Error); - - JS_ASSERT(f.fp() == cx->fp()); - switch (tpa) { - case TPA_Nothing: - return NULL; - - case TPA_Error: - if (!HandleErrorInExcessFrame(f, entryFrame, f.fp()->finishedInInterpreter())) - THROWV(NULL); - JS_ASSERT(!cx->fp()->hasImacropc()); - break; - - case TPA_RanStuff: - case TPA_Recorded: - break; - } - - /* - * The tracer could have dropped us off on any frame at any position. - * Well, it could not have removed frames (recursion is disabled). - * - * Frames after the entryFrame cannot be entered via JaegerShotAtSafePoint() - * unless each is at a safe point. We can JaegerShotAtSafePoint these - * frames individually, but we must unwind to the entryFrame. - * - * Note carefully that JaegerShotAtSafePoint can resume methods at - * arbitrary safe points whereas JaegerShot cannot. - * - * If we land on entryFrame without a safe point in sight, we'll end up - * at the RETURN op. This is an edge case with two paths: - * - * 1) The entryFrame is the last inline frame. If it fell on a RETURN, - * move the return value down. - * 2) The entryFrame is NOT the last inline frame. Pop the frame. - * - * In both cases, we hijack the stub to return to the force-return - * trampoline. This trampoline simulates the frame-popping portion of - * emitReturn (except without the benefit of the FrameState) and will - * produce the necessary register state to return to the caller. - */ - - restart: - /* Step 1. Finish frames created after the entry frame. */ - if (!FinishExcessFrames(f, entryFrame)) - THROWV(NULL); - - /* IMacros are guaranteed to have been removed by now. */ - JS_ASSERT(f.fp() == entryFrame); - JS_ASSERT(!entryFrame->hasImacropc()); - - /* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */ - if (FrameIsFinished(cx)) { - if (!HandleFinishedFrame(f, entryFrame)) - THROWV(NULL); - *f.returnAddressLocation() = cx->jaegerCompartment()->forceReturnFromFastCall(); - return NULL; - } - - /* Step 3. If entryFrame is at a safe point, just leave. */ - if (void *ncode = AtSafePoint(cx)) - return ncode; - - /* Step 4. Do a partial interp, then restart the whole process. */ - if (!PartialInterpret(f)) { - if (!HandleErrorInExcessFrame(f, entryFrame)) - THROWV(NULL); - } - - goto restart; -} - -#endif /* JS_TRACER */ - -#if defined JS_TRACER -# if defined JS_MONOIC -void *JS_FASTCALL -stubs::InvokeTracer(VMFrame &f, ic::TraceICInfo *ic) -{ - return RunTracer(f, *ic); -} - -# else - -void *JS_FASTCALL -stubs::InvokeTracer(VMFrame &f) -{ - return RunTracer(f); -} -# endif /* JS_MONOIC */ -#endif /* JS_TRACER */ - /* :XXX: common out with identical copy in Compiler.cpp */ #if defined(JS_METHODJIT_SPEW) static const char *OpcodeNames[] = { diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 6cce006393a..cf09b0079b0 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -1091,16 +1091,10 @@ JITScript::callSites() const return (js::mjit::CallSite *)&inlineFrames()[nInlineFrames]; } -JSObject ** -JITScript::rootedObjects() const -{ - return (JSObject **)&callSites()[nCallSites]; -} - char * JITScript::commonSectionLimit() const { - return (char *)&rootedObjects()[nRootedObjects]; + return (char *)&callSites()[nCallSites]; } #ifdef JS_MONOIC @@ -1129,16 +1123,10 @@ JITScript::equalityICs() const return (ic::EqualityICInfo *)&callICs()[nCallICs]; } -ic::TraceICInfo * -JITScript::traceICs() const -{ - return (ic::TraceICInfo *)&equalityICs()[nEqualityICs]; -} - char * JITScript::monoICSectionsLimit() const { - return (char *)&traceICs()[nTraceICs]; + return (char *)&equalityICs()[nEqualityICs]; } #else // JS_MONOIC char * @@ -1186,17 +1174,6 @@ static inline void Destroy(T &t) t.~T(); } -void -mjit::JITScript::purgeNativeCallStubs() -{ - for (unsigned i = 0; i < nativeCallStubs.length(); i++) { - JSC::ExecutablePool *pool = nativeCallStubs[i].pool; - if (pool) - pool->release(); - } - nativeCallStubs.clear(); -} - mjit::JITScript::~JITScript() { code.release(); @@ -1227,7 +1204,11 @@ mjit::JITScript::~JITScript() (*pExecPool)->release(); } - purgeNativeCallStubs(); + for (unsigned i = 0; i < nativeCallStubs.length(); i++) { + JSC::ExecutablePool *pool = nativeCallStubs[i].pool; + if (pool) + pool->release(); + } ic::CallICInfo *callICs_ = callICs(); for (uint32 i = 0; i < nCallICs; i++) { @@ -1272,13 +1253,11 @@ mjit::JITScript::scriptDataSize(JSUsableSizeFun usf) sizeof(NativeMapEntry) * nNmapPairs + sizeof(InlineFrame) * nInlineFrames + sizeof(CallSite) * nCallSites + - sizeof(JSObject *) * nRootedObjects + #if defined JS_MONOIC sizeof(ic::GetGlobalNameIC) * nGetGlobalNames + sizeof(ic::SetGlobalNameIC) * nSetGlobalNames + sizeof(ic::CallICInfo) * nCallICs + sizeof(ic::EqualityICInfo) * nEqualityICs + - sizeof(ic::TraceICInfo) * nTraceICs + #endif #if defined JS_POLYIC sizeof(ic::PICInfo) * nPICs + @@ -1413,20 +1392,4 @@ mjit::NativeToPC(JITScript *jit, void *ncode, mjit::CallSite **pinline) return jit->nativeToPC(ncode, pinline); } -void -JITScript::trace(JSTracer *trc) -{ - /* - * MICs and PICs attached to the JITScript are weak references, and either - * entirely purged or selectively purged on each GC. We do, however, need - * to maintain references to any scripts whose code was inlined into this. - */ - InlineFrame *inlineFrames_ = inlineFrames(); - for (unsigned i = 0; i < nInlineFrames; i++) - MarkObject(trc, *inlineFrames_[i].fun, "jitscript_fun"); - - for (uint32 i = 0; i < nRootedObjects; ++i) - MarkObject(trc, *rootedObjects()[i], "mjit rooted object"); -} - /* static */ const double mjit::Assembler::oneDouble = 1.0; diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index c65f2dd95fb..6e43f1c9232 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -500,7 +500,6 @@ namespace ic { struct GetGlobalNameIC; struct SetGlobalNameIC; struct EqualityICInfo; - struct TraceICInfo; struct CallICInfo; # endif } @@ -531,7 +530,6 @@ typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICI typedef void (JS_FASTCALL *VoidStubGetGlobal)(VMFrame &, js::mjit::ic::GetGlobalNameIC *); typedef void (JS_FASTCALL *VoidStubSetGlobal)(VMFrame &, js::mjit::ic::SetGlobalNameIC *); typedef JSBool (JS_FASTCALL *BoolStubEqualityIC)(VMFrame &, js::mjit::ic::EqualityICInfo *); -typedef void * (JS_FASTCALL *VoidPtrStubTraceIC)(VMFrame &, js::mjit::ic::TraceICInfo *); #endif #ifdef JS_POLYIC typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *); @@ -605,13 +603,11 @@ struct JITScript { bool singleStepMode:1; /* compiled in "single step mode" */ uint32 nInlineFrames; uint32 nCallSites; - uint32 nRootedObjects; #ifdef JS_MONOIC uint32 nGetGlobalNames; uint32 nSetGlobalNames; uint32 nCallICs; uint32 nEqualityICs; - uint32 nTraceICs; #endif #ifdef JS_POLYIC uint32 nGetElems; @@ -643,13 +639,11 @@ struct JITScript { NativeMapEntry *nmap() const; js::mjit::InlineFrame *inlineFrames() const; js::mjit::CallSite *callSites() const; - JSObject **rootedObjects() const; #ifdef JS_MONOIC ic::GetGlobalNameIC *getGlobalNames() const; ic::SetGlobalNameIC *setGlobalNames() const; ic::CallICInfo *callICs() const; ic::EqualityICInfo *equalityICs() const; - ic::TraceICInfo *traceICs() const; #endif #ifdef JS_POLYIC ic::GetElementIC *getElems() const; @@ -666,12 +660,6 @@ struct JITScript { } void nukeScriptDependentICs(); - void sweepCallICs(JSContext *cx, bool purgeAll); - void purgeMICs(); - void purgePICs(); - void purgeNativeCallStubs(); - - void trace(JSTracer *trc); /* |usf| can be NULL here, in which case the fallback size computation will be used. */ size_t scriptDataSize(JSUsableSizeFun usf); @@ -766,13 +754,6 @@ struct CallSite } }; -/* - * Re-enables a tracepoint in the method JIT. When full is true, we - * also reset the iteration counter. - */ -void -ResetTraceHint(JSScript *script, jsbytecode *pc, uint16_t index, bool full); - uintN GetCallTargetCount(JSScript *script, jsbytecode *pc); diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index b2504185ec5..be573b19009 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -1386,168 +1386,5 @@ JITScript::resetArgsCheck() repatch.relink(argsCheckJump, argsCheckStub); } -void -JITScript::purgeMICs() -{ - if (!nGetGlobalNames || !nSetGlobalNames) - return; - - Repatcher repatch(this); - - ic::GetGlobalNameIC *getGlobalNames_ = getGlobalNames(); - for (uint32 i = 0; i < nGetGlobalNames; i++) { - ic::GetGlobalNameIC &ic = getGlobalNames_[i]; - JSC::CodeLocationDataLabel32 label = ic.fastPathStart.dataLabel32AtOffset(ic.shapeOffset); - repatch.repatch(label, int(INVALID_SHAPE)); - } - - ic::SetGlobalNameIC *setGlobalNames_ = setGlobalNames(); - for (uint32 i = 0; i < nSetGlobalNames; i++) { - ic::SetGlobalNameIC &ic = setGlobalNames_[i]; - ic.patchInlineShapeGuard(repatch, int32(INVALID_SHAPE)); - - if (ic.hasExtraStub) { - Repatcher repatcher(ic.extraStub); - ic.patchExtraShapeGuard(repatcher, int32(INVALID_SHAPE)); - } - } -} - -void -ic::PurgeMICs(JSContext *cx, JSScript *script) -{ - /* MICs are purged during GC to handle changing shapes. */ - JS_ASSERT(cx->runtime->gcRegenShapes); - - if (script->jitNormal) - script->jitNormal->purgeMICs(); - if (script->jitCtor) - script->jitCtor->purgeMICs(); -} - -void -JITScript::nukeScriptDependentICs() -{ - if (!nCallICs) - return; - - Repatcher repatcher(this); - - ic::CallICInfo *callICs_ = callICs(); - for (uint32 i = 0; i < nCallICs; i++) { - ic::CallICInfo &ic = callICs_[i]; - if (!ic.fastGuardedObject) - continue; - repatcher.repatch(ic.funGuard, NULL); - repatcher.relink(ic.funJump, ic.slowPathStart); - ic.releasePool(CallICInfo::Pool_ClosureStub); - ic.fastGuardedObject = NULL; - ic.hasJsFunCheck = false; - } -} - -void -JITScript::sweepCallICs(JSContext *cx, bool purgeAll) -{ - Repatcher repatcher(this); - - /* - * If purgeAll is set, purge stubs in the script except those covered by PurgePICs - * (which is always called during GC). We want to remove references which can keep - * alive pools that we are trying to destroy (see JSCompartment::sweep). - */ - - ic::CallICInfo *callICs_ = callICs(); - for (uint32 i = 0; i < nCallICs; i++) { - ic::CallICInfo &ic = callICs_[i]; - - /* - * If the object is unreachable, we're guaranteed not to be currently - * executing a stub generated by a guard on that object. This lets us - * precisely GC call ICs while keeping the identity guard safe. - */ - bool fastFunDead = ic.fastGuardedObject && - (purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedObject)); - bool hasNative = ic.fastGuardedNative != NULL; - - /* - * There are three conditions where we need to relink: - * (1) purgeAll is true. - * (2) There is a native stub. These have a NativeCallStub, which will - * all be released if the compartment has no code on the stack. - * (3) The fastFun is dead *and* there is a closure stub. - * - * Note although both objects can be non-NULL, there can only be one - * of [closure, native] stub per call IC. - */ - if (purgeAll || hasNative || (fastFunDead && ic.hasJsFunCheck)) { - repatcher.relink(ic.funJump, ic.slowPathStart); - ic.hit = false; - } - - if (fastFunDead) { - repatcher.repatch(ic.funGuard, NULL); - ic.purgeGuardedObject(); - } - - if (hasNative) - ic.fastGuardedNative = NULL; - - if (purgeAll) { - ic.releasePool(CallICInfo::Pool_ScriptStub); - JSC::CodeLocationJump oolJump = ic.slowPathStart.jumpAtOffset(ic.oolJumpOffset); - JSC::CodeLocationLabel icCall = ic.slowPathStart.labelAtOffset(ic.icCallOffset); - repatcher.relink(oolJump, icCall); - } - } - - /* The arguments type check IC can refer to type objects which might be swept. */ - if (argsCheckPool) - resetArgsCheck(); - - if (purgeAll) { - /* Purge ICs generating stubs into execPools. */ - uint32 released = 0; - - ic::EqualityICInfo *equalityICs_ = equalityICs(); - for (uint32 i = 0; i < nEqualityICs; i++) { - ic::EqualityICInfo &ic = equalityICs_[i]; - if (!ic.generated) - continue; - - JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic::Equality)); - repatcher.relink(ic.stubCall, fptr); - repatcher.relink(ic.jumpToStub, ic.stubEntry); - - ic.generated = false; - released++; - } - - ic::SetGlobalNameIC *setGlobalNames_ = setGlobalNames(); - for (uint32 i = 0; i < nSetGlobalNames; i ++) { - ic::SetGlobalNameIC &ic = setGlobalNames_[i]; - if (!ic.hasExtraStub) - continue; - repatcher.relink(ic.fastPathStart.jumpAtOffset(ic.inlineShapeJump), ic.slowPathStart); - ic.hasExtraStub = false; - released++; - } - - JS_ASSERT(released == execPools.length()); - for (uint32 i = 0; i < released; i++) - execPools[i]->release(); - execPools.clear(); - } -} - -void -ic::SweepCallICs(JSContext *cx, JSScript *script, bool purgeAll) -{ - if (script->jitNormal) - script->jitNormal->sweepCallICs(cx, purgeAll); - if (script->jitCtor) - script->jitCtor->sweepCallICs(cx, purgeAll); -} - #endif /* JS_MONOIC */ diff --git a/js/src/methodjit/MonoIC.h b/js/src/methodjit/MonoIC.h index 0133055968d..88335833a1d 100644 --- a/js/src/methodjit/MonoIC.h +++ b/js/src/methodjit/MonoIC.h @@ -160,30 +160,6 @@ struct SetGlobalNameIC : public GlobalNameIC void patchExtraShapeGuard(Repatcher &repatcher, int32 shape); }; -struct TraceICInfo { - TraceICInfo() {} - - JSC::CodeLocationLabel stubEntry; - JSC::CodeLocationLabel fastTarget; - JSC::CodeLocationLabel slowTarget; - JSC::CodeLocationJump traceHint; - JSC::CodeLocationJump slowTraceHint; -#ifdef DEBUG - jsbytecode *jumpTargetPC; -#endif - - /* This data is used by the tracing JIT. */ - void *traceData; - uintN traceEpoch; - uint32 loopCounter; - uint32 loopCounterStart; - - bool initialized : 1; - bool hasSlowTraceHint : 1; -}; - -static const uint16 BAD_TRACEIC_INDEX = (uint16)0xffff; - void JS_FASTCALL GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic); void JS_FASTCALL SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic); @@ -301,9 +277,6 @@ JSBool JS_FASTCALL SplatApplyArgs(VMFrame &f); void GenerateArgumentCheckStub(VMFrame &f); -void PurgeMICs(JSContext *cx, JSScript *script); -void SweepCallICs(JSContext *cx, JSScript *script, bool purgeAll); - } /* namespace ic */ } /* namespace mjit */ } /* namespace js */ diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index bca2979dfab..91a1be6d31c 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -3242,57 +3242,5 @@ ic::SetElement(VMFrame &f, ic::SetElementIC *ic) template void JS_FASTCALL ic::SetElement(VMFrame &f, SetElementIC *ic); template void JS_FASTCALL ic::SetElement(VMFrame &f, SetElementIC *ic); -void -JITScript::purgePICs() -{ - if (!nPICs && !nGetElems && !nSetElems) - return; - - Repatcher repatcher(this); - - ic::PICInfo *pics_ = pics(); - for (uint32 i = 0; i < nPICs; i++) { - ic::PICInfo &pic = pics_[i]; - switch (pic.kind) { - case ic::PICInfo::SET: - case ic::PICInfo::SETMETHOD: - SetPropCompiler::reset(repatcher, pic); - break; - case ic::PICInfo::NAME: - case ic::PICInfo::XNAME: - case ic::PICInfo::CALLNAME: - ScopeNameCompiler::reset(repatcher, pic); - break; - case ic::PICInfo::BIND: - BindNameCompiler::reset(repatcher, pic); - break; - case ic::PICInfo::CALL: /* fall-through */ - case ic::PICInfo::GET: - GetPropCompiler::reset(repatcher, pic); - break; - default: - JS_NOT_REACHED("Unhandled PIC kind"); - break; - } - pic.reset(); - } - - ic::GetElementIC *getElems_ = getElems(); - ic::SetElementIC *setElems_ = setElems(); - for (uint32 i = 0; i < nGetElems; i++) - getElems_[i].purge(repatcher); - for (uint32 i = 0; i < nSetElems; i++) - setElems_[i].purge(repatcher); -} - -void -ic::PurgePICs(JSContext *cx, JSScript *script) -{ - if (script->jitNormal) - script->jitNormal->purgePICs(); - if (script->jitCtor) - script->jitCtor->purgePICs(); -} - #endif /* JS_POLYIC */ diff --git a/js/src/methodjit/PolyIC.h b/js/src/methodjit/PolyIC.h index 35984c70892..b70ae2c9c1c 100644 --- a/js/src/methodjit/PolyIC.h +++ b/js/src/methodjit/PolyIC.h @@ -60,8 +60,6 @@ namespace ic { static const uint32 MAX_PIC_STUBS = 16; static const uint32 MAX_GETELEM_IC_STUBS = 17; -void PurgePICs(JSContext *cx); - enum LookupStatus { Lookup_Error = 0, Lookup_Uncacheable, @@ -554,7 +552,6 @@ struct PICInfo : public BasePolyIC { }; #ifdef JS_POLYIC -void PurgePICs(JSContext *cx, JSScript *script); void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *); void JS_FASTCALL GetPropNoCache(VMFrame &f, ic::PICInfo *); void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *); diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index 5ce1762d9fb..67f82954be2 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -113,11 +113,6 @@ void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto); void JS_FASTCALL Throw(VMFrame &f); -#if JS_MONOIC -void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::TraceICInfo *tic); -#else -void * JS_FASTCALL InvokeTracer(VMFrame &f); -#endif void * JS_FASTCALL LookupSwitch(VMFrame &f, jsbytecode *pc); void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc); From a1019f02aa0ddae47ec37d52b61a1225d0b9a827 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Tue, 25 Oct 2011 16:52:52 +1300 Subject: [PATCH 38/42] Bug 696739. Treat inline fieldsets as pseudo-stacking-contexts. r=bz --- layout/generic/nsFrame.cpp | 8 ++------ layout/reftests/bugs/696739-1-ref.html | 22 ++++++++++++++++++++++ layout/reftests/bugs/696739-1.html | 22 ++++++++++++++++++++++ layout/reftests/bugs/reftest.list | 1 + 4 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 layout/reftests/bugs/696739-1-ref.html create mode 100644 layout/reftests/bugs/696739-1.html diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 69e6bec0552..4a8ffacde63 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1803,14 +1803,11 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, if (aChild->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return NS_OK; - const nsStyleDisplay* disp = aChild->GetStyleDisplay(); // true if this is a real or pseudo stacking context bool pseudoStackingContext = (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0; - // XXX we REALLY need a "are you an inline-block sort of thing?" here!!! if ((aFlags & DISPLAY_CHILD_INLINE) && - (disp->mDisplay != NS_STYLE_DISPLAY_INLINE || - (aChild->IsFrameOfType(eReplaced)))) { + !aChild->IsFrameOfType(eLineParticipant)) { // child is a non-inline frame in an inline context, i.e., // it acts like inline-block or inline-table. Therefore it is a // pseudo-stacking-context. @@ -1827,8 +1824,6 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, NS_ASSERTION(aChild, "No out of flow frame?"); if (!aChild || nsLayoutUtils::IsPopup(aChild)) return NS_OK; - // update for the new child - disp = aChild->GetStyleDisplay(); // Make sure that any attempt to use childType below is disappointed. We // could call GetType again but since we don't currently need it, let's // avoid the virtual call. @@ -1894,6 +1889,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // Child is composited if it's transformed, partially transparent, or has // SVG effects. + const nsStyleDisplay* disp = aChild->GetStyleDisplay(); bool isVisuallyAtomic = disp->mOpacity != 1.0f || aChild->IsTransformed() || nsSVGIntegrationUtils::UsingEffectsForFrame(aChild); diff --git a/layout/reftests/bugs/696739-1-ref.html b/layout/reftests/bugs/696739-1-ref.html new file mode 100644 index 00000000000..1001d88b7da --- /dev/null +++ b/layout/reftests/bugs/696739-1-ref.html @@ -0,0 +1,22 @@ + + + + + + +
+
+
I should be black text on a green background
+
+
+
I should be black text on a green background
+
+
+ + diff --git a/layout/reftests/bugs/696739-1.html b/layout/reftests/bugs/696739-1.html new file mode 100644 index 00000000000..efc5c38d763 --- /dev/null +++ b/layout/reftests/bugs/696739-1.html @@ -0,0 +1,22 @@ + + + + + + +
+
+
I should be black text on a green background
+
+
+
I should be black text on a green background
+
+
+ + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 4a2fb101941..462e9a12cfe 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1667,3 +1667,4 @@ fails-if(layersGPUAccelerated&&cocoaWidget) == 654950-1.html 654950-1-ref.html # == 670467-2.html 670467-2-ref.html != 691087-1.html 691087-1-ref.html == 691571-1.html 691571-1-ref.html +== 696739-1.html 696739-1-ref.html From d763025900e1683cde29c4d04032118c4988af00 Mon Sep 17 00:00:00 2001 From: Ginn Chen Date: Tue, 25 Oct 2011 12:50:03 +0800 Subject: [PATCH 39/42] Bug 689916 Fix yarr crashes on SPARC r=dmandelin --- js/src/yarr/YarrJIT.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/js/src/yarr/YarrJIT.cpp b/js/src/yarr/YarrJIT.cpp index 9cdfbd4bb11..d5b88fac767 100644 --- a/js/src/yarr/YarrJIT.cpp +++ b/js/src/yarr/YarrJIT.cpp @@ -1205,8 +1205,10 @@ class YarrGenerator : private MacroAssembler { // If we get here, the prior alternative matched - return success. // Adjust the stack pointer to remove the pattern's frame. +#if !WTF_CPU_SPARC if (m_pattern.m_body->m_callFrameSize) addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); +#endif // Load appropriate values into the return register and the first output // slot, and return. In the case of pattern with a fixed size, we will @@ -1513,8 +1515,10 @@ class YarrGenerator : private MacroAssembler { } case OpMatchFailed: +#if !WTF_CPU_SPARC if (m_pattern.m_body->m_callFrameSize) addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); +#endif move(TrustedImm32(-1), returnRegister); generateReturn(); break; @@ -1753,8 +1757,10 @@ class YarrGenerator : private MacroAssembler { // run any matches, and need to return a failure state from JIT code. matchFailed.link(this); +#if !WTF_CPU_SPARC if (m_pattern.m_body->m_callFrameSize) addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); +#endif move(TrustedImm32(-1), returnRegister); generateReturn(); break; @@ -2326,8 +2332,6 @@ class YarrGenerator : private MacroAssembler { push(SH4Registers::r13); #elif WTF_CPU_SPARC save(Imm32(-m_pattern.m_body->m_callFrameSize * sizeof(void*))); - // set m_callFrameSize to 0 avoid and stack movement later. - m_pattern.m_body->m_callFrameSize = 0; #elif WTF_CPU_MIPS // Do nothing. #endif @@ -2377,8 +2381,10 @@ public: if (!m_pattern.m_body->m_hasFixedSize) store32(index, Address(output)); +#if !WTF_CPU_SPARC if (m_pattern.m_body->m_callFrameSize) subPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); +#endif // Compile the pattern to the internal 'YarrOp' representation. opCompileBody(m_pattern.m_body); From 64d57d2c7d4de42afd90bc52bf040bfc77045cda Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Tue, 25 Oct 2011 09:18:13 +0100 Subject: [PATCH 40/42] Bug 653928 - Animation of path d attribute with elliptical arcs discrete instead of continuous when flags change. r=dholbert --- .../content/src/SVGPathSegListSMILType.cpp | 46 +++++-------------- .../test/test_pathAnimInterpolation.xhtml | 19 ++------ 2 files changed, 15 insertions(+), 50 deletions(-) diff --git a/content/svg/content/src/SVGPathSegListSMILType.cpp b/content/svg/content/src/SVGPathSegListSMILType.cpp index f7e015f27d9..d0414c7d7fd 100644 --- a/content/svg/content/src/SVGPathSegListSMILType.cpp +++ b/content/svg/content/src/SVGPathSegListSMILType.cpp @@ -95,21 +95,6 @@ SVGPathSegListSMILType::IsEqual(const nsSMILValue& aLeft, *static_cast(aRight.mU.mPtr); } -static bool -ArcFlagsDiffer(SVGPathDataAndOwner::const_iterator aPathData1, - SVGPathDataAndOwner::const_iterator aPathData2) -{ - NS_ABORT_IF_FALSE - (SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData1[0])), - "ArcFlagsDiffer called with non-arc segment"); - NS_ABORT_IF_FALSE - (SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData2[0])), - "ArcFlagsDiffer called with non-arc segment"); - - return aPathData1[LARGE_ARC_FLAG_IDX] != aPathData2[LARGE_ARC_FLAG_IDX] || - aPathData1[SWEEP_FLAG_IDX] != aPathData2[SWEEP_FLAG_IDX]; -} - enum PathInterpolationResult { eCannotInterpolate, eRequiresConversion, @@ -139,12 +124,6 @@ CanInterpolate(const SVGPathDataAndOwner& aStart, PRUint32 startType = SVGPathSegUtils::DecodeType(*pStart); PRUint32 endType = SVGPathSegUtils::DecodeType(*pEnd); - if (SVGPathSegUtils::IsArcType(startType) && - SVGPathSegUtils::IsArcType(endType) && - ArcFlagsDiffer(pStart, pEnd)) { - return eCannotInterpolate; - } - if (startType != endType) { if (!SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType)) { return eCannotInterpolate; @@ -215,25 +194,22 @@ AddWeightedPathSegs(double aCoeff1, NS_ABORT_IF_FALSE(!aSeg1 || SVGPathSegUtils::DecodeType(*aSeg1) == segType, "unexpected segment type"); - // FIRST: Directly copy the arguments that don't make sense to add. aResultSeg[0] = aSeg2[0]; // encoded segment type - bool isArcType = SVGPathSegUtils::IsArcType(segType); - if (isArcType) { - // Copy boolean arc flags. - NS_ABORT_IF_FALSE(!aSeg1 || !ArcFlagsDiffer(aSeg1, aSeg2), - "Expecting arc flags to match"); - aResultSeg[LARGE_ARC_FLAG_IDX] = aSeg2[LARGE_ARC_FLAG_IDX]; - aResultSeg[SWEEP_FLAG_IDX] = aSeg2[SWEEP_FLAG_IDX]; - } - - // SECOND: Add the arguments that are supposed to be added. + // FIRST: Add all the arguments. // (The 1's below are to account for segment type) PRUint32 numArgs = SVGPathSegUtils::ArgCountForType(segType); for (PRUint32 i = 1; i < 1 + numArgs; ++i) { - // Need to skip arc flags for arc-type segments. (already handled them) - if (!(isArcType && (i == LARGE_ARC_FLAG_IDX || i == SWEEP_FLAG_IDX))) { - aResultSeg[i] = (aSeg1 ? aCoeff1 * aSeg1[i] : 0.0) + aCoeff2 * aSeg2[i]; + aResultSeg[i] = (aSeg1 ? aCoeff1 * aSeg1[i] : 0.0) + aCoeff2 * aSeg2[i]; + } + + // SECOND: ensure non-zero flags become 1. + if (SVGPathSegUtils::IsArcType(segType)) { + if (aResultSeg[LARGE_ARC_FLAG_IDX] != 0.0f) { + aResultSeg[LARGE_ARC_FLAG_IDX] = 1.0f; + } + if (aResultSeg[SWEEP_FLAG_IDX] != 0.0f) { + aResultSeg[SWEEP_FLAG_IDX] = 1.0f; } } diff --git a/content/svg/content/test/test_pathAnimInterpolation.xhtml b/content/svg/content/test/test_pathAnimInterpolation.xhtml index 66b89f09f1c..36f8e4de5eb 100644 --- a/content/svg/content/test/test_pathAnimInterpolation.xhtml +++ b/content/svg/content/test/test_pathAnimInterpolation.xhtml @@ -138,10 +138,10 @@ var gSuffixes = { [40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]], QQ: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]], qq: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]], - AA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100], - [35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]], - aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100], - [35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]], + AA: [[10, 20, 30, 1, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100], + [35, 45, 55, 1, 0, 65, 75], [45, 65, 85, 1, 0, 105, 125]], + aa: [[10, 20, 30, 0, 1, 40, 50], [60, 70, 80, 0, 0, 90, 100], + [35, 45, 55, 0, 1, 65, 75], [45, 65, 85, 0, 1, 105, 125]], HH: [[10], [20], [15], [25]], hh: [[10], [20], [15], [25]], VV: [[10], [20], [15], [25]], @@ -286,17 +286,6 @@ function run() } } - // Test that differences in arc flag parameters cause the - // interpolation/addition not to occur. - addTest(1, "M100,100", - "A", [10, 20, 30, 0, 0, 40, 50], - "a", [60, 70, 80, 0, 1, 90, 100], - "a", [60, 70, 80, 0, 1, 90, 100], additive); - addTest(1, "M100,100", - "A", [10, 20, 30, 0, 0, 40, 50], - "a", [60, 70, 80, 1, 0, 90, 100], - "a", [60, 70, 80, 1, 0, 90, 100], additive); - // Test all pairs of segment types that cannot be interpolated between. for each (let fromType in gTypes) { let fromArguments = generatePathSegmentArguments(fromType, 0); From 7f22062486314ec7991503af36d1c08ed60477ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20O=C3=A2=E2=82=AC=E2=84=A2Shannessy?= Date: Tue, 25 Oct 2011 10:22:03 +0200 Subject: [PATCH 41/42] Bug 679590 - Intermittent failure in browser_607016.js | sanity check that tab doesn't have extData. r=dietrich --- .../sessionstore/test/browser/browser_607016.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/browser/components/sessionstore/test/browser/browser_607016.js b/browser/components/sessionstore/test/browser/browser_607016.js index 654f39744a9..eb465294d2d 100644 --- a/browser/components/sessionstore/test/browser/browser_607016.js +++ b/browser/components/sessionstore/test/browser/browser_607016.js @@ -84,16 +84,18 @@ function test() { let curState = JSON.parse(ss.getBrowserState()); for (let i = 0; i < curState.windows[0].tabs.length; i++) { - if (state.windows[0].tabs[i].extData) { - is(curState.windows[0].tabs[i].extData["uniq"], - state.windows[0].tabs[i].extData["uniq"], + let tabState = state.windows[0].tabs[i]; + let tabCurState = curState.windows[0].tabs[i]; + if (tabState.extData) { + is(tabCurState.extData["uniq"], tabState.extData["uniq"], "sanity check that tab has correct extData"); } else { - ok(!("extData" in curState.windows[0].tabs[i]), - "sanity check that tab doesn't have extData"); - //XXXzpao output the tab state to help debug bug 679590 - info("tabState: " + JSON.stringify(curState.windows[0].tabs[i])); + // We aren't expecting there to be any data on extData, but panorama + // may be setting something, so we need to make sure that if we do have + // data, we just don't have anything for "uniq". + ok(!("extData" in tabCurState) || !("uniq" in tabCurState.extData), + "sanity check that tab doesn't have extData or extData doesn't have 'uniq'"); } } From 4c7bd5e1abb1443ef47af5b3303132279f3ebd92 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Tue, 25 Oct 2011 08:49:31 -0400 Subject: [PATCH 42/42] Bug 692991: Refactor some VERSION_CHANGE transaction locking stuff and disallow transaction creation before databases are completely open. r=bent --- dom/indexedDB/AsyncConnectionHelper.cpp | 4 +- dom/indexedDB/AsyncConnectionHelper.h | 5 ++ dom/indexedDB/DatabaseInfo.cpp | 3 +- dom/indexedDB/DatabaseInfo.h | 4 +- dom/indexedDB/IDBDatabase.cpp | 32 +++++++ dom/indexedDB/IDBDatabase.h | 3 + dom/indexedDB/IndexedDatabaseManager.cpp | 79 +++++++++++++--- dom/indexedDB/IndexedDatabaseManager.h | 22 +++++ dom/indexedDB/OpenDatabaseHelper.cpp | 39 +++++++- dom/indexedDB/OpenDatabaseHelper.h | 1 + dom/indexedDB/TransactionThreadPool.cpp | 16 +--- dom/indexedDB/TransactionThreadPool.h | 6 -- dom/indexedDB/test/Makefile.in | 1 + dom/indexedDB/test/browser_quotaPrompt.html | 3 + .../test/event_propagation_iframe.html | 2 +- dom/indexedDB/test/leaving_page_iframe.html | 2 +- .../test/test_autoIncrement_indexes.html | 3 + dom/indexedDB/test/test_clear.html | 2 +- .../test_create_index_with_integer_keys.html | 4 +- dom/indexedDB/test/test_cursor_mutation.html | 2 +- dom/indexedDB/test/test_getAll.html | 2 + dom/indexedDB/test/test_index_getAll.html | 4 +- .../test/test_index_getAllObjects.html | 2 +- .../test/test_index_object_cursors.html | 2 +- dom/indexedDB/test/test_indexes.html | 2 +- .../test/test_indexes_bad_values.html | 2 + dom/indexedDB/test/test_object_identity.html | 2 +- dom/indexedDB/test/test_odd_result_order.html | 2 +- dom/indexedDB/test/test_open_objectStore.html | 2 +- .../test/test_overlapping_transactions.html | 2 +- .../test/test_readonly_transactions.html | 3 + .../test/test_setVersion_exclusion.html | 90 +++++++++++++++++++ .../test/test_success_events_after_abort.html | 2 +- .../test/test_transaction_abort.html | 3 + .../test/test_transaction_lifetimes.html | 2 +- .../test_transaction_lifetimes_nested.html | 2 +- .../test/test_writer_starvation.html | 2 +- 37 files changed, 298 insertions(+), 61 deletions(-) create mode 100644 dom/indexedDB/test/test_setVersion_exclusion.html diff --git a/dom/indexedDB/AsyncConnectionHelper.cpp b/dom/indexedDB/AsyncConnectionHelper.cpp index 19e87f8c63b..df2b7b340d4 100644 --- a/dom/indexedDB/AsyncConnectionHelper.cpp +++ b/dom/indexedDB/AsyncConnectionHelper.cpp @@ -560,8 +560,8 @@ TransactionPoolEventTarget::Dispatch(nsIRunnable* aRunnable, NS_ASSERTION(aRunnable, "Null pointer!"); NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "Unsupported!"); - TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); - NS_ENSURE_TRUE(pool, NS_ERROR_FAILURE); + TransactionThreadPool* pool = TransactionThreadPool::Get(); + NS_ASSERTION(pool, "This should never be null!"); return pool->Dispatch(mTransaction, aRunnable, false, nsnull); } diff --git a/dom/indexedDB/AsyncConnectionHelper.h b/dom/indexedDB/AsyncConnectionHelper.h index b0038d48e3a..6f72a567f4a 100644 --- a/dom/indexedDB/AsyncConnectionHelper.h +++ b/dom/indexedDB/AsyncConnectionHelper.h @@ -125,6 +125,11 @@ public: static IDBTransaction* GetCurrentTransaction(); + bool HasTransaction() + { + return mTransaction; + } + nsISupports* GetSource() { return mRequest ? mRequest->Source() : nsnull; diff --git a/dom/indexedDB/DatabaseInfo.cpp b/dom/indexedDB/DatabaseInfo.cpp index f7ed1136285..0433aa75da8 100644 --- a/dom/indexedDB/DatabaseInfo.cpp +++ b/dom/indexedDB/DatabaseInfo.cpp @@ -85,7 +85,8 @@ EnumerateObjectStoreNames(const nsAString& aKey, DatabaseInfo::DatabaseInfo() : id(0), nextObjectStoreId(1), - nextIndexId(1) + nextIndexId(1), + runningVersionChange(false) { MOZ_COUNT_CTOR(DatabaseInfo); } diff --git a/dom/indexedDB/DatabaseInfo.h b/dom/indexedDB/DatabaseInfo.h index 1fdab7e3285..1f2ff398e0a 100644 --- a/dom/indexedDB/DatabaseInfo.h +++ b/dom/indexedDB/DatabaseInfo.h @@ -54,7 +54,8 @@ struct DatabaseInfo ~DatabaseInfo(); #else DatabaseInfo() - : id(0), nextObjectStoreId(1), nextIndexId(1) { } + : id(0), nextObjectStoreId(1), nextIndexId(1), runningVersionChange(false) + { } #endif static bool Get(PRUint32 aId, @@ -73,6 +74,7 @@ struct DatabaseInfo nsString filePath; PRInt64 nextObjectStoreId; PRInt64 nextIndexId; + bool runningVersionChange; nsAutoRefCnt referenceCount; }; diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 34c86d40e61..a87a325c75b 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -428,6 +428,34 @@ IDBDatabase::IsClosed() return mClosed; } +void +IDBDatabase::EnterSetVersionTransaction() +{ + DatabaseInfo* dbInfo; + if (!DatabaseInfo::Get(mDatabaseId, &dbInfo)) { + NS_ERROR("This should never fail!"); + } + + NS_ASSERTION(!dbInfo->runningVersionChange, "How did that happen?"); + dbInfo->runningVersionChange = true; +} + +void +IDBDatabase::ExitSetVersionTransaction() +{ + DatabaseInfo* dbInfo; + if (!DatabaseInfo::Get(mDatabaseId, &dbInfo)) { + NS_ERROR("This should never fail!"); + } + + NS_ASSERTION(dbInfo->runningVersionChange, "How did that happen?"); + dbInfo->runningVersionChange = false; + + IndexedDatabaseManager* manager = IndexedDatabaseManager::Get(); + NS_ASSERTION(manager, "We should always have a manager here"); + manager->UnblockSetVersionRunnable(this); +} + void IDBDatabase::OnUnlink() { @@ -710,6 +738,10 @@ IDBDatabase::Transaction(nsIVariant* aStoreNames, NS_ERROR("This should never fail!"); } + if (info->runningVersionChange) { + return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; + } + nsTArray storesToOpen; switch (type) { diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h index 5a2ab7f582e..9df3cb4f3c2 100644 --- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -135,6 +135,9 @@ public: // Whether or not the database has had Close called on it. bool IsClosed(); + void EnterSetVersionTransaction(); + void ExitSetVersionTransaction(); + private: IDBDatabase(); ~IDBDatabase(); diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index e611fa5676e..baa3784f00a 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -693,19 +693,15 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) // Now run the helper if there are no more live databases. if (runnable->mHelper && runnable->mDatabases.IsEmpty()) { - // Don't hold the callback alive longer than necessary. - nsRefPtr helper; - helper.swap(runnable->mHelper); + // At this point, all databases are closed, so no new transactions can + // be started. There may, however, still be outstanding transactions + // that have not completed. We need to wait for those before we + // dispatch the helper. - if (NS_FAILED(helper->DispatchToTransactionPool())) { - NS_WARNING("Failed to dispatch to thread pool!"); - } + TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); - // Now wait for the transaction to complete. Completing the transaction - // will be our cue to remove the SetVersionRunnable from our list and - // therefore allow other SetVersion requests to begin. - TransactionThreadPool* pool = TransactionThreadPool::Get(); - NS_ASSERTION(pool, "This should never be null!"); + nsRefPtr waitRunnable = + new WaitForTransactionsToFinishRunnable(runnable); // All other databases should be closed, so we only need to wait on this // one. @@ -714,8 +710,8 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) NS_ERROR("This should never fail!"); } - // Use the SetVersionRunnable as the callback. - if (!pool->WaitForAllDatabasesToComplete(array, runnable)) { + // Use the WaitForTransactionsToFinishRunnable as the callback. + if (!pool->WaitForAllDatabasesToComplete(array, waitRunnable)) { NS_WARNING("Failed to wait for transaction to complete!"); } } @@ -724,6 +720,27 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) } } +void +IndexedDatabaseManager::UnblockSetVersionRunnable(IDBDatabase* aDatabase) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aDatabase, "Null pointer!"); + + // Check through the list of SetVersionRunnables to find the one we're seeking. + for (PRUint32 index = 0; index < mSetVersionRunnables.Length(); index++) { + nsRefPtr& runnable = mSetVersionRunnables[index]; + + if (runnable->mRequestingDatabase->Id() == aDatabase->Id()) { + NS_ASSERTION(!runnable->mHelper, + "Why are we unblocking a runnable if the helper didn't run?"); + NS_DispatchToCurrentThread(runnable); + return; + } + } + + NS_NOTREACHED("How did we get here!"); +} + // static bool IndexedDatabaseManager::SetCurrentDatabase(IDBDatabase* aDatabase) @@ -1283,3 +1300,39 @@ IndexedDatabaseManager::SetVersionRunnable::Run() return NS_OK; } + +NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::WaitForTransactionsToFinishRunnable, + nsIRunnable) + +NS_IMETHODIMP +IndexedDatabaseManager::WaitForTransactionsToFinishRunnable::Run() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + // Don't hold the callback alive longer than necessary. + nsRefPtr helper; + helper.swap(mRunnable->mHelper); + + nsRefPtr runnable; + runnable.swap(mRunnable); + + // If the helper has a transaction, dispatch it to the transaction + // threadpool. + if (helper->HasTransaction()) { + if (NS_FAILED(helper->DispatchToTransactionPool())) { + NS_WARNING("Failed to dispatch to thread pool!"); + } + } + // Otherwise, dispatch it to the IO thread. + else { + IndexedDatabaseManager* manager = IndexedDatabaseManager::Get(); + NS_ASSERTION(manager, "We should definitely have a manager here"); + + helper->Dispatch(manager->IOThread()); + } + + // The helper is responsible for calling + // IndexedDatabaseManager::UnblockSetVersionRunnable. + + return NS_OK; +} diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index 853d239482a..7287394d233 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -204,6 +204,8 @@ private: // Called when AsyncUsageRunnable has finished its Run() method. inline void OnUsageCheckComplete(AsyncUsageRunnable* aRunnable); + void UnblockSetVersionRunnable(IDBDatabase* aDatabase); + // Responsible for waiting until all databases have been closed before running // the version change transaction. Created when // IndexedDatabaseManager::SetDatabaseVersion is called. Runs only once on the @@ -227,6 +229,26 @@ private: // Called when SetVersionRunnable has finished its Run() method. inline void OnSetVersionRunnableComplete(SetVersionRunnable* aRunnable); + + // A callback runnable used by the TransactionPool when it's safe to proceed + // with a SetVersion/DeleteDatabase/etc. + class WaitForTransactionsToFinishRunnable : public nsIRunnable + { + public: + WaitForTransactionsToFinishRunnable(SetVersionRunnable* aRunnable) + : mRunnable(aRunnable) + { + NS_ASSERTION(mRunnable, "Why don't we have a runnable?"); + NS_ASSERTION(mRunnable->mDatabases.IsEmpty(), "We're here too early!"); + } + + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + + private: + nsRefPtr mRunnable; + }; + // Maintains a list of live databases per origin. nsClassHashtable > mLiveDatabases; diff --git a/dom/indexedDB/OpenDatabaseHelper.cpp b/dom/indexedDB/OpenDatabaseHelper.cpp index 7e7559b47c8..ffe82ba4cbc 100644 --- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -495,10 +495,13 @@ public: NS_DECL_ISUPPORTS_INHERITED - nsresult DoDatabaseWork(mozIStorageConnection* aConnection); nsresult GetSuccessResult(JSContext* aCx, jsval* aVal); +protected: + nsresult DoDatabaseWork(mozIStorageConnection* aConnection); + nsresult Init(); + // SetVersionHelper never fires an error event at the request. It hands that // responsibility back to the OpenDatabaseHelper void OnError() { } @@ -554,6 +557,8 @@ OpenDatabaseHelper::DoDatabaseWork() } #endif + mState = eFiringEvents; // In case we fail somewhere along the line. + if (IndexedDatabaseManager::IsShuttingDown()) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -646,9 +651,10 @@ OpenDatabaseHelper::DoDatabaseWork() return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR; } - mState = mCurrentVersion != mRequestedVersion ? - eSetVersionPending : - eFiringEvents; + if (mCurrentVersion != mRequestedVersion) { + mState = eSetVersionPending; + } + return NS_OK; } @@ -684,6 +690,7 @@ OpenDatabaseHelper::StartSetVersion() // The SetVersionHelper is responsible for dispatching us back to the // main thread again and changing the state to eSetVersionCompleted. mState = eSetVersionPending; + return NS_OK; } @@ -711,6 +718,12 @@ OpenDatabaseHelper::Run() mState == eSetVersionCompleted, "Why are we here?"); if (mState == eSetVersionCompleted) { + // Allow transaction creation/other version change transactions to proceed + // before we fire events. Other version changes will be postd to the end + // of the event loop, and will be behind whatever the page does in + // its error/success event handlers. + mDatabase->ExitSetVersionTransaction(); + mState = eFiringEvents; } else { // Notify the request that we're done, but only if we didn't just finish @@ -865,6 +878,15 @@ OpenDatabaseHelper::NotifySetVersionFinished() return NS_DispatchToCurrentThread(this); } +void +OpenDatabaseHelper::BlockDatabase() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mDatabase, "This is going bad fast."); + + mDatabase->EnterSetVersionTransaction(); +} + void OpenDatabaseHelper::DispatchSuccessEvent() { @@ -916,6 +938,15 @@ OpenDatabaseHelper::ReleaseMainThreadObjects() NS_IMPL_ISUPPORTS_INHERITED0(SetVersionHelper, AsyncConnectionHelper); +nsresult +SetVersionHelper::Init() +{ + // Block transaction creation until we are done. + mOpenHelper->BlockDatabase(); + + return NS_OK; +} + nsresult SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { diff --git a/dom/indexedDB/OpenDatabaseHelper.h b/dom/indexedDB/OpenDatabaseHelper.h index 0d91db6b2dc..53334867ab3 100644 --- a/dom/indexedDB/OpenDatabaseHelper.h +++ b/dom/indexedDB/OpenDatabaseHelper.h @@ -82,6 +82,7 @@ public: } nsresult NotifySetVersionFinished(); + void BlockDatabase(); protected: // Methods only called on the main thread diff --git a/dom/indexedDB/TransactionThreadPool.cpp b/dom/indexedDB/TransactionThreadPool.cpp index 2bb66132b48..c78ba97ef90 100644 --- a/dom/indexedDB/TransactionThreadPool.cpp +++ b/dom/indexedDB/TransactionThreadPool.cpp @@ -238,7 +238,6 @@ TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction) #ifdef DEBUG if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) { - NS_ASSERTION(dbTransactionInfo->locked, "Should be locked!"); NS_ASSERTION(transactionCount == 1, "More transactions running than should be!"); } @@ -344,10 +343,6 @@ TransactionThreadPool::TransactionCanRun(IDBTransaction* aTransaction, PRUint32 transactionCount = transactionsInProgress.Length(); NS_ASSERTION(transactionCount, "Should never be 0!"); - if (mode == IDBTransaction::VERSION_CHANGE) { - dbTransactionInfo->lockPending = true; - } - for (PRUint32 index = 0; index < transactionCount; index++) { // See if this transaction is in out list of current transactions. const TransactionInfo& info = transactionsInProgress[index]; @@ -358,11 +353,7 @@ TransactionThreadPool::TransactionCanRun(IDBTransaction* aTransaction, } } - if (dbTransactionInfo->locked || dbTransactionInfo->lockPending) { - *aCanRun = false; - *aExistingQueue = nsnull; - return NS_OK; - } + NS_ASSERTION(mode != IDBTransaction::VERSION_CHANGE, "How did we get here?"); bool writeOverlap; nsresult rv = @@ -448,11 +439,6 @@ TransactionThreadPool::Dispatch(IDBTransaction* aTransaction, dbTransactionInfo = autoDBTransactionInfo; } - if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) { - NS_ASSERTION(!dbTransactionInfo->locked, "Already locked?!"); - dbTransactionInfo->locked = true; - } - const nsTArray& objectStoreNames = aTransaction->mObjectStoreNames; nsTArray& storesInUse = diff --git a/dom/indexedDB/TransactionThreadPool.h b/dom/indexedDB/TransactionThreadPool.h index 97d15c46917..4fdd152c4b0 100644 --- a/dom/indexedDB/TransactionThreadPool.h +++ b/dom/indexedDB/TransactionThreadPool.h @@ -123,12 +123,6 @@ protected: struct DatabaseTransactionInfo { - DatabaseTransactionInfo() - : locked(false), lockPending(false) - { } - - bool locked; - bool lockPending; nsTArray transactions; nsTArray storesReading; nsTArray storesWriting; diff --git a/dom/indexedDB/test/Makefile.in b/dom/indexedDB/test/Makefile.in index 555fbeaf93f..9d7d00f15f6 100644 --- a/dom/indexedDB/test/Makefile.in +++ b/dom/indexedDB/test/Makefile.in @@ -98,6 +98,7 @@ TEST_FILES = \ test_setVersion.html \ test_setVersion_abort.html \ test_setVersion_events.html \ + test_setVersion_exclusion.html \ test_writer_starvation.html \ third_party_iframe1.html \ third_party_iframe2.html \ diff --git a/dom/indexedDB/test/browser_quotaPrompt.html b/dom/indexedDB/test/browser_quotaPrompt.html index 8b9cba0c94a..15e991cee6c 100644 --- a/dom/indexedDB/test/browser_quotaPrompt.html +++ b/dom/indexedDB/test/browser_quotaPrompt.html @@ -69,6 +69,9 @@ db.createObjectStore("foo", { autoIncrement: true }); + request.onsuccess = grabEventAndContinueHandler; + yield; + setTimeout(testFinishedCallback, 0, "ready"); yield; } diff --git a/dom/indexedDB/test/event_propagation_iframe.html b/dom/indexedDB/test/event_propagation_iframe.html index 2d1f745fad5..035fa86248a 100644 --- a/dom/indexedDB/test/event_propagation_iframe.html +++ b/dom/indexedDB/test/event_propagation_iframe.html @@ -104,7 +104,7 @@ db.onerror = errorEventCounter; db.addEventListener("error", errorEventCounter, true); - event.target.transaction.oncomplete = grabEventAndContinueHandler; + event.target.onsuccess = grabEventAndContinueHandler; db.createObjectStore("foo", { autoIncrement: true }); yield; diff --git a/dom/indexedDB/test/leaving_page_iframe.html b/dom/indexedDB/test/leaving_page_iframe.html index 43d0e00ab98..00bcff71243 100644 --- a/dom/indexedDB/test/leaving_page_iframe.html +++ b/dom/indexedDB/test/leaving_page_iframe.html @@ -12,7 +12,7 @@ function startDBWork() { } var store = db.createObjectStore("mystore"); store.add({ hello: "world" }, 42); - trans.oncomplete = madeMod; + e.target.onsuccess = madeMod; }; } diff --git a/dom/indexedDB/test/test_autoIncrement_indexes.html b/dom/indexedDB/test/test_autoIncrement_indexes.html index b24d4896c9d..951be60eef9 100644 --- a/dom/indexedDB/test/test_autoIncrement_indexes.html +++ b/dom/indexedDB/test/test_autoIncrement_indexes.html @@ -33,6 +33,9 @@ let key = event.target.result; ok(key, "Added entry"); + request.onsuccess = grabEventAndContinueHandler; + + event = yield; let objectStore = db.transaction("foo").objectStore("foo"); let first = objectStore.index("first"); diff --git a/dom/indexedDB/test/test_clear.html b/dom/indexedDB/test/test_clear.html index 648f234dafd..44f85569357 100644 --- a/dom/indexedDB/test/test_clear.html +++ b/dom/indexedDB/test/test_clear.html @@ -25,7 +25,7 @@ let db = request.result; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; let objectStore = db.createObjectStore("foo", { autoIncrement: true }); diff --git a/dom/indexedDB/test/test_create_index_with_integer_keys.html b/dom/indexedDB/test/test_create_index_with_integer_keys.html index 37c3d601656..6b058ce6b9c 100644 --- a/dom/indexedDB/test/test_create_index_with_integer_keys.html +++ b/dom/indexedDB/test/test_create_index_with_integer_keys.html @@ -23,7 +23,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; // Make object store, add data. let objectStore = db.createObjectStore("foo", { keyPath: "id" }); @@ -39,7 +39,7 @@ let db2 = event.target.result; db2.onerror = errorHandler; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; // Create index. event.target.transaction.objectStore("foo").createIndex("foo", "num"); diff --git a/dom/indexedDB/test/test_cursor_mutation.html b/dom/indexedDB/test/test_cursor_mutation.html index 7812c1f2ec9..c3d6e52a3ec 100644 --- a/dom/indexedDB/test/test_cursor_mutation.html +++ b/dom/indexedDB/test/test_cursor_mutation.html @@ -35,7 +35,7 @@ let event = yield; let db = event.target.result; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; let objectStore = db.createObjectStore("foo", { keyPath: "ss" }); objectStore.createIndex("name", "name", { unique: true }); diff --git a/dom/indexedDB/test/test_getAll.html b/dom/indexedDB/test/test_getAll.html index 3c873efdfde..7f76c438531 100644 --- a/dom/indexedDB/test/test_getAll.html +++ b/dom/indexedDB/test/test_getAll.html @@ -26,6 +26,7 @@ let objectStore = db.createObjectStore("foo", { autoIncrement: true }); + request.onsuccess = grabEventAndContinueHandler; request = objectStore.getAll(); request.onerror = errorHandler; request.onsuccess = grabEventAndContinueHandler; @@ -46,6 +47,7 @@ } } yield; + yield; request = db.transaction("foo").objectStore("foo").getAll(); request.onerror = errorHandler; diff --git a/dom/indexedDB/test/test_index_getAll.html b/dom/indexedDB/test/test_index_getAll.html index 584bb9ff3e3..ba9fa0067a0 100644 --- a/dom/indexedDB/test/test_index_getAll.html +++ b/dom/indexedDB/test/test_index_getAll.html @@ -60,6 +60,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -78,7 +79,7 @@ } } } - event = yield; + yield; ok(true, "1"); // Now create the indexes. @@ -88,7 +89,6 @@ } is(objectStore.indexNames.length, indexData.length, "Good index count"); - continueToNextStep(); yield; ok(true, "2"); diff --git a/dom/indexedDB/test/test_index_getAllObjects.html b/dom/indexedDB/test/test_index_getAllObjects.html index a65a0536945..585a0d117ac 100644 --- a/dom/indexedDB/test/test_index_getAllObjects.html +++ b/dom/indexedDB/test/test_index_getAllObjects.html @@ -60,6 +60,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -87,7 +88,6 @@ } is(objectStore.indexNames.length, indexData.length, "Good index count"); - continueToNextStep(); yield; objectStore = db.transaction(objectStoreName) diff --git a/dom/indexedDB/test/test_index_object_cursors.html b/dom/indexedDB/test/test_index_object_cursors.html index b7091206d5d..fb8228e2ca1 100644 --- a/dom/indexedDB/test/test_index_object_cursors.html +++ b/dom/indexedDB/test/test_index_object_cursors.html @@ -37,7 +37,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; for (let objectStoreIndex in objectStoreData) { const objectStoreInfo = objectStoreData[objectStoreIndex]; diff --git a/dom/indexedDB/test/test_indexes.html b/dom/indexedDB/test/test_indexes.html index 05132a87d22..98e33701ae6 100644 --- a/dom/indexedDB/test/test_indexes.html +++ b/dom/indexedDB/test/test_indexes.html @@ -71,6 +71,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -97,7 +98,6 @@ indexData[i].options); } is(objectStore.indexNames.length, indexData.length, "Good index count"); - continueToNextStep(); yield; objectStore = db.transaction(objectStoreName) diff --git a/dom/indexedDB/test/test_indexes_bad_values.html b/dom/indexedDB/test/test_indexes_bad_values.html index e28aa6dd718..ffb92160868 100644 --- a/dom/indexedDB/test/test_indexes_bad_values.html +++ b/dom/indexedDB/test/test_indexes_bad_values.html @@ -51,6 +51,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -87,6 +88,7 @@ } } yield; + yield; objectStore = db.transaction(objectStoreName) .objectStore(objectStoreName); diff --git a/dom/indexedDB/test/test_object_identity.html b/dom/indexedDB/test/test_object_identity.html index 211725d3055..600bc396cdd 100644 --- a/dom/indexedDB/test/test_object_identity.html +++ b/dom/indexedDB/test/test_object_identity.html @@ -28,7 +28,7 @@ let index2 = objectStore2.index("bar"); ok(index1 === index2, "Got same indexes"); - transaction.oncomplete = continueToNextStep; + request.onsuccess = continueToNextStep; yield; transaction = db.transaction("foo"); diff --git a/dom/indexedDB/test/test_odd_result_order.html b/dom/indexedDB/test/test_odd_result_order.html index 2a127e6b832..1d14fcf66ce 100644 --- a/dom/indexedDB/test/test_odd_result_order.html +++ b/dom/indexedDB/test/test_odd_result_order.html @@ -29,7 +29,7 @@ autoIncrement: true }); let index = objectStore.createIndex("foo", "index"); - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; yield; objectStore = db.transaction("foo", IDBTransaction.READ_WRITE) diff --git a/dom/indexedDB/test/test_open_objectStore.html b/dom/indexedDB/test/test_open_objectStore.html index 93f09ee1e76..48ece9f2af5 100644 --- a/dom/indexedDB/test/test_open_objectStore.html +++ b/dom/indexedDB/test/test_open_objectStore.html @@ -20,6 +20,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -31,7 +32,6 @@ is(db.objectStoreNames.length, 1, "Bad objectStores list"); is(db.objectStoreNames.item(0), objectStoreName, "Bad name"); - continueToNextStep(); yield; objectStore = db.transaction(objectStoreName).objectStore(objectStoreName); diff --git a/dom/indexedDB/test/test_overlapping_transactions.html b/dom/indexedDB/test/test_overlapping_transactions.html index b59f60f3fe4..39746ce8a37 100644 --- a/dom/indexedDB/test/test_overlapping_transactions.html +++ b/dom/indexedDB/test/test_overlapping_transactions.html @@ -26,7 +26,7 @@ let db = event.target.result; is(db.objectStoreNames.length, 0, "Correct objectStoreNames list"); - event.target.transaction.oncomplete = grabEventAndContinueHandler; + event.target.onsuccess = grabEventAndContinueHandler; for (let i in objectStores) { db.createObjectStore(objectStores[i], { autoIncrement: true }); } diff --git a/dom/indexedDB/test/test_readonly_transactions.html b/dom/indexedDB/test/test_readonly_transactions.html index 5987a1c35f1..58a0a8ad9e3 100644 --- a/dom/indexedDB/test/test_readonly_transactions.html +++ b/dom/indexedDB/test/test_readonly_transactions.html @@ -22,6 +22,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -29,6 +30,8 @@ db.createObjectStore(osName, { autoIncrement: "true" }); + yield; + let key1, key2; request = db.transaction([osName], READ_WRITE) diff --git a/dom/indexedDB/test/test_setVersion_exclusion.html b/dom/indexedDB/test/test_setVersion_exclusion.html new file mode 100644 index 00000000000..96d0d322c28 --- /dev/null +++ b/dom/indexedDB/test/test_setVersion_exclusion.html @@ -0,0 +1,90 @@ + + + + Indexed Database Property Test + + + + + + + + + + + + diff --git a/dom/indexedDB/test/test_success_events_after_abort.html b/dom/indexedDB/test/test_success_events_after_abort.html index c9d75a4cb78..31a29d59d56 100644 --- a/dom/indexedDB/test/test_success_events_after_abort.html +++ b/dom/indexedDB/test/test_success_events_after_abort.html @@ -19,7 +19,7 @@ let db = event.target.result; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; let objectStore = db.createObjectStore("foo"); objectStore.add({}, 1).onerror = errorHandler; diff --git a/dom/indexedDB/test/test_transaction_abort.html b/dom/indexedDB/test/test_transaction_abort.html index 9558212a9a3..e7d6906c202 100644 --- a/dom/indexedDB/test/test_transaction_abort.html +++ b/dom/indexedDB/test/test_transaction_abort.html @@ -29,6 +29,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -157,6 +158,8 @@ ok(true, "RemoveIndex threw"); } + yield; + request = db.transaction("foo", READ_WRITE).objectStore("foo").add({}); request.onerror = errorHandler; request.onsuccess = grabEventAndContinueHandler; diff --git a/dom/indexedDB/test/test_transaction_lifetimes.html b/dom/indexedDB/test/test_transaction_lifetimes.html index c402ec8b81d..7e9e2e368bd 100644 --- a/dom/indexedDB/test/test_transaction_lifetimes.html +++ b/dom/indexedDB/test/test_transaction_lifetimes.html @@ -20,7 +20,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; db.createObjectStore("foo", { autoIncrement: true }); yield; diff --git a/dom/indexedDB/test/test_transaction_lifetimes_nested.html b/dom/indexedDB/test/test_transaction_lifetimes_nested.html index de7b1cebc74..2fde9b81dd5 100644 --- a/dom/indexedDB/test/test_transaction_lifetimes_nested.html +++ b/dom/indexedDB/test/test_transaction_lifetimes_nested.html @@ -20,7 +20,7 @@ let db = event.target.result; db.onerror = errorHandler; - event.target.transaction.oncomplete = continueToNextStep; + event.target.onsuccess = continueToNextStep; db.createObjectStore("foo"); yield; diff --git a/dom/indexedDB/test/test_writer_starvation.html b/dom/indexedDB/test/test_writer_starvation.html index d29bdf8bb5f..456975bcca9 100644 --- a/dom/indexedDB/test/test_writer_starvation.html +++ b/dom/indexedDB/test/test_writer_starvation.html @@ -23,6 +23,7 @@ let request = mozIndexedDB.open(name, 1, description); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; let event = yield; let db = event.target.result; @@ -39,7 +40,6 @@ let key = event.target.result; ok(key, "Got a key"); - SimpleTest.executeSoon(function() { testGenerator.next(); }); yield; let continueReading = true;