From 0826d276ae57cbce1130e2df998f5e370dfaa185 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 21 Oct 2011 15:43:55 -0700 Subject: [PATCH 01/66] Bug 710032 - CollectNativeRegions() utility to simplify handling of inlined frames. r=bhackett --HG-- extra : rebase_source : 310d2cc0b58bad53d07e09924ae4d37762542457 --- js/src/jsprobes.cpp | 109 +++++++++++++++++++++++++++++++++- js/src/jsprobes.h | 29 +++++++-- js/src/methodjit/Compiler.cpp | 21 +++++-- js/src/methodjit/Compiler.h | 41 +++++++------ 4 files changed, 170 insertions(+), 30 deletions(-) diff --git a/js/src/jsprobes.cpp b/js/src/jsprobes.cpp index c8a66fcadc9..c1fa21803eb 100644 --- a/js/src/jsprobes.cpp +++ b/js/src/jsprobes.cpp @@ -55,6 +55,10 @@ #include "jsscript.h" #include "jsstr.h" +#ifdef JS_METHODJIT +# include "methodjit/Compiler.h" +#endif + #include "jsobjinlines.h" #define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v)) @@ -114,16 +118,115 @@ Probes::JITGranularityRequested() return want; } +/* + * Flatten the tree of inlined frames into a series of native code regions, one + * for each contiguous section of native code that belongs to a single + * ActiveFrame. (Note that some of these regions may be zero-length, for + * example if two ActiveFrames end at the same place.) + */ +typedef mjit::Compiler::ActiveFrame ActiveFrame; + +bool +Probes::JITWatcher::CollectNativeRegions(RegionVector ®ions, + JSRuntime *rt, + mjit::JITScript *jit, + mjit::JSActiveFrame *outerFrame, + mjit::JSActiveFrame **inlineFrames) +{ + regions.resize(jit->nInlineFrames * 2 + 2); + + mjit::JSActiveFrame **stack = + rt->array_new(jit->nInlineFrames+2); + if (!stack) + return false; + uint32_t depth = 0; + uint32_t ip = 0; + + stack[depth++] = NULL; + stack[depth++] = outerFrame; + regions[0].frame = outerFrame; + regions[0].script = outerFrame->script; + regions[0].pc = outerFrame->script->code; + regions[0].enter = true; + ip++; + + for (uint32_t i = 0; i <= jit->nInlineFrames; i++) { + mjit::JSActiveFrame *frame = (i < jit->nInlineFrames) ? inlineFrames[i] : outerFrame; + + // Not a down frame; pop the current frame, then pop until we reach + // this frame's parent, recording subframe ends as we go + while (stack[depth-1] != frame->parent) { + depth--; + JS_ASSERT(depth > 0); + // Pop up from regions[ip-1].frame to top of the stack: start a + // region in the destination frame and close off the source + // (origin) frame at the end of its script + mjit::JSActiveFrame *src = regions[ip-1].frame; + mjit::JSActiveFrame *dst = stack[depth-1]; + JS_ASSERT_IF(!dst, i == jit->nInlineFrames); + regions[ip].frame = dst; + regions[ip].script = dst ? dst->script : NULL; + regions[ip].pc = src->parentPC + 1; + regions[ip-1].endpc = src->script->code + src->script->length; + regions[ip].enter = false; + ip++; + } + + if (i < jit->nInlineFrames) { + // Push a frame (enter an inlined function). Start a region at the + // beginning of the new frame's script, and end the previous region + // at parentPC. + stack[depth++] = frame; + + regions[ip].frame = frame; + regions[ip].script = frame->script; + regions[ip].pc = frame->script->code; + regions[ip-1].endpc = frame->parentPC; + regions[ip].enter = true; + ip++; + } + } + + // Final region is always zero-length and not particularly useful + ip--; + regions.popBack(); + + mjit::JSActiveFrame *prev = NULL; + for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter) { + mjit::JSActiveFrame *frame = iter->frame; + if (iter->enter) { + // Pushing down a frame, so region starts at the beginning of the + // (destination) frame + iter->mainOffset = frame->mainCodeStart; + iter->stubOffset = frame->stubCodeStart; + } else { + // Popping up a level, so region starts at the end of the (source) frame + iter->mainOffset = prev->mainCodeEnd; + iter->stubOffset = prev->stubCodeEnd; + } + prev = frame; + } + + JS_ASSERT(ip == 2 * jit->nInlineFrames + 1); + rt->array_delete(stack); + + // All of the stub code comes immediately after the main code + for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter) + iter->stubOffset += outerFrame->mainCodeEnd; + + return true; +} + #ifdef JS_METHODJIT void Probes::registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr, - JSScript *script, JSFunction *fun, - js::mjit::Compiler_ActiveFrame **inlineFrames, + js::mjit::JSActiveFrame *outerFrame, + js::mjit::JSActiveFrame **inlineFrames, void *mainCodeAddress, size_t mainCodeSize, void *stubCodeAddress, size_t stubCodeSize) { for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p) - (*p)->registerMJITCode(cx, jscr, script, fun, + (*p)->registerMJITCode(cx, jscr, outerFrame, inlineFrames, mainCodeAddress, mainCodeSize, stubCodeAddress, stubCodeSize); diff --git a/js/src/jsprobes.h b/js/src/jsprobes.h index ec2f484034c..00f3e052c8f 100644 --- a/js/src/jsprobes.h +++ b/js/src/jsprobes.h @@ -52,7 +52,7 @@ namespace js { namespace mjit { struct NativeAddressInfo; -struct Compiler_ActiveFrame; +struct JSActiveFrame; } namespace Probes { @@ -230,12 +230,31 @@ enum JITReportGranularity { */ class JITWatcher { public: + struct NativeRegion { + mjit::JSActiveFrame *frame; + JSScript *script; + size_t inlinedOffset; + jsbytecode *pc; + jsbytecode *endpc; + uintptr_t mainOffset; + uintptr_t stubOffset; + bool enter; + }; + + typedef Vector RegionVector; + + static bool CollectNativeRegions(RegionVector ®ions, + JSRuntime *rt, + mjit::JITScript *jit, + mjit::JSActiveFrame *outerFrame, + mjit::JSActiveFrame **inlineFrames); + virtual JITReportGranularity granularityRequested() = 0; #ifdef JS_METHODJIT virtual void registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr, - JSScript *script, JSFunction *fun, - mjit::Compiler_ActiveFrame** inlineFrames, + mjit::JSActiveFrame *outerFrame, + mjit::JSActiveFrame **inlineFrames, void *mainCodeAddress, size_t mainCodeSize, void *stubCodeAddress, size_t stubCodeSize) = 0; @@ -282,8 +301,8 @@ JITGranularityRequested(); */ void registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr, - JSScript *script, JSFunction *fun, - mjit::Compiler_ActiveFrame** inlineFrames, + mjit::JSActiveFrame *outerFrame, + mjit::JSActiveFrame **inlineFrames, void *mainCodeAddress, size_t mainCodeSize, void *stubCodeAddress, size_t stubCodeSize); diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 63e04a6f30c..2e098dde216 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -486,8 +486,10 @@ void mjit::Compiler::popActiveFrame() { JS_ASSERT(a->parent); + a->mainCodeEnd = masm.size(); + a->stubCodeEnd = stubcc.size(); this->PC = a->parentPC; - this->a = a->parent; + this->a = (ActiveFrame *) a->parent; this->script = a->script; this->analysis = this->script->analysis(); @@ -551,9 +553,14 @@ mjit::Compiler::performCompilation(JITScript **jitp) #undef CHECK_STATUS +mjit::JSActiveFrame::JSActiveFrame() + : parent(NULL), parentPC(NULL), script(NULL), inlineIndex(UINT32_MAX) +{ +} + mjit::Compiler::ActiveFrame::ActiveFrame(JSContext *cx) - : parent(NULL), parentPC(NULL), script(NULL), jumpMap(NULL), - inlineIndex(UINT32_MAX), varTypes(NULL), needReturnValue(false), + : jumpMap(NULL), + varTypes(NULL), needReturnValue(false), syncReturnValue(false), returnValueDouble(false), returnSet(false), returnEntry(NULL), returnJumps(NULL), exitState(NULL) {} @@ -922,6 +929,9 @@ mjit::Compiler::finishThisUp(JITScript **jitp) return Compile_Abort; } + a->mainCodeEnd = masm.size(); + a->stubCodeEnd = stubcc.size(); + for (size_t i = 0; i < branchPatches.length(); i++) { Label label = labelOf(branchPatches[i].pc, branchPatches[i].inlineIndex); branchPatches[i].jump.linkTo(label, &masm); @@ -1385,8 +1395,9 @@ mjit::Compiler::finishThisUp(JITScript **jitp) JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size()); JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size()); - Probes::registerMJITCode(cx, jit, script, script->function() ? script->function() : NULL, - (mjit::Compiler_ActiveFrame**) inlineFrames.begin(), + Probes::registerMJITCode(cx, jit, + a, + (JSActiveFrame**) inlineFrames.begin(), result, masm.size(), result + masm.size(), stubcc.size()); diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 889235c2fa9..635815b8216 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -62,6 +62,27 @@ struct InvariantCodePatch { InvariantCodePatch() : hasPatch(false) {} }; +struct JSActiveFrame { + JSActiveFrame *parent; + jsbytecode *parentPC; + JSScript *script; + + /* + * Index into inlineFrames or OUTER_FRAME, matches this frame's index in + * the cross script SSA. + */ + uint32_t inlineIndex; + + /* JIT code generation tracking state */ + size_t mainCodeStart; + size_t stubCodeStart; + size_t mainCodeEnd; + size_t stubCodeEnd; + size_t inlinePCOffset; + + JSActiveFrame(); +}; + class Compiler : public BaseCompiler { friend class StubCompiler; @@ -355,26 +376,12 @@ class Compiler : public BaseCompiler */ public: - struct ActiveFrame { - ActiveFrame *parent; - jsbytecode *parentPC; - JSScript *script; + struct ActiveFrame : public JSActiveFrame { Label *jumpMap; - /* - * Index into inlineFrames or OUTER_FRAME, matches this frame's index - * in the cross script SSA. - */ - uint32_t inlineIndex; - /* Current types for non-escaping vars in the script. */ VarType *varTypes; - /* JIT code generation tracking state */ - size_t mainCodeStart; - size_t stubCodeStart; - size_t inlinePCOffset; - /* State for managing return from inlined frames. */ bool needReturnValue; /* Return value will be used. */ bool syncReturnValue; /* Return value should be fully synced. */ @@ -470,7 +477,7 @@ private: return PC; ActiveFrame *scan = a; while (scan && scan->parent != outer) - scan = scan->parent; + scan = static_cast(scan->parent); return scan->parentPC; } @@ -491,7 +498,7 @@ private: while (na->parent) { if (na->exitState) return true; - na = na->parent; + na = static_cast(na->parent); } return false; } From 8164216a1173dc46b4d50806072d8e3f5e1e0855 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Mon, 21 Nov 2011 16:17:49 -0800 Subject: [PATCH 02/66] Bug 710055 - Merge SetVMFrameRegs into PushActiveVMFrame. r=bhackett --HG-- extra : rebase_source : 7c4f8dde05533a082a7139077d6c983b610925fc --- js/src/methodjit/MethodJIT.cpp | 26 +++++--------------------- js/src/methodjit/TrampolineMasmX64.asm | 3 --- js/src/methodjit/TrampolineMingwX64.s | 3 --- js/src/methodjit/TrampolineSUNWX64.s | 2 -- js/src/methodjit/TrampolineSUNWX86.s | 1 - js/src/methodjit/TrampolineSparc.s | 2 -- 6 files changed, 5 insertions(+), 32 deletions(-) diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 0962d6fbb6f..d66b6d5bc7d 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -140,27 +140,23 @@ static const size_t STUB_CALLS_FOR_OP_COUNT = 255; static uint32_t StubCallsForOp[STUB_CALLS_FOR_OP_COUNT]; #endif +// Called from JaegerTrampoline only extern "C" void JS_FASTCALL PushActiveVMFrame(VMFrame &f) { + f.oldregs = &f.cx->stack.regs(); + f.cx->stack.repointRegs(&f.regs); f.entryfp->script()->compartment()->jaegerCompartment()->pushActiveFrame(&f); f.entryfp->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn)); f.regs.clearInlined(); } +// Called from JaegerTrampolineReturn, JaegerThrowpoline, JaegerInterpoline extern "C" void JS_FASTCALL PopActiveVMFrame(VMFrame &f) { f.entryfp->script()->compartment()->jaegerCompartment()->popActiveFrame(); -} - -extern "C" void JS_FASTCALL -SetVMFrameRegs(VMFrame &f) -{ - f.oldregs = &f.cx->stack.regs(); - - /* Restored on exit from EnterMethodJIT. */ - f.cx->stack.repointRegs(&f.regs); + f.cx->stack.repointRegs(f.oldregs); } #if defined(__APPLE__) || (defined(XP_WIN) && !defined(JS_CPU_X64)) || defined(XP_OS2) @@ -328,8 +324,6 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Set cx->regs and set the active frame. Save rdx and align frame in one. */ "pushq %rdx" "\n" "movq %rsp, %rdi" "\n" - "call " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n" - "movq %rsp, %rdi" "\n" "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n" /* Jump into the JIT'd code. */ @@ -514,8 +508,6 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Jump into the JIT'd code. */ "movl %esp, %ecx" "\n" - "call " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n" - "movl %esp, %ecx" "\n" "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n" "movl 28(%esp), %ebp" "\n" /* load fp for JIT code */ @@ -730,8 +722,6 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Preserve 'fp' (r1) in r10 (JSFrameReg). */ " mov r10, r1" "\n" -" mov r0, sp" "\n" -" blx " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n" " mov r0, sp" "\n" " blx " SYMBOL_STRING_VMFRAME(PushActiveVMFrame)"\n" @@ -877,8 +867,6 @@ extern "C" { /* Jump into into the JIT'd code. */ mov ecx, esp; - call SetVMFrameRegs; - mov ecx, esp; call PushActiveVMFrame; mov ebp, [esp + 28]; /* load fp for JIT code */ @@ -1055,7 +1043,6 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi #endif JS_ASSERT(cx->fp() == fp); - FrameRegs &oldRegs = cx->regs(); JSBool ok; { @@ -1069,9 +1056,6 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms()); #endif - /* Undo repointRegs in SetVMFrameRegs. */ - cx->stack.repointRegs(&oldRegs); - JaegerStatus status = cx->compartment->jaegerCompartment()->lastUnfinished(); if (status) { if (partial) { diff --git a/js/src/methodjit/TrampolineMasmX64.asm b/js/src/methodjit/TrampolineMasmX64.asm index 0320b9cc25b..57fc6b97529 100644 --- a/js/src/methodjit/TrampolineMasmX64.asm +++ b/js/src/methodjit/TrampolineMasmX64.asm @@ -37,7 +37,6 @@ extern js_InternalThrow:PROC -extern SetVMFrameRegs:PROC extern PushActiveVMFrame:PROC extern PopActiveVMFrame:PROC extern js_InternalInterpret:PROC @@ -95,8 +94,6 @@ JaegerTrampoline PROC FRAME push r8 mov rcx, rsp sub rsp, 20h - call SetVMFrameRegs - lea rcx, [rsp+20h] call PushActiveVMFrame add rsp, 20h diff --git a/js/src/methodjit/TrampolineMingwX64.s b/js/src/methodjit/TrampolineMingwX64.s index ea956403f6d..197d0b1c366 100644 --- a/js/src/methodjit/TrampolineMingwX64.s +++ b/js/src/methodjit/TrampolineMingwX64.s @@ -37,7 +37,6 @@ .extern js_InternalThrow -.extern SetVMFrameRegs .extern PushActiveVMFrame .extern PopActiveVMFrame .extern js_InternalInterpret @@ -101,8 +100,6 @@ JaegerTrampoline: push r8 mov rcx, rsp sub rsp, 0x20 - call SetVMFrameRegs - lea rcx, [rsp+0x20] call PushActiveVMFrame add rsp, 0x20 diff --git a/js/src/methodjit/TrampolineSUNWX64.s b/js/src/methodjit/TrampolineSUNWX64.s index e03e462a984..3787e396bc5 100644 --- a/js/src/methodjit/TrampolineSUNWX64.s +++ b/js/src/methodjit/TrampolineSUNWX64.s @@ -79,8 +79,6 @@ JaegerTrampoline: /* Set cx->regs and set the active frame. Save rdx and align frame in one. */ pushq %rdx movq %rsp, %rdi - call SetVMFrameRegs - movq %rsp, %rdi call PushActiveVMFrame /* Jump into into the JIT'd code. */ diff --git a/js/src/methodjit/TrampolineSUNWX86.s b/js/src/methodjit/TrampolineSUNWX86.s index 4c54f13459f..146525b1250 100644 --- a/js/src/methodjit/TrampolineSUNWX86.s +++ b/js/src/methodjit/TrampolineSUNWX86.s @@ -66,7 +66,6 @@ JaegerTrampoline: /* Jump into the JIT'd code. */ /* No fastcall for sunstudio. */ pushl %esp - call SetVMFrameRegs call PushActiveVMFrame popl %edx diff --git a/js/src/methodjit/TrampolineSparc.s b/js/src/methodjit/TrampolineSparc.s index 1b15369ab12..fb79b5a2abf 100644 --- a/js/src/methodjit/TrampolineSparc.s +++ b/js/src/methodjit/TrampolineSparc.s @@ -51,8 +51,6 @@ JaegerTrampoline: st %i1, [%fp - 24] ! entryFp st %i1, [%fp - 20] ! entryncode st %g0, [%fp - 16] ! stubRejoin - call SetVMFrameRegs - mov %sp, %o0 call PushActiveVMFrame mov %sp, %o0 ld [%fp - 36], %l0 ! fp From 6f8ce74f52f29cb99c7d47bb52154e4cab988786 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 28 Dec 2011 23:56:55 -0800 Subject: [PATCH 03/66] Bug 709885 - Simplify read barriers for shapes and types (r=bhackett) --- js/src/gc/Barrier.h | 4 ++++ js/src/jsinfer.cpp | 8 +------- js/src/jsinfer.h | 6 +++--- js/src/jsinferinlines.h | 5 ++--- js/src/jsscope.cpp | 20 ++++---------------- js/src/jsscope.h | 4 ++-- js/src/jsscopeinlines.h | 10 ++++------ 7 files changed, 20 insertions(+), 37 deletions(-) diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index ef6e663f55f..1503bcf9306 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -426,6 +426,7 @@ class ReadBarriered T *value; public: + ReadBarriered() : value(NULL) {} ReadBarriered(T *value) : value(value) {} T *get() const { @@ -437,6 +438,9 @@ class ReadBarriered operator T*() const { return get(); } + T &operator*() const { return *get(); } + T *operator->() const { return get(); } + T *unsafeGet() { return value; } void set(T *v) { value = v; } diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 083bc6530ce..4e3e52b3b31 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2617,7 +2617,7 @@ struct types::ObjectTableKey struct types::ObjectTableEntry { - TypeObject *object; + ReadBarriered object; Type *types; }; @@ -5837,9 +5837,6 @@ JSObject::getNewType(JSContext *cx, JSFunction *fun) if (type->newScript && type->newScript->fun != fun) type->clearNewScript(cx); - if (cx->compartment->needsBarrier()) - TypeObject::readBarrier(type); - return type; } @@ -5905,9 +5902,6 @@ JSCompartment::getLazyType(JSContext *cx, JSObject *proto) TypeObject *type = *p; JS_ASSERT(type->lazy()); - if (cx->compartment->needsBarrier()) - TypeObject::readBarrier(type); - return type; } diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index d03e04e4625..bc685b84a43 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -898,7 +898,7 @@ struct TypeObjectEntry static inline HashNumber hash(JSObject *base); static inline bool match(TypeObject *key, JSObject *lookup); }; -typedef HashSet TypeObjectSet; +typedef HashSet, TypeObjectEntry, SystemAllocPolicy> TypeObjectSet; /* * Call to mark a script's arguments as having been created, recompile any @@ -1120,14 +1120,14 @@ class TypeScript }; struct ArrayTableKey; -typedef HashMap ArrayTypeTable; +typedef HashMap,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable; struct ObjectTableKey; struct ObjectTableEntry; typedef HashMap ObjectTypeTable; struct AllocationSiteKey; -typedef HashMap AllocationSiteTable; +typedef HashMap,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable; /* Type information for a compartment. */ struct TypeCompartment diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index e76bfc31895..16574dacdbc 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -1279,9 +1279,8 @@ TypeObject::readBarrier(TypeObject *type) { #ifdef JSGC_INCREMENTAL JSCompartment *comp = type->compartment(); - JS_ASSERT(comp->needsBarrier()); - - MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "read barrier"); + if (comp->needsBarrier()) + MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "read barrier"); #endif } diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index c5d32ae07d6..77bc003a26d 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -1275,14 +1275,8 @@ BaseShape::getUnowned(JSContext *cx, const BaseShape &base) BaseShapeSet::AddPtr p = table.lookupForAdd(&base); - if (p) { - UnownedBaseShape *base = *p; - - if (cx->compartment->needsBarrier()) - BaseShape::readBarrier(base); - - return base; - } + if (p) + return *p; BaseShape *nbase_ = js_NewGCBaseShape(cx); if (!nbase_) @@ -1382,14 +1376,8 @@ EmptyShape::getInitialShape(JSContext *cx, Class *clasp, JSObject *proto, JSObje InitialShapeSet::AddPtr p = table.lookupForAdd(lookup); - if (p) { - Shape *shape = p->shape; - - if (cx->compartment->needsBarrier()) - Shape::readBarrier(shape); - - return shape; - } + if (p) + return p->shape; BaseShape base(clasp, parent, objectFlags); UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); diff --git a/js/src/jsscope.h b/js/src/jsscope.h index 14a53cd723a..63239c8c9ad 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -493,7 +493,7 @@ struct BaseShapeEntry static inline HashNumber hash(const BaseShape *base); static inline bool match(UnownedBaseShape *key, const BaseShape *lookup); }; -typedef HashSet BaseShapeSet; +typedef HashSet, BaseShapeEntry, SystemAllocPolicy> BaseShapeSet; struct Shape : public js::gc::Cell { @@ -975,7 +975,7 @@ struct InitialShapeEntry * certain classes (e.g. String, RegExp) which may add certain baked-in * properties. */ - js::Shape *shape; + ReadBarriered shape; /* * Matching prototype for the entry. The shape of an object determines its diff --git a/js/src/jsscopeinlines.h b/js/src/jsscopeinlines.h index c6ffc2136d0..fe8e3a6b774 100644 --- a/js/src/jsscopeinlines.h +++ b/js/src/jsscopeinlines.h @@ -362,9 +362,8 @@ Shape::readBarrier(const Shape *shape) { #ifdef JSGC_INCREMENTAL JSCompartment *comp = shape->compartment(); - JS_ASSERT(comp->needsBarrier()); - - MarkShapeUnbarriered(comp->barrierTracer(), shape, "read barrier"); + if (comp->needsBarrier()) + MarkShapeUnbarriered(comp->barrierTracer(), shape, "read barrier"); #endif } @@ -391,9 +390,8 @@ BaseShape::readBarrier(BaseShape *base) { #ifdef JSGC_INCREMENTAL JSCompartment *comp = base->compartment(); - JS_ASSERT(comp->needsBarrier()); - - MarkBaseShapeUnbarriered(comp->barrierTracer(), base, "read barrier"); + if (comp->needsBarrier()) + MarkBaseShapeUnbarriered(comp->barrierTracer(), base, "read barrier"); #endif } From 20da5ba557964d45aa9a1117dfff989478f26780 Mon Sep 17 00:00:00 2001 From: Bernd Date: Thu, 29 Dec 2011 09:17:55 +0100 Subject: [PATCH 04/66] bug 710098 - use effective rowspan r=mats --- layout/tables/crashtests/710098-1.html | 7 +++++++ layout/tables/crashtests/crashtests.list | 1 + layout/tables/nsCellMap.cpp | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 layout/tables/crashtests/710098-1.html diff --git a/layout/tables/crashtests/710098-1.html b/layout/tables/crashtests/710098-1.html new file mode 100644 index 00000000000..4886f8388a3 --- /dev/null +++ b/layout/tables/crashtests/710098-1.html @@ -0,0 +1,7 @@ + + + + +
+ + diff --git a/layout/tables/crashtests/crashtests.list b/layout/tables/crashtests/crashtests.list index 3fe5f837409..e713b832e10 100644 --- a/layout/tables/crashtests/crashtests.list +++ b/layout/tables/crashtests/crashtests.list @@ -123,3 +123,4 @@ load 695430-1.html load 707622-1.html load 705996-1.html load 705996-2.html +load 710098-1.html diff --git a/layout/tables/nsCellMap.cpp b/layout/tables/nsCellMap.cpp index 9362fb21957..2fe9ce552d9 100644 --- a/layout/tables/nsCellMap.cpp +++ b/layout/tables/nsCellMap.cpp @@ -2182,7 +2182,7 @@ void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap, // get the rowspan and colspan from the cell map since the content may have changed bool zeroColSpan; PRUint32 numCols = aMap.GetColCount(); - PRInt32 rowSpan = GetRowSpan(aRowIndex, aColIndex, false); + PRInt32 rowSpan = GetRowSpan(aRowIndex, aColIndex, true); PRUint32 colSpan = GetEffectiveColSpan(aMap, aRowIndex, aColIndex, zeroColSpan); PRUint32 endRowIndex = aRowIndex + rowSpan - 1; PRUint32 endColIndex = aColIndex + colSpan - 1; From efe0797ce30f22b051978013014b47521c6f027e Mon Sep 17 00:00:00 2001 From: Steffen Wilberg Date: Thu, 29 Dec 2011 10:42:57 +0100 Subject: [PATCH 05/66] Bug 707570: Use Services.prefs instead of gPrefService in browser_bug435325.js. r=bz --- docshell/test/browser/browser_bug435325.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docshell/test/browser/browser_bug435325.js b/docshell/test/browser/browser_bug435325.js index e01246886d2..36326468697 100644 --- a/docshell/test/browser/browser_bug435325.js +++ b/docshell/test/browser/browser_bug435325.js @@ -12,8 +12,8 @@ function test() { // Go offline and disable the cache, then try to load the test URL. Services.io.offline = true; - gPrefService.setBoolPref("browser.cache.disk.enable", false); - gPrefService.setBoolPref("browser.cache.memory.enable", false); + Services.prefs.setBoolPref("browser.cache.disk.enable", false); + Services.prefs.setBoolPref("browser.cache.memory.enable", false); content.location = "http://example.com/"; } @@ -42,8 +42,8 @@ function checkPage() { } registerCleanupFunction(function() { - gPrefService.setBoolPref("browser.cache.disk.enable", true); - gPrefService.setBoolPref("browser.cache.memory.enable", true); + Services.prefs.setBoolPref("browser.cache.disk.enable", true); + Services.prefs.setBoolPref("browser.cache.memory.enable", true); Services.io.offline = false; gBrowser.removeCurrentTab(); }); From 5a6bbdb6ac4529d2b9c2b07c856b32a301e29235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Thu, 29 Dec 2011 12:33:39 +0100 Subject: [PATCH 06/66] Bug 713829 - Remove unused brandFullName string in nsGNOMEShellService::SetDefaultBrowser. r=karlt --- browser/components/shell/src/nsGNOMEShellService.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/browser/components/shell/src/nsGNOMEShellService.cpp b/browser/components/shell/src/nsGNOMEShellService.cpp index 768b76a2299..dd0c2b62560 100644 --- a/browser/components/shell/src/nsGNOMEShellService.cpp +++ b/browser/components/shell/src/nsGNOMEShellService.cpp @@ -317,11 +317,9 @@ nsGNOMEShellService::SetDefaultBrowser(bool aClaimAllTypes, rv = bundleService->CreateBundle(BRAND_PROPERTIES, getter_AddRefs(brandBundle)); NS_ENSURE_SUCCESS(rv, rv); - nsString brandShortName, brandFullName; + nsString brandShortName; brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(), getter_Copies(brandShortName)); - brandBundle->GetStringFromName(NS_LITERAL_STRING("brandFullName").get(), - getter_Copies(brandFullName)); // use brandShortName as the application id. NS_ConvertUTF16toUTF8 id(brandShortName); From 5e6915e9608063ce8251e651dd0724311acc7f82 Mon Sep 17 00:00:00 2001 From: Cameron Kaiser Date: Thu, 29 Dec 2011 12:36:22 +0100 Subject: [PATCH 07/66] Bug 713463 - Support JM+TI for architectures without native square root. r=dmandelin --- js/src/methodjit/FastBuiltins.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/methodjit/FastBuiltins.cpp b/js/src/methodjit/FastBuiltins.cpp index 60339e35692..c794b2b6c2c 100644 --- a/js/src/methodjit/FastBuiltins.cpp +++ b/js/src/methodjit/FastBuiltins.cpp @@ -909,6 +909,7 @@ mjit::Compiler::inlineNativeFunction(uint32_t argc, bool callingNew) return compileRound(arg, Round); } if (native == js_math_sqrt && type == JSVAL_TYPE_DOUBLE && + masm.supportsFloatingPointSqrt() && (argType == JSVAL_TYPE_INT32 || argType == JSVAL_TYPE_DOUBLE)) { return compileMathSqrt(arg); } @@ -949,6 +950,7 @@ mjit::Compiler::inlineNativeFunction(uint32_t argc, bool callingNew) JSValueType arg2Type = arg2->isTypeKnown() ? arg2->getKnownType() : JSVAL_TYPE_UNKNOWN; if (native == js_math_pow && type == JSVAL_TYPE_DOUBLE && + masm.supportsFloatingPointSqrt() && (arg1Type == JSVAL_TYPE_DOUBLE || arg1Type == JSVAL_TYPE_INT32) && arg2Type == JSVAL_TYPE_DOUBLE && arg2->isConstant()) { From c97fe6b302109b0dff79b438665f2089f3086fa4 Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Thu, 29 Dec 2011 11:59:02 +0000 Subject: [PATCH 08/66] Bug 713413 - Fix crash when dynamically adding foreignObject as a child of non-displayed element. r=dholbert --- layout/svg/base/src/nsSVGForeignObjectFrame.cpp | 7 ++++++- layout/svg/crashtests/713413-1.svg | 12 ++++++++++++ layout/svg/crashtests/crashtests.list | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 layout/svg/crashtests/713413-1.svg diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp index f4f8973c5c0..ab488d4d090 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -140,7 +140,12 @@ nsSVGForeignObjectFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) { nsSVGForeignObjectFrameBase::DidSetStyleContext(aOldStyleContext); - UpdateGraphic(); + // No need to invalidate before first reflow - that will happen elsewhere. + // Moreover we haven't been initialised properly yet so we may not have the + // right state bits. + if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { + UpdateGraphic(); + } } NS_IMETHODIMP diff --git a/layout/svg/crashtests/713413-1.svg b/layout/svg/crashtests/713413-1.svg new file mode 100644 index 00000000000..7131202335e --- /dev/null +++ b/layout/svg/crashtests/713413-1.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index 7b7c6f76dc7..1634d742332 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -121,3 +121,4 @@ load 692203-2.svg load 693424-1.svg load 709920-1.svg load 709920-2.svg +load 713413-1.svg From 33b08976008af077203751dc771e68fddfb0de6f Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Thu, 29 Dec 2011 14:21:00 +0100 Subject: [PATCH 09/66] Bug 700031 - "ASSERTION: Can only call this on frames that have been reflowed" with too-deep frame tree. r=roc --- layout/generic/nsTextFrameThebes.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 4a8744b7c72..7ee52edb061 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -2337,8 +2337,10 @@ nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag, { NS_ASSERTION(mTextRun, "Need textrun here"); // This should not be used during reflow. We need our TEXT_REFLOW_FLAGS - // to be set correctly. - NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW), + // to be set correctly. If our parent wasn't reflowed due to the frame + // tree being too deep then the return value doesn't matter. + NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW) || + (GetParent()->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE), "Can only call this on frames that have been reflowed"); NS_ASSERTION(!(GetStateBits() & NS_FRAME_IN_REFLOW), "Can only call this on frames that are not being reflowed"); From f5b31f9bbe9ce3b1c86c192e57c80472b4cb17be Mon Sep 17 00:00:00 2001 From: Jesse Ruderman Date: Thu, 29 Dec 2011 14:21:00 +0100 Subject: [PATCH 10/66] Bug 700031 - Crash test. --- layout/generic/crashtests/700031.xhtml | 9 +++++++++ layout/generic/crashtests/crashtests.list | 1 + 2 files changed, 10 insertions(+) create mode 100644 layout/generic/crashtests/700031.xhtml diff --git a/layout/generic/crashtests/700031.xhtml b/layout/generic/crashtests/700031.xhtml new file mode 100644 index 00000000000..70f924279ef --- /dev/null +++ b/layout/generic/crashtests/700031.xhtml @@ -0,0 +1,9 @@ + + +
+ +abcdef + +
+ + diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index cd3cd607931..f0343c131df 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -382,3 +382,4 @@ load 683702-1.xhtml load 688996-1.html load 688996-2.html load 683712.html +load 700031.xhtml From 24a7a6fb9942d78027f91676f0dde5cd463c115e Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Thu, 29 Dec 2011 14:21:00 +0100 Subject: [PATCH 11/66] Bug 713610 - For a marker that is inactive we should guess false. r=roc --- layout/generic/TextOverflow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/layout/generic/TextOverflow.cpp b/layout/generic/TextOverflow.cpp index 599923a3324..cb7bdcc7e1e 100644 --- a/layout/generic/TextOverflow.cpp +++ b/layout/generic/TextOverflow.cpp @@ -531,8 +531,8 @@ TextOverflow::ExamineLineFrames(nsLineBox* aLine, guessRight == (mRight.mActive && mRight.IsNeeded())) { break; } else { - guessLeft = mLeft.IsNeeded(); - guessRight = mRight.IsNeeded(); + guessLeft = mLeft.mActive && mLeft.IsNeeded(); + guessRight = mRight.mActive && mRight.IsNeeded(); mLeft.Reset(); mRight.Reset(); aFramesToHide->Clear(); From af8b139d44014ffc67b12b68411f1a4bf5849099 Mon Sep 17 00:00:00 2001 From: Jesse Ruderman Date: Thu, 29 Dec 2011 14:21:00 +0100 Subject: [PATCH 12/66] Bug 713610 - Crash test. --- layout/generic/crashtests/crashtests.list | 1 + layout/generic/crashtests/text-overflow-bug713610.html | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 layout/generic/crashtests/text-overflow-bug713610.html diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index f0343c131df..3e0b16c5817 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -382,4 +382,5 @@ load 683702-1.xhtml load 688996-1.html load 688996-2.html load 683712.html +load text-overflow-bug713610.html load 700031.xhtml diff --git a/layout/generic/crashtests/text-overflow-bug713610.html b/layout/generic/crashtests/text-overflow-bug713610.html new file mode 100644 index 00000000000..145295364a1 --- /dev/null +++ b/layout/generic/crashtests/text-overflow-bug713610.html @@ -0,0 +1,6 @@ + + + +
+ + From 65ca491a7af1908e70cc3d69372137338d2d0284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20=C3=81vila=20de=20Esp=C3=ADndola?= Date: Thu, 29 Dec 2011 10:03:00 -0500 Subject: [PATCH 13/66] Bug 713297 - send quit-application toolkit/mozapps/downloads/tests/. r=mak. --- toolkit/mozapps/downloads/tests/unit/head_downloads.js | 5 +++++ toolkit/mozapps/downloads/tests/unit/xpcshell.ini | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 toolkit/mozapps/downloads/tests/unit/head_downloads.js diff --git a/toolkit/mozapps/downloads/tests/unit/head_downloads.js b/toolkit/mozapps/downloads/tests/unit/head_downloads.js new file mode 100644 index 00000000000..4f199e5cf3b --- /dev/null +++ b/toolkit/mozapps/downloads/tests/unit/head_downloads.js @@ -0,0 +1,5 @@ +Components.utils.import("resource://gre/modules/Services.jsm"); + +do_register_cleanup(function() { + Services.obs.notifyObservers(null, "quit-application", null); +}); diff --git a/toolkit/mozapps/downloads/tests/unit/xpcshell.ini b/toolkit/mozapps/downloads/tests/unit/xpcshell.ini index 7c79798ffa8..558d009bbf0 100644 --- a/toolkit/mozapps/downloads/tests/unit/xpcshell.ini +++ b/toolkit/mozapps/downloads/tests/unit/xpcshell.ini @@ -1,5 +1,5 @@ [DEFAULT] -head = +head = head_downloads.js tail = [test_DownloadLastDir.js] From 33cf6df88d75b7af9b397d03a46925d8ed5e4c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20=C3=81vila=20de=20Esp=C3=ADndola?= Date: Thu, 29 Dec 2011 10:08:03 -0500 Subject: [PATCH 14/66] Bug 713293 - Send quit-application in toolkit/components/downloads/test/schema_migration/head_migration.js. r=mak. --- .../downloads/test/schema_migration/head_migration.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/toolkit/components/downloads/test/schema_migration/head_migration.js b/toolkit/components/downloads/test/schema_migration/head_migration.js index 7961beaa6e7..a71fe32cd55 100644 --- a/toolkit/components/downloads/test/schema_migration/head_migration.js +++ b/toolkit/components/downloads/test/schema_migration/head_migration.js @@ -34,12 +34,18 @@ * * ***** END LICENSE BLOCK ***** */ +Components.utils.import("resource://gre/modules/Services.jsm"); + const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; do_get_profile(); +do_register_cleanup(function() { + Services.obs.notifyObservers(null, "quit-application", null); +}); + var dirSvc = Cc["@mozilla.org/file/directory_service;1"]. getService(Ci.nsIProperties); From ca349d913d48d85a1424146ab9490afad15c10a2 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Thu, 29 Dec 2011 11:59:29 -0500 Subject: [PATCH 15/66] Bug 714078: Don't do anything risking a deadlock in IAudioSessionEvents callbacks. r=ehsan --- widget/src/windows/AudioSession.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/widget/src/windows/AudioSession.cpp b/widget/src/windows/AudioSession.cpp index 83116aad05b..a782fe3c9dc 100644 --- a/widget/src/windows/AudioSession.cpp +++ b/widget/src/windows/AudioSession.cpp @@ -51,6 +51,7 @@ #include "nsAutoPtr.h" #include "nsServiceManagerUtils.h" #include "nsString.h" +#include "nsThreadUtils.h" #include "nsXULAppAPI.h" #include @@ -84,6 +85,9 @@ public: STDMETHODIMP OnGroupingParamChanged(LPCGUID aGroupingParam, LPCGUID aContext); STDMETHODIMP OnIconPathChanged(LPCWSTR aIconPath, LPCGUID aContext); STDMETHODIMP OnSessionDisconnected(AudioSessionDisconnectReason aReason); +private: + nsresult OnSessionDisconnectedInternal(); +public: STDMETHODIMP OnSimpleVolumeChanged(float aVolume, BOOL aMute, LPCGUID aContext); @@ -431,14 +435,26 @@ AudioSession::OnIconPathChanged(LPCWSTR aIconPath, STDMETHODIMP AudioSession::OnSessionDisconnected(AudioSessionDisconnectReason aReason) +{ + // Run our code asynchronously. Per MSDN we can't do anything interesting + // in this callback. + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &AudioSession::OnSessionDisconnectedInternal); + NS_DispatchToMainThread(runnable); + return S_OK; +} + +nsresult +AudioSession::OnSessionDisconnectedInternal() { if (!mAudioSessionControl) - return S_OK; + return NS_OK; mAudioSessionControl->UnregisterAudioSessionNotification(this); mAudioSessionControl = nsnull; - Start(); // If it fails there's not much we can do - return S_OK; + + Start(); // If it fails there's not much we can do. + return NS_OK; } STDMETHODIMP From d527057e5ecae7fbd3ffc13083b1ba2b7ecac10c Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Thu, 29 Dec 2011 15:45:51 -0500 Subject: [PATCH 16/66] Back out Bug 711895 for breaking l10n builds. --- configure.in | 81 +++++------------------ content/xslt/src/base/txExpandedNameMap.h | 6 +- gfx/ycbcr/QuellGccWarnings.patch | 40 ----------- gfx/ycbcr/README | 2 - gfx/ycbcr/update.sh | 1 - gfx/ycbcr/yuv_convert.cpp | 1 - gfx/ycbcr/yuv_row.h | 2 +- js/src/configure.in | 79 ++++------------------ js/src/jsarray.cpp | 15 +---- js/src/jsgc.cpp | 4 +- js/src/jstypedarray.cpp | 2 + layout/generic/nsHTMLReflowMetrics.h | 4 +- 12 files changed, 41 insertions(+), 196 deletions(-) delete mode 100644 gfx/ycbcr/QuellGccWarnings.patch diff --git a/configure.in b/configure.in index 8fe2b1334c0..0a18e0aed96 100644 --- a/configure.in +++ b/configure.in @@ -1864,20 +1864,15 @@ if test "$GNU_CC"; then _MOZ_RTTI_FLAGS_ON=-frtti _MOZ_RTTI_FLAGS_OFF=-fno-rtti - # Turn on GNU-specific warnings: - # -Wall - turn on a lot of warnings - # -pedantic - this is turned on below - # -Wpointer-arith - enabled with -pedantic, but good to have even if not - # -Werror=declaration-after-statement - MSVC doesn't like these - # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives - # - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -Wpointer-arith -Wdeclaration-after-statement -Wempty-body" - - # Turn off the following warnings that -Wall/-pedantic turn on: - # -Woverlength-strings - we exceed the minimum maximum length all the time - # - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wno-overlength-strings" - + # Turn on GNU specific features + # -Wall - turn on all warnings + # -pedantic - make compiler warn about non-ANSI stuff, and + # be a little bit stricter + # -Wdeclaration-after-statement - MSVC doesn't like these + # Warnings slamm took out for now (these were giving more noise than help): + # -Wbad-function-cast - warns when casting a function to a new return type + # -Wshadow - removed because it generates more noise than help --pete + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -W -Wno-unused -Wpointer-arith -Wdeclaration-after-statement" if test -z "$INTEL_CC" -a -z "$CLANG_CC"; then # Don't use -Wcast-align with ICC or clang case "$CPU_ARCH" in @@ -1893,26 +1888,12 @@ if test "$GNU_CC"; then dnl Turn pedantic on but disable the warnings for long long _PEDANTIC=1 + if test -z "$INTEL_CC"; then + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -W" + fi + _DEFINES_CFLAGS='-include $(DEPTH)/mozilla-config.h -DMOZILLA_CLIENT' _USE_CPP_INCLUDE_FLAG=1 - - AC_CACHE_CHECK(whether the compiler supports -Wtype-limits, - ac_cc_has_wtype_limits, - [ - AC_LANG_SAVE - AC_LANG_C - _SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -Wtype-limits" - AC_TRY_COMPILE([], - [return(0);], - ac_cc_has_wtype_limits="yes", - ac_cc_has_wtype_limits="no") - CFLAGS="$_SAVE_CFLAGS" - AC_LANG_RESTORE - ]) - if test "$ac_cc_has_wtype_limits" = "yes"; then - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wtype-limits" - fi elif test "$SOLARIS_SUNPRO_CC"; then DSO_CFLAGS='' if test "$CPU_ARCH" = "sparc"; then @@ -1940,22 +1921,8 @@ fi if test "$GNU_CXX"; then # FIXME: Let us build with strict aliasing. bug 414641. CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-strict-aliasing" - - # Turn on GNU-specific warnings: - # -Wall - turn on a lot of warnings - # -pedantic - this is turned on below - # -Wpointer-arith - enabled with -pedantic, but good to have even if not - # -Woverloaded-virtual - ??? - # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives - # - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wempty-body" - - # Turn off the following warnings that -Wall/-pedantic turn on: - # -Woverlength-strings - we exceed the minimum maximum length all the time - # -Wctor-dtor-privacy - ??? - # - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-overlength-strings -Wno-ctor-dtor-privacy" - + # Turn on GNU specific features + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor" if test -z "$INTEL_CXX" -a -z "$CLANG_CXX"; then # Don't use -Wcast-align with ICC or clang case "$CPU_ARCH" in @@ -2056,24 +2023,6 @@ if test "$GNU_CXX"; then _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type" fi - AC_CACHE_CHECK(whether the compiler supports -Wtype-limits, - ac_has_wtype_limits, - [ - AC_LANG_SAVE - AC_LANG_CPLUSPLUS - _SAVE_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS -Wtype-limits" - AC_TRY_COMPILE([], - [return(0);], - ac_has_wtype_limits="yes", - ac_has_wtype_limits="no") - CXXFLAGS="$_SAVE_CXXFLAGS" - AC_LANG_RESTORE - ]) - if test "$ac_has_wtype_limits" = "yes"; then - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wtype-limits" - fi - else _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -D_MOZILLA_CONFIG_H_ $(ACDEFINES)' fi diff --git a/content/xslt/src/base/txExpandedNameMap.h b/content/xslt/src/base/txExpandedNameMap.h index 03d784aa0d9..fa1d5cfd46a 100644 --- a/content/xslt/src/base/txExpandedNameMap.h +++ b/content/xslt/src/base/txExpandedNameMap.h @@ -102,7 +102,8 @@ protected: const txExpandedName key() { - NS_ASSERTION(mCurrentPos < mMap.mItems.Length(), + NS_ASSERTION(mCurrentPos >= 0 && + mCurrentPos < mMap.mItems.Length(), "invalid position in txExpandedNameMap::iterator"); return txExpandedName(mMap.mItems[mCurrentPos].mNamespaceID, mMap.mItems[mCurrentPos].mLocalName); @@ -111,7 +112,8 @@ protected: protected: void* itemValue() { - NS_ASSERTION(mCurrentPos < mMap.mItems.Length(), + NS_ASSERTION(mCurrentPos >= 0 && + mCurrentPos < mMap.mItems.Length(), "invalid position in txExpandedNameMap::iterator"); return mMap.mItems[mCurrentPos].mValue; } diff --git a/gfx/ycbcr/QuellGccWarnings.patch b/gfx/ycbcr/QuellGccWarnings.patch deleted file mode 100644 index 82482205df8..00000000000 --- a/gfx/ycbcr/QuellGccWarnings.patch +++ /dev/null @@ -1,40 +0,0 @@ -diff --git a/gfx/ycbcr/yuv_convert.cpp b/gfx/ycbcr/yuv_convert.cpp ---- a/gfx/ycbcr/yuv_convert.cpp -+++ b/gfx/ycbcr/yuv_convert.cpp -@@ -337,16 +337,17 @@ NS_GFX_(void) ScaleYCbCrToRGB32(const ui - source_dx_uv >> kFractionBits); - } - } - else { - ScaleYUVToRGB32Row_C(y_ptr, u_ptr, v_ptr, - dest_pixel, width, source_dx); - } - #else -+ (void)source_dx_uv; - ScaleYUVToRGB32Row(y_ptr, u_ptr, v_ptr, - dest_pixel, width, source_dx); - #endif - } - } - // MMX used for FastConvertYUVToRGB32Row and FilterRows requires emms. - if (has_mmx) - EMMS(); -diff --git a/gfx/ycbcr/yuv_row.h b/gfx/ycbcr/yuv_row.h ---- a/gfx/ycbcr/yuv_row.h -+++ b/gfx/ycbcr/yuv_row.h -@@ -129,14 +129,14 @@ extern SIMD_ALIGNED(int16 kCoefficientsR - #if defined(ARCH_CPU_X86) && !defined(ARCH_CPU_X86_64) - #if defined(_MSC_VER) - #define EMMS() __asm emms - #pragma warning(disable: 4799) - #else - #define EMMS() asm("emms") - #endif - #else --#define EMMS() -+#define EMMS() ((void)0) - #endif - - } // extern "C" - - #endif // MEDIA_BASE_YUV_ROW_H_ diff --git a/gfx/ycbcr/README b/gfx/ycbcr/README index a951bc83a84..95ea0fdb270 100644 --- a/gfx/ycbcr/README +++ b/gfx/ycbcr/README @@ -25,5 +25,3 @@ convert.patch contains the following changes: win64.patch: SSE2 optimization for Microsoft Visual C++ x64 version TypeFromSize.patch: Bug 656185 - Add a method to detect YUVType from plane sizes. - -QuellGccWarnings.patch: Bug 711895 - Avoid some GCC compilation warnings. diff --git a/gfx/ycbcr/update.sh b/gfx/ycbcr/update.sh index 3a38fe81a3d..9b0c07ab54f 100644 --- a/gfx/ycbcr/update.sh +++ b/gfx/ycbcr/update.sh @@ -9,4 +9,3 @@ cp $1/media/base/yuv_row_posix.cc yuv_row_c.cpp patch -p3 -static void -AssertGreaterThanZero(IndexType index) -{ - JS_ASSERT(index >= 0); -} - -template<> -void -AssertGreaterThanZero(jsuint index) -{ -} - template static JSBool GetElement(JSContext *cx, JSObject *obj, IndexType index, JSBool *hole, Value *vp) { - AssertGreaterThanZero(index); + JS_ASSERT(index >= 0); if (obj->isDenseArray() && index < obj->getDenseArrayInitializedLength() && !(*vp = obj->getDenseArrayElement(uint32_t(index))).isMagic(JS_ARRAY_HOLE)) { *hole = JS_FALSE; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 5a1e2e7cdea..84a9f10ce6d 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1342,7 +1342,7 @@ JSRuntime::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind) void JSRuntime::reduceGCTriggerBytes(uint32_t amount) { JS_ASSERT(amount > 0); - JS_ASSERT(gcTriggerBytes >= amount); + JS_ASSERT(gcTriggerBytes - amount >= 0); if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR) return; gcTriggerBytes -= amount; @@ -1361,7 +1361,7 @@ JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind) void JSCompartment::reduceGCTriggerBytes(uint32_t amount) { JS_ASSERT(amount > 0); - JS_ASSERT(gcTriggerBytes >= amount); + JS_ASSERT(gcTriggerBytes - amount >= 0); if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR) return; gcTriggerBytes -= amount; diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 7267622d802..a0a0b25511b 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -1687,7 +1687,9 @@ class TypedArrayTemplate { JS_ASSERT(tarray); + JS_ASSERT(0 <= begin); JS_ASSERT(begin <= getLength(tarray)); + JS_ASSERT(0 <= end); JS_ASSERT(end <= getLength(tarray)); JSObject *bufobj = getBuffer(tarray); diff --git a/layout/generic/nsHTMLReflowMetrics.h b/layout/generic/nsHTMLReflowMetrics.h index 21c357b4482..5c54a205b5a 100644 --- a/layout/generic/nsHTMLReflowMetrics.h +++ b/layout/generic/nsHTMLReflowMetrics.h @@ -71,11 +71,11 @@ private: nsRect mRects[2]; public: nsRect& Overflow(size_t aIndex) { - NS_ASSERTION(aIndex < 2, "index out of range"); + NS_ASSERTION(0 <= aIndex && aIndex < 2, "index out of range"); return mRects[aIndex]; } const nsRect& Overflow(size_t aIndex) const { - NS_ASSERTION(aIndex < 2, "index out of range"); + NS_ASSERTION(0 <= aIndex && aIndex < 2, "index out of range"); return mRects[aIndex]; } From c0357a7c110ba46bf4acfcb27e1b410a821d7aea Mon Sep 17 00:00:00 2001 From: Quentin Headen Date: Thu, 29 Dec 2011 16:06:56 -0500 Subject: [PATCH 17/66] Bug 352037 - Add an "Undo add to dictionary" item to spell checker's context menu item; r=ehsan --- browser/base/content/browser-context.inc | 4 + browser/base/content/nsContextMenu.js | 1 + .../base/content/test/test_contextmenu.html | 39 +++++++-- .../txtsvc/public/nsIInlineSpellChecker.idl | 3 +- .../spellcheck/src/mozInlineSpellChecker.cpp | 18 ++++ toolkit/content/InlineSpellChecker.jsm | 21 +++++ toolkit/content/tests/chrome/Makefile.in | 1 + .../tests/chrome/test_textbox_dictionary.xul | 85 +++++++++++++++++++ toolkit/content/widgets/textbox.xml | 4 + .../en-US/chrome/global/textcontext.dtd | 2 + 10 files changed, 170 insertions(+), 8 deletions(-) create mode 100644 toolkit/content/tests/chrome/test_textbox_dictionary.xul diff --git a/browser/base/content/browser-context.inc b/browser/base/content/browser-context.inc index a4b7c8645d0..2f9d64304df 100644 --- a/browser/base/content/browser-context.inc +++ b/browser/base/content/browser-context.inc @@ -46,6 +46,10 @@ label="&spellAddToDictionary.label;" accesskey="&spellAddToDictionary.accesskey;" oncommand="InlineSpellCheckerUI.addToDictionary();"/> + RemoveWordFromDictionary(wordstr.get()); + NS_ENSURE_SUCCESS(rv, rv); + + mozInlineSpellStatus status(this); + nsCOMPtr range = do_QueryInterface(NULL); // Check everything + rv = status.InitForRange(range); + NS_ENSURE_SUCCESS(rv, rv); + return ScheduleSpellCheck(status); +} + // mozInlineSpellChecker::IgnoreWord NS_IMETHODIMP diff --git a/toolkit/content/InlineSpellChecker.jsm b/toolkit/content/InlineSpellChecker.jsm index 8942c9427a5..0d25229e681 100644 --- a/toolkit/content/InlineSpellChecker.jsm +++ b/toolkit/content/InlineSpellChecker.jsm @@ -38,9 +38,11 @@ var EXPORTED_SYMBOLS = [ "InlineSpellChecker" ]; var gLanguageBundle; var gRegionBundle; +const MAX_UNDO_STACK_DEPTH = 1; function InlineSpellChecker(aEditor) { this.init(aEditor); + this.mAddedWordStack = []; // We init this here to preserve it between init/uninit calls } InlineSpellChecker.prototype = { @@ -284,8 +286,27 @@ InlineSpellChecker.prototype = { // callback for adding the current misspelling to the user-defined dictionary addToDictionary: function() { + // Prevent the undo stack from growing over the max depth + if (this.mAddedWordStack.length == MAX_UNDO_STACK_DEPTH) + this.mAddedWordStack.shift(); + + this.mAddedWordStack.push(this.mMisspelling); this.mInlineSpellChecker.addWordToDictionary(this.mMisspelling); }, + // callback for removing the last added word to the dictionary LIFO fashion + undoAddToDictionary: function() + { + if (this.mAddedWordStack.length > 0) + { + var word = this.mAddedWordStack.pop(); + this.mInlineSpellChecker.removeWordFromDictionary(word); + } + }, + canUndo : function() + { + // Return true if we have words on the stack + return (this.mAddedWordStack.length > 0); + }, ignoreWord: function() { this.mInlineSpellChecker.ignoreWord(this.mMisspelling); diff --git a/toolkit/content/tests/chrome/Makefile.in b/toolkit/content/tests/chrome/Makefile.in index e4e2233f539..6857bb620b0 100644 --- a/toolkit/content/tests/chrome/Makefile.in +++ b/toolkit/content/tests/chrome/Makefile.in @@ -144,6 +144,7 @@ _TEST_FILES += \ test_textbox_emptytext.xul \ test_textbox_number.xul \ test_textbox_search.xul \ + test_textbox_dictionary.xul\ test_toolbar.xul \ xul_selectcontrol.js \ test_popupincontent.xul \ diff --git a/toolkit/content/tests/chrome/test_textbox_dictionary.xul b/toolkit/content/tests/chrome/test_textbox_dictionary.xul new file mode 100644 index 00000000000..53a0bb9e081 --- /dev/null +++ b/toolkit/content/tests/chrome/test_textbox_dictionary.xul @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + diff --git a/toolkit/content/widgets/textbox.xml b/toolkit/content/widgets/textbox.xml index 50d0bfe361a..b2a663dc8cc 100644 --- a/toolkit/content/widgets/textbox.xml +++ b/toolkit/content/widgets/textbox.xml @@ -515,6 +515,8 @@ + @@ -580,6 +582,7 @@ this._setMenuItemVisibility("spell-check-enabled", false); this._setMenuItemVisibility("spell-check-separator", false); this._setMenuItemVisibility("spell-add-to-dictionary", false); + this._setMenuItemVisibility("spell-undo-add-to-dictionary", false); this._setMenuItemVisibility("spell-suggestions-separator", false); this._setMenuItemVisibility("spell-dictionaries", false); return; @@ -593,6 +596,7 @@ var overMisspelling = spellui.overMisspelling; this._setMenuItemVisibility("spell-add-to-dictionary", overMisspelling); + this._setMenuItemVisibility("spell-undo-add-to-dictionary", spellui.canUndo()); this._setMenuItemVisibility("spell-suggestions-separator", overMisspelling); // suggestion list diff --git a/toolkit/locales/en-US/chrome/global/textcontext.dtd b/toolkit/locales/en-US/chrome/global/textcontext.dtd index c870eeca52b..26c66d6f4dd 100644 --- a/toolkit/locales/en-US/chrome/global/textcontext.dtd +++ b/toolkit/locales/en-US/chrome/global/textcontext.dtd @@ -13,6 +13,8 @@ + + From 330f029e7679ac35c0b01e718c70fd1ac78c408d Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 8 Dec 2011 17:35:36 -0500 Subject: [PATCH 18/66] Bug 711491. Port the linux backend to OS X. r=bgirard This will let us use the signal based approach used on linux on OS X. This is helpful because backtrace() only works on the current thread. --HG-- extra : rebase_source : 68e39af6025e5ba12f64708c453d6be3bb9f7a70 --- tools/profiler/sps/platform-linux.cc | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tools/profiler/sps/platform-linux.cc b/tools/profiler/sps/platform-linux.cc index 8a855ad5269..22e1fa61925 100644 --- a/tools/profiler/sps/platform-linux.cc +++ b/tools/profiler/sps/platform-linux.cc @@ -39,9 +39,7 @@ #include #include -// Real time signals are not supported on android. -// This behaves as a standard signal. -#define SIGNAL_SAVE_PROFILE 42 +#define SIGNAL_SAVE_PROFILE SIGUSR2 #define PATH_MAX_TOSTRING(x) #x #define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x) @@ -193,9 +191,11 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { sActiveSampler->Tick(sample); } +#ifndef XP_MACOSX void tgkill(pid_t tgid, pid_t tid, int signalno) { syscall(SYS_tgkill, tgid, tid, signalno); } +#endif class Sampler::PlatformData : public Malloced { public: @@ -203,16 +203,26 @@ class Sampler::PlatformData : public Malloced { : sampler_(sampler), signal_handler_installed_(false), vm_tgid_(getpid()), +#ifndef XP_MACOSX vm_tid_(gettid()), - signal_sender_launched_(false) { +#endif + signal_sender_launched_(false) +#ifdef XP_MACOSX + , signal_receiver_(pthread_self()) +#endif + { } void SignalSender() { while (sampler_->IsActive()) { sampler_->HandleSaveRequest(); +#ifdef XP_MACOSX + pthread_kill(signal_receiver_, SIGPROF); +#else // Glibc doesn't provide a wrapper for tgkill(2). tgkill(vm_tgid_, vm_tid_, SIGPROF); +#endif // Convert ms to us and subtract 100 us to compensate delays // occuring during signal delivery. @@ -240,6 +250,9 @@ class Sampler::PlatformData : public Malloced { pid_t vm_tid_; bool signal_sender_launched_; pthread_t signal_sender_thread_; +#ifdef XP_MACOSX + pthread_t signal_receiver_; +#endif }; From c5191f212ddeef0dffaadb7800d8f545a9e5c863 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 13 Dec 2011 16:09:02 -0800 Subject: [PATCH 19/66] Bug 710414 - Split out the conservative testing for a GCThing from marking it. r=billm Testing an arbitrary pointer for valid GCThing-ness is useful for things other tan the GC --HG-- extra : rebase_source : 93098ac37970970feaea4440f89f01a501326e3f --- js/src/jsgc.cpp | 51 ++++++++++++++++++++++++++++++++++++++----------- js/src/jsgc.h | 3 +++ 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index acdadd9dd79..f399808e9fa 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -929,11 +929,12 @@ InFreeList(ArenaHeader *aheader, uintptr_t addr) } /* - * Returns CGCT_VALID and mark it if the w can be a live GC thing and sets - * thingKind accordingly. Otherwise returns the reason for rejection. + * Tests whether w is a (possibly dead) GC thing. Returns CGCT_VALID and + * details about the thing if so. On failure, returns the reason for rejection. */ inline ConservativeGCTest -MarkIfGCThingWord(JSTracer *trc, jsuword w) +IsAddressableGCThing(JSRuntime *rt, jsuword w, + gc::AllocKind *thingKindPtr, ArenaHeader **arenaHeader, void **thing) { /* * We assume that the compiler never uses sub-word alignment to store @@ -959,7 +960,7 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) Chunk *chunk = Chunk::fromAddress(addr); - if (!trc->runtime->gcChunkSet.has(chunk)) + if (!rt->gcChunkSet.has(chunk)) return CGCT_NOTCHUNK; /* @@ -980,7 +981,7 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) if (!aheader->allocated()) return CGCT_FREEARENA; - JSCompartment *curComp = trc->runtime->gcCurrentCompartment; + JSCompartment *curComp = rt->gcCurrentCompartment; if (curComp && curComp != aheader->compartment) return CGCT_OTHERCOMPARTMENT; @@ -994,20 +995,41 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) uintptr_t shift = (offset - minOffset) % Arena::thingSize(thingKind); addr -= shift; + if (thing) + *thing = reinterpret_cast(addr); + if (arenaHeader) + *arenaHeader = aheader; + if (thingKindPtr) + *thingKindPtr = thingKind; + return CGCT_VALID; +} + +/* + * Returns CGCT_VALID and mark it if the w can be a live GC thing and sets + * thingKind accordingly. Otherwise returns the reason for rejection. + */ +inline ConservativeGCTest +MarkIfGCThingWord(JSTracer *trc, jsuword w) +{ + void *thing; + ArenaHeader *aheader; + AllocKind thingKind; + ConservativeGCTest status = IsAddressableGCThing(trc->runtime, w, &thingKind, &aheader, &thing); + if (status != CGCT_VALID) + return status; + /* * Check if the thing is free. We must use the list of free spans as at * this point we no longer have the mark bits from the previous GC run and * we must account for newly allocated things. */ - if (InFreeList(aheader, addr)) + if (InFreeList(aheader, uintptr_t(thing))) return CGCT_NOTLIVE; - void *thing = reinterpret_cast(addr); - #ifdef DEBUG const char pattern[] = "machine_stack %lx"; - char nameBuf[sizeof(pattern) - 3 + sizeof(addr) * 2]; - JS_snprintf(nameBuf, sizeof(nameBuf), "machine_stack %lx", (unsigned long) addr); + char nameBuf[sizeof(pattern) - 3 + sizeof(thing) * 2]; + JS_snprintf(nameBuf, sizeof(nameBuf), "machine_stack %lx", (unsigned long) thing); JS_SET_TRACING_NAME(trc, nameBuf); #endif MarkKind(trc, thing, MapAllocToTraceKind(thingKind)); @@ -1017,10 +1039,11 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) GCMarker *marker = static_cast(trc); if (marker->conservativeDumpFileName) marker->conservativeRoots.append(thing); - if (shift) + if (jsuword(thing) != w) marker->conservativeStats.unaligned++; } #endif + return CGCT_VALID; } @@ -1160,6 +1183,12 @@ RecordNativeStackTopForGC(JSContext *cx) } /* namespace js */ +bool +js_IsAddressableGCThing(JSRuntime *rt, jsuword w, gc::AllocKind *thingKind, void **thing) +{ + return js::IsAddressableGCThing(rt, w, thingKind, NULL, thing) == CGCT_VALID; +} + #ifdef DEBUG static void CheckLeakedRoots(JSRuntime *rt); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 72fdc4db766..5dab399accf 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1363,6 +1363,9 @@ js_GCThingIsMarked(void *thing, uintN color); extern void js_TraceStackFrame(JSTracer *trc, js::StackFrame *fp); +extern bool +js_IsAddressableGCThing(JSRuntime *rt, jsuword w, js::gc::AllocKind *thingKind, void **thing); + namespace js { extern JS_REQUIRES_STACK void From a27b0b5418071c30ee85be60598c2980f4c423fa Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 15 Dec 2011 06:58:00 -0500 Subject: [PATCH 20/66] Bug 711491. Add a proper = operator to MapEntry. r=bgirard This fixes us a bug where we weren't copying the data properly. --HG-- extra : rebase_source : a0331cf5d5b61c355447832119a6f7876922e190 --- tools/profiler/sps/platform.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/profiler/sps/platform.h b/tools/profiler/sps/platform.h index 092113928f6..dc0ad2e1a04 100644 --- a/tools/profiler/sps/platform.h +++ b/tools/profiler/sps/platform.h @@ -40,6 +40,15 @@ public: , mName(strdup(aEntry.mName)) {} + MapEntry& operator=(const MapEntry& aEntry) + { + mStart = aEntry.mStart; + mEnd = aEntry.mEnd; + mOffset = aEntry.mOffset; + mName = strdup(aEntry.mName); + return *this; + } + ~MapEntry() { free(mName); @@ -50,6 +59,8 @@ public: char* GetName() { return mName; } private: + explicit MapEntry() {} + unsigned long mStart; unsigned long mEnd; unsigned long mOffset; From 47a9a22ae002a15a9150a2f987dd31553e32b8da Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 15 Dec 2011 07:31:41 -0500 Subject: [PATCH 21/66] Bug 711491. Refactor MapInfo. r=bgirard Moves MapInfo out of platform and renames it to SharedLibrary. There will eventually be an implementation for all major platforms. --HG-- extra : rebase_source : c7eae4bc0f0e27f2801c4e639d7dc82b47465f0b --- tools/profiler/Makefile.in | 1 + tools/profiler/sps/TableTicker.cpp | 27 ++--- tools/profiler/sps/platform-linux.cc | 58 ---------- tools/profiler/sps/platform.h | 68 ------------ tools/profiler/sps/shared-libraries-linux.cc | 67 ++++++++++++ tools/profiler/sps/shared-libraries.h | 108 +++++++++++++++++++ 6 files changed, 190 insertions(+), 139 deletions(-) create mode 100644 tools/profiler/sps/shared-libraries-linux.cc create mode 100644 tools/profiler/sps/shared-libraries.h diff --git a/tools/profiler/Makefile.in b/tools/profiler/Makefile.in index 9b430997d78..84a556d675b 100644 --- a/tools/profiler/Makefile.in +++ b/tools/profiler/Makefile.in @@ -83,6 +83,7 @@ ifneq (,$(filter Android Linux,$(OS_TARGET))) DEFINES += -DMOZ_ENABLE_PROFILER_SPS CPPSRCS += \ + shared-libraries-linux.cc \ platform-linux.cc \ TableTicker.cpp \ $(NULL) diff --git a/tools/profiler/sps/TableTicker.cpp b/tools/profiler/sps/TableTicker.cpp index ae90763ac34..ae06ac30c9f 100644 --- a/tools/profiler/sps/TableTicker.cpp +++ b/tools/profiler/sps/TableTicker.cpp @@ -43,6 +43,7 @@ #include "nsXULAppAPI.h" #include "nsThreadUtils.h" #include "prenv.h" +#include "shared-libraries.h" using std::string; @@ -156,9 +157,9 @@ public: void ToString(string* profile) { // Can't be called from signal because - // get_maps calls non reentrant functions. + // getting the shared library information can call non-reentrant functions. #ifdef ENABLE_SPS_LEAF_DATA - mMaps = getmaps(getpid()); + mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf(); #endif *profile = ""; @@ -173,9 +174,9 @@ public: void WriteProfile(FILE* stream) { // Can't be called from signal because - // get_maps calls non reentrant functions. + // getting the shared library information can call non-reentrant functions. #ifdef ENABLE_SPS_LEAF_DATA - mMaps = getmaps(getpid()); + mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf(); #endif int oldReadPos = mReadPos; @@ -187,9 +188,9 @@ public: } #ifdef ENABLE_SPS_LEAF_DATA - MapInfo& getMap() + SharedLibraryInfo& getSharedLibraryInfo() { - return mMaps; + return mSharedLibraryInfo; } #endif private: @@ -200,7 +201,7 @@ private: int mReadPos; // points to the next entry we will read to int mEntrySize; #ifdef ENABLE_SPS_LEAF_DATA - MapInfo mMaps; + SharedLibraryInfo mSharedLibraryInfo; #endif }; @@ -351,11 +352,11 @@ string ProfileEntry::TagToString(Profile *profile) if (mLeafAddress) { bool found = false; char tagBuff[1024]; - MapInfo& maps = profile->getMap(); + SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo(); unsigned long pc = (unsigned long)mLeafAddress; // TODO Use binary sort (STL) - for (size_t i = 0; i < maps.GetSize(); i++) { - MapEntry &e = maps.GetEntry(i); + for (size_t i = 0; i < shlibInfo.GetSize(); i++) { + SharedLibrary &e = shlibInfo.GetEntry(i); if (pc > e.GetStart() && pc < e.GetEnd()) { if (e.GetName()) { found = true; @@ -381,11 +382,11 @@ void ProfileEntry::WriteTag(Profile *profile, FILE *stream) #ifdef ENABLE_SPS_LEAF_DATA if (mLeafAddress) { bool found = false; - MapInfo& maps = profile->getMap(); + SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo(); unsigned long pc = (unsigned long)mLeafAddress; // TODO Use binary sort (STL) - for (size_t i = 0; i < maps.GetSize(); i++) { - MapEntry &e = maps.GetEntry(i); + for (size_t i = 0; i < shlibInfo.GetSize(); i++) { + SharedLibrary &e = shlibInfo.GetEntry(i); if (pc > e.GetStart() && pc < e.GetEnd()) { if (e.GetName()) { found = true; diff --git a/tools/profiler/sps/platform-linux.cc b/tools/profiler/sps/platform-linux.cc index 22e1fa61925..44fcae99e87 100644 --- a/tools/profiler/sps/platform-linux.cc +++ b/tools/profiler/sps/platform-linux.cc @@ -41,9 +41,6 @@ #define SIGNAL_SAVE_PROFILE SIGUSR2 -#define PATH_MAX_TOSTRING(x) #x -#define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x) - #if defined(__GLIBC__) // glibc doesn't implement gettid(2). #include @@ -53,61 +50,6 @@ pid_t gettid() } #endif -#ifdef ENABLE_SPS_LEAF_DATA -/* a crapy version of getline, because it's not included in bionic */ -static ssize_t getline(char **lineptr, size_t *n, FILE *stream) -{ - char *ret; - if (!*lineptr) { - *lineptr = (char*)malloc(4096); - } - ret = fgets(*lineptr, 4096, stream); - if (!ret) - return 0; - return strlen(*lineptr); -} - -MapInfo getmaps(pid_t pid) -{ - MapInfo info; - char path[PATH_MAX]; - snprintf(path, PATH_MAX, "/proc/%d/maps", pid); - FILE *maps = fopen(path, "r"); - char *line = NULL; - int count = 0; - size_t line_size = 0; - while (maps && getline (&line, &line_size, maps) > 0) { - int ret; - //XXX: needs input sanitizing - unsigned long start; - unsigned long end; - char perm[6] = ""; - unsigned long offset; - char name[PATH_MAX] = ""; - ret = sscanf(line, - "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n", - &start, &end, perm, &offset, name); - if (!strchr(perm, 'x')) { - // Ignore non executable entries - continue; - } - if (ret != 5 && ret != 4) { - LOG("Get maps line failed"); - continue; - } - MapEntry entry(start, end, offset, name); - info.AddMapEntry(entry); - if (count > 10000) { - LOG("Get maps failed"); - break; - } - count++; - } - free(line); - return info; -} -#endif - static Sampler* sActiveSampler = NULL; diff --git a/tools/profiler/sps/platform.h b/tools/profiler/sps/platform.h index dc0ad2e1a04..87fdf0f54a3 100644 --- a/tools/profiler/sps/platform.h +++ b/tools/profiler/sps/platform.h @@ -24,74 +24,6 @@ typedef uint8_t* Address; -class MapEntry { -public: - MapEntry(unsigned long aStart, unsigned long aEnd, unsigned long aOffset, char *aName) - : mStart(aStart) - , mEnd(aEnd) - , mOffset(aOffset) - , mName(strdup(aName)) - {} - - MapEntry(const MapEntry& aEntry) - : mStart(aEntry.mStart) - , mEnd(aEntry.mEnd) - , mOffset(aEntry.mOffset) - , mName(strdup(aEntry.mName)) - {} - - MapEntry& operator=(const MapEntry& aEntry) - { - mStart = aEntry.mStart; - mEnd = aEntry.mEnd; - mOffset = aEntry.mOffset; - mName = strdup(aEntry.mName); - return *this; - } - - ~MapEntry() - { - free(mName); - } - - unsigned long GetStart() { return mStart; } - unsigned long GetEnd() { return mEnd; } - char* GetName() { return mName; } - -private: - explicit MapEntry() {} - - unsigned long mStart; - unsigned long mEnd; - unsigned long mOffset; - char *mName; -}; - -class MapInfo { -public: - MapInfo() {} - - void AddMapEntry(MapEntry entry) - { - mEntries.push_back(entry); - } - - MapEntry& GetEntry(size_t i) - { - return mEntries[i]; - } - - size_t GetSize() - { - return mEntries.size(); - } -private: - std::vector mEntries; -}; - -#ifdef ENABLE_SPS_LEAF_DATA -struct MapInfo getmaps(pid_t pid); -#endif // ---------------------------------------------------------------------------- // Mutex // diff --git a/tools/profiler/sps/shared-libraries-linux.cc b/tools/profiler/sps/shared-libraries-linux.cc new file mode 100644 index 00000000000..85237eeddb6 --- /dev/null +++ b/tools/profiler/sps/shared-libraries-linux.cc @@ -0,0 +1,67 @@ +#define PATH_MAX_TOSTRING(x) #x +#define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x) +#include +#include +#include +#include +#include +#include "platform.h" +#include "shared-libraries.h" + +#ifdef ENABLE_SPS_LEAF_DATA +#ifndef __GLIBC__ +/* a crapy version of getline, because it's not included in bionic */ +static ssize_t getline(char **lineptr, size_t *n, FILE *stream) +{ + char *ret; + if (!*lineptr) { + *lineptr = (char*)malloc(4096); + } + ret = fgets(*lineptr, 4096, stream); + if (!ret) + return 0; + return strlen(*lineptr); +} +#endif + +SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() +{ + pid_t pid = getpid(); + SharedLibraryInfo info; + char path[PATH_MAX]; + snprintf(path, PATH_MAX, "/proc/%d/maps", pid); + FILE *maps = fopen(path, "r"); + char *line = NULL; + int count = 0; + size_t line_size = 0; + while (maps && getline (&line, &line_size, maps) > 0) { + int ret; + //XXX: needs input sanitizing + unsigned long start; + unsigned long end; + char perm[6] = ""; + unsigned long offset; + char name[PATH_MAX] = ""; + ret = sscanf(line, + "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n", + &start, &end, perm, &offset, name); + if (!strchr(perm, 'x')) { + // Ignore non executable entries + continue; + } + if (ret != 5 && ret != 4) { + LOG("Get maps line failed"); + continue; + } + SharedLibrary shlib(start, end, offset, name); + info.AddSharedLibrary(shlib); + if (count > 10000) { + LOG("Get maps failed"); + break; + } + count++; + } + free(line); + return info; +} +#endif diff --git a/tools/profiler/sps/shared-libraries.h b/tools/profiler/sps/shared-libraries.h new file mode 100644 index 00000000000..6aa2acb79e8 --- /dev/null +++ b/tools/profiler/sps/shared-libraries.h @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 et cindent: */ +/* ***** 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 code. + * + * 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): + * Jeff Muizelaar + * + * 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 +#include +#include +#include + +class SharedLibrary { +public: + SharedLibrary(unsigned long aStart, unsigned long aEnd, unsigned long aOffset, char *aName) + : mStart(aStart) + , mEnd(aEnd) + , mOffset(aOffset) + , mName(strdup(aName)) + {} + + SharedLibrary(const SharedLibrary& aEntry) + : mStart(aEntry.mStart) + , mEnd(aEntry.mEnd) + , mOffset(aEntry.mOffset) + , mName(strdup(aEntry.mName)) + {} + + SharedLibrary& operator=(const SharedLibrary& aEntry) + { + mStart = aEntry.mStart; + mEnd = aEntry.mEnd; + mOffset = aEntry.mOffset; + mName = strdup(aEntry.mName); + return *this; + } + + ~SharedLibrary() + { + free(mName); + } + + uintptr_t GetStart() { return mStart; } + uintptr_t GetEnd() { return mEnd; } + char* GetName() { return mName; } + +private: + explicit SharedLibrary() {} + + uintptr_t mStart; + uintptr_t mEnd; + uintptr_t mOffset; + char *mName; +}; + +class SharedLibraryInfo { +public: + static SharedLibraryInfo GetInfoForSelf(); + SharedLibraryInfo() {} + + void AddSharedLibrary(SharedLibrary entry) + { + mEntries.push_back(entry); + } + + SharedLibrary& GetEntry(size_t i) + { + return mEntries[i]; + } + + size_t GetSize() + { + return mEntries.size(); + } +private: + std::vector mEntries; +}; From 7762a0d5828a6665b38d8d5b2028dfe1e6ce3e36 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 15 Dec 2011 06:56:52 -0500 Subject: [PATCH 22/66] Bug 711491. Implement SharedLibraryInfo on OS X. r=bgirard This is based on a patch from bug 698002. --HG-- extra : rebase_source : ccaf8849e3b8f88bb86e895cf3a22ea289a5e94d --- tools/profiler/Makefile.in | 1 + tools/profiler/sps/shared-libraries-macos.cc | 147 +++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 tools/profiler/sps/shared-libraries-macos.cc diff --git a/tools/profiler/Makefile.in b/tools/profiler/Makefile.in index 84a556d675b..da35265c1cc 100644 --- a/tools/profiler/Makefile.in +++ b/tools/profiler/Makefile.in @@ -93,6 +93,7 @@ ifeq ($(OS_TARGET),Darwin) DEFINES += -DMOZ_ENABLE_PROFILER_SPS CPPSRCS += \ + shared-libraries-macos.cc \ platform-macos.cc \ TableTicker.cpp \ $(NULL) diff --git a/tools/profiler/sps/shared-libraries-macos.cc b/tools/profiler/sps/shared-libraries-macos.cc new file mode 100644 index 00000000000..0b8ae85d864 --- /dev/null +++ b/tools/profiler/sps/shared-libraries-macos.cc @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 2; 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.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benoit Girard + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "shared-libraries.h" + +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 +// borrowed from Breakpad +// Fallback declarations for TASK_DYLD_INFO and friends, introduced in +// in the Mac OS X 10.6 SDK. +#define TASK_DYLD_INFO 17 +struct task_dyld_info { + mach_vm_address_t all_image_info_addr; + mach_vm_size_t all_image_info_size; + }; +typedef struct task_dyld_info task_dyld_info_data_t; +typedef struct task_dyld_info *task_dyld_info_t; +#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t)) + +#endif + +// Architecture specific abstraction. +#ifdef __i386__ +typedef mach_header platform_mach_header; +typedef segment_command mach_segment_command_type; +#define MACHO_MAGIC_NUMBER MH_MAGIC +#define CMD_SEGMENT LC_SEGMENT +#define seg_size uint32_t +#else +typedef mach_header_64 platform_mach_header; +typedef segment_command_64 mach_segment_command_type; +#define MACHO_MAGIC_NUMBER MH_MAGIC_64 +#define CMD_SEGMENT LC_SEGMENT_64 +#define seg_size uint64_t +#endif + +static +void addSharedLibrary(const platform_mach_header* header, char *name, SharedLibraryInfo &info) { + const struct load_command *cmd = + reinterpret_cast(header + 1); + + seg_size size; + // Find the cmd segment in the macho image. It will contain the offset we care about. + for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) { + if (cmd->cmd == CMD_SEGMENT) { + const mach_segment_command_type *seg = + reinterpret_cast(cmd); + + if (!strcmp(seg->segname, "__TEXT")) { + size = seg->vmsize; + unsigned long long start = reinterpret_cast(header); + info.AddSharedLibrary(SharedLibrary(start, start+seg->vmsize, seg->vmsize, name)); + return; + } + } + + cmd = reinterpret_cast + (reinterpret_cast(cmd) + cmd->cmdsize); + } +} + +// Use dyld to inspect the macho image information. We can build the SharedLibraryEntry structure +// giving us roughtly the same info as /proc/PID/maps in Linux. +SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() +{ + SharedLibraryInfo sharedLibraryInfo; + + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + if (task_info(mach_task_self (), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, + &count) != KERN_SUCCESS) { + return sharedLibraryInfo; + } + + struct dyld_all_image_infos* aii = (struct dyld_all_image_infos*)task_dyld_info.all_image_info_addr; + size_t infoCount = aii->infoArrayCount; + + // Iterate through all dyld images (loaded libraries) to get their names + // and offests. + for (size_t i = 0; i < infoCount; ++i) { + const dyld_image_info *info = &aii->infoArray[i]; + + // If the magic number doesn't match then go no further + // since we're not pointing to where we think we are. + if (info->imageLoadAddress->magic != MACHO_MAGIC_NUMBER) { + continue; + } + + const platform_mach_header* header = + reinterpret_cast(info->imageLoadAddress); + + // Add the entry for this image. + addSharedLibrary(header, (char*)info->imageFilePath, sharedLibraryInfo); + + } + return sharedLibraryInfo; +} + From 28c467fe31365548c245c4986a4ef3df1cb61f91 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Fri, 16 Dec 2011 10:56:06 -0500 Subject: [PATCH 23/66] Bug 711491. Rough out SharedLibraryInfo for win32. r=bgirard --HG-- extra : rebase_source : 17e182c652d3eab2d083b98a9d0f5f04fc344a87 --- tools/profiler/Makefile.in | 1 + tools/profiler/sps/shared-libraries-win32.cc | 60 ++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 tools/profiler/sps/shared-libraries-win32.cc diff --git a/tools/profiler/Makefile.in b/tools/profiler/Makefile.in index da35265c1cc..4d3976d6348 100644 --- a/tools/profiler/Makefile.in +++ b/tools/profiler/Makefile.in @@ -104,6 +104,7 @@ ifeq ($(OS_TARGET),WINNT) DEFINES += -DMOZ_ENABLE_PROFILER_SPS CPPSRCS += \ + shared-libraries-win32.cc \ platform-win32.cc \ TableTicker.cpp \ $(NULL) diff --git a/tools/profiler/sps/shared-libraries-win32.cc b/tools/profiler/sps/shared-libraries-win32.cc new file mode 100644 index 00000000000..396e679f8b5 --- /dev/null +++ b/tools/profiler/sps/shared-libraries-win32.cc @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 2; 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.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jeff Muizelaar + * + * 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 +#include + +#include "shared-libraries.h" + +SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() +{ + SharedLibraryInfo sharedLibraryInfo; + + /* + HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); + + MODULEENTRY32 module; + Module32First(snap, &module); + do { + // process module + } while (Module32Next(snap, &module)); + */ + + return sharedLibraryInfo; +} + From ff6d932bed3c6c5bd569c9187b6fbd228bceb7b5 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Fri, 16 Dec 2011 11:54:22 -0500 Subject: [PATCH 24/66] Bug 711491. Decide whether to get shared library info at runtime r=bgirard Now that we have implementations of SharedLibraryInfo for all platforms we don't need to build support for it conditionally. --HG-- extra : rebase_source : d40cf1b0b28fab3ef31ab4511fc1ddda98a37a38 --- tools/profiler/sps/TableTicker.cpp | 29 ++++++++++---------- tools/profiler/sps/shared-libraries-linux.cc | 2 -- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/tools/profiler/sps/TableTicker.cpp b/tools/profiler/sps/TableTicker.cpp index ae06ac30c9f..7573dc18f80 100644 --- a/tools/profiler/sps/TableTicker.cpp +++ b/tools/profiler/sps/TableTicker.cpp @@ -135,6 +135,10 @@ public: , mEntrySize(aEntrySize) { mEntries = new ProfileEntry[mEntrySize]; + mNeedsSharedLibraryInfo = false; +#ifdef ENABLE_SPS_LEAF_DATA + mNeedsSharedLibraryInfo = true; +#endif } ~Profile() @@ -156,11 +160,11 @@ public: void ToString(string* profile) { - // Can't be called from signal because - // getting the shared library information can call non-reentrant functions. -#ifdef ENABLE_SPS_LEAF_DATA - mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf(); -#endif + if (mNeedsSharedLibraryInfo) { + // Can't be called from signal because + // getting the shared library information can call non-reentrant functions. + mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf(); + } *profile = ""; int oldReadPos = mReadPos; @@ -173,11 +177,11 @@ public: void WriteProfile(FILE* stream) { - // Can't be called from signal because - // getting the shared library information can call non-reentrant functions. -#ifdef ENABLE_SPS_LEAF_DATA - mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf(); -#endif + if (mNeedsSharedLibraryInfo) { + // Can't be called from signal because + // getting the shared library information can call non-reentrant functions. + mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf(); + } int oldReadPos = mReadPos; while (mReadPos != mWritePos) { @@ -187,12 +191,10 @@ public: mReadPos = oldReadPos; } -#ifdef ENABLE_SPS_LEAF_DATA SharedLibraryInfo& getSharedLibraryInfo() { return mSharedLibraryInfo; } -#endif private: // Circular buffer 'Keep One Slot Open' implementation // for simplicity @@ -200,9 +202,8 @@ private: int mWritePos; // points to the next entry we will write to int mReadPos; // points to the next entry we will read to int mEntrySize; -#ifdef ENABLE_SPS_LEAF_DATA + bool mNeedsSharedLibraryInfo; SharedLibraryInfo mSharedLibraryInfo; -#endif }; class SaveProfileTask; diff --git a/tools/profiler/sps/shared-libraries-linux.cc b/tools/profiler/sps/shared-libraries-linux.cc index 85237eeddb6..fb9baa0e2a7 100644 --- a/tools/profiler/sps/shared-libraries-linux.cc +++ b/tools/profiler/sps/shared-libraries-linux.cc @@ -8,7 +8,6 @@ #include "platform.h" #include "shared-libraries.h" -#ifdef ENABLE_SPS_LEAF_DATA #ifndef __GLIBC__ /* a crapy version of getline, because it's not included in bionic */ static ssize_t getline(char **lineptr, size_t *n, FILE *stream) @@ -64,4 +63,3 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() free(line); return info; } -#endif From ee97d1150ba8029acb0eac822e2527007bf2e9fa Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Fri, 16 Dec 2011 08:53:30 -0500 Subject: [PATCH 25/66] Bug 711491. Add a StringBuilder class. r=bgirard --HG-- extra : rebase_source : ff726c89f01ca985167e7b0e7a45f6ef86cc27e6 --- xpcom/ds/Makefile.in | 1 + xpcom/ds/StringBuilder.h | 116 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 xpcom/ds/StringBuilder.h diff --git a/xpcom/ds/Makefile.in b/xpcom/ds/Makefile.in index 584b3b7b2fb..09d6b5bdc8f 100644 --- a/xpcom/ds/Makefile.in +++ b/xpcom/ds/Makefile.in @@ -90,6 +90,7 @@ EXPORTS_NAMESPACES = mozilla EXPORTS_mozilla = \ CharTokenizer.h \ TimeStamp.h \ + StringBuilder.h \ $(NULL) EXPORTS = \ diff --git a/xpcom/ds/StringBuilder.h b/xpcom/ds/StringBuilder.h new file mode 100644 index 00000000000..5fcd478dcf4 --- /dev/null +++ b/xpcom/ds/StringBuilder.h @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 et cindent: */ +/* ***** 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 code. + * + * 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): + * Jeff Muizelaar + * + * 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 ***** */ + +/* We would use std::max but MS makes it painful +// windef.h defines min and max macros that we don't want +// http://support.microsoft.com/kb/143208 +#ifdef _WIN32 +#define NOMINMAX +#endif +*/ + +#include +#include +#include "nsAlgorithm.h" + +/* This is a standard string builder like ones in Java + or C#. It uses a doubling allocation strategy + to grow when out of capacity. + + This does not use nsTArray because nsTArray starts + growing by multiples of page size after it is the + size of one page. We want to keep doubling in size + so that we can continue to append at high speed even + for large strings. + + Eventually, this should be templated for wide characters. + + */ + +namespace mozilla { + +class StringBuilder +{ +public: + StringBuilder() { + mCapacity = 16; + mLength = 0; + mBuffer = static_cast(malloc(sizeof(char)*mCapacity)); + mBuffer[0] = '\0'; + } + + void Append(const char *s) { + size_t newLength = strlen(s); + + EnsureCapacity(mLength + newLength+1); + + // copy the entire string including the null terminator + memcpy(&mBuffer[mLength], s, newLength+1); + mLength += newLength; + } + + char *Buffer() { + return mBuffer; + } + + size_t Length() { + return mLength; + } + + size_t EnsureCapacity(size_t capacity) { + if (capacity > mCapacity) { + // make sure we at least double in size + mCapacity = NS_MAX(capacity, mCapacity*2); + mBuffer = static_cast(realloc(mBuffer, mCapacity)); + mCapacity = moz_malloc_usable_size(mBuffer); + } + return mCapacity; + } + + ~StringBuilder() + { + free(mBuffer); + } + +private: + char *mBuffer; + size_t mLength; // the length of the contained string not including the null terminator + size_t mCapacity; // the total size of mBuffer +}; + +} From 6bc3a3327042666159594f161941bc8d08a52599 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Fri, 16 Dec 2011 09:03:54 -0500 Subject: [PATCH 26/66] Bug 711491. Switch profile export to StringBuilder. This should help performance some. --HG-- extra : rebase_source : 53aa5b61e95745475283940dd8cd053c84d2bd74 --- tools/profiler/sps/TableTicker.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tools/profiler/sps/TableTicker.cpp b/tools/profiler/sps/TableTicker.cpp index 7573dc18f80..efd3e75265e 100644 --- a/tools/profiler/sps/TableTicker.cpp +++ b/tools/profiler/sps/TableTicker.cpp @@ -44,8 +44,10 @@ #include "nsThreadUtils.h" #include "prenv.h" #include "shared-libraries.h" +#include "mozilla/StringBuilder.h" using std::string; +using namespace mozilla; #ifdef XP_WIN #include @@ -158,7 +160,7 @@ public: } } - void ToString(string* profile) + void ToString(StringBuilder &profile) { if (mNeedsSharedLibraryInfo) { // Can't be called from signal because @@ -166,10 +168,9 @@ public: mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf(); } - *profile = ""; int oldReadPos = mReadPos; while (mReadPos != mWritePos) { - *profile += mEntries[mReadPos].TagToString(this); + profile.Append(mEntries[mReadPos].TagToString(this).c_str()); mReadPos = (mReadPos + 1) % mEntrySize; } mReadPos = oldReadPos; @@ -456,11 +457,11 @@ char* mozilla_sampler_get_profile() { return NULL; } - string profile; - t->GetProfile()->ToString(&profile); + StringBuilder profile; + t->GetProfile()->ToString(profile); - char *rtn = (char*)malloc( (strlen(profile.c_str())+1) * sizeof(char) ); - strcpy(rtn, profile.c_str()); + char *rtn = (char*)malloc( (profile.Length()+1) * sizeof(char) ); + strcpy(rtn, profile.Buffer()); return rtn; } From 8d5cecf8134ebd2d06a544a340d999ea4cd735ca Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Fri, 16 Dec 2011 09:12:46 -0500 Subject: [PATCH 27/66] Bug 711491. Remove WriteTag. r=bgirard Just use .TagToString() and fwrite that instead of having a separate function. --HG-- extra : rebase_source : 410d052883778de3db85d4573707b040760376ca --- tools/profiler/sps/TableTicker.cpp | 31 ++---------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/tools/profiler/sps/TableTicker.cpp b/tools/profiler/sps/TableTicker.cpp index efd3e75265e..215109586a5 100644 --- a/tools/profiler/sps/TableTicker.cpp +++ b/tools/profiler/sps/TableTicker.cpp @@ -116,7 +116,6 @@ public: { } string TagToString(Profile *profile); - void WriteTag(Profile *profile, FILE* stream); private: union { @@ -186,7 +185,8 @@ public: int oldReadPos = mReadPos; while (mReadPos != mWritePos) { - mEntries[mReadPos].WriteTag(this, stream); + string tag = mEntries[mReadPos].TagToString(this); + fwrite(tag.data(), 1, tag.length(), stream); mReadPos = (mReadPos + 1) % mEntrySize; } mReadPos = oldReadPos; @@ -377,33 +377,6 @@ string ProfileEntry::TagToString(Profile *profile) return tag; } -void ProfileEntry::WriteTag(Profile *profile, FILE *stream) -{ - fprintf(stream, "%c-%s\n", mTagName, mTagData); - -#ifdef ENABLE_SPS_LEAF_DATA - if (mLeafAddress) { - bool found = false; - SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo(); - unsigned long pc = (unsigned long)mLeafAddress; - // TODO Use binary sort (STL) - for (size_t i = 0; i < shlibInfo.GetSize(); i++) { - SharedLibrary &e = shlibInfo.GetEntry(i); - if (pc > e.GetStart() && pc < e.GetEnd()) { - if (e.GetName()) { - found = true; - fprintf(stream, "l-%s@%li\n", e.GetName(), pc - e.GetStart()); - break; - } - } - } - if (!found) { - fprintf(stream, "l-???@%li\n", pc); - } - } -#endif -} - #define PROFILE_DEFAULT_ENTRY 100000 #define PROFILE_DEFAULT_INTERVAL 10 From 5251d592de9ca11ff25436d56a33a5a8927b8fd7 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Mon, 19 Dec 2011 20:33:00 -0500 Subject: [PATCH 28/66] Bug 711491. Add in conditional backtrace support. r=bgirard This still needs work to be togglable at runtime and is at best a work in progress. --HG-- extra : rebase_source : 59f17aae034799065f5227d68693f4a5c5de3e1a --- tools/profiler/sps/TableTicker.cpp | 93 +++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/tools/profiler/sps/TableTicker.cpp b/tools/profiler/sps/TableTicker.cpp index 215109586a5..ee5126da6a3 100644 --- a/tools/profiler/sps/TableTicker.cpp +++ b/tools/profiler/sps/TableTicker.cpp @@ -46,6 +46,12 @@ #include "shared-libraries.h" #include "mozilla/StringBuilder.h" +// we eventually want to make this runtime switchable +//#define USE_BACKTRACE +#ifdef USE_BACKTRACE +#include +#endif + using std::string; using namespace mozilla; @@ -74,6 +80,7 @@ using namespace mozilla; #define snprintf _snprintf #endif + mozilla::tls::key pkey_stack; mozilla::tls::key pkey_ticker; // We need to track whether we've been initialized otherwise @@ -121,6 +128,7 @@ private: union { const char* mTagData; float mTagFloat; + Address mTagAddress; }; Address mLeafAddress; char mTagName; @@ -137,7 +145,7 @@ public: { mEntries = new ProfileEntry[mEntrySize]; mNeedsSharedLibraryInfo = false; -#ifdef ENABLE_SPS_LEAF_DATA +#if defined(ENABLE_SPS_LEAF_DATA) || defined(USE_BACKTRACE) mNeedsSharedLibraryInfo = true; #endif } @@ -306,6 +314,49 @@ void TableTicker::HandleSaveRequest() NS_DispatchToMainThread(runnable); } +#ifdef USE_BACKTRACE +static +void doBacktrace(Profile &aProfile) +{ + void *array[100]; + int count = backtrace (array, 100); + + bool isSignal = true; +#ifndef __i386__ + // the test doesn't work for 64bit + isSignal = false; +#endif + for (int i = count-1; i >= 0; i--) { + if( isSignal ) { + if( (intptr_t)array[i] == -1 ) { // signal frames have addresses of -1? + isSignal = false; + } + continue; + } + aProfile.addTag(ProfileEntry('l', (const char*)array[i])); + } + aProfile.addTag(ProfileEntry('s', "XRE_Main", 0)); +} +#endif + +static +void doSampleStackTrace(Stack *aStack, Profile &aProfile, TickSample *sample) +{ + // Sample + // 's' tag denotes the start of a sample block + // followed by 0 or more 'c' tags. + for (int i = 0; i < aStack->mStackPointer; i++) { + if (i == 0) { + Address pc = 0; + if (sample) { + pc = sample->pc; + } + aProfile.addTag(ProfileEntry('s', aStack->mStack[i], pc)); + } else { + aProfile.addTag(ProfileEntry('c', aStack->mStack[i])); + } + } +} void TableTicker::Tick(TickSample* sample) { @@ -318,20 +369,11 @@ void TableTicker::Tick(TickSample* sample) } mStack->mQueueClearMarker = true; - // Sample - // 's' tag denotes the start of a sample block - // followed by 0 or more 'c' tags. - for (int i = 0; i < mStack->mStackPointer; i++) { - if (i == 0) { - Address pc = 0; - if (sample) { - pc = sample->pc; - } - mProfile.addTag(ProfileEntry('s', mStack->mStack[i], pc)); - } else { - mProfile.addTag(ProfileEntry('c', mStack->mStack[i])); - } - } +#ifdef USE_BACKTRACE + doBacktrace(mProfile); +#else + doSampleStackTrace(mStack, mProfile, sample); +#endif if (!sLastTracerEvent.IsNull()) { TimeDuration delta = sample->timestamp - sLastTracerEvent; @@ -346,6 +388,27 @@ string ProfileEntry::TagToString(Profile *profile) char buff[50]; snprintf(buff, 50, "%-40f", mTagFloat); tag += string(1, mTagName) + string("-") + string(buff) + string("\n"); + } else if (mTagName == 'l') { + bool found = false; + char tagBuff[1024]; + SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo(); + Address pc = mTagAddress; + // TODO Use binary sort (STL) + for (size_t i = 0; i < shlibInfo.GetSize(); i++) { + SharedLibrary &e = shlibInfo.GetEntry(i); + if (pc > (Address)e.GetStart() && pc < (Address)e.GetEnd()) { + if (e.GetName()) { + found = true; + snprintf(tagBuff, 1024, "l-%s@%p\n", e.GetName(), pc - e.GetStart()); + tag += string(tagBuff); + break; + } + } + } + if (!found) { + snprintf(tagBuff, 1024, "l-???@%p\n", pc); + tag += string(tagBuff); + } } else { tag += string(1, mTagName) + string("-") + string(mTagData) + string("\n"); } From 99b613ab8f7c15f7fa99ccb3a4bbc30feafc3b0a Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Thu, 29 Dec 2011 22:22:21 +0100 Subject: [PATCH 29/66] bug 714066 - Missed FreeChunkList call in JSRuntime::onOutOfMemory. r=wmccloskey --- js/src/jscntxt.cpp | 30 ++------------------ js/src/jscntxt.h | 6 ---- js/src/jsgc.cpp | 71 +++++++++++++++++++++------------------------- js/src/jsgc.h | 3 ++ 4 files changed, 37 insertions(+), 73 deletions(-) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index cc10655ab5c..1b901cb3d00 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -104,7 +104,6 @@ ThreadData::ThreadData(JSRuntime *rt) #ifdef JS_THREADSAFE requestDepth(0), #endif - waiveGCQuota(false), tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), execAlloc(NULL), bumpAlloc(NULL), @@ -1270,34 +1269,9 @@ js_InvokeOperationCallback(JSContext *cx) #endif JS_UNLOCK_GC(rt); - if (rt->gcIsNeeded) { + if (rt->gcIsNeeded) js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason); - /* - * On trace we can exceed the GC quota, see comments in NewGCArena. So - * we check the quota and report OOM here when we are off trace. - */ - if (checkOutOfMemory(rt)) { -#ifdef JS_THREADSAFE - /* - * We have to wait until the background thread is done in order - * to get a correct answer. - */ - { - AutoLockGC lock(rt); - rt->gcHelperThread.waitBackgroundSweepEnd(); - } - if (checkOutOfMemory(rt)) { - js_ReportOutOfMemory(cx); - return false; - } -#else - js_ReportOutOfMemory(cx); - return false; -#endif - } - } - #ifdef JS_THREADSAFE /* * We automatically yield the current context every time the operation @@ -1598,7 +1572,7 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) AutoLockGC lock(this); gcHelperThread.waitBackgroundSweepOrAllocEnd(); #endif - gcChunkPool.expire(this, true); + gcChunkPool.expireAndFree(this, true); } if (!p) p = OffTheBooks::malloc_(nbytes); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 7a878701a9e..59d60ce4506 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -140,12 +140,6 @@ struct ThreadData { /* Keeper of the contiguous stack used by all contexts in this thread. */ StackSpace stackSpace; - /* - * Flag indicating that we are waiving any soft limits on the GC heap - * because we want allocations to be infallible (except when we hit OOM). - */ - bool waiveGCQuota; - /* Temporary arena pool used while compiling and decompiling. */ static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; LifoAlloc tempLifoAlloc; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 84a9f10ce6d..acdadd9dd79 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -517,6 +517,22 @@ ChunkPool::expire(JSRuntime *rt, bool releaseAll) return freeList; } +static void +FreeChunkList(Chunk *chunkListHead) +{ + while (Chunk *chunk = chunkListHead) { + JS_ASSERT(!chunk->info.numArenasFreeCommitted); + chunkListHead = chunk->info.next; + FreeChunk(chunk); + } +} + +void +ChunkPool::expireAndFree(JSRuntime *rt, bool releaseAll) +{ + FreeChunkList(expire(rt, releaseAll)); +} + JS_FRIEND_API(int64_t) ChunkPool::countCleanDecommittedArenas(JSRuntime *rt) { @@ -553,16 +569,6 @@ Chunk::release(JSRuntime *rt, Chunk *chunk) FreeChunk(chunk); } -static void -FreeChunkList(Chunk *chunkListHead) -{ - while (Chunk *chunk = chunkListHead) { - JS_ASSERT(!chunk->info.numArenasFreeCommitted); - chunkListHead = chunk->info.next; - FreeChunk(chunk); - } -} - inline void Chunk::prepareToBeFreed(JSRuntime *rt) { @@ -1182,7 +1188,7 @@ js_FinishGC(JSRuntime *rt) * Finish the pool after the background thread stops in case it was doing * the background sweeping. */ - FreeChunkList(rt->gcChunkPool.expire(rt, true)); + rt->gcChunkPool.expireAndFree(rt, true); #ifdef DEBUG if (!rt->gcRootsHash.empty()) @@ -1641,12 +1647,6 @@ RunLastDitchGC(JSContext *cx) #endif } -inline bool -IsGCAllowed(JSContext *cx) -{ - return !JS_THREAD_DATA(cx)->waiveGCQuota; -} - /* static */ void * ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) { @@ -1658,7 +1658,7 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) bool runGC = !!rt->gcIsNeeded; for (;;) { - if (JS_UNLIKELY(runGC) && IsGCAllowed(cx)) { + if (JS_UNLIKELY(runGC)) { RunLastDitchGC(cx); /* Report OOM of the GC failed to free enough memory. */ @@ -1679,14 +1679,11 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) return thing; /* - * We failed to allocate. Run the GC if we can unless we have done it - * already. Otherwise report OOM but first schedule a new GC soon. + * We failed to allocate. Run the GC if we haven't done it already. + * Otherwise report OOM. */ - if (runGC || !IsGCAllowed(cx)) { - AutoLockGC lock(rt); - TriggerGC(rt, gcstats::REFILL); + if (runGC) break; - } runGC = true; } @@ -2993,12 +2990,10 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) js_PurgeThreads_PostGlobalSweep(cx); #ifdef JS_THREADSAFE - if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { + if (cx->gcBackgroundFree) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); cx->gcBackgroundFree = NULL; rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK); - } else { - JS_ASSERT(!cx->gcBackgroundFree); } #endif @@ -3279,19 +3274,17 @@ void RunDebugGC(JSContext *cx) { #ifdef JS_GC_ZEAL - if (IsGCAllowed(cx)) { - JSRuntime *rt = cx->runtime; + JSRuntime *rt = cx->runtime; - /* - * If rt->gcDebugCompartmentGC is true, only GC the current - * compartment. But don't GC the atoms compartment. - */ - rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; - if (rt->gcTriggerCompartment == rt->atomsCompartment) - rt->gcTriggerCompartment = NULL; - - RunLastDitchGC(cx); - } + /* + * If rt->gcDebugCompartmentGC is true, only GC the current + * compartment. But don't GC the atoms compartment. + */ + rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; + if (rt->gcTriggerCompartment == rt->atomsCompartment) + rt->gcTriggerCompartment = NULL; + + RunLastDitchGC(cx); #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index d4bfa3a09aa..72fdc4db766 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -808,6 +808,9 @@ class ChunkPool { */ Chunk *expire(JSRuntime *rt, bool releaseAll); + /* Must be called with the GC lock taken. */ + void expireAndFree(JSRuntime *rt, bool releaseAll); + /* Must be called either during the GC or with the GC lock taken. */ JS_FRIEND_API(int64_t) countCleanDecommittedArenas(JSRuntime *rt); }; From 16851217f9493a4495bf6e91b64f6d133c3e48a0 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 30 Dec 2011 11:09:16 +1300 Subject: [PATCH 30/66] Backed out changeset c245807aad3a for bug 714066 --- js/src/jscntxt.cpp | 30 ++++++++++++++++++-- js/src/jscntxt.h | 6 ++++ js/src/jsgc.cpp | 71 +++++++++++++++++++++++++--------------------- js/src/jsgc.h | 3 -- 4 files changed, 73 insertions(+), 37 deletions(-) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 1b901cb3d00..cc10655ab5c 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -104,6 +104,7 @@ ThreadData::ThreadData(JSRuntime *rt) #ifdef JS_THREADSAFE requestDepth(0), #endif + waiveGCQuota(false), tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), execAlloc(NULL), bumpAlloc(NULL), @@ -1269,9 +1270,34 @@ js_InvokeOperationCallback(JSContext *cx) #endif JS_UNLOCK_GC(rt); - if (rt->gcIsNeeded) + if (rt->gcIsNeeded) { js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason); + /* + * On trace we can exceed the GC quota, see comments in NewGCArena. So + * we check the quota and report OOM here when we are off trace. + */ + if (checkOutOfMemory(rt)) { +#ifdef JS_THREADSAFE + /* + * We have to wait until the background thread is done in order + * to get a correct answer. + */ + { + AutoLockGC lock(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); + } + if (checkOutOfMemory(rt)) { + js_ReportOutOfMemory(cx); + return false; + } +#else + js_ReportOutOfMemory(cx); + return false; +#endif + } + } + #ifdef JS_THREADSAFE /* * We automatically yield the current context every time the operation @@ -1572,7 +1598,7 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) AutoLockGC lock(this); gcHelperThread.waitBackgroundSweepOrAllocEnd(); #endif - gcChunkPool.expireAndFree(this, true); + gcChunkPool.expire(this, true); } if (!p) p = OffTheBooks::malloc_(nbytes); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 59d60ce4506..7a878701a9e 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -140,6 +140,12 @@ struct ThreadData { /* Keeper of the contiguous stack used by all contexts in this thread. */ StackSpace stackSpace; + /* + * Flag indicating that we are waiving any soft limits on the GC heap + * because we want allocations to be infallible (except when we hit OOM). + */ + bool waiveGCQuota; + /* Temporary arena pool used while compiling and decompiling. */ static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; LifoAlloc tempLifoAlloc; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index acdadd9dd79..84a9f10ce6d 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -517,22 +517,6 @@ ChunkPool::expire(JSRuntime *rt, bool releaseAll) return freeList; } -static void -FreeChunkList(Chunk *chunkListHead) -{ - while (Chunk *chunk = chunkListHead) { - JS_ASSERT(!chunk->info.numArenasFreeCommitted); - chunkListHead = chunk->info.next; - FreeChunk(chunk); - } -} - -void -ChunkPool::expireAndFree(JSRuntime *rt, bool releaseAll) -{ - FreeChunkList(expire(rt, releaseAll)); -} - JS_FRIEND_API(int64_t) ChunkPool::countCleanDecommittedArenas(JSRuntime *rt) { @@ -569,6 +553,16 @@ Chunk::release(JSRuntime *rt, Chunk *chunk) FreeChunk(chunk); } +static void +FreeChunkList(Chunk *chunkListHead) +{ + while (Chunk *chunk = chunkListHead) { + JS_ASSERT(!chunk->info.numArenasFreeCommitted); + chunkListHead = chunk->info.next; + FreeChunk(chunk); + } +} + inline void Chunk::prepareToBeFreed(JSRuntime *rt) { @@ -1188,7 +1182,7 @@ js_FinishGC(JSRuntime *rt) * Finish the pool after the background thread stops in case it was doing * the background sweeping. */ - rt->gcChunkPool.expireAndFree(rt, true); + FreeChunkList(rt->gcChunkPool.expire(rt, true)); #ifdef DEBUG if (!rt->gcRootsHash.empty()) @@ -1647,6 +1641,12 @@ RunLastDitchGC(JSContext *cx) #endif } +inline bool +IsGCAllowed(JSContext *cx) +{ + return !JS_THREAD_DATA(cx)->waiveGCQuota; +} + /* static */ void * ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) { @@ -1658,7 +1658,7 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) bool runGC = !!rt->gcIsNeeded; for (;;) { - if (JS_UNLIKELY(runGC)) { + if (JS_UNLIKELY(runGC) && IsGCAllowed(cx)) { RunLastDitchGC(cx); /* Report OOM of the GC failed to free enough memory. */ @@ -1679,11 +1679,14 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) return thing; /* - * We failed to allocate. Run the GC if we haven't done it already. - * Otherwise report OOM. + * We failed to allocate. Run the GC if we can unless we have done it + * already. Otherwise report OOM but first schedule a new GC soon. */ - if (runGC) + if (runGC || !IsGCAllowed(cx)) { + AutoLockGC lock(rt); + TriggerGC(rt, gcstats::REFILL); break; + } runGC = true; } @@ -2990,10 +2993,12 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) js_PurgeThreads_PostGlobalSweep(cx); #ifdef JS_THREADSAFE - if (cx->gcBackgroundFree) { + if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); cx->gcBackgroundFree = NULL; rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK); + } else { + JS_ASSERT(!cx->gcBackgroundFree); } #endif @@ -3274,17 +3279,19 @@ void RunDebugGC(JSContext *cx) { #ifdef JS_GC_ZEAL - JSRuntime *rt = cx->runtime; + if (IsGCAllowed(cx)) { + JSRuntime *rt = cx->runtime; - /* - * If rt->gcDebugCompartmentGC is true, only GC the current - * compartment. But don't GC the atoms compartment. - */ - rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; - if (rt->gcTriggerCompartment == rt->atomsCompartment) - rt->gcTriggerCompartment = NULL; - - RunLastDitchGC(cx); + /* + * If rt->gcDebugCompartmentGC is true, only GC the current + * compartment. But don't GC the atoms compartment. + */ + rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; + if (rt->gcTriggerCompartment == rt->atomsCompartment) + rt->gcTriggerCompartment = NULL; + + RunLastDitchGC(cx); + } #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 72fdc4db766..d4bfa3a09aa 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -808,9 +808,6 @@ class ChunkPool { */ Chunk *expire(JSRuntime *rt, bool releaseAll); - /* Must be called with the GC lock taken. */ - void expireAndFree(JSRuntime *rt, bool releaseAll); - /* Must be called either during the GC or with the GC lock taken. */ JS_FRIEND_API(int64_t) countCleanDecommittedArenas(JSRuntime *rt); }; From fda2a403ee086902a652d7436ed7a27913a8dc8e Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Wed, 28 Dec 2011 16:24:18 +1300 Subject: [PATCH 31/66] Bug 591718. Part 1: rename some poorly-named methods, rework global-transform methods to avoid computing bounding-boxes more than once when there are are multiple transformed ancestors, make sure nsIFrame::GetTransformMatrix can stop at any desired ancestor. r=mats --- layout/base/nsDisplayList.cpp | 2 +- layout/base/nsLayoutUtils.cpp | 97 +++++++++++-------- layout/base/nsLayoutUtils.h | 22 +++-- layout/forms/nsComboboxControlFrame.cpp | 4 +- layout/generic/nsFrame.cpp | 8 +- layout/generic/nsIFrame.h | 21 ++-- .../svg/base/src/nsSVGForeignObjectFrame.cpp | 3 +- layout/svg/base/src/nsSVGForeignObjectFrame.h | 3 +- 8 files changed, 95 insertions(+), 65 deletions(-) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 50d8f6afa1b..a3eadf257b2 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -460,7 +460,7 @@ GetDisplayPortBounds(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) } const nsRect* displayport = aBuilder->GetDisplayPort(); - nsRect result = nsLayoutUtils::TransformRectToBoundsInAncestor( + nsRect result = nsLayoutUtils::TransformAncestorRectToFrame( frame, nsRect(0, 0, displayport->width, displayport->height), aBuilder->ReferenceFrame()); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 2ac11bea1d7..753384b96c8 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -975,7 +975,7 @@ nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aF * out how to convert back to aFrame's coordinates and must use the CTM. */ if (transformFound) - return InvertTransformsToRoot(aFrame, widgetToView); + return TransformRootPointToFrame(aFrame, widgetToView); /* Otherwise, all coordinate systems are translations of one another, * so we can just subtract out the different. @@ -1117,68 +1117,62 @@ nsLayoutUtils::MatrixTransformPoint(const nsPoint &aPoint, NSFloatPixelsToAppUnits(float(image.y), aFactor)); } -static gfxPoint -InvertTransformsToAncestor(nsIFrame *aFrame, - const gfxPoint &aPoint, - nsIFrame *aStopAtAncestor = nsnull) +static gfx3DMatrix +GetTransformToAncestor(nsIFrame *aFrame, nsIFrame *aAncestor) { - NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?"); - - /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform - * the point. - */ - nsIFrame *parent = nsnull; - gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent); - gfxPoint result = aPoint; - - if (parent && parent != aStopAtAncestor) { - result = InvertTransformsToAncestor(parent, aPoint, aStopAtAncestor); + nsIFrame* parent; + gfx3DMatrix ctm = aFrame->GetTransformMatrix(aAncestor, &parent); + while (parent && parent != aAncestor) { + ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent); } + return ctm; +} - result = ctm.Inverse().ProjectPoint(result); - return result; +static gfxPoint +TransformGfxPointFromAncestor(nsIFrame *aFrame, + const gfxPoint &aPoint, + nsIFrame *aAncestor) +{ + gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor); + return ctm.Inverse().ProjectPoint(aPoint); } static gfxRect -InvertGfxRectToAncestor(nsIFrame *aFrame, - const gfxRect &aRect, - nsIFrame *aStopAtAncestor = nsnull) +TransformGfxRectFromAncestor(nsIFrame *aFrame, + const gfxRect &aRect, + nsIFrame *aAncestor) { - NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?"); + gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor); + return ctm.Inverse().ProjectRectBounds(aRect); +} - /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform - * the point. - */ - nsIFrame *parent = nsnull; - gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent); - gfxRect result = aRect; - - if (parent && parent != aStopAtAncestor) { - result = InvertGfxRectToAncestor(parent, aRect, aStopAtAncestor); - } - - result = ctm.Inverse().ProjectRectBounds(result); - return result; +static gfxRect +TransformGfxRectToAncestor(nsIFrame *aFrame, + const gfxRect &aRect, + nsIFrame *aAncestor) +{ + gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor); + return ctm.ProjectRectBounds(aRect); } nsPoint -nsLayoutUtils::InvertTransformsToRoot(nsIFrame *aFrame, - const nsPoint &aPoint) +nsLayoutUtils::TransformRootPointToFrame(nsIFrame *aFrame, + const nsPoint &aPoint) { float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor), NSAppUnitsToFloatPixels(aPoint.y, factor)); - result = InvertTransformsToAncestor(aFrame, result); + result = TransformGfxPointFromAncestor(aFrame, result, nsnull); return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor), NSFloatPixelsToAppUnits(float(result.y), factor)); } nsRect -nsLayoutUtils::TransformRectToBoundsInAncestor(nsIFrame* aFrame, - const nsRect &aRect, - nsIFrame* aStopAtAncestor) +nsLayoutUtils::TransformAncestorRectToFrame(nsIFrame* aFrame, + const nsRect &aRect, + nsIFrame* aAncestor) { float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor), @@ -1186,7 +1180,7 @@ nsLayoutUtils::TransformRectToBoundsInAncestor(nsIFrame* aFrame, NSAppUnitsToFloatPixels(aRect.width, factor), NSAppUnitsToFloatPixels(aRect.height, factor)); - result = InvertGfxRectToAncestor(aFrame, result, aStopAtAncestor); + result = TransformGfxRectFromAncestor(aFrame, result, aAncestor); return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor), NSFloatPixelsToAppUnits(float(result.y), factor), @@ -1194,6 +1188,25 @@ nsLayoutUtils::TransformRectToBoundsInAncestor(nsIFrame* aFrame, NSFloatPixelsToAppUnits(float(result.height), factor)); } +nsRect +nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame, + const nsRect& aRect, + nsIFrame* aAncestor) +{ + float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); + gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor), + NSAppUnitsToFloatPixels(aRect.y, factor), + NSAppUnitsToFloatPixels(aRect.width, factor), + NSAppUnitsToFloatPixels(aRect.height, factor)); + + result = TransformGfxRectToAncestor(aFrame, result, aAncestor); + + return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor), + NSFloatPixelsToAppUnits(float(result.y), factor), + NSFloatPixelsToAppUnits(float(result.width), factor), + NSFloatPixelsToAppUnits(float(result.height), factor)); +} + static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) { nsIntPoint offset(0, 0); nsIWidget* parent = aWidget->GetParent(); diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index fc53096a714..1396a99f49c 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -515,11 +515,21 @@ public: bool aShouldIgnoreSuppression = false, bool aIgnoreRootScrollFrame = false); - + /** + * Transform aRect relative to aAncestor down to the coordinate system of + * aFrame. Computes the bounding-box of the true quadrilateral. + */ + static nsRect TransformAncestorRectToFrame(nsIFrame* aFrame, + const nsRect& aRect, + nsIFrame* aAncestor); - static nsRect TransformRectToBoundsInAncestor(nsIFrame* aFrame, - const nsRect& aRect, - nsIFrame* aStopAtAncestor); + /** + * Transform aRect relative to aFrame up to the coordinate system of + * aAncestor. Computes the bounding-box of the true quadrilateral. + */ + static nsRect TransformFrameRectToAncestor(nsIFrame* aFrame, + const nsRect& aRect, + nsIFrame* aAncestor); /** * Given a point in the global coordinate space, returns that point expressed @@ -530,8 +540,8 @@ public: * @param aPoint The point, in the global space, to get in the frame-local space. * @return aPoint, expressed in aFrame's canonical coordinate space. */ - static nsPoint InvertTransformsToRoot(nsIFrame* aFrame, - const nsPoint &aPt); + static nsPoint TransformRootPointToFrame(nsIFrame* aFrame, + const nsPoint &aPt); /** * Helper function that, given a rectangle and a matrix, returns the smallest diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp index f74f1e789fc..b232bab9366 100644 --- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -550,8 +550,8 @@ nsComboboxControlFrame::GetCSSTransformTranslation() bool is3DTransform = false; gfxMatrix transform; while (frame) { - nsIFrame* parent = nsnull; - gfx3DMatrix ctm = frame->GetTransformMatrix(&parent); + nsIFrame* parent; + gfx3DMatrix ctm = frame->GetTransformMatrix(nsnull, &parent); gfxMatrix matrix; if (ctm.Is2D(&matrix)) { transform = transform * matrix; diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index b5f78934dd3..ae69c9064ba 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4541,7 +4541,8 @@ nsIFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY, } gfx3DMatrix -nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor) +nsIFrame::GetTransformMatrix(nsIFrame* aStopAtAncestor, + nsIFrame** aOutAncestor) { NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!"); @@ -4553,7 +4554,8 @@ nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor) /* Compute the delta to the parent, which we need because we are converting * coordinates to our parent. */ - NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), "Cannot transform the viewport frame!"); + NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), + "Cannot transform the viewport frame!"); PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel(); gfx3DMatrix result = @@ -4582,7 +4584,7 @@ nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor) return gfx3DMatrix(); /* Keep iterating while the frame can't possibly be transformed. */ - while (!(*aOutAncestor)->IsTransformed()) { + while (!(*aOutAncestor)->IsTransformed() && *aOutAncestor != aStopAtAncestor) { /* If no parent, stop iterating. Otherwise, update the ancestor. */ nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor); if (!parent) diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 3dfeecc8710..f44a90f795d 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1932,17 +1932,20 @@ public: virtual nsIAtom* GetType() const = 0; /** - * Returns a transformation matrix that converts points in this frame's coordinate space - * to points in some ancestor frame's coordinate space. The frame decides which ancestor - * it will use as a reference point. If this frame has no ancestor, aOutAncestor will be - * set to null. + * Returns a transformation matrix that converts points in this frame's + * coordinate space to points in some ancestor frame's coordinate space. + * The frame decides which ancestor it will use as a reference point. + * If this frame has no ancestor, aOutAncestor will be set to null. * - * @param aOutAncestor [out] The ancestor frame the frame has chosen. If this frame has no - * ancestor, aOutAncestor will be nsnull. - * @return A gfxMatrix that converts points in this frame's coordinate space into - * points in aOutAncestor's coordinate space. + * @param aStopAtAncestor don't look further than aStopAtAncestor. If null, + * all ancestors (including across documents) will be traversed. + * @param aOutAncestor [out] The ancestor frame the frame has chosen. If + * this frame has no ancestor, *aOutAncestor will be set to null. + * @return A gfxMatrix that converts points in this frame's coordinate space + * into points in aOutAncestor's coordinate space. */ - virtual gfx3DMatrix GetTransformMatrix(nsIFrame **aOutAncestor); + virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aStopAtAncestor, + nsIFrame **aOutAncestor); /** * Bit-flags to pass to IsFrameOfType() diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp index ab488d4d090..d6c1045205b 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -283,7 +283,8 @@ nsSVGForeignObjectFrame::PaintSVG(nsSVGRenderState *aContext, } gfx3DMatrix -nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame **aOutAncestor) +nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame* aAncestor, + nsIFrame **aOutAncestor) { NS_PRECONDITION(aOutAncestor, "We need an ancestor to write to!"); diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.h b/layout/svg/base/src/nsSVGForeignObjectFrame.h index c73f062ce7f..6f5914f71ce 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.h +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h @@ -93,7 +93,8 @@ public: /** * Foreign objects can return a transform matrix. */ - virtual gfx3DMatrix GetTransformMatrix(nsIFrame **aOutAncestor); + virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aAncestor, + nsIFrame **aOutAncestor); /** * Get the "type" of the frame From 8a29fd912e4b1100f1d35ad0650ad83a64702293 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Wed, 28 Dec 2011 16:24:29 +1300 Subject: [PATCH 32/66] Bug 591718. Make nsLayoutUtils::GetAllInFlowRectsUnion support taking transforms into account when converting coordinates. r=mats --- layout/base/nsLayoutUtils.cpp | 39 +++++++++++++++++++---------------- layout/base/nsLayoutUtils.h | 18 ++++++++++++---- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 753384b96c8..6c54f6a11be 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1847,27 +1847,35 @@ nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback) } struct BoxToBorderRect : public nsLayoutUtils::BoxCallback { - nsIFrame* mRelativeTo; + nsIFrame* mRelativeTo; nsLayoutUtils::RectCallback* mCallback; + PRUint32 mFlags; - BoxToBorderRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback) - : mRelativeTo(aRelativeTo), mCallback(aCallback) {} + BoxToBorderRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback, + PRUint32 aFlags) + : mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags) {} virtual void AddBox(nsIFrame* aFrame) { nsRect r; nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r); - if (outer) { - mCallback->AddRect(r + outer->GetOffsetTo(mRelativeTo)); - } else - mCallback->AddRect(nsRect(aFrame->GetOffsetTo(mRelativeTo), aFrame->GetSize())); + if (!outer) { + outer = aFrame; + r = nsRect(nsPoint(0, 0), aFrame->GetSize()); + } + if (mFlags & nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS) { + r = nsLayoutUtils::TransformFrameRectToAncestor(outer, r, mRelativeTo); + } else { + r += outer->GetOffsetTo(mRelativeTo); + } + mCallback->AddRect(r); } }; void nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo, - RectCallback* aCallback) + RectCallback* aCallback, PRUint32 aFlags) { - BoxToBorderRect converter(aRelativeTo, aCallback); + BoxToBorderRect converter(aRelativeTo, aCallback, aFlags); GetAllInFlowBoxes(aFrame, &converter); } @@ -1893,19 +1901,14 @@ void nsLayoutUtils::RectListBuilder::AddRect(const nsRect& aRect) { nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame) { - // get the nearest enclosing SVG foreign object frame or the root frame - while (aFrame->GetParent() && - !aFrame->IsFrameOfType(nsIFrame::eSVGForeignObject)) { - aFrame = aFrame->GetParent(); - } - - return aFrame; + return aFrame->PresContext()->PresShell()->GetRootFrame(); } nsRect -nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo) { +nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo, + PRUint32 aFlags) { RectAccumulator accumulator; - GetAllInFlowRects(aFrame, aRelativeTo, &accumulator); + GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags); return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect : accumulator.mResultRect; } diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 1396a99f49c..3b355bf397d 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -704,8 +704,8 @@ public: }; struct RectAccumulator : public RectCallback { - nsRect mResultRect; - nsRect mFirstRect; + nsRect mResultRect; + nsRect mFirstRect; bool mSeenFirstRect; RectAccumulator(); @@ -723,6 +723,9 @@ public: static nsIFrame* GetContainingBlockForClientRect(nsIFrame* aFrame); + enum { + RECTS_ACCOUNT_FOR_TRANSFORMS = 0x01 + }; /** * Collect all CSS border-boxes associated with aFrame and its * continuations, "drilling down" through outer table frames and @@ -731,15 +734,22 @@ public: * into account) and passed to the callback in frame-tree order. * If aFrame is null, no boxes are returned. * For SVG frames, returns one rectangle, the bounding box. + * If aFlags includes RECTS_ACCOUNT_FOR_TRANSFORMS, then when converting + * the boxes into aRelativeTo coordinates, transforms (including CSS + * and SVG transforms) are taken into account. */ static void GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo, - RectCallback* aCallback); + RectCallback* aCallback, PRUint32 aFlags = 0); /** * Computes the union of all rects returned by GetAllInFlowRects. If * the union is empty, returns the first rect. + * If aFlags includes RECTS_ACCOUNT_FOR_TRANSFORMS, then when converting + * the boxes into aRelativeTo coordinates, transforms (including CSS + * and SVG transforms) are taken into account. */ - static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo); + static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo, + PRUint32 aFlags = 0); enum { EXCLUDE_BLUR_SHADOWS = 0x01 From bec96e8d29ca7391cf8c4ceb40121d0d31a9096e Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Wed, 28 Dec 2011 16:26:46 +1300 Subject: [PATCH 33/66] Bug 591718. Make getClientRects/getBoundingClientRect take transforms into account, and not treat SVG as establishing a new viewport. r=mats --- content/base/src/nsGenericElement.cpp | 6 +- dom/tests/mochitest/general/Makefile.in | 1 + .../mochitest/general/test_clientRects.html | 125 ++++++++++++++++++ layout/base/tests/test_bug677878.html | 4 +- 4 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 dom/tests/mochitest/general/test_clientRects.html diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 49e7610c94d..4195f7e4f19 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -2136,7 +2136,8 @@ nsGenericElement::GetBoundingClientRect(nsIDOMClientRect** aResult) } nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(frame, - nsLayoutUtils::GetContainingBlockForClientRect(frame)); + nsLayoutUtils::GetContainingBlockForClientRect(frame), + nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS); rect->SetLayoutRect(r); return NS_OK; } @@ -2164,7 +2165,8 @@ nsGenericElement::GetClientRects(nsIDOMClientRectList** aResult) nsLayoutUtils::RectListBuilder builder(rectList); nsLayoutUtils::GetAllInFlowRects(frame, - nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder); + nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder, + nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS); if (NS_FAILED(builder.mRV)) return builder.mRV; *aResult = rectList.forget().get(); diff --git a/dom/tests/mochitest/general/Makefile.in b/dom/tests/mochitest/general/Makefile.in index a25019cc09c..0b359ce6577 100644 --- a/dom/tests/mochitest/general/Makefile.in +++ b/dom/tests/mochitest/general/Makefile.in @@ -58,6 +58,7 @@ _TEST_FILES = \ test_bug631440.html \ test_bug653364.html \ test_bug629535.html \ + test_clientRects.html \ test_consoleAPI.html \ test_domWindowUtils.html \ test_domWindowUtils_scrollXY.html \ diff --git a/dom/tests/mochitest/general/test_clientRects.html b/dom/tests/mochitest/general/test_clientRects.html new file mode 100644 index 00000000000..de4a8bdfbb8 --- /dev/null +++ b/dom/tests/mochitest/general/test_clientRects.html @@ -0,0 +1,125 @@ + + + + Tests for getClientRects/getBoundingClientRect + + + + + + + + +
+ +
+
+
+
+
+
+ + + +
+
+
+ + + +
+
+ + +
+ +
+
+
+ +
+
+
+
+
+ +
+ + + + + + + + + + +

+ + + + diff --git a/layout/base/tests/test_bug677878.html b/layout/base/tests/test_bug677878.html index db34829d29a..f972546c3ba 100644 --- a/layout/base/tests/test_bug677878.html +++ b/layout/base/tests/test_bug677878.html @@ -36,7 +36,9 @@ runtests(); function runtests() { function doClick() { document.getElementById("test2").addEventListener("mousedown", testFinish, true); - synthesizeMouseAtCenter(document.getElementById("test2"), { type: "mousedown" }) + // Don't target the center because the center could actually be outside the + // viewport. + synthesizeMouse(document.getElementById("test2"), 10, 10, { type: "mousedown" }) } setTimeout(doClick, 300); } From f1f309c2f620040d0590930637eaf0aee2c55c2e Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 30 Dec 2011 11:14:04 +1300 Subject: [PATCH 34/66] Bug 591718. Fix mobile browser test to handle getBoundingClientRect including a transform. r=mfinkle --- mobile/android/chrome/tests/browser_scrollbar.js | 4 +++- mobile/xul/chrome/tests/browser_scrollbar.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mobile/android/chrome/tests/browser_scrollbar.js b/mobile/android/chrome/tests/browser_scrollbar.js index 008d43c35f9..231b6fde4c6 100644 --- a/mobile/android/chrome/tests/browser_scrollbar.js +++ b/mobile/android/chrome/tests/browser_scrollbar.js @@ -65,7 +65,9 @@ function checkScrollbarsPosition(aX) { let verticalRect = verticalScrollbar.getBoundingClientRect(); let margin = parseInt(verticalScrollbar.getAttribute("end")); - let expectedPosition = window.innerWidth - aX - margin; + let matches = verticalScrollbar.style.MozTransform.match(/^translate\(([-0-9]+)px/); + let translateX = matches ? parseInt(matches[1]) : 0; + let expectedPosition = window.innerWidth - aX - margin + translateX; is(verticalRect.right, expectedPosition, "The vertical scrollbar should be position to " + expectedPosition + " (got " + verticalRect.right + ")"); EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" }); diff --git a/mobile/xul/chrome/tests/browser_scrollbar.js b/mobile/xul/chrome/tests/browser_scrollbar.js index 008d43c35f9..231b6fde4c6 100644 --- a/mobile/xul/chrome/tests/browser_scrollbar.js +++ b/mobile/xul/chrome/tests/browser_scrollbar.js @@ -65,7 +65,9 @@ function checkScrollbarsPosition(aX) { let verticalRect = verticalScrollbar.getBoundingClientRect(); let margin = parseInt(verticalScrollbar.getAttribute("end")); - let expectedPosition = window.innerWidth - aX - margin; + let matches = verticalScrollbar.style.MozTransform.match(/^translate\(([-0-9]+)px/); + let translateX = matches ? parseInt(matches[1]) : 0; + let expectedPosition = window.innerWidth - aX - margin + translateX; is(verticalRect.right, expectedPosition, "The vertical scrollbar should be position to " + expectedPosition + " (got " + verticalRect.right + ")"); EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" }); From 2e7c75854ec509ecff8ee69dba7076c84beaf9db Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Wed, 28 Dec 2011 10:48:54 -0600 Subject: [PATCH 35/66] Add introductory comments to the files in mfbt/ that aren't copied from elsewhere, to facilitate easier MXR directory skimming. No bug, r=lumpy --HG-- extra : rebase_source : dd834c64e625a0184b64d13e2a2e6fd8960ca832 --- mfbt/Assertions.h | 2 ++ mfbt/RangedPtr.h | 5 +++++ mfbt/RefPtr.h | 6 ++---- mfbt/Util.h | 5 +++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/mfbt/Assertions.h b/mfbt/Assertions.h index 3fb7f33e1e8..e01194bb7a5 100644 --- a/mfbt/Assertions.h +++ b/mfbt/Assertions.h @@ -38,6 +38,8 @@ * * ***** END LICENSE BLOCK ***** */ +/* Implementations of runtime and static assertion macros for C and C++. */ + #ifndef mozilla_Assertions_h_ #define mozilla_Assertions_h_ diff --git a/mfbt/RangedPtr.h b/mfbt/RangedPtr.h index dd1e76abf97..74ad1c75e45 100644 --- a/mfbt/RangedPtr.h +++ b/mfbt/RangedPtr.h @@ -38,6 +38,11 @@ * * ***** END LICENSE BLOCK ***** */ +/* + * Implements a smart pointer asserted to remain within a range specified at + * construction. + */ + #ifndef mozilla_RangedPtr_h_ #define mozilla_RangedPtr_h_ diff --git a/mfbt/RefPtr.h b/mfbt/RefPtr.h index ebb1a41dc3b..8c3ff94f102 100644 --- a/mfbt/RefPtr.h +++ b/mfbt/RefPtr.h @@ -38,16 +38,14 @@ * * ***** END LICENSE BLOCK ***** */ +/* Helpers for defining and using refcounted objects. */ + #ifndef mozilla_RefPtr_h_ #define mozilla_RefPtr_h_ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" -/** - * Helpers for defining and using refcounted objects. - */ - namespace mozilla { template class RefCounted; diff --git a/mfbt/Util.h b/mfbt/Util.h index cb28c0b5b71..a573b9e2283 100644 --- a/mfbt/Util.h +++ b/mfbt/Util.h @@ -37,6 +37,11 @@ * * ***** END LICENSE BLOCK ***** */ +/* + * Miscellaneous uncategorized functionality. Please add new functionality to + * new headers, or to other appropriate existing headers, not here. + */ + #ifndef mozilla_Util_h_ #define mozilla_Util_h_ From 64e5e4be686adfd4116df976bf3a72525c8087ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hub=20Figui=C3=A8re?= Date: Thu, 29 Dec 2011 13:37:04 -0800 Subject: [PATCH 36/66] Bug 714169 - Fix warning in HashTable.h by using |continue;| instead of |;|. r=jwalden --HG-- extra : rebase_source : 176f652ee0e2c23b2c995b51814e4e1fd1ae0149 --- js/public/HashTable.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/public/HashTable.h b/js/public/HashTable.h index 8245ebf7192..f4542abf0b0 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -201,7 +201,8 @@ class HashTable : private AllocPolicy void popFront() { JS_ASSERT(!empty()); - while (++cur != end && !cur->isLive()); + while (++cur != end && !cur->isLive()) + continue; } }; From 58628863925ba9c30a12f998bd2022966e65f8dc Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 29 Dec 2011 15:50:33 -0600 Subject: [PATCH 37/66] Use < rather than != when comparing a bunch of pointers to sentinel end-pointer values, as a small hedge against things going awry and skipping past the sentinel. No bug, r=firebot --HG-- extra : rebase_source : 567f70397ec9ef006c55c01f3acde305ab527b56 --- js/public/HashTable.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/js/public/HashTable.h b/js/public/HashTable.h index f4542abf0b0..648e4e93c62 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -181,7 +181,7 @@ class HashTable : private AllocPolicy friend class HashTable; Range(Entry *c, Entry *e) : cur(c), end(e) { - while (cur != end && !cur->isLive()) + while (cur < end && !cur->isLive()) ++cur; } @@ -201,7 +201,7 @@ class HashTable : private AllocPolicy void popFront() { JS_ASSERT(!empty()); - while (++cur != end && !cur->isLive()) + while (++cur < end && !cur->isLive()) continue; } }; @@ -343,14 +343,14 @@ class HashTable : private AllocPolicy Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry)); if (!newTable) return NULL; - for (Entry *e = newTable, *end = e + capacity; e != end; ++e) + for (Entry *e = newTable, *end = e + capacity; e < end; ++e) new(e) Entry(); return newTable; } static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity) { - for (Entry *e = oldTable, *end = e + capacity; e != end; ++e) + for (Entry *e = oldTable, *end = e + capacity; e < end; ++e) e->~Entry(); alloc.free_(oldTable); } @@ -566,7 +566,7 @@ class HashTable : private AllocPolicy table = newTable; /* Copy only live entries, leaving removed ones behind. */ - for (Entry *src = oldTable, *end = src + oldCap; src != end; ++src) { + for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) { if (src->isLive()) { src->unsetCollision(); findFreeEntry(src->getKeyHash()) = Move(*src); @@ -608,7 +608,7 @@ class HashTable : private AllocPolicy memset(table, 0, sizeof(*table) * capacity()); } else { uint32_t tableCapacity = capacity(); - for (Entry *e = table, *end = table + tableCapacity; e != end; ++e) + for (Entry *e = table, *end = table + tableCapacity; e < end; ++e) *e = Move(Entry()); } removedCount = 0; From 6f1d572a55a6f1bf28fdda5c3fbb5b606635743e Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 29 Dec 2011 14:39:25 -0800 Subject: [PATCH 38/66] Bug 714183: Import downstream widget/src/gonk changes from b2g. rs=cjones,mwu --- widget/src/gonk/nsAppShell.cpp | 43 +++++++++++++++++++--------------- widget/src/gonk/nsAppShell.h | 3 +++ 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/widget/src/gonk/nsAppShell.cpp b/widget/src/gonk/nsAppShell.cpp index 0e57c7ebdb3..6b4fc8419e7 100644 --- a/widget/src/gonk/nsAppShell.cpp +++ b/widget/src/gonk/nsAppShell.cpp @@ -45,17 +45,18 @@ #include #include #include -#include #include #include +#include #include +#include "mozilla/Hal.h" +#include "mozilla/Services.h" #include "nsAppShell.h" #include "nsGkAtoms.h" #include "nsGUIEvent.h" -#include "nsWindow.h" #include "nsIObserverService.h" -#include "mozilla/Services.h" +#include "nsWindow.h" #include "android/log.h" @@ -399,11 +400,6 @@ nsAppShell::~nsAppShell() nsresult nsAppShell::Init() { - epoll_event event = { - EPOLLIN, - { 0 } - }; - nsresult rv = nsBaseAppShell::Init(); NS_ENSURE_SUCCESS(rv, rv); @@ -413,12 +409,8 @@ nsAppShell::Init() int ret = pipe2(signalfds, O_NONBLOCK); NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED); - FdHandler *handler = mHandlers.AppendElement(); - handler->fd = signalfds[0]; - handler->func = pipeHandler; - event.data.u32 = mHandlers.Length() - 1; - ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, signalfds[0], &event); - NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED); + rv = AddFdHandler(signalfds[0], pipeHandler); + NS_ENSURE_SUCCESS(rv, rv); DIR *dir = opendir("/dev/input"); NS_ENSURE_TRUE(dir, NS_ERROR_UNEXPECTED); @@ -461,17 +453,30 @@ nsAppShell::Init() if (!handlerFunc) continue; - handler = mHandlers.AppendElement(); - handler->fd = fd; - handler->func = handlerFunc; - event.data.u32 = mHandlers.Length() - 1; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event)) + rv = AddFdHandler(fd, handlerFunc); + if (NS_FAILED(rv)) LOG("Failed to add fd to epoll fd"); } return rv; } +nsresult +nsAppShell::AddFdHandler(int fd, FdHandlerCallback handlerFunc) +{ + epoll_event event = { + EPOLLIN, + { 0 } + }; + + FdHandler *handler = mHandlers.AppendElement(); + handler->fd = fd; + handler->func = handlerFunc; + event.data.u32 = mHandlers.Length() - 1; + return epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) ? + NS_ERROR_UNEXPECTED : NS_OK; +} + void nsAppShell::ScheduleNativeEventCallback() { diff --git a/widget/src/gonk/nsAppShell.h b/widget/src/gonk/nsAppShell.h index bb82c279da7..37337c733c9 100644 --- a/widget/src/gonk/nsAppShell.h +++ b/widget/src/gonk/nsAppShell.h @@ -86,6 +86,9 @@ protected: virtual void ScheduleNativeEventCallback(); +private: + nsresult AddFdHandler(int fd, FdHandlerCallback handlerFunc); + // This is somewhat racy but is perfectly safe given how the callback works bool mNativeCallbackRequest; nsTArray mHandlers; From b2e1818fca9633e7b8b499a90064a2000941a472 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 29 Dec 2011 14:39:25 -0800 Subject: [PATCH 39/66] Followup to bug 708154: Fix review comments. r=mwu --- widget/src/gonk/nsAppShell.cpp | 3 +-- widget/src/gonk/nsWindow.cpp | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/widget/src/gonk/nsAppShell.cpp b/widget/src/gonk/nsAppShell.cpp index 6b4fc8419e7..5af89fef0c3 100644 --- a/widget/src/gonk/nsAppShell.cpp +++ b/widget/src/gonk/nsAppShell.cpp @@ -328,8 +328,7 @@ singleTouchHandler(int fd, FdHandler *data) default: maybeSendKeyEvent(*event); } - } - else if (event->type == EV_ABS) { + } else if (event->type == EV_ABS) { switch (event->code) { case ABS_X: data->mtX = event->value; diff --git a/widget/src/gonk/nsWindow.cpp b/widget/src/gonk/nsWindow.cpp index 48d149d4567..c9daf2e0049 100644 --- a/widget/src/gonk/nsWindow.cpp +++ b/widget/src/gonk/nsWindow.cpp @@ -78,7 +78,7 @@ nsWindow::nsWindow() sGLContext = GLContextProvider::CreateForWindow(this); // CreateForWindow sets up gScreenBounds if (!sGLContext) { - LOG("Failed to create GL context for fb, trying /dev/fb0"); + LOG("Failed to create GL context for fb, trying /dev/graphics/fb0"); // We can't delete gNativeWindow. @@ -87,7 +87,7 @@ nsWindow::nsWindow() gScreenBounds = nsIntRect(nsIntPoint(0, 0), screenSize); if (!sFramebufferOpen) { LOG("Failed to mmap fb(?!?), aborting ..."); - NS_RUNTIMEABORT("Can't open GL context and can't fall back on /dev/fb0 ..."); + NS_RUNTIMEABORT("Can't open GL context and can't fall back on /dev/graphics/fb0 ..."); } } } From 8eefe4004de432889f4b74c05ba5330bce22911c Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 29 Dec 2011 14:39:25 -0800 Subject: [PATCH 40/66] Bug 713168: Add support for different screen/input-device resolutions and virtual buttons. Temporary. rs=mwu --- widget/src/gonk/nsAppShell.cpp | 284 ++++++++++++++++++++++++++------- widget/src/gonk/nsAppShell.h | 50 +++++- 2 files changed, 270 insertions(+), 64 deletions(-) diff --git a/widget/src/gonk/nsAppShell.cpp b/widget/src/gonk/nsAppShell.cpp index 5af89fef0c3..ce5229b5501 100644 --- a/widget/src/gonk/nsAppShell.cpp +++ b/widget/src/gonk/nsAppShell.cpp @@ -40,6 +40,7 @@ #define _GNU_SOURCE #include +#include #include #include #include @@ -50,7 +51,8 @@ #include #include -#include "mozilla/Hal.h" +#include "nscore.h" +#include "mozilla/FileUtils.h" #include "mozilla/Services.h" #include "nsAppShell.h" #include "nsGkAtoms.h" @@ -80,7 +82,15 @@ #define SYN_MT_REPORT 2 #endif -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) +#define LOG(args...) \ + __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) +#ifdef VERBOSE_LOG_ENABLED +# define VERBOSE_LOG(args...) \ + __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) +#else +# define VERBOSE_LOG(args...) \ + (void)0 +#endif using namespace mozilla; @@ -120,14 +130,14 @@ PRUint64 timevalToMS(const struct timeval &time) } static void -sendMouseEvent(PRUint32 msg, struct timeval *time, int x, int y) +sendMouseEvent(PRUint32 msg, struct timeval& time, int x, int y) { nsMouseEvent event(true, msg, NULL, nsMouseEvent::eReal, nsMouseEvent::eNormal); event.refPoint.x = x; event.refPoint.y = y; - event.time = timevalToMS(*time); + event.time = timevalToMS(time); event.isShift = false; event.isControl = false; event.isMeta = false; @@ -137,7 +147,6 @@ sendMouseEvent(PRUint32 msg, struct timeval *time, int x, int y) event.clickCount = 1; nsWindow::DispatchInputEvent(event); - //LOG("Dispatched type %d at %dx%d", msg, x, y); } static nsEventStatus @@ -173,57 +182,175 @@ sendSpecialKeyEvent(nsIAtom *command, const timeval &time) nsWindow::DispatchInputEvent(event); } +static void +maybeSendKeyEvent(int keyCode, bool pressed, const timeval& time) +{ + switch (keyCode) { + case KEY_BACK: + sendKeyEvent(NS_VK_ESCAPE, pressed, time); + break; + case KEY_MENU: + if (!pressed) + sendSpecialKeyEvent(nsGkAtoms::Menu, time); + break; + case KEY_SEARCH: + if (pressed) + sendSpecialKeyEvent(nsGkAtoms::Search, time); + break; + case KEY_HOME: + sendKeyEvent(NS_VK_HOME, pressed, time); + break; + case KEY_POWER: + sendKeyEvent(NS_VK_SLEEP, pressed, time); + break; + case KEY_VOLUMEUP: + if (pressed) + sendSpecialKeyEvent(nsGkAtoms::VolumeUp, time); + break; + case KEY_VOLUMEDOWN: + if (pressed) + sendSpecialKeyEvent(nsGkAtoms::VolumeDown, time); + break; + default: + VERBOSE_LOG("Got unknown key event code. type 0x%04x code 0x%04x value %d", + keyCode, pressed); + } +} + static void maybeSendKeyEvent(const input_event& e) { if (e.type != EV_KEY) { - LOG("Got unknown key event type. type 0x%04x code 0x%04x value %d", + VERBOSE_LOG("Got unknown key event type. type 0x%04x code 0x%04x value %d", e.type, e.code, e.value); return; } if (e.value != 0 && e.value != 1) { - LOG("Got unknown key event value. type 0x%04x code 0x%04x value %d", + VERBOSE_LOG("Got unknown key event value. type 0x%04x code 0x%04x value %d", e.type, e.code, e.value); return; } bool pressed = e.value == 1; - switch (e.code) { - case KEY_BACK: - sendKeyEvent(NS_VK_ESCAPE, pressed, e.time); - break; - case KEY_MENU: - if (!pressed) - sendSpecialKeyEvent(nsGkAtoms::Menu, e.time); - break; - case KEY_SEARCH: - if (pressed) - sendSpecialKeyEvent(nsGkAtoms::Search, e.time); - break; - case KEY_HOME: - sendKeyEvent(NS_VK_HOME, pressed, e.time); - break; - case KEY_POWER: - sendKeyEvent(NS_VK_SLEEP, pressed, e.time); - break; - case KEY_VOLUMEUP: - if (pressed) - sendSpecialKeyEvent(nsGkAtoms::VolumeUp, e.time); - break; - case KEY_VOLUMEDOWN: - if (pressed) - sendSpecialKeyEvent(nsGkAtoms::VolumeDown, e.time); - break; - default: - LOG("Got unknown key event code. type 0x%04x code 0x%04x value %d", - e.type, e.code, e.value); + maybeSendKeyEvent(e.code, pressed, e.time); +} + +static void +configureVButtons(FdHandler& data) +{ + char vbuttonsPath[PATH_MAX]; + snprintf(vbuttonsPath, sizeof(vbuttonsPath), + "/sys/board_properties/virtualkeys.%s", + data.name); + ScopedClose fd(open(vbuttonsPath, O_RDONLY)); + if (0 > fd.mFd) { + LOG("No vbuttons for mt device %s", data.name); + return; } + + // This device has vbuttons. Process the configuration. + char config[1024]; + ssize_t nread; + do { + nread = read(fd.mFd, config, sizeof(config)); + } while (-1 == nread && EINTR == errno); + + if (0 > nread) { + LOG("Error reading virtualkey configuration"); + return; + } + + config[nread] = '\0'; + + LOG("Device %s has vbutton config '%s'", data.name, config); + + char* startStr = config; + for (size_t i = 0; i < FdHandler::kMaxVButtons; ++i) { + FdHandler::VButton& vbutton = data.vbuttons[i]; + char* token; + char* state; + + // XXX not clear what "0x01" is ... maybe a version + // number? See InputManager.java. + if (!(token = strtok_r(startStr, ":", &state)) || + strcmp(token, "0x01")) { + LOG(" magic 0x01 tag missing"); + break; + } + startStr = NULL; + + if (!(token = strtok_r(NULL, ":", &state))) { + LOG(" failed to read keycode"); + break; + } + vbutton.keyCode = atoi(token); + + const char *centerX, *centerY, *width, *height; + if (!((centerX = strtok_r(NULL, ":", &state)) && + (centerY = strtok_r(NULL, ":", &state)) && + (width = strtok_r(NULL, ":", &state)) && + (height = strtok_r(NULL, ":", &state)))) { + LOG(" failed to read bound params"); + break; + } + + // NB: these coordinates are in *screen* space, not input + // space. That means the values in /sys/board_config make + // assumptions about how the raw input events are mapped + // ... le sigh. + nsIntRect rect; + rect.width = atoi(width); + rect.height = atoi(height); + rect.x = atoi(centerX) - rect.width / 2; + rect.y = atoi(centerY) - rect.height / 2; + vbutton.buttonRect = rect; + + LOG(" configured vbutton code=%d at ", + vbutton.keyCode, rect.x, rect.y, rect.width, rect.height); + } +} + +static bool +calibrateMultitouchDevice(FdHandler& data) +{ + if (data.calibrated) + return true; + if (gScreenBounds.IsEmpty()) { + // The framebuffer hasn't been initialized yet. We *could* + // force it to be initialized here, but that's another patch. + LOG("Deferring multitouch calibrate, fb not ready"); + return false; + } + + struct input_absinfo xInfo, yInfo; + if (0 > ioctl(data.fd, EVIOCGABS(ABS_MT_POSITION_X), &xInfo) || + 0 > ioctl(data.fd, EVIOCGABS(ABS_MT_POSITION_Y), &yInfo)) { + LOG("Couldn't get absinfo for multitouch axes"); + return false; + } + LOG("Input coordinate bounds: xmin=%d, xmax=%d, ymin=%d, ymax=%d", + xInfo.minimum, xInfo.maximum, yInfo.minimum, yInfo.maximum); + + data.inputMinX = xInfo.minimum; + data.inputMinY = yInfo.minimum; + data.inputToScreenScaleX = + float(gScreenBounds.width) / float(xInfo.maximum - xInfo.minimum); + data.inputToScreenScaleY = + float(gScreenBounds.height) / float(yInfo.maximum - yInfo.minimum); + + configureVButtons(data); + + data.calibrated = true; + return true; } static void multitouchHandler(int fd, FdHandler *data) { + if (!calibrateMultitouchDevice(*data)) + return; + // The Linux's input documentation (Documentation/input/input.txt) // says that we'll always read a multiple of sizeof(input_event) bytes here. input_event events[16]; @@ -259,14 +386,15 @@ multitouchHandler(int fd, FdHandler *data) case ABS_MT_PRESSURE: break; case ABS_MT_POSITION_X: - data->mtX = event->value; + data->mtX = data->inputXToScreenX(event->value); break; case ABS_MT_POSITION_Y: - data->mtY = event->value; + data->mtY = data->inputYToScreenY(event->value); break; default: - LOG("Got unknown event type 0x%04x with code 0x%04x and value %d", - event->type, event->code, event->value); + VERBOSE_LOG("Got unknown mt event type 0x%04x with code 0x%04x and value %d", + event->type, event->code, event->value); + break; } } else if (event->type == EV_SYN) { switch (event->code) { @@ -275,30 +403,60 @@ multitouchHandler(int fd, FdHandler *data) data->mtState = FdHandler::MT_IGNORE; break; case SYN_REPORT: - if ((!data->mtMajor || data->mtState == FdHandler::MT_START)) { - sendMouseEvent(NS_MOUSE_BUTTON_UP, &event->time, - data->mtX, data->mtY); + if (!data->mtMajor || data->mtState == FdHandler::MT_START) { data->mtDown = false; - //LOG("Up mouse event"); + if (data->keyCode) { + maybeSendKeyEvent(data->keyCode, data->mtDown, + event->time); + data->keyCode = 0; + } else { + sendMouseEvent(NS_MOUSE_BUTTON_UP, event->time, + data->mtX, data->mtY); + } } else if (!data->mtDown) { - sendMouseEvent(NS_MOUSE_BUTTON_DOWN, &event->time, - data->mtX, data->mtY); + int x = data->mtX, y = data->mtY; + + bool isKeyEvent = false; + if (!gScreenBounds.Contains(x, y)) { + // Off-screen mt down. Should be a vbutton. + for (size_t i = 0; i < FdHandler::kMaxVButtons; ++i) { + const FdHandler::VButton& vbutton = data->vbuttons[i]; + if (vbutton.buttonRect.IsEmpty()) + break; + + if (vbutton.buttonRect.Contains(x, y)) { + isKeyEvent = true; + data->keyCode = vbutton.keyCode; + break; + } + } + } data->mtDown = true; - //LOG("Down mouse event"); - } else { - sendMouseEvent(NS_MOUSE_MOVE, &event->time, + + if (isKeyEvent) { + maybeSendKeyEvent(data->keyCode, data->mtDown, + event->time); + } else { + sendMouseEvent(NS_MOUSE_BUTTON_DOWN, event->time, + data->mtX, data->mtY); + } + } else if (!data->keyCode) { + sendMouseEvent(NS_MOUSE_MOVE, event->time, data->mtX, data->mtY); data->mtDown = true; } + data->mtState = FdHandler::MT_START; + break; default: - LOG("Got unknown event type 0x%04x with code 0x%04x and value %d", - event->type, event->code, event->value); + VERBOSE_LOG("Got unknown mt event type 0x%04x with code 0x%04x and value %d", + event->type, event->code, event->value); + } } else - LOG("Got unknown event type 0x%04x with code 0x%04x and value %d", - event->type, event->code, event->value); + VERBOSE_LOG("Got unknown mt event type 0x%04x with code 0x%04x and value %d", + event->type, event->code, event->value); } } @@ -343,16 +501,16 @@ singleTouchHandler(int fd, FdHandler *data) } else if (event->type == EV_SYN) { if (data->mtState == FdHandler::MT_START) { MOZ_ASSERT(data->mtDown); - sendMouseEvent(NS_MOUSE_BUTTON_DOWN, &event->time, + sendMouseEvent(NS_MOUSE_BUTTON_DOWN, event->time, data->mtX, data->mtY); data->mtState = FdHandler::MT_COLLECT; } else if (data->mtDown) { MOZ_ASSERT(data->mtDown); - sendMouseEvent(NS_MOUSE_MOVE, &event->time, + sendMouseEvent(NS_MOUSE_MOVE, event->time, data->mtX, data->mtY); } else { MOZ_ASSERT(!data->mtDown); - sendMouseEvent(NS_MOUSE_BUTTON_UP, &event->time, + sendMouseEvent(NS_MOUSE_BUTTON_UP, event->time, data->mtX, data->mtY); data->mtDown = false; data->mtState = FdHandler::MT_START; @@ -408,13 +566,13 @@ nsAppShell::Init() int ret = pipe2(signalfds, O_NONBLOCK); NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED); - rv = AddFdHandler(signalfds[0], pipeHandler); + rv = AddFdHandler(signalfds[0], pipeHandler, ""); NS_ENSURE_SUCCESS(rv, rv); DIR *dir = opendir("/dev/input"); NS_ENSURE_TRUE(dir, NS_ERROR_UNEXPECTED); -#define BITSET(bit, flags) (flags[bit >> 3] & (1 << (bit & 0x7))) +#define IS_BIT_SET(bit, flags) (flags[bit >> 3] & (1 << (bit & 0x7))) struct dirent *entry; while ((entry = readdir(dir))) { @@ -435,12 +593,12 @@ nsAppShell::Init() char flags[(NS_MAX(ABS_MAX, KEY_MAX) + 1) / 8]; if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(flags)), flags) >= 0 && - BITSET(ABS_MT_POSITION_X, flags)) { + IS_BIT_SET(ABS_MT_POSITION_X, flags)) { LOG("Found multitouch input device"); handlerFunc = multitouchHandler; } else if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(flags)), flags) >= 0 && - BITSET(ABS_X, flags)) { + IS_BIT_SET(ABS_X, flags)) { LOG("Found single touch input device"); handlerFunc = singleTouchHandler; } else if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(flags)), flags) >= 0) { @@ -452,7 +610,7 @@ nsAppShell::Init() if (!handlerFunc) continue; - rv = AddFdHandler(fd, handlerFunc); + rv = AddFdHandler(fd, handlerFunc, entryName); if (NS_FAILED(rv)) LOG("Failed to add fd to epoll fd"); } @@ -461,7 +619,8 @@ nsAppShell::Init() } nsresult -nsAppShell::AddFdHandler(int fd, FdHandlerCallback handlerFunc) +nsAppShell::AddFdHandler(int fd, FdHandlerCallback handlerFunc, + const char* deviceName) { epoll_event event = { EPOLLIN, @@ -470,6 +629,7 @@ nsAppShell::AddFdHandler(int fd, FdHandlerCallback handlerFunc) FdHandler *handler = mHandlers.AppendElement(); handler->fd = fd; + strncpy(handler->name, deviceName, sizeof(handler->name) - 1); handler->func = handlerFunc; event.data.u32 = mHandlers.Length() - 1; return epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) ? diff --git a/widget/src/gonk/nsAppShell.h b/widget/src/gonk/nsAppShell.h index 37337c733c9..c28c17c5e29 100644 --- a/widget/src/gonk/nsAppShell.h +++ b/widget/src/gonk/nsAppShell.h @@ -39,6 +39,7 @@ #define nsAppShell_h #include "nsBaseAppShell.h" +#include "nsRect.h" #include "nsTArray.h" namespace mozilla { @@ -53,9 +54,17 @@ typedef void(*FdHandlerCallback)(int, FdHandler *); class FdHandler { public: - FdHandler() : mtState(MT_START), mtDown(false) { } + FdHandler() + : mtState(MT_START) + , keyCode(0) + , mtDown(false) + , calibrated(false) + { + memset(name, 0, sizeof(name)); + } int fd; + char name[64]; FdHandlerCallback func; enum mtStates { MT_START, @@ -64,12 +73,48 @@ public: } mtState; int mtX, mtY; int mtMajor; + int keyCode; bool mtDown; + // FIXME/bug 712973: we should be using libui here instead of + // recreating all that logic ourselves. Please don't extend the + // hacks here further than what's below. + bool calibrated; + // Multitouch events are delivered to us in "input space", which + // is a coordinate space defined by the multitouch device driver. + // The coordinate space has top-left at P_min = when in normal-portrait orientation. The input + // device and the screen might have different resolutions. The + // resolution difference is Scale = . So going from input to screen space + // (when in normal portrait orientation) is an affine transform + // defined by + // + // P_screen = Scale * (P_input - P_min) + // + int inputMinX, inputMinY; + float inputToScreenScaleX, inputToScreenScaleY; + // Some touch devices use virtual buttons instead of hardware + // buttons. When the device uses vbuttons, we convert touch + // events into key events of type |keyCode| when the start of the + // touch is within |buttonRect|. |buttonRect| must be disjoint + // from the screen rect. + static const size_t kMaxVButtons = 4; + struct VButton { + nsIntRect buttonRect; // in screen space + int keyCode; + } vbuttons[kMaxVButtons]; void run() { func(fd, this); } + + int inputXToScreenX(int inputX) { + return inputToScreenScaleX * (inputX - inputMinX); + } + int inputYToScreenY(int inputY) { + return inputToScreenScaleY * (inputY - inputMinY); + } }; class nsAppShell : public nsBaseAppShell { @@ -87,7 +132,8 @@ protected: virtual void ScheduleNativeEventCallback(); private: - nsresult AddFdHandler(int fd, FdHandlerCallback handlerFunc); + nsresult AddFdHandler(int fd, FdHandlerCallback handlerFunc, + const char* deviceName); // This is somewhat racy but is perfectly safe given how the callback works bool mNativeCallbackRequest; From 6ec2660b1838276231566a10eef6a14cec1596be Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Thu, 29 Dec 2011 15:00:38 -0800 Subject: [PATCH 41/66] Bug 713935 - Fix debug builds on gonk due not having HAVE_SYS_UIO_H defined, r=khuey --- configure.in | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.in b/configure.in index 0a18e0aed96..aa48cc884d5 100644 --- a/configure.in +++ b/configure.in @@ -336,6 +336,7 @@ if test -n "$gonkdir" ; then fi AC_DEFINE(ANDROID) + AC_DEFINE(HAVE_SYS_UIO_H) CROSS_COMPILE=1 MOZ_CHROME_FILE_FORMAT=omni ZLIB_DIR=yes From 6e6a7fd17091dd2c0ab4144c5fcef37065299125 Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Thu, 29 Dec 2011 15:01:51 -0800 Subject: [PATCH 42/66] Bug 711867 - Track dirty region in gonk widget backend, r=cjones --- widget/src/gonk/nsWindow.cpp | 13 ++++++++++--- widget/src/gonk/nsWindow.h | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/widget/src/gonk/nsWindow.cpp b/widget/src/gonk/nsWindow.cpp index c9daf2e0049..e4fa9d70afd 100644 --- a/widget/src/gonk/nsWindow.cpp +++ b/widget/src/gonk/nsWindow.cpp @@ -106,7 +106,8 @@ nsWindow::DoDraw(void) } nsPaintEvent event(true, NS_PAINT, gWindowToRedraw); - event.region = gScreenBounds; + event.region = gWindowToRedraw->mDirtyRegion; + gWindowToRedraw->mDirtyRegion.SetEmpty(); LayerManager* lm = gWindowToRedraw->GetLayerManager(); if (LayerManager::LAYERS_OPENGL == lm->GetBackendType()) { @@ -293,9 +294,15 @@ nsWindow::Invalidate(const nsIntRect &aRect, return NS_OK; } + mDirtyRegion.Or(mDirtyRegion, aRect); gWindowToRedraw = this; - gDrawRequest = true; - mozilla::NotifyEvent(); + if (aIsSynchronous) { + gDrawRequest = false; + DoDraw(); + } else { + gDrawRequest = true; + mozilla::NotifyEvent(); + } return NS_OK; } diff --git a/widget/src/gonk/nsWindow.h b/widget/src/gonk/nsWindow.h index 03647637912..743067c5c39 100644 --- a/widget/src/gonk/nsWindow.h +++ b/widget/src/gonk/nsWindow.h @@ -129,10 +129,10 @@ public: protected: nsWindow* mParent; bool mVisible; + nsIntRegion mDirtyRegion; + InputContext mInputContext; void BringToTop(); - - InputContext mInputContext; }; #endif /* nsWindow_h */ From 999455e4311fb32592527cd74c02a411838cd6f2 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 29 Dec 2011 15:10:26 -0800 Subject: [PATCH 43/66] Bug 709492 - Part 1: Add an observer that allows us to determine when a new page is shown. r=bz --- layout/base/nsDocumentViewer.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 20a6f4f77da..4dfba589e63 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -194,6 +194,8 @@ static const char sPrintOptionsContractID[] = "@mozilla.org/gfx/printset //switch to page layout #include "nsGfxCIID.h" +#include "nsObserverService.h" + #include "mozilla/dom/Element.h" using namespace mozilla; @@ -509,6 +511,18 @@ public: nsCOMPtr mTop; }; +class nsDocumentShownDispatcher : public nsRunnable +{ +public: + nsDocumentShownDispatcher(nsIDocument *aDocument) + : mDocument(aDocument) {} + + NS_IMETHOD Run(); + +private: + nsCOMPtr mDocument; +}; + //------------------------------------------------------------------ // DocumentViewerImpl @@ -2039,6 +2053,10 @@ DocumentViewerImpl::Show(void) } } + // Notify observers that a new page has been shown. (But not right now; + // running JS at this time is not safe.) + NS_DispatchToMainThread(new nsDocumentShownDispatcher(mDocument)); + return NS_OK; } @@ -4371,3 +4389,17 @@ DocumentViewerImpl::SetPrintPreviewPresentation(nsIViewManager* aViewManager, mPresContext = aPresContext; mPresShell = aPresShell; } + +// Fires the "document-shown" event so that interested parties (right now, the +// mobile browser) are aware of it. +NS_IMETHODIMP +nsDocumentShownDispatcher::Run() +{ + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->NotifyObservers(mDocument, "document-shown", NULL); + } + return NS_OK; +} + From b11a6ec7ee942910d33ad6b420813931c66ce758 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 29 Dec 2011 15:10:27 -0800 Subject: [PATCH 44/66] Bug 709492 - Part 2: Implement draw suppression. r=kats --- mobile/android/chrome/content/browser.js | 12 +++++++++++- widget/src/android/nsIAndroidBridge.idl | 8 +++++++- widget/src/android/nsWindow.cpp | 22 +++++++++++++++++----- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 27a6b639f8d..7c110f2d3d3 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -155,6 +155,16 @@ var Strings = {}; }); }); +var MetadataProvider = { + getDrawMetadata: function getDrawMetadata() { + return BrowserApp.getDrawMetadata(); + }, + + drawingAllowed: function drawingAllowed() { + return !BrowserApp.selectedTab.suppressDrawing; + } +}; + var BrowserApp = { _tabs: [], _selectedTab: null, @@ -169,7 +179,7 @@ var BrowserApp = { BrowserEventHandler.init(); ViewportHandler.init(); - getBridge().setDrawMetadataProvider(this.getDrawMetadata.bind(this)); + getBridge().setDrawMetadataProvider(MetadataProvider); Services.obs.addObserver(this, "Tab:Add", false); Services.obs.addObserver(this, "Tab:Load", false); diff --git a/widget/src/android/nsIAndroidBridge.idl b/widget/src/android/nsIAndroidBridge.idl index 7e2d837e52c..5e75d72ff89 100644 --- a/widget/src/android/nsIAndroidBridge.idl +++ b/widget/src/android/nsIAndroidBridge.idl @@ -1,8 +1,14 @@ #include "nsISupports.idl" -[scriptable, function, uuid(9feed1e5-bb90-4663-b70a-e03cb27a9e8b)] +[scriptable, uuid(c60bf6cf-5e31-4bf8-a1f0-a82c061d65a8)] interface nsIAndroidDrawMetadataProvider : nsISupports { AString getDrawMetadata(); + + /* + * Returns true if drawing should be allowed or false if it should be suppressed (during page + * transitions). + */ + boolean drawingAllowed(); }; [scriptable, uuid(7dd8441a-4f38-49b2-bd90-da69d02a96cf)] diff --git a/widget/src/android/nsWindow.cpp b/widget/src/android/nsWindow.cpp index 63461edb20b..3bcb8184943 100644 --- a/widget/src/android/nsWindow.cpp +++ b/widget/src/android/nsWindow.cpp @@ -1168,6 +1168,21 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) if (gAndroidBounds.width <= 0 || gAndroidBounds.height <= 0) return; + /* + * Check to see whether browser.js wants us to draw. This will be false during page + * transitions, in which case we immediately bail out. + */ + nsCOMPtr metadataProvider = + AndroidBridge::Bridge()->GetDrawMetadataProvider(); + + bool shouldDraw = true; + if (metadataProvider) { + metadataProvider->DrawingAllowed(&shouldDraw); + } + if (!shouldDraw) { + return; + } + AndroidGeckoSoftwareLayerClient &client = AndroidBridge::Bridge()->GetSoftwareLayerClient(); client.BeginDrawing(gAndroidBounds.width, gAndroidBounds.height); @@ -1207,11 +1222,8 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) DrawTo(targetSurface, ae->Rect()); } - { - nsCOMPtr metadataProvider = - AndroidBridge::Bridge()->GetDrawMetadataProvider(); - if (metadataProvider) - metadataProvider->GetDrawMetadata(metadata); + if (metadataProvider) { + metadataProvider->GetDrawMetadata(metadata); } } if (sHasDirectTexture) { From 1f1ddd265f6343332471f0f2325236fe929c1a8f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 29 Dec 2011 15:10:28 -0800 Subject: [PATCH 45/66] Bug 709492 - Part 3: Implement expose events. r=kats --- mobile/android/base/GeckoEvent.java | 1 + widget/src/android/AndroidJavaWrappers.h | 1 + widget/src/android/nsWindow.cpp | 22 +++++++++++++++------- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index a13e0bf81c4..ca12bdaed20 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -78,6 +78,7 @@ public class GeckoEvent { public static final int ACTIVITY_START = 17; public static final int BROADCAST = 19; public static final int VIEWPORT = 20; + public static final int EXPOSE = 21; public static final int IME_COMPOSITION_END = 0; public static final int IME_COMPOSITION_BEGIN = 1; diff --git a/widget/src/android/AndroidJavaWrappers.h b/widget/src/android/AndroidJavaWrappers.h index f05cff9874c..377c721ac23 100644 --- a/widget/src/android/AndroidJavaWrappers.h +++ b/widget/src/android/AndroidJavaWrappers.h @@ -532,6 +532,7 @@ public: ACTIVITY_START = 17, BROADCAST = 19, VIEWPORT = 20, + EXPOSE = 21, dummy_java_enum_list_end }; diff --git a/widget/src/android/nsWindow.cpp b/widget/src/android/nsWindow.cpp index 3bcb8184943..1dc1baf463b 100644 --- a/widget/src/android/nsWindow.cpp +++ b/widget/src/android/nsWindow.cpp @@ -978,6 +978,7 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) break; case AndroidGeckoEvent::DRAW: + case AndroidGeckoEvent::EXPOSE: win->OnDraw(ae); break; @@ -1168,15 +1169,16 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) if (gAndroidBounds.width <= 0 || gAndroidBounds.height <= 0) return; - /* - * Check to see whether browser.js wants us to draw. This will be false during page - * transitions, in which case we immediately bail out. - */ nsCOMPtr metadataProvider = AndroidBridge::Bridge()->GetDrawMetadataProvider(); + /* + * If this is a DRAW event (not an EXPOSE event), check to see whether browser.js wants us to + * draw. This will be false during page transitions, in which case we immediately bail out. + */ + bool shouldDraw = true; - if (metadataProvider) { + if (metadataProvider && ae->Type() == AndroidGeckoEvent::DRAW) { metadataProvider->DrawingAllowed(&shouldDraw); } if (!shouldDraw) { @@ -1187,6 +1189,12 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) AndroidBridge::Bridge()->GetSoftwareLayerClient(); client.BeginDrawing(gAndroidBounds.width, gAndroidBounds.height); + // Redraw the entire tile on an EXPOSE event. Otherwise (on a DRAW event), redraw only the + // portion specified by the event. + nsIntRect rect(0, 0, gAndroidBounds.width, gAndroidBounds.height); + if (ae->Type() == AndroidGeckoEvent::DRAW) + rect = ae->Rect(); + nsAutoString metadata; unsigned char *bits = NULL; if (sHasDirectTexture) { @@ -1219,7 +1227,7 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) // XXX: lock only the dirty rect above and pass it in here DrawTo(targetSurface); } else { - DrawTo(targetSurface, ae->Rect()); + DrawTo(targetSurface, rect); } if (metadataProvider) { @@ -1232,7 +1240,7 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) client.UnlockBuffer(); } } - client.EndDrawing(ae->Rect(), metadata); + client.EndDrawing(rect, metadata); return; #endif From 971701084384787778853d2b58ec2df26341993a Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 29 Dec 2011 15:10:30 -0800 Subject: [PATCH 46/66] Bug 709492 - Part 4: Hide the page when navigating, and expose it when the new page appears. r=kats --- mobile/android/base/GeckoApp.java | 7 ++- .../base/gfx/GeckoSoftwareLayerClient.java | 23 ++++----- mobile/android/base/ui/PanZoomController.java | 5 +- mobile/android/chrome/content/browser.js | 48 +++++++++++++++++-- 4 files changed, 64 insertions(+), 19 deletions(-) diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 3e2db6ee1d0..202d32c4bec 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -615,8 +615,11 @@ abstract public class GeckoApp if (mLastUri == lastHistoryEntry.mUri && mLastTitle == lastHistoryEntry.mTitle) return; - - mLastViewport = mSoftwareLayerClient.getGeckoViewportMetrics().toJSON(); + + ViewportMetrics viewportMetrics = mSoftwareLayerClient.getGeckoViewportMetrics(); + if (viewportMetrics != null) + mLastViewport = viewportMetrics.toJSON(); + mLastUri = lastHistoryEntry.mUri; mLastTitle = lastHistoryEntry.mTitle; Bitmap bitmap = mSoftwareLayerClient.getBitmap(); diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index 55f8f98a261..b01a188472e 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -140,7 +140,8 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL layerController.setViewportMetrics(mGeckoViewport); } - GeckoAppShell.registerGeckoEventListener("Viewport:Update", this); + GeckoAppShell.registerGeckoEventListener("Viewport:Expose", this); + GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this); GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this); } @@ -340,19 +341,15 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL } public void handleMessage(String event, JSONObject message) { - if ("Viewport:Update".equals(event)) { - beginTransaction(mTileLayer); - try { - updateViewport(message.getString("viewport"), false); - } catch (JSONException e) { - Log.e(LOGTAG, "Unable to update viewport", e); - } finally { - endTransaction(mTileLayer); - } + if ("Viewport:Expose".equals(event)) { + GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.EXPOSE)); + } else if ("Viewport:UpdateAndDraw".equals(event)) { + mUpdateViewportOnEndDraw = true; + + // Redraw everything. + Rect rect = new Rect(0, 0, mBufferSize.width, mBufferSize.height); + GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.DRAW, rect)); } else if ("Viewport:UpdateLater".equals(event)) { - if (!mTileLayer.inTransaction()) { - Log.e(LOGTAG, "Viewport:UpdateLater called while not in transaction. You should be using Viewport:Update instead!"); - } mUpdateViewportOnEndDraw = true; } } diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index 6800252b66b..1b879507aa5 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -264,7 +264,10 @@ public class PanZoomController // the screen orientation changed) so abort it and start a new one to // ensure the viewport doesn't contain out-of-bounds areas case NOTHING: - bounce(); + // Don't do animations here; they're distracting and can cause flashes on page + // transitions. + mController.setViewportMetrics(getValidViewportMetrics()); + mController.notifyLayerClientOfGeometryChange(); break; } } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 7c110f2d3d3..788ec0c85da 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -200,6 +200,7 @@ var BrowserApp = { Services.obs.addObserver(this, "Viewport:Change", false); Services.obs.addObserver(this, "AgentMode:Change", false); Services.obs.addObserver(this, "SearchEngines:Get", false); + Services.obs.addObserver(this, "document-shown", false); function showFullScreenWarning() { NativeWindow.toast.show(Strings.browser.GetStringFromName("alertFullScreenToast"), "short"); @@ -715,6 +716,20 @@ var BrowserApp = { ViewportHandler.onResize(); } else if (aTopic == "SearchEngines:Get") { this.getSearchEngines(); + } else if (aTopic == "document-shown") { + let tab = this.selectedTab; + if (tab.browser.contentDocument != aSubject) { + return; + } + + ViewportHandler.updateMetadata(tab); + + // Unsuppress drawing unless the page was being thawed from the bfcache (which is an atomic + // operation, so there is no drawing to suppress). + if (tab.suppressDrawing) { + tab.sendExposeEvent(); + tab.suppressDrawing = false; + } } }, @@ -722,7 +737,7 @@ var BrowserApp = { delete this.defaultBrowserWidth; let width = Services.prefs.getIntPref("browser.viewport.desktopWidth"); return this.defaultBrowserWidth = width; - } + } }; var NativeWindow = { @@ -1194,6 +1209,12 @@ Tab.prototype = { this.browser.addEventListener("PluginClickToPlay", this, true); this.browser.addEventListener("pagehide", this, true); + let chromeEventHandler = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell) + .chromeEventHandler; + chromeEventHandler.addEventListener("DOMWindowCreated", this, false); + Services.obs.addObserver(this, "http-on-modify-request", false); if (!aParams.delayLoad) { @@ -1403,8 +1424,7 @@ Tab.prototype = { return; sendMessageToJava({ gecko: { - type: "Viewport:Update", - viewport: JSON.stringify(this.viewport) + type: "Viewport:UpdateAndDraw" } }); }, @@ -1547,6 +1567,18 @@ Tab.prototype = { } break; } + + case "DOMWindowCreated": { + // Conveniently, this call to getBrowserForDocument() will return null if the document is + // not the top-level content document of the browser. + let browser = BrowserApp.getBrowserForDocument(aEvent.originalTarget); + if (!browser) + break; + + let tab = BrowserApp.getTabForBrowser(browser); + tab.suppressDrawing = true; + break; + } } }, @@ -1806,6 +1838,16 @@ Tab.prototype = { } }, + sendExposeEvent: function() { + // Now that the document is actually on the screen, send an expose event. + this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + sendMessageToJava({ + gecko: { + type: "Viewport:Expose" + } + }); + }, + QueryInterface: XPCOMUtils.generateQI([ Ci.nsIWebProgressListener, Ci.nsISHistoryListener, From 7455dd5ff7ba31b56a3e7b6f50c577c2af3fa0e3 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 29 Dec 2011 15:10:31 -0800 Subject: [PATCH 47/66] Bug 709492 - Part 5: Reset the metadata instead of updating it when a new page is shown. r=kats --- mobile/android/chrome/content/browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 788ec0c85da..aaf0a772309 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -722,7 +722,7 @@ var BrowserApp = { return; } - ViewportHandler.updateMetadata(tab); + ViewportHandler.resetMetadata(tab); // Unsuppress drawing unless the page was being thawed from the bfcache (which is an atomic // operation, so there is no drawing to suppress). From 859fe9155c8f255c98e2745863813a173fb4e2ed Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 29 Dec 2011 15:19:26 -0800 Subject: [PATCH 48/66] No bug. Fix --disable-methodjit build breakage from 23936f566781 (bug 710032). r=woof! --- js/src/jsprobes.cpp | 6 ++---- js/src/jsprobes.h | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/js/src/jsprobes.cpp b/js/src/jsprobes.cpp index c1fa21803eb..2527bbd3b6f 100644 --- a/js/src/jsprobes.cpp +++ b/js/src/jsprobes.cpp @@ -55,9 +55,7 @@ #include "jsscript.h" #include "jsstr.h" -#ifdef JS_METHODJIT -# include "methodjit/Compiler.h" -#endif +#include "methodjit/Compiler.h" #include "jsobjinlines.h" @@ -118,6 +116,7 @@ Probes::JITGranularityRequested() return want; } +#ifdef JS_METHODJIT /* * Flatten the tree of inlined frames into a series of native code regions, one * for each contiguous section of native code that belongs to a single @@ -217,7 +216,6 @@ Probes::JITWatcher::CollectNativeRegions(RegionVector ®ions, return true; } -#ifdef JS_METHODJIT void Probes::registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr, js::mjit::JSActiveFrame *outerFrame, diff --git a/js/src/jsprobes.h b/js/src/jsprobes.h index 00f3e052c8f..903312275ad 100644 --- a/js/src/jsprobes.h +++ b/js/src/jsprobes.h @@ -243,15 +243,15 @@ public: typedef Vector RegionVector; + virtual JITReportGranularity granularityRequested() = 0; + +#ifdef JS_METHODJIT static bool CollectNativeRegions(RegionVector ®ions, JSRuntime *rt, mjit::JITScript *jit, mjit::JSActiveFrame *outerFrame, mjit::JSActiveFrame **inlineFrames); - virtual JITReportGranularity granularityRequested() = 0; - -#ifdef JS_METHODJIT virtual void registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr, mjit::JSActiveFrame *outerFrame, mjit::JSActiveFrame **inlineFrames, From 222e9fbd53ca56c0186568f9db3b0a5b30489fce Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Fri, 30 Dec 2011 00:33:44 +0100 Subject: [PATCH 49/66] bug 714066 - Missed FreeChunkList call in JSRuntime::onOutOfMemory. r=wmccloskey --- js/src/jscntxt.cpp | 3 +- js/src/jscntxt.h | 6 ---- js/src/jsgc.cpp | 71 +++++++++++++++++++++------------------------- js/src/jsgc.h | 3 ++ 4 files changed, 36 insertions(+), 47 deletions(-) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index cc10655ab5c..e1de74165c0 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -104,7 +104,6 @@ ThreadData::ThreadData(JSRuntime *rt) #ifdef JS_THREADSAFE requestDepth(0), #endif - waiveGCQuota(false), tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), execAlloc(NULL), bumpAlloc(NULL), @@ -1598,7 +1597,7 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) AutoLockGC lock(this); gcHelperThread.waitBackgroundSweepOrAllocEnd(); #endif - gcChunkPool.expire(this, true); + gcChunkPool.expireAndFree(this, true); } if (!p) p = OffTheBooks::malloc_(nbytes); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 7a878701a9e..59d60ce4506 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -140,12 +140,6 @@ struct ThreadData { /* Keeper of the contiguous stack used by all contexts in this thread. */ StackSpace stackSpace; - /* - * Flag indicating that we are waiving any soft limits on the GC heap - * because we want allocations to be infallible (except when we hit OOM). - */ - bool waiveGCQuota; - /* Temporary arena pool used while compiling and decompiling. */ static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; LifoAlloc tempLifoAlloc; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 4f26bcfd57d..f399808e9fa 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -517,6 +517,22 @@ ChunkPool::expire(JSRuntime *rt, bool releaseAll) return freeList; } +static void +FreeChunkList(Chunk *chunkListHead) +{ + while (Chunk *chunk = chunkListHead) { + JS_ASSERT(!chunk->info.numArenasFreeCommitted); + chunkListHead = chunk->info.next; + FreeChunk(chunk); + } +} + +void +ChunkPool::expireAndFree(JSRuntime *rt, bool releaseAll) +{ + FreeChunkList(expire(rt, releaseAll)); +} + JS_FRIEND_API(int64_t) ChunkPool::countCleanDecommittedArenas(JSRuntime *rt) { @@ -553,16 +569,6 @@ Chunk::release(JSRuntime *rt, Chunk *chunk) FreeChunk(chunk); } -static void -FreeChunkList(Chunk *chunkListHead) -{ - while (Chunk *chunk = chunkListHead) { - JS_ASSERT(!chunk->info.numArenasFreeCommitted); - chunkListHead = chunk->info.next; - FreeChunk(chunk); - } -} - inline void Chunk::prepareToBeFreed(JSRuntime *rt) { @@ -1211,7 +1217,7 @@ js_FinishGC(JSRuntime *rt) * Finish the pool after the background thread stops in case it was doing * the background sweeping. */ - FreeChunkList(rt->gcChunkPool.expire(rt, true)); + rt->gcChunkPool.expireAndFree(rt, true); #ifdef DEBUG if (!rt->gcRootsHash.empty()) @@ -1670,12 +1676,6 @@ RunLastDitchGC(JSContext *cx) #endif } -inline bool -IsGCAllowed(JSContext *cx) -{ - return !JS_THREAD_DATA(cx)->waiveGCQuota; -} - /* static */ void * ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) { @@ -1687,7 +1687,7 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) bool runGC = !!rt->gcIsNeeded; for (;;) { - if (JS_UNLIKELY(runGC) && IsGCAllowed(cx)) { + if (JS_UNLIKELY(runGC)) { RunLastDitchGC(cx); /* Report OOM of the GC failed to free enough memory. */ @@ -1708,14 +1708,11 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) return thing; /* - * We failed to allocate. Run the GC if we can unless we have done it - * already. Otherwise report OOM but first schedule a new GC soon. + * We failed to allocate. Run the GC if we haven't done it already. + * Otherwise report OOM. */ - if (runGC || !IsGCAllowed(cx)) { - AutoLockGC lock(rt); - TriggerGC(rt, gcstats::REFILL); + if (runGC) break; - } runGC = true; } @@ -3022,12 +3019,10 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) js_PurgeThreads_PostGlobalSweep(cx); #ifdef JS_THREADSAFE - if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { + if (cx->gcBackgroundFree) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); cx->gcBackgroundFree = NULL; rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK); - } else { - JS_ASSERT(!cx->gcBackgroundFree); } #endif @@ -3308,19 +3303,17 @@ void RunDebugGC(JSContext *cx) { #ifdef JS_GC_ZEAL - if (IsGCAllowed(cx)) { - JSRuntime *rt = cx->runtime; + JSRuntime *rt = cx->runtime; - /* - * If rt->gcDebugCompartmentGC is true, only GC the current - * compartment. But don't GC the atoms compartment. - */ - rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; - if (rt->gcTriggerCompartment == rt->atomsCompartment) - rt->gcTriggerCompartment = NULL; - - RunLastDitchGC(cx); - } + /* + * If rt->gcDebugCompartmentGC is true, only GC the current + * compartment. But don't GC the atoms compartment. + */ + rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; + if (rt->gcTriggerCompartment == rt->atomsCompartment) + rt->gcTriggerCompartment = NULL; + + RunLastDitchGC(cx); #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 9f50438106e..5dab399accf 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -808,6 +808,9 @@ class ChunkPool { */ Chunk *expire(JSRuntime *rt, bool releaseAll); + /* Must be called with the GC lock taken. */ + void expireAndFree(JSRuntime *rt, bool releaseAll); + /* Must be called either during the GC or with the GC lock taken. */ JS_FRIEND_API(int64_t) countCleanDecommittedArenas(JSRuntime *rt); }; From 808af35fc1ffed9c776819245dcd9a955f79bb84 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Fri, 30 Dec 2011 11:09:03 +1100 Subject: [PATCH 50/66] Bug 710863 - Finish mochitests when they are run in single test mode. r=jmaher --- testing/mochitest/tests/SimpleTest/SimpleTest.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js index 2fb1e9408ed..6a933fa0773 100644 --- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -16,7 +16,11 @@ var SimpleTest = { }; var parentRunner = null; -var isPrimaryTestWindow = !!parent.TestRunner; + +// In normal test runs, the window that has a TestRunner in its parent is +// the primary window. In single test runs, if there is no parent and there +// is no opener then it is the primary window. +var isPrimaryTestWindow = !!parent.TestRunner || (parent == window && !opener); // Finds the TestRunner for this test run and the SpecialPowers object (in // case it is not defined) from a parent/opener window. From 6da27141a3d81950ccced2350a6f51f4e3de29e3 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Fri, 30 Dec 2011 02:40:56 +0100 Subject: [PATCH 51/66] Bug 713408 - Reduce log output from Profile Migrator. r=mfinkle --- mobile/android/base/ProfileMigrator.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/mobile/android/base/ProfileMigrator.java b/mobile/android/base/ProfileMigrator.java index d3d29883f27..2c24a5e29d3 100644 --- a/mobile/android/base/ProfileMigrator.java +++ b/mobile/android/base/ProfileMigrator.java @@ -165,10 +165,6 @@ public class ProfileMigrator { // Places URL hit is newer than Android, // allow it to be updated with places date allowUpdate = true; - } else { - Log.i(LOGTAG, "Android history is newer, not adding: " + url - + " date: " + (new Date(date)).toString() - + " android: " + (new Date(androidDate)).toString()); } } @@ -178,7 +174,6 @@ public class ProfileMigrator { if (title != null) { BrowserDB.updateHistoryTitle(mCr, url, title); } - Log.i(LOGTAG, "Adding history: " + url); } } @@ -203,8 +198,6 @@ public class ProfileMigrator { String title = cursor.getString(titleCol); // Convert from us (Places) to ms (Java, Android) long date = cursor.getLong(dateCol) / (long)1000; - Log.i(LOGTAG, "History: " + title + " URL: " + url - + " time: " + (new Date(date)).toString()); addHistory(androidHistory, url, title, date); placesHistory.add(url); cursor.moveToNext(); @@ -231,7 +224,6 @@ public class ProfileMigrator { protected void addBookmark(String url, String title) { if (!BrowserDB.isBookmark(mCr, url)) { - Log.i(LOGTAG, "Adding bookmark: " + url); if (title == null) { title = url; } @@ -254,7 +246,6 @@ public class ProfileMigrator { while (!cursor.isAfterLast()) { String url = cursor.getString(urlCol); String title = cursor.getString(titleCol); - Log.i(LOGTAG, "Bookmark: " + title + " URL: " + url); addBookmark(url, title); cursor.moveToNext(); } @@ -274,9 +265,8 @@ public class ProfileMigrator { BitmapDrawable image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src"); try { BrowserDB.updateFaviconForUrl(mCr, url, image); - Log.i(LOGTAG, "Favicon added: " + mime + " URL: " + url); } catch (SQLiteException e) { - Log.i(LOGTAG, "Favicon failed: " + mime + " URL: " + url + Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url + " error:" + e.getMessage()); } } @@ -349,6 +339,8 @@ public class ProfileMigrator { dbFile.delete(); dbFileWal.delete(); dbFileShm.delete(); + + Log.i(LOGTAG, "Profile migration finished"); } catch (SQLiteException e) { if (db != null) { db.close(); From e5d385ba4cd9e2ff2d4205a0f27bdc5d6bae7d57 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Fri, 30 Dec 2011 02:41:16 +0100 Subject: [PATCH 52/66] Bug 714148 - Check whether favicon images are decoded successfully. r=mfinkle --- mobile/android/base/ProfileMigrator.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mobile/android/base/ProfileMigrator.java b/mobile/android/base/ProfileMigrator.java index 2c24a5e29d3..7c455c54a0a 100644 --- a/mobile/android/base/ProfileMigrator.java +++ b/mobile/android/base/ProfileMigrator.java @@ -263,11 +263,13 @@ public class ProfileMigrator { protected void addFavicon(String url, String mime, byte[] data) { ByteArrayInputStream byteStream = new ByteArrayInputStream(data); BitmapDrawable image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src"); - try { - BrowserDB.updateFaviconForUrl(mCr, url, image); - } catch (SQLiteException e) { - Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url - + " error:" + e.getMessage()); + if (image != null) { + try { + BrowserDB.updateFaviconForUrl(mCr, url, image); + } catch (SQLiteException e) { + Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url + + " error:" + e.getMessage()); + } } } From 50c53d52d7714e75c20dcacf49f123c1fadee0cb Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Wed, 28 Dec 2011 21:08:44 +0100 Subject: [PATCH 53/66] bug 713916 - JS API to shrink GC buffers. r=wmccloskey --- js/src/jscntxt.cpp | 6 ++-- js/src/jsfriendapi.cpp | 8 ++++- js/src/jsfriendapi.h | 3 ++ js/src/jsgc.cpp | 81 ++++++++++++++++++++++++++++++++---------- js/src/jsgc.h | 14 ++++++-- 5 files changed, 87 insertions(+), 25 deletions(-) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index e1de74165c0..00106a8b3f6 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1592,13 +1592,13 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) * Retry when we are done with the background sweeping and have stopped * all the allocations and released the empty GC chunks. */ - { + ShrinkGCBuffers(this); #ifdef JS_THREADSAFE + { AutoLockGC lock(this); gcHelperThread.waitBackgroundSweepOrAllocEnd(); -#endif - gcChunkPool.expireAndFree(this, true); } +#endif if (!p) p = OffTheBooks::malloc_(nbytes); else if (p == reinterpret_cast(1)) diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 1a58096577e..ab450aeec64 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -125,12 +125,18 @@ JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObj return obj; } -JS_PUBLIC_API(void) +JS_FRIEND_API(void) JS_ShrinkingGC(JSContext *cx) { js_GC(cx, NULL, GC_SHRINK, gcstats::PUBLIC_API); } +JS_FRIEND_API(void) +JS_ShrinkGCBuffers(JSRuntime *rt) +{ + ShrinkGCBuffers(rt); +} + JS_FRIEND_API(JSPrincipals *) JS_GetCompartmentPrincipals(JSCompartment *compartment) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 5a382c87d98..b9e2fd8c14f 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -73,6 +73,9 @@ JS_ObjectCountDynamicSlots(JSObject *obj); extern JS_FRIEND_API(void) JS_ShrinkingGC(JSContext *cx); +extern JS_FRIEND_API(void) +JS_ShrinkGCBuffers(JSRuntime *rt); + extern JS_FRIEND_API(size_t) JS_GetE4XObjectsCreated(JSContext *cx); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index f399808e9fa..3a55f2e13f2 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2423,28 +2423,53 @@ GCHelperThread::threadLoop() } bool -GCHelperThread::prepareForBackgroundSweep(JSContext *cx) +GCHelperThread::prepareForBackgroundSweep() { - JS_ASSERT(cx->runtime == rt); JS_ASSERT(state == IDLE); size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * rt->compartments.length(); - if (!finalizeVector.reserve(maxArenaLists)) - return false; - context = cx; - return true; + return finalizeVector.reserve(maxArenaLists); } /* Must be called with the GC lock taken. */ -inline void -GCHelperThread::startBackgroundSweep(bool shouldShrink) +void +GCHelperThread::startBackgroundSweep(JSContext *cx, bool shouldShrink) { /* The caller takes the GC lock. */ JS_ASSERT(state == IDLE); + JS_ASSERT(cx); + JS_ASSERT(!finalizationContext); + finalizationContext = cx; shrinkFlag = shouldShrink; state = SWEEPING; PR_NotifyCondVar(wakeup); } +/* Must be called with the GC lock taken. */ +void +GCHelperThread::startBackgroundShrink() +{ + switch (state) { + case IDLE: + JS_ASSERT(!finalizationContext); + shrinkFlag = true; + state = SWEEPING; + PR_NotifyCondVar(wakeup); + break; + case SWEEPING: + shrinkFlag = true; + break; + case ALLOCATING: + case CANCEL_ALLOCATION: + /* + * If we have started background allocation there is nothing to + * shrink. + */ + break; + case SHUTDOWN: + JS_NOT_REACHED("No shrink on shutdown"); + } +} + /* Must be called with the GC lock taken. */ void GCHelperThread::waitBackgroundSweepEnd() @@ -2496,9 +2521,8 @@ GCHelperThread::replenishAndFreeLater(void *ptr) void GCHelperThread::doSweep() { - JS_ASSERT(context); - - { + if (JSContext *cx = finalizationContext) { + finalizationContext = NULL; AutoUnlockGC unlock(rt); /* @@ -2506,11 +2530,9 @@ GCHelperThread::doSweep() * finalizeObjects. */ for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i) - ArenaLists::backgroundFinalize(context, *i); + ArenaLists::backgroundFinalize(cx, *i); finalizeVector.resize(0); - context = NULL; - if (freeCursor) { void **array = freeCursorEnd - FREE_ARRAY_LENGTH; freeElementsAndArray(array, freeCursor); @@ -2525,7 +2547,18 @@ GCHelperThread::doSweep() freeVector.resize(0); } - ExpireChunksAndArenas(rt, shouldShrink()); + bool shrinking = shrinkFlag; + ExpireChunksAndArenas(rt, shrinking); + + /* + * The main thread may have called ShrinkGCBuffers while + * ExpireChunksAndArenas(rt, false) was running, so we recheck the flag + * afterwards. + */ + if (!shrinking && shrinkFlag) { + shrinkFlag = false; + ExpireChunksAndArenas(rt, true); + } } #endif /* JS_THREADSAFE */ @@ -3008,7 +3041,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) JS_ASSERT(!cx->gcBackgroundFree); rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { - if (rt->gcHelperThread.prepareForBackgroundSweep(cx)) + if (rt->gcHelperThread.prepareForBackgroundSweep()) cx->gcBackgroundFree = &rt->gcHelperThread; } #endif @@ -3022,7 +3055,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) if (cx->gcBackgroundFree) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); cx->gcBackgroundFree = NULL; - rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK); + rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK); } #endif @@ -3100,6 +3133,18 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcstats::Re namespace js { +void +ShrinkGCBuffers(JSRuntime *rt) +{ + AutoLockGC lock(rt); + JS_ASSERT(!rt->gcRunning); +#ifndef JS_THREADSAFE + ExpireChunksAndArenas(rt, true); +#else + rt->gcHelperThread.startBackgroundShrink(); +#endif +} + class AutoCopyFreeListToArenas { JSRuntime *rt; @@ -3312,7 +3357,7 @@ RunDebugGC(JSContext *cx) rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; if (rt->gcTriggerCompartment == rt->atomsCompartment) rt->gcTriggerCompartment = NULL; - + RunLastDitchGC(cx); #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 5dab399accf..706d0bc1984 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1388,6 +1388,9 @@ TriggerCompartmentGC(JSCompartment *comp, js::gcstats::Reason reason); extern void MaybeGC(JSContext *cx); +extern void +ShrinkGCBuffers(JSRuntime *rt); + } /* namespace js */ /* @@ -1459,7 +1462,7 @@ class GCHelperThread { PRCondVar *done; volatile State state; - JSContext *context; + JSContext *finalizationContext; bool shrinkFlag; Vector freeVector; @@ -1495,6 +1498,8 @@ class GCHelperThread { wakeup(NULL), done(NULL), state(IDLE), + finalizationContext(NULL), + shrinkFlag(false), freeCursor(NULL), freeCursorEnd(NULL), backgroundAllocation(true) @@ -1504,7 +1509,10 @@ class GCHelperThread { void finish(); /* Must be called with the GC lock taken. */ - inline void startBackgroundSweep(bool shouldShrink); + void startBackgroundSweep(JSContext *cx, bool shouldShrink); + + /* Must be called with the GC lock taken. */ + void startBackgroundShrink(); /* Must be called with the GC lock taken. */ void waitBackgroundSweepEnd(); @@ -1549,7 +1557,7 @@ class GCHelperThread { } /* Must be called with the GC lock taken. */ - bool prepareForBackgroundSweep(JSContext *cx); + bool prepareForBackgroundSweep(); }; #endif /* JS_THREADSAFE */ From f874bc6c93b9062cd36b3f1ff0354b8976dfaa80 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Tue, 27 Dec 2011 12:59:29 +0100 Subject: [PATCH 54/66] bug 713916 - DOMGCFinishedCallback should schedule just GC buffer shrinking, not a full shrinking GC. r=bent --- dom/base/nsJSEnvironment.cpp | 78 +++++++++++++++++++++++++++++++----- dom/base/nsJSEnvironment.h | 4 ++ 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 6464d8da2f2..99ef44573d8 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -129,6 +129,8 @@ static PRLogModuleInfo* gJSDiagnostics; // a page) and doing the actual GC. #define NS_GC_DELAY 4000 // ms +#define NS_SHRINK_GC_BUFFERS_DELAY 4000 // ms + // The amount of time we wait from the first request to GC to actually // doing the first GC. #define NS_FIRST_GC_DELAY 10000 // ms @@ -142,6 +144,7 @@ static PRLogModuleInfo* gJSDiagnostics; // if you add statics here, add them to the list in nsJSRuntime::Startup static nsITimer *sGCTimer; +static nsITimer *sShrinkGCBuffersTimer; static nsITimer *sCCTimer; static bool sGCHasRun; @@ -1098,8 +1101,9 @@ nsJSContext::~nsJSContext() void nsJSContext::DestroyJSContext() { - if (!mContext) + if (!mContext) { return; + } // Clear our entry in the JSContext, bugzilla bug 66413 ::JS_SetContextPrivate(mContext, nsnull); @@ -1108,14 +1112,14 @@ nsJSContext::DestroyJSContext() Preferences::UnregisterCallback(JSOptionChangedCallback, js_options_dot_str, this); - bool do_gc = mGCOnDestruction && !sGCTimer; - + if (mGCOnDestruction) { + PokeGC(); + } + // Let xpconnect destroy the JSContext when it thinks the time is right. nsIXPConnect *xpc = nsContentUtils::XPConnect(); if (xpc) { - xpc->ReleaseJSContext(mContext, !do_gc); - } else if (do_gc) { - ::JS_DestroyContext(mContext); + xpc->ReleaseJSContext(mContext, true); } else { ::JS_DestroyContextNoGC(mContext); } @@ -3224,6 +3228,7 @@ nsJSContext::GarbageCollectNow(bool shrinkingGC) SAMPLE_LABEL("GC", "GarbageCollectNow"); KillGCTimer(); + KillShrinkGCBuffersTimer(); // Reset sPendingLoadCount in case the timer that fired was a // timer we scheduled due to a normal GC timer firing while @@ -3239,6 +3244,18 @@ nsJSContext::GarbageCollectNow(bool shrinkingGC) } } +//static +void +nsJSContext::ShrinkGCBuffersNow() +{ + NS_TIME_FUNCTION_MIN(1.0); + SAMPLE_LABEL("GC", "ShrinkGCBuffersNow"); + + KillShrinkGCBuffersTimer(); + + JS_ShrinkGCBuffers(nsJSRuntime::sRuntime); +} + //Static void nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener) @@ -3296,6 +3313,14 @@ GCTimerFired(nsITimer *aTimer, void *aClosure) nsJSContext::GarbageCollectNow(); } +void +ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure) +{ + NS_RELEASE(sShrinkGCBuffersTimer); + + nsJSContext::ShrinkGCBuffersNow(); +} + // static void CCTimerFired(nsITimer *aTimer, void *aClosure) @@ -3359,6 +3384,26 @@ nsJSContext::PokeGC() first = false; } +// static +void +nsJSContext::PokeShrinkGCBuffers() +{ + if (sShrinkGCBuffersTimer) { + return; + } + + CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer); + + if (!sShrinkGCBuffersTimer) { + // Failed to create timer (probably because we're in XPCOM shutdown) + return; + } + + sShrinkGCBuffersTimer->InitWithFuncCallback(ShrinkGCBuffersTimerFired, nsnull, + NS_SHRINK_GC_BUFFERS_DELAY, + nsITimer::TYPE_ONE_SHOT); +} + // static void nsJSContext::MaybePokeCC() @@ -3400,6 +3445,17 @@ nsJSContext::KillGCTimer() } } +//static +void +nsJSContext::KillShrinkGCBuffersTimer() +{ + if (sShrinkGCBuffersTimer) { + sShrinkGCBuffersTimer->Cancel(); + + NS_RELEASE(sShrinkGCBuffersTimer); + } +} + //static void nsJSContext::KillCCTimer() @@ -3465,10 +3521,11 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) } } - // If we didn't end up scheduling a GC, and there are unused - // chunks waiting to expire, make sure we will GC again soon. - if (!sGCTimer && JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS) > 0) { - nsJSContext::PokeGC(); + // If we didn't end up scheduling a GC, make sure that we release GC buffers + // soon after canceling previous shrinking attempt + nsJSContext::KillShrinkGCBuffersTimer(); + if (!sGCTimer) { + nsJSContext::PokeShrinkGCBuffers(); } } @@ -3808,6 +3865,7 @@ void nsJSRuntime::Shutdown() { nsJSContext::KillGCTimer(); + nsJSContext::KillShrinkGCBuffersTimer(); nsJSContext::KillCCTimer(); NS_IF_RELEASE(gNameSpaceManager); diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 78ff3def233..a45f2448dc9 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -183,11 +183,15 @@ public: static void LoadEnd(); static void GarbageCollectNow(bool shrinkingGC = false); + static void ShrinkGCBuffersNow(); static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull); static void PokeGC(); static void KillGCTimer(); + static void PokeShrinkGCBuffers(); + static void KillShrinkGCBuffersTimer(); + static void PokeCC(); static void MaybePokeCC(); static void KillCCTimer(); From b01fca43da578ee8cbbfe6f55c4a636e9e57d07c Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 9 Dec 2011 20:26:12 -0500 Subject: [PATCH 55/66] Bug 710932 - Create debugger statements using a constructor that doesn't examine the token stream. r=jorendorff --HG-- extra : rebase_source : eb913a09cc70398e252c1c8914104c60f79073ab --- js/src/frontend/ParseNode.h | 7 +++++++ js/src/frontend/Parser.cpp | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 29d8bbe969c..9d23930ca36 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -1059,6 +1059,13 @@ ParseNode::asContinueStatement() return *static_cast(this); } +class DebuggerStatement : public ParseNode { + public: + DebuggerStatement(const TokenPos &pos) + : ParseNode(PNK_DEBUGGER, JSOP_NOP, PN_NULLARY, pos) + { } +}; + ParseNode * CloneLeftHandSide(ParseNode *opn, TreeContext *tc); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index c87cae9e40c..efbc83015af 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4226,7 +4226,7 @@ Parser::statement() return pn; case TOK_DEBUGGER: - pn = NullaryNode::create(PNK_DEBUGGER, tc); + pn = tc->parser->new_(tokenStream.currentToken().pos); if (!pn) return NULL; tc->flags |= TCF_FUN_HEAVYWEIGHT; From b8337742d7230c2be05598aececbbcbcce7b3d6c Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 9 Dec 2011 23:28:59 -0500 Subject: [PATCH 56/66] Bug 710932 - Create expressions using a constructor that doesn't examine the token stream. r=jorendorff --HG-- extra : rebase_source : f3b8ca1065e7694cb411d14800052e7cef48f853 --- js/src/frontend/BytecodeEmitter.cpp | 22 +++++++++------ js/src/frontend/FoldConstants.cpp | 6 ++-- js/src/frontend/ParseNode.h | 44 +++++++++++++++++++++++++---- js/src/frontend/Parser.cpp | 15 ++++------ js/src/frontend/TokenStream.h | 7 +++-- js/src/jsreflect.cpp | 23 +++++---------- js/src/jsxml.cpp | 7 +++-- 7 files changed, 78 insertions(+), 46 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index f30d3f4696b..afa5970344a 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1863,23 +1863,29 @@ EmitIndexOp(JSContext *cx, JSOp op, uintN index, BytecodeEmitter *bce, JSOp *psu JS_END_MACRO static bool -EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL) +EmitAtomOp(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL) { JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); - if (op == JSOP_GETPROP && - pn->pn_atom == cx->runtime->atomState.lengthAtom) { + if (op == JSOP_GETPROP && atom == cx->runtime->atomState.lengthAtom) { /* Specialize length accesses for the interpreter. */ op = JSOP_LENGTH; } jsatomid index; - if (!bce->makeAtomIndex(pn->pn_atom, &index)) + if (!bce->makeAtomIndex(atom, &index)) return false; return EmitIndexOp(cx, op, index, bce, psuffix); } +static bool +EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL) +{ + JS_ASSERT(pn->pn_atom != NULL); + return EmitAtomOp(cx, pn->pn_atom, op, bce, psuffix); +} + static JSBool EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce) { @@ -5478,16 +5484,16 @@ EmitXMLTag(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) } static bool -EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) +EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, XMLProcessingInstruction &pi) { JS_ASSERT(!bce->inStrictMode()); jsatomid index; - if (!bce->makeAtomIndex(pn->pn_pidata, &index)) + if (!bce->makeAtomIndex(pi.data(), &index)) return false; if (!EmitIndexOp(cx, JSOP_QNAMEPART, index, bce)) return false; - if (!EmitAtomOp(cx, pn, JSOP_XMLPI, bce)) + if (!EmitAtomOp(cx, pi.target(), JSOP_XMLPI, bce)) return false; return true; } @@ -7594,7 +7600,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) break; case PNK_XMLPI: - if (!EmitXMLProcessingInstruction(cx, bce, pn)) + if (!EmitXMLProcessingInstruction(cx, bce, pn->asXMLProcessingInstruction())) return false; break; #endif /* JS_HAS_XML_SUPPORT */ diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 46e84891470..97c2ee07b2b 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -273,11 +273,13 @@ FoldXMLConstants(JSContext *cx, ParseNode *pn, TreeContext *tc) return JS_FALSE; break; - case PNK_XMLPI: - str = js_MakeXMLPIString(cx, pn2->pn_pitarget, pn2->pn_pidata); + case PNK_XMLPI: { + XMLProcessingInstruction &pi = pn2->asXMLProcessingInstruction(); + str = js_MakeXMLPIString(cx, pi.target(), pi.data()); if (!str) return JS_FALSE; break; + } cantfold: default: diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 9d23930ca36..c5d83512513 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -485,6 +485,7 @@ struct Definition; class LoopControlStatement; class BreakStatement; class ContinueStatement; +class XMLProcessingInstruction; struct ParseNode { private: @@ -617,15 +618,16 @@ struct ParseNode { AtomDefnMapPtr defnMap; ParseNode *tree; /* sub-tree containing name uses */ } nameset; - struct { /* PN_NULLARY variant for E4X XML PI */ - PropertyName *target; /* target in */ - JSAtom *data; /* data (or null) in */ - } xmlpi; jsdouble dval; /* aligned numeric literal value */ class { friend class LoopControlStatement; PropertyName *label; /* target of break/continue statement */ } loopControl; + class { /* E4X XML PI */ + friend class XMLProcessingInstruction; + PropertyName *target; /* non-empty */ + JSAtom *data; /* may be empty, never null */ + } xmlpi; } pn_u; #define pn_funbox pn_u.name.funbox @@ -656,8 +658,6 @@ struct ParseNode { #define pn_names pn_u.nameset.defnMap #define pn_tree pn_u.nameset.tree #define pn_dval pn_u.dval -#define pn_pitarget pn_u.xmlpi.target -#define pn_pidata pn_u.xmlpi.data protected: void init(TokenKind type, JSOp op, ParseNodeArity arity) { @@ -924,6 +924,9 @@ struct ParseNode { /* Casting operations. */ inline BreakStatement &asBreakStatement(); inline ContinueStatement &asContinueStatement(); +#if JS_HAS_XML_SUPPORT + inline XMLProcessingInstruction &asXMLProcessingInstruction(); +#endif }; struct NullaryNode : public ParseNode { @@ -1066,6 +1069,35 @@ class DebuggerStatement : public ParseNode { { } }; +#if JS_HAS_XML_SUPPORT +class XMLProcessingInstruction : public ParseNode { + public: + XMLProcessingInstruction(PropertyName *target, JSAtom *data, const TokenPos &pos) + : ParseNode(PNK_XMLPI, JSOP_NOP, PN_NULLARY, pos) + { + pn_u.xmlpi.target = target; + pn_u.xmlpi.data = data; + } + + PropertyName *target() const { + return pn_u.xmlpi.target; + } + + JSAtom *data() const { + return pn_u.xmlpi.data; + } +}; + +inline XMLProcessingInstruction & +ParseNode::asXMLProcessingInstruction() +{ + JS_ASSERT(isKind(PNK_XMLPI)); + JS_ASSERT(isOp(JSOP_NOP)); + JS_ASSERT(pn_arity == PN_NULLARY); + return *static_cast(this); +} +#endif + ParseNode * CloneLeftHandSide(ParseNode *opn, TreeContext *tc); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index efbc83015af..fcdce727025 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6363,13 +6363,10 @@ Parser::xmlElementContent(ParseNode *pn) pn2->pn_xflags &= ~PNX_XMLROOT; pn->pn_xflags |= pn2->pn_xflags; } else if (tt == TOK_XMLPI) { - pn2 = NullaryNode::create(PNK_XMLPI, tc); + const Token &tok = tokenStream.currentToken(); + pn2 = new_(tok.xmlPITarget(), tok.xmlPIData(), tok.pos); if (!pn2) return false; - const Token &tok = tokenStream.currentToken(); - pn2->setOp(tok.t_op); - pn2->pn_pitarget = tok.xmlPITarget(); - pn2->pn_pidata = tok.xmlPIData(); } else { JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT); pn2 = atomNode(tt == TOK_XMLCDATA ? PNK_XMLCDATA : PNK_XMLCOMMENT, @@ -7053,14 +7050,14 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) break; #if JS_HAS_XML_SUPPORT - case TOK_XMLPI: + case TOK_XMLPI: { JS_ASSERT(!tc->inStrictMode()); - pn = NullaryNode::create(PNK_XMLPI, tc); + const Token &tok = tokenStream.currentToken(); + pn = new_(tok.xmlPITarget(), tok.xmlPIData(), tok.pos); if (!pn) return NULL; - pn->pn_pitarget = tokenStream.currentToken().xmlPITarget(); - pn->pn_pidata = tokenStream.currentToken().xmlPIData(); break; + } #endif case TOK_NAME: diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 0490804e7c3..274a4a18f8f 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -330,8 +330,8 @@ struct Token { private: friend struct Token; struct { /* pair for XML PI */ - JSAtom *data; /* auxiliary atom table entry */ - PropertyName *target; /* main atom table entry */ + PropertyName *target; /* non-empty */ + JSAtom *data; /* maybe empty, never null */ } xmlpi; uint16_t sharpNumber; /* sharp variable number: #1# or #1= */ jsdouble number; /* floating point number */ @@ -359,6 +359,9 @@ struct Token { } void setProcessingInstruction(PropertyName *target, JSAtom *data) { + JS_ASSERT(target); + JS_ASSERT(data); + JS_ASSERT(!target->empty()); u.xmlpi.target = target; u.xmlpi.data = data; } diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index def4ab44aeb..b08cb675e2c 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -619,8 +619,6 @@ class NodeBuilder bool xmlComment(Value text, TokenPos *pos, Value *dst); - bool xmlPI(Value target, TokenPos *pos, Value *dst); - bool xmlPI(Value target, Value content, TokenPos *pos, Value *dst); }; @@ -1570,12 +1568,6 @@ NodeBuilder::xmlComment(Value text, TokenPos *pos, Value *dst) return newNode(AST_XMLCOMMENT, pos, "contents", text, dst); } -bool -NodeBuilder::xmlPI(Value target, TokenPos *pos, Value *dst) -{ - return xmlPI(target, NullValue(), pos, dst); -} - bool NodeBuilder::xmlPI(Value target, Value contents, TokenPos *pos, Value *dst) { @@ -2795,14 +2787,13 @@ ASTSerializer::xml(ParseNode *pn, Value *dst) case PNK_XMLCOMMENT: return builder.xmlComment(atomContents(pn->pn_atom), &pn->pn_pos, dst); - case PNK_XMLPI: - if (!pn->pn_pidata) - return builder.xmlPI(atomContents(pn->pn_pitarget), &pn->pn_pos, dst); - else - return builder.xmlPI(atomContents(pn->pn_pitarget), - atomContents(pn->pn_pidata), - &pn->pn_pos, - dst); + case PNK_XMLPI: { + XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction(); + return builder.xmlPI(atomContents(pi.target()), + atomContents(pi.data()), + &pi.pn_pos, + dst); + } #endif default: diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index 75b0605e90e..a063021fb48 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -1621,11 +1621,12 @@ ParseNodeToXML(Parser *parser, ParseNode *pn, goto skip_child; xml_class = JSXML_CLASS_COMMENT; } else if (pn->isKind(PNK_XMLPI)) { + XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction(); if (IS_XML(str)) { Value v = StringValue(str); JSAutoByteString bytes; if (js_ValueToPrintable(cx, v, &bytes)) { - ReportCompileErrorNumber(cx, &parser->tokenStream, pn, + ReportCompileErrorNumber(cx, &parser->tokenStream, &pi, JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr()); } goto fail; @@ -1634,11 +1635,11 @@ ParseNodeToXML(Parser *parser, ParseNode *pn, if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) goto skip_child; - qn = ParseNodeToQName(parser, pn, inScopeNSes, JS_FALSE); + qn = ParseNodeToQName(parser, &pi, inScopeNSes, JS_FALSE); if (!qn) goto fail; - str = pn->pn_pidata ? pn->pn_pidata : cx->runtime->emptyString; + str = pi.data(); xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; } else { /* CDATA section content, or element text. */ From 3b4232cb044572ea8e9e9b4144a96568ddd6f6db Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 13 Dec 2011 15:53:30 -0500 Subject: [PATCH 57/66] Bug 710932 - Create ?: conditional expressions using a constructor that doesn't examine the token stream. r=jorendorff --HG-- extra : rebase_source : cf8d22e47436d543a46115171460a4bfeae10971 --- js/src/frontend/BytecodeEmitter.cpp | 12 +++---- js/src/frontend/FoldConstants.cpp | 6 ++-- js/src/frontend/ParseNode.h | 47 ++++++++++++++++++++++++-- js/src/frontend/Parser.cpp | 51 +++++++++++++---------------- js/src/jsreflect.cpp | 2 +- 5 files changed, 77 insertions(+), 41 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index afa5970344a..a0ab9b45941 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -6772,16 +6772,16 @@ EmitSyntheticStatements(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrd } static bool -EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) +EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ConditionalExpression &conditional) { /* Emit the condition, then branch if false to the else part. */ - if (!EmitTree(cx, bce, pn->pn_kid1)) + if (!EmitTree(cx, bce, &conditional.condition())) return false; ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_COND); if (noteIndex < 0) return false; ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, 0); - if (beq < 0 || !EmitTree(cx, bce, pn->pn_kid2)) + if (beq < 0 || !EmitTree(cx, bce, &conditional.thenExpression())) return false; /* Jump around else, fixup the branch, emit else, fixup jump. */ @@ -6802,7 +6802,7 @@ EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) */ JS_ASSERT(bce->stackDepth > 0); bce->stackDepth--; - if (!EmitTree(cx, bce, pn->pn_kid3)) + if (!EmitTree(cx, bce, &conditional.elseExpression())) return false; CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, jmp); return SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq); @@ -7227,8 +7227,8 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) return false; break; - case PNK_HOOK: - ok = EmitConditionalExpression(cx, bce, pn); + case PNK_CONDITIONAL: + ok = EmitConditionalExpression(cx, bce, pn->asConditionalExpression()); break; case PNK_OR: diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 97c2ee07b2b..3d621febdf0 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -558,7 +558,7 @@ js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond) break; /* FALL THROUGH */ - case PNK_HOOK: + case PNK_CONDITIONAL: /* Reduce 'if (C) T; else E' into T for true C, E for false. */ switch (pn1->getKind()) { case PNK_NUMBER: @@ -593,8 +593,8 @@ js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond) * False condition and no else, or an empty then-statement was * moved up over pn. Either way, make pn an empty block (not an * empty statement, which does not decompile, even when labeled). - * NB: pn must be a PNK_IF as PNK_HOOK can never have a null kid - * or an empty statement for a child. + * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null + * kid or an empty statement for a child. */ pn->setKind(PNK_STATEMENTLIST); pn->setArity(PN_LIST); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index c5d83512513..57f9f3e62e9 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -41,6 +41,8 @@ #ifndef ParseNode_h__ #define ParseNode_h__ +#include "mozilla/Attributes.h" + #include "jsscript.h" #include "frontend/ParseMaps.h" @@ -61,7 +63,7 @@ namespace js { enum ParseNodeKind { PNK_SEMI, PNK_COMMA, - PNK_HOOK, + PNK_CONDITIONAL, PNK_COLON, PNK_OR, PNK_AND, @@ -313,7 +315,8 @@ enum ParseNodeKind { * PNK_MULASSIGN, * PNK_DIVASSIGN, * PNK_MODASSIGN - * PNK_HOOK ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else + * PNK_CONDITIONAL ternary (cond ? trueExpr : falseExpr) + * pn_kid1: cond, pn_kid2: then, pn_kid3: else * PNK_OR binary pn_left: first in || chain, pn_right: rest of chain * PNK_AND binary pn_left: first in && chain, pn_right: rest of chain * PNK_BITOR binary pn_left: left-assoc | expr, pn_right: ^ expr @@ -486,6 +489,7 @@ class LoopControlStatement; class BreakStatement; class ContinueStatement; class XMLProcessingInstruction; +class ConditionalExpression; struct ParseNode { private: @@ -496,6 +500,8 @@ struct ParseNode { pn_used : 1, /* name node is on a use-chain */ pn_defn : 1; /* this node is a Definition */ + void operator=(const ParseNode &other) MOZ_DELETE; + public: ParseNode(ParseNodeKind kind, JSOp op, ParseNodeArity arity) : pn_type(kind), pn_op(op), pn_arity(arity), pn_parens(0), pn_used(0), pn_defn(0), @@ -927,6 +933,7 @@ struct ParseNode { #if JS_HAS_XML_SUPPORT inline XMLProcessingInstruction &asXMLProcessingInstruction(); #endif + inline ConditionalExpression &asConditionalExpression(); }; struct NullaryNode : public ParseNode { @@ -1098,6 +1105,42 @@ ParseNode::asXMLProcessingInstruction() } #endif +class ConditionalExpression : public ParseNode { + public: + ConditionalExpression(ParseNode *condition, ParseNode *thenExpr, ParseNode *elseExpr) + : ParseNode(PNK_CONDITIONAL, JSOP_NOP, PN_TERNARY, + TokenPos::make(condition->pn_pos.begin, elseExpr->pn_pos.end)) + { + JS_ASSERT(condition); + JS_ASSERT(thenExpr); + JS_ASSERT(elseExpr); + pn_u.ternary.kid1 = condition; + pn_u.ternary.kid2 = thenExpr; + pn_u.ternary.kid3 = elseExpr; + } + + ParseNode &condition() const { + return *pn_u.ternary.kid1; + } + + ParseNode &thenExpression() const { + return *pn_u.ternary.kid2; + } + + ParseNode &elseExpression() const { + return *pn_u.ternary.kid3; + } +}; + +inline ConditionalExpression & +ParseNode::asConditionalExpression() +{ + JS_ASSERT(isKind(PNK_CONDITIONAL)); + JS_ASSERT(isOp(JSOP_NOP)); + JS_ASSERT(pn_arity == PN_TERNARY); + return *static_cast(this); +} + ParseNode * CloneLeftHandSide(ParseNode *opn, TreeContext *tc); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index fcdce727025..2f4f64a87b5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4643,37 +4643,30 @@ Parser::orExpr1() JS_ALWAYS_INLINE ParseNode * Parser::condExpr1() { - ParseNode *pn = orExpr1(); - if (pn && tokenStream.isCurrentTokenType(TOK_HOOK)) { - ParseNode *pn1 = pn; - pn = TernaryNode::create(PNK_HOOK, tc); - if (!pn) - return NULL; + ParseNode *condition = orExpr1(); + if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK)) + return condition; - /* - * Always accept the 'in' operator in the middle clause of a ternary, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - uintN oldflags = tc->flags; - tc->flags &= ~TCF_IN_FOR_INIT; - ParseNode *pn2 = assignExpr(); - tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); + /* + * Always accept the 'in' operator in the middle clause of a ternary, + * where it's unambiguous, even if we might be parsing the init of a + * for statement. + */ + uintN oldflags = tc->flags; + tc->flags &= ~TCF_IN_FOR_INIT; + ParseNode *thenExpr = assignExpr(); + tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); + if (!thenExpr) + return NULL; - if (!pn2) - return NULL; - MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); - ParseNode *pn3 = assignExpr(); - if (!pn3) - return NULL; - pn->pn_pos.begin = pn1->pn_pos.begin; - pn->pn_pos.end = pn3->pn_pos.end; - pn->pn_kid1 = pn1; - pn->pn_kid2 = pn2; - pn->pn_kid3 = pn3; - tokenStream.getToken(); /* need to read one token past the end */ - } - return pn; + MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); + + ParseNode *elseExpr = assignExpr(); + if (!elseExpr) + return NULL; + + tokenStream.getToken(); /* read one token past the end */ + return new_(condition, thenExpr, elseExpr); } bool diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index b08cb675e2c..d4224678014 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -2402,7 +2402,7 @@ ASTSerializer::expression(ParseNode *pn, Value *dst) builder.sequenceExpression(exprs, &pn->pn_pos, dst); } - case PNK_HOOK: + case PNK_CONDITIONAL: { Value test, cons, alt; From 7fd4360fc9398b0c09e012b9655508e1c7f2e2f5 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 13 Dec 2011 15:53:30 -0500 Subject: [PATCH 58/66] Bug 710932 - Create defsharp/usesharp expressions with a constructor that doesn't examine the token stream. r=jorendorff --HG-- extra : rebase_source : e93396c31f7593b9698549f02a9b4aecda134340 --- js/src/frontend/BytecodeEmitter.cpp | 7 ++-- js/src/frontend/ParseNode.cpp | 2 +- js/src/frontend/ParseNode.h | 61 ++++++++++++++++++++++++++++- js/src/frontend/Parser.cpp | 52 ++++++++++++------------ js/src/jsreflect.cpp | 11 ++++-- 5 files changed, 99 insertions(+), 34 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index a0ab9b45941..5afb88e20fb 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -7447,7 +7447,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) case PNK_DEFSHARP: { JS_ASSERT(bce->hasSharps()); - int sharpnum = pn->pn_num; + jsint sharpnum = pn->asDefSharpExpression().number(); pn = pn->pn_kid; if (pn->isKind(PNK_RB)) { ok = EmitArray(cx, bce, pn, sharpnum); @@ -7466,13 +7466,14 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (!EmitTree(cx, bce, pn)) return JS_FALSE; - EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, bce->sharpSlotBase, (jsatomid) sharpnum); + EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, bce->sharpSlotBase, jsatomid(sharpnum)); break; } case PNK_USESHARP: JS_ASSERT(bce->hasSharps()); - EMIT_UINT16PAIR_IMM_OP(JSOP_USESHARP, bce->sharpSlotBase, (jsatomid) pn->pn_num); + EMIT_UINT16PAIR_IMM_OP(JSOP_USESHARP, bce->sharpSlotBase, + jsatomid(pn->asUseSharpExpression().number())); break; #endif /* JS_HAS_SHARP_VARS */ diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 74256ed0792..50acf52a7e5 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -535,7 +535,7 @@ CloneParseTree(ParseNode *opn, TreeContext *tc) case PN_UNARY: NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc)); - pn->pn_num = opn->pn_num; + pn->pn_u.unary.num = opn->pn_u.unary.num; pn->pn_hidden = opn->pn_hidden; break; diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 57f9f3e62e9..6b8c992c90b 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -490,6 +490,8 @@ class BreakStatement; class ContinueStatement; class XMLProcessingInstruction; class ConditionalExpression; +class DefSharpExpression; +class UseSharpExpression; struct ParseNode { private: @@ -597,7 +599,7 @@ struct ParseNode { } binary; struct { /* one kid if unary */ ParseNode *kid; - jsint num; /* -1 or sharp variable number */ + jsint num; /* sharp variable number or unused */ JSBool hidden; /* hidden genexp-induced JSOP_YIELD or directive prologue member (as pn_prologue) */ @@ -634,6 +636,10 @@ struct ParseNode { PropertyName *target; /* non-empty */ JSAtom *data; /* may be empty, never null */ } xmlpi; + class { + friend class UseSharpExpression; + jsint number; /* #number# */ + } usesharp; } pn_u; #define pn_funbox pn_u.name.funbox @@ -654,7 +660,6 @@ struct ParseNode { #define pn_pval pn_u.binary.pval #define pn_iflags pn_u.binary.iflags #define pn_kid pn_u.unary.kid -#define pn_num pn_u.unary.num #define pn_hidden pn_u.unary.hidden #define pn_prologue pn_u.unary.hidden #define pn_atom pn_u.name.atom @@ -934,6 +939,8 @@ struct ParseNode { inline XMLProcessingInstruction &asXMLProcessingInstruction(); #endif inline ConditionalExpression &asConditionalExpression(); + inline DefSharpExpression &asDefSharpExpression(); + inline UseSharpExpression &asUseSharpExpression(); }; struct NullaryNode : public ParseNode { @@ -1141,6 +1148,56 @@ ParseNode::asConditionalExpression() return *static_cast(this); } +class DefSharpExpression : public ParseNode { + public: + DefSharpExpression(uint16_t number, ParseNode *expr, + const TokenPtr &begin, const TokenPtr &end) + : ParseNode(PNK_DEFSHARP, JSOP_NOP, PN_UNARY, TokenPos::make(begin, end)) + { + pn_u.unary.num = number; + pn_u.unary.kid = expr; + } + + jsint number() const { + return pn_u.unary.num; + } + + ParseNode &expression() const { + return *pn_u.unary.kid; + } +}; + +inline DefSharpExpression & +ParseNode::asDefSharpExpression() +{ + JS_ASSERT(isKind(PNK_DEFSHARP)); + JS_ASSERT(isOp(JSOP_NOP)); + JS_ASSERT(pn_arity == PN_UNARY); + return *static_cast(this); +} + +class UseSharpExpression : public ParseNode { + public: + UseSharpExpression(uint16_t number, const TokenPos &pos) + : ParseNode(PNK_USESHARP, JSOP_NOP, PN_NULLARY, pos) + { + pn_u.usesharp.number = number; + } + + jsint number() const { + return pn_u.usesharp.number; + } +}; + +inline UseSharpExpression & +ParseNode::asUseSharpExpression() +{ + JS_ASSERT(isKind(PNK_USESHARP)); + JS_ASSERT(isOp(JSOP_NOP)); + JS_ASSERT(pn_arity == PN_NULLARY); + return *static_cast(this); +} + ParseNode * CloneLeftHandSide(ParseNode *opn, TreeContext *tc); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 2f4f64a87b5..f1589254cbf 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2931,7 +2931,6 @@ Parser::letBlock(LetContext letContext) if (!semi) return NULL; - semi->pn_num = -1; semi->pn_kid = pnlet; letContext = LetExpresion; @@ -6944,40 +6943,45 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) #endif #if JS_HAS_SHARP_VARS - case TOK_DEFSHARP: - pn = UnaryNode::create(PNK_DEFSHARP, tc); - if (!pn) + case TOK_DEFSHARP: { + if (!tc->ensureSharpSlots()) return NULL; - pn->pn_num = tokenStream.currentToken().sharpNumber(); + const Token &tok = tokenStream.currentToken(); + TokenPtr begin = tok.pos.begin; + uint16_t number = tok.sharpNumber(); + tt = tokenStream.getToken(TSF_OPERAND); - pn->pn_kid = primaryExpr(tt, JS_FALSE); - if (!pn->pn_kid) + ParseNode *expr = primaryExpr(tt, false); + if (!expr) return NULL; - if (pn->pn_kid->isKind(PNK_USESHARP) || - pn->pn_kid->isKind(PNK_DEFSHARP) || - pn->pn_kid->isKind(PNK_STRING) || - pn->pn_kid->isKind(PNK_NUMBER) || - pn->pn_kid->isKind(PNK_TRUE) || - pn->pn_kid->isKind(PNK_FALSE) || - pn->pn_kid->isKind(PNK_NULL) || - pn->pn_kid->isKind(PNK_THIS)) + if (expr->isKind(PNK_USESHARP) || + expr->isKind(PNK_DEFSHARP) || + expr->isKind(PNK_STRING) || + expr->isKind(PNK_NUMBER) || + expr->isKind(PNK_TRUE) || + expr->isKind(PNK_FALSE) || + expr->isKind(PNK_NULL) || + expr->isKind(PNK_THIS)) { - reportErrorNumber(pn->pn_kid, JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF); + reportErrorNumber(expr, JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF); return NULL; } - if (!tc->ensureSharpSlots()) - return NULL; - break; - - case TOK_USESHARP: - /* Check for forward/dangling references at runtime, to allow eval. */ - pn = NullaryNode::create(PNK_USESHARP, tc); + pn = new_(number, expr, begin, tokenStream.currentToken().pos.end); if (!pn) return NULL; + break; + } + + case TOK_USESHARP: { if (!tc->ensureSharpSlots()) return NULL; - pn->pn_num = tokenStream.currentToken().sharpNumber(); + /* Check for forward/dangling references at runtime, to allow eval. */ + const Token &tok = tokenStream.currentToken(); + pn = new_(tok.sharpNumber(), tok.pos); + if (!pn) + return NULL; break; + } #endif /* JS_HAS_SHARP_VARS */ case TOK_LP: diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index d4224678014..da0edbac1c9 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -2622,13 +2622,16 @@ ASTSerializer::expression(ParseNode *pn, Value *dst) case PNK_DEFSHARP: { + DefSharpExpression &defsharp = pn->asDefSharpExpression(); Value expr; - return expression(pn->pn_kid, &expr) && - builder.graphExpression(pn->pn_num, expr, &pn->pn_pos, dst); + return expression(&defsharp.expression(), &expr) && + builder.graphExpression(defsharp.number(), expr, &defsharp.pn_pos, dst); } - case PNK_USESHARP: - return builder.graphIndexExpression(pn->pn_num, &pn->pn_pos, dst); + case PNK_USESHARP: { + UseSharpExpression &expr = pn->asUseSharpExpression(); + return builder.graphIndexExpression(expr.number(), &expr.pn_pos, dst); + } case PNK_ARRAYCOMP: /* NB: it's no longer the case that pn_count could be 2. */ From f002154d4369a13c11dc12bef26a62218f30228c Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Wed, 14 Dec 2011 20:03:35 -0500 Subject: [PATCH 59/66] Bug 710932 - Create true/false/this/null parse nodes with a constructor that doesn't examine the token stream. r=jorendorff --HG-- extra : rebase_source : 29dc590794dd36409a5a171f8043aad1546b7b9a --- js/src/frontend/ParseNode.h | 17 +++++++++++++++++ js/src/frontend/Parser.cpp | 18 ++++-------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 6b8c992c90b..e0dbe6a29b1 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -1198,6 +1198,23 @@ ParseNode::asUseSharpExpression() return *static_cast(this); } +class ThisLiteral : public ParseNode { + public: + ThisLiteral(const TokenPos &pos) : ParseNode(PNK_THIS, JSOP_THIS, PN_NULLARY, pos) { } +}; + +class NullLiteral : public ParseNode { + public: + NullLiteral(const TokenPos &pos) : ParseNode(PNK_NULL, JSOP_NULL, PN_NULLARY, pos) { } +}; + +class BooleanLiteral : public ParseNode { + public: + BooleanLiteral(bool b, const TokenPos &pos) + : ParseNode(b ? PNK_TRUE : PNK_FALSE, b ? JSOP_TRUE : JSOP_FALSE, PN_NULLARY, pos) + { } +}; + ParseNode * CloneLeftHandSide(ParseNode *opn, TreeContext *tc); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index f1589254cbf..784767543be 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6565,16 +6565,6 @@ Parser::parseXMLText(JSObject *chain, bool allowList) #endif /* JS_HAS_XMLSUPPORT */ -static ParseNode * -PrimaryExprNode(ParseNodeKind kind, JSOp op, TreeContext *tc) -{ - ParseNode *pn = NullaryNode::create(kind, tc); - if (!pn) - return NULL; - pn->setOp(op); - return pn; -} - ParseNode * Parser::primaryExpr(TokenKind tt, JSBool afterDot) { @@ -7217,13 +7207,13 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) break; case TOK_TRUE: - return PrimaryExprNode(PNK_TRUE, JSOP_TRUE, tc); + return new_(true, tokenStream.currentToken().pos); case TOK_FALSE: - return PrimaryExprNode(PNK_FALSE, JSOP_FALSE, tc); + return new_(false, tokenStream.currentToken().pos); case TOK_THIS: - return PrimaryExprNode(PNK_THIS, JSOP_THIS, tc); + return new_(tokenStream.currentToken().pos); case TOK_NULL: - return PrimaryExprNode(PNK_NULL, JSOP_NULL, tc); + return new_(tokenStream.currentToken().pos); case TOK_ERROR: /* The scanner or one of its subroutines reported the error. */ From a61585d888192c983d421c0b50eba7cf9709ba00 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Mon, 19 Dec 2011 09:02:47 -0500 Subject: [PATCH 60/66] Bug 580786. Require gWindowUtils. r=dbaron We should have DOMWindowUtils everywhere and this cleans up the code. --- layout/tools/reftest/reftest.js | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index ddcb8960561..a7e544f8db7 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -345,13 +345,9 @@ function InitAndStartRefTests() gThisChunk = 0; } - try { - gWindowUtils = gContainingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils); - if (gWindowUtils && !gWindowUtils.compareCanvases) - gWindowUtils = null; - } catch (e) { - gWindowUtils = null; - } + gWindowUtils = gContainingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils); + if (!gWindowUtils || !gWindowUtils.compareCanvases) + throw "nsIDOMWindowUtils inteface missing"; gIOService = CC[IO_SERVICE_CONTRACTID].getService(CI.nsIIOService); gDebug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2); @@ -521,9 +517,9 @@ function BuildConditionSandbox(aURL) { } sandbox.layersGPUAccelerated = - gWindowUtils && gWindowUtils.layerManagerType != "Basic"; + gWindowUtils.layerManagerType != "Basic"; sandbox.layersOpenGL = - gWindowUtils && gWindowUtils.layerManagerType == "OpenGL"; + gWindowUtils.layerManagerType == "OpenGL"; // Shortcuts for widget toolkits. sandbox.Android = xr.OS == "Android"; @@ -1403,15 +1399,8 @@ function RecordResult(testRunTime, errorMsg, scriptResults) // whether the two renderings match: var equal; - if (gWindowUtils) { - differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, {}); - equal = (differences == 0); - } else { - differences = -1; - var k1 = gCanvas1.toDataURL(); - var k2 = gCanvas2.toDataURL(); - equal = (k1 == k2); - } + differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, {}); + equal = (differences == 0); // whether the comparison result matches what is in the manifest var test_passed = (equal == (gURLs[0].type == TYPE_REFTEST_EQUAL)); From 03047e6e643879a6912156c3a836180964982be5 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Mon, 19 Dec 2011 09:02:53 -0500 Subject: [PATCH 61/66] Bug 580786. Add support for fuzzy reftest matching. r=dbaron This will let us convert a bunch of random-if() to fuzzy-if() --- layout/reftests/reftest-sanity/fuzzy-ref.html | 6 ++++ layout/reftests/reftest-sanity/fuzzy.html | 6 ++++ layout/reftests/reftest-sanity/reftest.list | 6 ++++ layout/reftests/reftest-sanity/too-fuzzy.html | 6 ++++ layout/tools/reftest/README.txt | 8 +++++ layout/tools/reftest/reftest.js | 31 ++++++++++++++----- 6 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 layout/reftests/reftest-sanity/fuzzy-ref.html create mode 100644 layout/reftests/reftest-sanity/fuzzy.html create mode 100644 layout/reftests/reftest-sanity/too-fuzzy.html diff --git a/layout/reftests/reftest-sanity/fuzzy-ref.html b/layout/reftests/reftest-sanity/fuzzy-ref.html new file mode 100644 index 00000000000..776c7911d85 --- /dev/null +++ b/layout/reftests/reftest-sanity/fuzzy-ref.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/reftests/reftest-sanity/fuzzy.html b/layout/reftests/reftest-sanity/fuzzy.html new file mode 100644 index 00000000000..d288560e96f --- /dev/null +++ b/layout/reftests/reftest-sanity/fuzzy.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/reftests/reftest-sanity/reftest.list b/layout/reftests/reftest-sanity/reftest.list index ea8d338694c..7e23cdce272 100644 --- a/layout/reftests/reftest-sanity/reftest.list +++ b/layout/reftests/reftest-sanity/reftest.list @@ -129,3 +129,9 @@ pref(font.default.x-western,"sans-serif") == font-sans-serif.html font-default.h pref(font.default.x-western,"sans-serif") != font-serif.html font-default.html fails pref(font.default.x-western,true) == font-serif.html font-default.html fails pref(font.default.x-western,0) == font-serif.html font-default.html +# reftest syntax: fuzzy +fuzzy == fuzzy.html fuzzy-ref.html +fuzzy != too-fuzzy.html fuzzy-ref.html +fuzzy-if(true) == fuzzy.html fuzzy-ref.html +fuzzy-if(false) == fuzzy-ref.html fuzzy-ref.html +fails fuzzy-if(false) == fuzzy.html fuzzy-ref.html diff --git a/layout/reftests/reftest-sanity/too-fuzzy.html b/layout/reftests/reftest-sanity/too-fuzzy.html new file mode 100644 index 00000000000..7b26dd33a27 --- /dev/null +++ b/layout/reftests/reftest-sanity/too-fuzzy.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/tools/reftest/README.txt b/layout/tools/reftest/README.txt index 833e2cb3c04..72dc43c4b57 100644 --- a/layout/tools/reftest/README.txt +++ b/layout/tools/reftest/README.txt @@ -106,6 +106,14 @@ must be one of the following: fast on a 32-bit system but inordinately slow on a 64-bit system). + fuzzy This allows a test to pass if the pixel value differences + are <= 2. It can also be used with '!=' to ensure that the + difference is greater than 2. + + fuzzy-if(condition) If the condition is met, the test is treated as if + 'fuzzy' had been specified. This is useful if there + are differences on particular platforms. + require-or(cond1&&cond2&&...,fallback) Require some particular setup be performed or environmental condition(s) made true (eg setting debug mode) before the test diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index a7e544f8db7..860edaa80c2 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -149,6 +149,7 @@ const EXPECTED_PASS = 0; const EXPECTED_FAIL = 1; const EXPECTED_RANDOM = 2; const EXPECTED_DEATH = 3; // test must be skipped to avoid e.g. crash/hang +const EXPECTED_FUZZY = 4; // types of preference value we might want to set for a specific test const PREF_BOOLEAN = 0; @@ -686,16 +687,16 @@ function ReadManifest(aURL, inherited_status) var slow = false; var prefSettings = []; - while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref)/)) { + while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|fuzzy)/)) { var item = items.shift(); var stat; var cond; - var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/); + var m = item.match(/^(fails|random|skip|silentfail|fuzzy)-if(\(.*\))$/); if (m) { stat = m[1]; // Note: m[2] contains the parentheses, and we want them. cond = Components.utils.evalInSandbox(m[2], sandbox); - } else if (item.match(/^(fails|random|skip)$/)) { + } else if (item.match(/^(fails|random|skip|fuzzy)$/)) { stat = item; cond = true; } else if (item == "needs-focus") { @@ -777,6 +778,8 @@ function ReadManifest(aURL, inherited_status) expected_status = EXPECTED_RANDOM; } else if (stat == "skip") { expected_status = EXPECTED_DEATH; + } else if (stat == "fuzzy") { + expected_status = EXPECTED_FUZZY; } else if (stat == "silentfail") { allow_silent_fail = true; } @@ -1296,6 +1299,8 @@ function RecordResult(testRunTime, errorMsg, scriptResults) true: {s: "TEST-PASS" + randomMsg , n: "Random"}, false: {s: "TEST-KNOWN-FAIL" + randomMsg, n: "Random"} }; + outputs[EXPECTED_FUZZY] = outputs[EXPECTED_PASS]; + var output; if (gURLs[0].type == TYPE_LOAD) { @@ -1398,14 +1403,25 @@ function RecordResult(testRunTime, errorMsg, scriptResults) var differences; // whether the two renderings match: var equal; + var maxDifference = {}; - differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, {}); + differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, maxDifference); equal = (differences == 0); + // what is expected on this platform (PASS, FAIL, or RANDOM) + var expected = gURLs[0].expected; + + if (maxDifference.value > 0 && maxDifference.value <= 2) { + if (equal) { + throw "Inconsistent result from compareCanvases."; + } + equal = expected == EXPECTED_FUZZY; + gDumpLog("REFTEST fuzzy match\n"); + } + // whether the comparison result matches what is in the manifest var test_passed = (equal == (gURLs[0].type == TYPE_REFTEST_EQUAL)); - // what is expected on this platform (PASS, FAIL, or RANDOM) - var expected = gURLs[0].expected; + output = outputs[expected][test_passed]; ++gTestResults[output.n]; @@ -1423,11 +1439,12 @@ function RecordResult(testRunTime, errorMsg, scriptResults) gDumpLog(result + "\n"); if (!test_passed && expected == EXPECTED_PASS || + !test_passed && expected == EXPECTED_FUZZY || test_passed && expected == EXPECTED_FAIL) { if (!equal) { gDumpLog("REFTEST IMAGE 1 (TEST): " + gCanvas1.toDataURL() + "\n"); gDumpLog("REFTEST IMAGE 2 (REFERENCE): " + gCanvas2.toDataURL() + "\n"); - gDumpLog("REFTEST number of differing pixels: " + differences + "\n"); + gDumpLog("REFTEST number of differing pixels: " + differences + " max difference: " + maxDifference.value + "\n"); } else { gDumpLog("REFTEST IMAGE: " + gCanvas1.toDataURL() + "\n"); } From 1eeb73d20a2677e392911985239f32e69b08d640 Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Thu, 29 Dec 2011 23:35:53 -0500 Subject: [PATCH 62/66] Bug 713887 - Javascript error when using the menu quit to quit fennec r=mbrubeck --- mobile/android/chrome/content/browser.js | 1 - 1 file changed, 1 deletion(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index aaf0a772309..d19d7810084 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -438,7 +438,6 @@ var BrowserApp = { }, quit: function quit() { - Cu.reportError("got quit quit message"); window.QueryInterface(Ci.nsIDOMChromeWindow).minimize(); window.close(); }, From 54acacf726e3588b14a2778fd5ed9a33fac53eda Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 29 Dec 2011 22:29:16 -0500 Subject: [PATCH 63/66] Bug 713729 - Ensure that a fling is in progress when doing velocity-based fling adjustments. r=pcwalton --- mobile/android/base/ui/PanZoomController.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index 1b879507aa5..ff4e70545c9 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -669,18 +669,18 @@ public class PanZoomController if (flingingX || flingingY) { mX.displace(); mY.displace(); updatePosition(); - } - /* - * If we're still flinging with an appreciable velocity, stop here. The threshold is - * higher in the case of overscroll, so we bounce back eagerly when overscrolling but - * coast smoothly to a stop when not. - */ - float excess = PointUtils.distance(new PointF(mX.getExcess(), mY.getExcess())); - PointF velocityVector = new PointF(mX.getRealVelocity(), mY.getRealVelocity()); - float threshold = (excess >= 1.0f) ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD; - if (PointUtils.distance(velocityVector) >= threshold) - return; + /* + * If we're still flinging with an appreciable velocity, stop here. The threshold is + * higher in the case of overscroll, so we bounce back eagerly when overscrolling but + * coast smoothly to a stop when not. + */ + float excess = PointUtils.distance(new PointF(mX.getExcess(), mY.getExcess())); + PointF velocityVector = new PointF(mX.getRealVelocity(), mY.getRealVelocity()); + float threshold = (excess >= 1.0f) ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD; + if (PointUtils.distance(velocityVector) >= threshold) + return; + } /* * Perform a bounce-back animation if overscrolled, unless panning is being overridden From 11b9c1e857aee605cbb0c865b52a033e51131143 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 29 Dec 2011 22:30:47 -0500 Subject: [PATCH 64/66] Bug 709924 - Adjust viewportExcess after scrollIntoView(). r=Cwiiis We need to adjust the viewportExcess after calling scrollIntoView() because Gecko may not properly scroll the item into view. This happens because Gecko is unaware of the actual visible content area being affected by the applied zoom. --- mobile/android/chrome/content/browser.js | 85 ++++++++++++++++++++---- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index d19d7810084..4bf837da5e8 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -649,8 +649,65 @@ var BrowserApp = { return; let focused = doc.activeElement; if ((focused instanceof HTMLInputElement && focused.mozIsTextField(false)) || (focused instanceof HTMLTextAreaElement)) { - focused.scrollIntoView(false); - BrowserApp.getTabForBrowser(aBrowser).sendViewportUpdate(); + let tab = BrowserApp.getTabForBrowser(aBrowser); + let win = aBrowser.contentWindow; + + // tell gecko to scroll the field into view. this will scroll any nested scrollable elements + // as well as the browser's content window, and modify the scrollX and scrollY on the content window. + focused.scrollIntoView(true); + + // update userScrollPos so that we don't send a duplicate viewport update by triggering + // our scroll listener + tab.userScrollPos.x = win.scrollX; + tab.userScrollPos.y = win.scrollY; + + // note that: + // 1. because of the way we do zooming using a CSS transform, gecko does not take into + // account the effect of the zoom on the viewport size. + // 2. if the input element is near the bottom/right of the page (less than one viewport + // height/width away from the bottom/right), the scrollIntoView call will make gecko scroll to the + // bottom/right of the page in an attempt to align the input field with the top of the viewport. + // however, since gecko doesn't know about the zoom, what it thinks is the "bottom/right of + // the page" isn't actually the bottom/right of the page at the current zoom level, and we + // need to adjust this further. + // 3. we can't actually adjust this by changing the window scroll position, as gecko already thinks + // we're at the bottom/right, so instead we do it by changing the viewportExcess on the tab and + // moving the browser element. + + let visibleContentWidth = tab._viewport.width / tab._viewport.zoom; + let visibleContentHeight = tab._viewport.height / tab._viewport.zoom; + // get the rect that the focused element occupies relative to what gecko thinks the viewport is, + // and adjust it by viewportExcess to so that it is relative to what the user sees as the viewport. + let focusedRect = focused.getBoundingClientRect(); + focusedRect = { + left: focusedRect.left - tab.viewportExcess.x, + right: focusedRect.right - tab.viewportExcess.x, + top: focusedRect.top - tab.viewportExcess.y, + bottom: focusedRect.bottom - tab.viewportExcess.y + }; + let transformChanged = false; + if (focusedRect.right >= visibleContentWidth && focusedRect.left > 0) { + // the element is too far off the right side, so we need to scroll to the right more + tab.viewportExcess.x += Math.min(focusedRect.left, focusedRect.right - visibleContentWidth); + transformChanged = true; + } else if (focusedRect.left < 0) { + // the element is too far off the left side, so we need to scroll to the left more + tab.viewportExcess.x += focusedRect.left; + transformChanged = true; + } + if (focusedRect.bottom >= visibleContentHeight && focusedRect.top > 0) { + // the element is too far down, so we need to scroll down more + tab.viewportExcess.y += Math.min(focusedRect.top, focusedRect.bottom - visibleContentHeight); + transformChanged = true; + } else if (focusedRect.top < 0) { + // the element is too far up, so we need to scroll up more + tab.viewportExcess.y += focusedRect.top; + transformChanged = true; + } + if (transformChanged) + tab.updateTransform(); + // finally, let java know where we ended up + tab.sendViewportUpdate(); } }, @@ -1350,20 +1407,22 @@ Tab.prototype = { transformChanged = true; } + if (transformChanged) + this.updateTransform(); + }, + + updateTransform: function() { let hasZoom = (Math.abs(this._viewport.zoom - 1.0) >= 1e-6); + let x = this._viewport.offsetX + Math.round(-this.viewportExcess.x * this._viewport.zoom); + let y = this._viewport.offsetY + Math.round(-this.viewportExcess.y * this._viewport.zoom); - if (transformChanged) { - let x = this._viewport.offsetX + Math.round(-excessX * this._viewport.zoom); - let y = this._viewport.offsetY + Math.round(-excessY * this._viewport.zoom); + let transform = + "translate(" + x + "px, " + + y + "px)"; + if (hasZoom) + transform += " scale(" + this._viewport.zoom + ")"; - let transform = - "translate(" + x + "px, " + - y + "px)"; - if (hasZoom) - transform += " scale(" + this._viewport.zoom + ")"; - - this.browser.style.MozTransform = transform; - } + this.browser.style.MozTransform = transform; }, get viewport() { From 2d6474a7250175365976d2a8b75d4d601cb1b260 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Thu, 29 Dec 2011 21:43:06 -0800 Subject: [PATCH 65/66] Back out 5bd2b99f0645 and 9a4874754660 (bug 580786) for Android R1 failures --- layout/reftests/reftest-sanity/fuzzy-ref.html | 6 --- layout/reftests/reftest-sanity/fuzzy.html | 6 --- layout/reftests/reftest-sanity/reftest.list | 6 --- layout/reftests/reftest-sanity/too-fuzzy.html | 6 --- layout/tools/reftest/README.txt | 8 --- layout/tools/reftest/reftest.js | 52 ++++++++----------- 6 files changed, 23 insertions(+), 61 deletions(-) delete mode 100644 layout/reftests/reftest-sanity/fuzzy-ref.html delete mode 100644 layout/reftests/reftest-sanity/fuzzy.html delete mode 100644 layout/reftests/reftest-sanity/too-fuzzy.html diff --git a/layout/reftests/reftest-sanity/fuzzy-ref.html b/layout/reftests/reftest-sanity/fuzzy-ref.html deleted file mode 100644 index 776c7911d85..00000000000 --- a/layout/reftests/reftest-sanity/fuzzy-ref.html +++ /dev/null @@ -1,6 +0,0 @@ - - - -
- - diff --git a/layout/reftests/reftest-sanity/fuzzy.html b/layout/reftests/reftest-sanity/fuzzy.html deleted file mode 100644 index d288560e96f..00000000000 --- a/layout/reftests/reftest-sanity/fuzzy.html +++ /dev/null @@ -1,6 +0,0 @@ - - - -
- - diff --git a/layout/reftests/reftest-sanity/reftest.list b/layout/reftests/reftest-sanity/reftest.list index 7e23cdce272..ea8d338694c 100644 --- a/layout/reftests/reftest-sanity/reftest.list +++ b/layout/reftests/reftest-sanity/reftest.list @@ -129,9 +129,3 @@ pref(font.default.x-western,"sans-serif") == font-sans-serif.html font-default.h pref(font.default.x-western,"sans-serif") != font-serif.html font-default.html fails pref(font.default.x-western,true) == font-serif.html font-default.html fails pref(font.default.x-western,0) == font-serif.html font-default.html -# reftest syntax: fuzzy -fuzzy == fuzzy.html fuzzy-ref.html -fuzzy != too-fuzzy.html fuzzy-ref.html -fuzzy-if(true) == fuzzy.html fuzzy-ref.html -fuzzy-if(false) == fuzzy-ref.html fuzzy-ref.html -fails fuzzy-if(false) == fuzzy.html fuzzy-ref.html diff --git a/layout/reftests/reftest-sanity/too-fuzzy.html b/layout/reftests/reftest-sanity/too-fuzzy.html deleted file mode 100644 index 7b26dd33a27..00000000000 --- a/layout/reftests/reftest-sanity/too-fuzzy.html +++ /dev/null @@ -1,6 +0,0 @@ - - - -
- - diff --git a/layout/tools/reftest/README.txt b/layout/tools/reftest/README.txt index 72dc43c4b57..833e2cb3c04 100644 --- a/layout/tools/reftest/README.txt +++ b/layout/tools/reftest/README.txt @@ -106,14 +106,6 @@ must be one of the following: fast on a 32-bit system but inordinately slow on a 64-bit system). - fuzzy This allows a test to pass if the pixel value differences - are <= 2. It can also be used with '!=' to ensure that the - difference is greater than 2. - - fuzzy-if(condition) If the condition is met, the test is treated as if - 'fuzzy' had been specified. This is useful if there - are differences on particular platforms. - require-or(cond1&&cond2&&...,fallback) Require some particular setup be performed or environmental condition(s) made true (eg setting debug mode) before the test diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index 860edaa80c2..ddcb8960561 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -149,7 +149,6 @@ const EXPECTED_PASS = 0; const EXPECTED_FAIL = 1; const EXPECTED_RANDOM = 2; const EXPECTED_DEATH = 3; // test must be skipped to avoid e.g. crash/hang -const EXPECTED_FUZZY = 4; // types of preference value we might want to set for a specific test const PREF_BOOLEAN = 0; @@ -346,9 +345,13 @@ function InitAndStartRefTests() gThisChunk = 0; } - gWindowUtils = gContainingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils); - if (!gWindowUtils || !gWindowUtils.compareCanvases) - throw "nsIDOMWindowUtils inteface missing"; + try { + gWindowUtils = gContainingWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils); + if (gWindowUtils && !gWindowUtils.compareCanvases) + gWindowUtils = null; + } catch (e) { + gWindowUtils = null; + } gIOService = CC[IO_SERVICE_CONTRACTID].getService(CI.nsIIOService); gDebug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2); @@ -518,9 +521,9 @@ function BuildConditionSandbox(aURL) { } sandbox.layersGPUAccelerated = - gWindowUtils.layerManagerType != "Basic"; + gWindowUtils && gWindowUtils.layerManagerType != "Basic"; sandbox.layersOpenGL = - gWindowUtils.layerManagerType == "OpenGL"; + gWindowUtils && gWindowUtils.layerManagerType == "OpenGL"; // Shortcuts for widget toolkits. sandbox.Android = xr.OS == "Android"; @@ -687,16 +690,16 @@ function ReadManifest(aURL, inherited_status) var slow = false; var prefSettings = []; - while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|fuzzy)/)) { + while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref)/)) { var item = items.shift(); var stat; var cond; - var m = item.match(/^(fails|random|skip|silentfail|fuzzy)-if(\(.*\))$/); + var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/); if (m) { stat = m[1]; // Note: m[2] contains the parentheses, and we want them. cond = Components.utils.evalInSandbox(m[2], sandbox); - } else if (item.match(/^(fails|random|skip|fuzzy)$/)) { + } else if (item.match(/^(fails|random|skip)$/)) { stat = item; cond = true; } else if (item == "needs-focus") { @@ -778,8 +781,6 @@ function ReadManifest(aURL, inherited_status) expected_status = EXPECTED_RANDOM; } else if (stat == "skip") { expected_status = EXPECTED_DEATH; - } else if (stat == "fuzzy") { - expected_status = EXPECTED_FUZZY; } else if (stat == "silentfail") { allow_silent_fail = true; } @@ -1299,8 +1300,6 @@ function RecordResult(testRunTime, errorMsg, scriptResults) true: {s: "TEST-PASS" + randomMsg , n: "Random"}, false: {s: "TEST-KNOWN-FAIL" + randomMsg, n: "Random"} }; - outputs[EXPECTED_FUZZY] = outputs[EXPECTED_PASS]; - var output; if (gURLs[0].type == TYPE_LOAD) { @@ -1403,25 +1402,21 @@ function RecordResult(testRunTime, errorMsg, scriptResults) var differences; // whether the two renderings match: var equal; - var maxDifference = {}; - differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, maxDifference); - equal = (differences == 0); - - // what is expected on this platform (PASS, FAIL, or RANDOM) - var expected = gURLs[0].expected; - - if (maxDifference.value > 0 && maxDifference.value <= 2) { - if (equal) { - throw "Inconsistent result from compareCanvases."; - } - equal = expected == EXPECTED_FUZZY; - gDumpLog("REFTEST fuzzy match\n"); + if (gWindowUtils) { + differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, {}); + equal = (differences == 0); + } else { + differences = -1; + var k1 = gCanvas1.toDataURL(); + var k2 = gCanvas2.toDataURL(); + equal = (k1 == k2); } // whether the comparison result matches what is in the manifest var test_passed = (equal == (gURLs[0].type == TYPE_REFTEST_EQUAL)); - + // what is expected on this platform (PASS, FAIL, or RANDOM) + var expected = gURLs[0].expected; output = outputs[expected][test_passed]; ++gTestResults[output.n]; @@ -1439,12 +1434,11 @@ function RecordResult(testRunTime, errorMsg, scriptResults) gDumpLog(result + "\n"); if (!test_passed && expected == EXPECTED_PASS || - !test_passed && expected == EXPECTED_FUZZY || test_passed && expected == EXPECTED_FAIL) { if (!equal) { gDumpLog("REFTEST IMAGE 1 (TEST): " + gCanvas1.toDataURL() + "\n"); gDumpLog("REFTEST IMAGE 2 (REFERENCE): " + gCanvas2.toDataURL() + "\n"); - gDumpLog("REFTEST number of differing pixels: " + differences + " max difference: " + maxDifference.value + "\n"); + gDumpLog("REFTEST number of differing pixels: " + differences + "\n"); } else { gDumpLog("REFTEST IMAGE: " + gCanvas1.toDataURL() + "\n"); } From f5076064af7e7f5cae5793d0fe3d1dd7bdfa837b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 12 Dec 2011 19:04:12 -0800 Subject: [PATCH 66/66] Bug 708159 - Avoid unnecessary work done by multi-reporters in nsMemoryReporterManager::GetExplicit. r=jlebar,bent. --- dom/base/nsDOMMemoryReporter.cpp | 9 ++ dom/workers/WorkerPrivate.cpp | 49 ++++-- dom/workers/WorkerPrivate.h | 3 +- js/src/jscntxt.cpp | 24 +-- js/src/jsgc.cpp | 22 +++ js/src/jsgc.h | 6 + js/xpconnect/src/XPCJSRuntime.cpp | 81 +++++++++- js/xpconnect/src/xpcpublic.h | 4 +- layout/base/nsPresShell.cpp | 7 + .../aboutmemory/content/aboutMemory.js | 3 + .../aboutmemory/tests/test_aboutmemory.xul | 17 +- toolkit/components/telemetry/TelemetryPing.js | 25 ++- xpcom/base/MapsMemoryReporter.cpp | 9 +- xpcom/base/nsIMemoryReporter.idl | 14 +- xpcom/base/nsMemoryReporterManager.cpp | 150 +++++++++++++----- 15 files changed, 336 insertions(+), 87 deletions(-) diff --git a/dom/base/nsDOMMemoryReporter.cpp b/dom/base/nsDOMMemoryReporter.cpp index 61c8187ac43..b9909d3529a 100644 --- a/dom/base/nsDOMMemoryReporter.cpp +++ b/dom/base/nsDOMMemoryReporter.cpp @@ -230,3 +230,12 @@ nsDOMMemoryMultiReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, return NS_OK; } +NS_IMETHODIMP +nsDOMMemoryMultiReporter::GetExplicitNonHeap(PRInt64* aAmount) +{ + // This reporter only measures heap memory. + *aAmount = 0; + return NS_OK; +} + + diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 40a0466e55f..87d30b4c13b 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -186,17 +186,14 @@ public: NS_LITERAL_CSTRING(")/"); } - NS_IMETHOD - CollectReports(nsIMemoryMultiReporterCallback* aCallback, - nsISupports* aClosure) + nsresult + CollectForRuntime(bool aIsQuick, void* aData) { AssertIsOnMainThread(); - IterateData data; - if (mWorkerPrivate) { bool disabled; - if (!mWorkerPrivate->BlockAndCollectRuntimeStats(&data, &disabled)) { + if (!mWorkerPrivate->BlockAndCollectRuntimeStats(aIsQuick, aData, &disabled)) { return NS_ERROR_FAILURE; } @@ -217,12 +214,35 @@ public: mWorkerPrivate = nsnull; } } + return NS_OK; + } + + NS_IMETHOD + CollectReports(nsIMemoryMultiReporterCallback* aCallback, + nsISupports* aClosure) + { + AssertIsOnMainThread(); + + IterateData data; + nsresult rv = CollectForRuntime(/* isQuick = */false, &data); + if (NS_FAILED(rv)) { + return rv; + } // Always report, even if we're disabled, so that we at least get an entry // in about::memory. ReportJSRuntimeStats(data, mPathPrefix, aCallback, aClosure); + return NS_OK; } + + NS_IMETHOD + GetExplicitNonHeap(PRInt64 *aAmount) + { + AssertIsOnMainThread(); + + return CollectForRuntime(/* isQuick = */true, aAmount); + } }; NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerMemoryReporter, nsIMemoryMultiReporter) @@ -1378,16 +1398,17 @@ class CollectRuntimeStatsRunnable : public WorkerControlRunnable Mutex mMutex; CondVar mCondVar; volatile bool mDone; - IterateData* mData; + bool mIsQuick; + void* mData; bool* mSucceeded; public: - CollectRuntimeStatsRunnable(WorkerPrivate* aWorkerPrivate, IterateData* aData, - bool* aSucceeded) + CollectRuntimeStatsRunnable(WorkerPrivate* aWorkerPrivate, bool aIsQuick, + void* aData, bool* aSucceeded) : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount), mMutex("CollectRuntimeStatsRunnable::mMutex"), mCondVar(mMutex, "CollectRuntimeStatsRunnable::mCondVar"), mDone(false), - mData(aData), mSucceeded(aSucceeded) + mIsQuick(aIsQuick), mData(aData), mSucceeded(aSucceeded) { } bool @@ -1429,7 +1450,9 @@ public: { JSAutoSuspendRequest asr(aCx); - *mSucceeded = CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), mData); + *mSucceeded = mIsQuick ? + mozilla::xpconnect::memory::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), mData) : + mozilla::xpconnect::memory::CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), mData); { MutexAutoLock lock(mMutex); @@ -2569,7 +2592,7 @@ WorkerPrivate::ScheduleDeletion(bool aWasPending) } bool -WorkerPrivate::BlockAndCollectRuntimeStats(IterateData* aData, bool* aDisabled) +WorkerPrivate::BlockAndCollectRuntimeStats(bool aIsQuick, void* aData, bool* aDisabled) { AssertIsOnMainThread(); NS_ASSERTION(aData, "Null data!"); @@ -2589,7 +2612,7 @@ WorkerPrivate::BlockAndCollectRuntimeStats(IterateData* aData, bool* aDisabled) bool succeeded; nsRefPtr runnable = - new CollectRuntimeStatsRunnable(this, aData, &succeeded); + new CollectRuntimeStatsRunnable(this, aIsQuick, aData, &succeeded); if (!runnable->Dispatch(nsnull)) { NS_WARNING("Failed to dispatch runnable!"); succeeded = false; diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index ac4742311e9..4f1caea3760 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -661,8 +661,7 @@ public: ScheduleDeletion(bool aWasPending); bool - BlockAndCollectRuntimeStats(mozilla::xpconnect::memory::IterateData* aData, - bool* aDisabled); + BlockAndCollectRuntimeStats(bool isQuick, void* aData, bool* aDisabled); bool DisableMemoryReporter(); diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 00106a8b3f6..42e77995db3 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -151,17 +151,22 @@ ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, * The computedSize is 0 because sizeof(DtoaState) isn't available here and * it's not worth making it available. */ - *normal = mallocSizeOf(dtoaState, /* sizeof(DtoaState) */0); + if (normal) + *normal = mallocSizeOf(dtoaState, /* sizeof(DtoaState) */0); - *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); + if (temporary) + *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); - size_t method = 0, regexp = 0, unused = 0; - if (execAlloc) - execAlloc->sizeOfCode(&method, ®exp, &unused); - JS_ASSERT(method == 0); /* this execAlloc is only used for regexp code */ - *regexpCode = regexp + unused; + if (regexpCode) { + size_t method = 0, regexp = 0, unused = 0; + if (execAlloc) + execAlloc->sizeOfCode(&method, ®exp, &unused); + JS_ASSERT(method == 0); /* this execAlloc is only used for regexp code */ + *regexpCode = regexp + unused; + } - *stackCommitted = stackSpace.sizeOfCommitted(); + if (stackCommitted) + *stackCommitted = stackSpace.sizeOfCommitted(); } #endif @@ -252,7 +257,8 @@ JSThread::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, si size_t *regexpCode, size_t *stackCommitted) { data.sizeOfExcludingThis(mallocSizeOf, normal, temporary, regexpCode, stackCommitted); - *normal += mallocSizeOf(this, sizeof(JSThread)); + if (normal) + *normal += mallocSizeOf(this, sizeof(JSThread)); } JSThread * diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 3a55f2e13f2..3c9196f8e39 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3223,6 +3223,28 @@ struct IterateCellCallbackOp void operator()(Cell *cell) { (*callback)(cx, data, cell, traceKind, thingSize); } }; +void +IterateCompartments(JSContext *cx, void *data, + IterateCompartmentCallback compartmentCallback) +{ + CHECK_REQUEST(cx); + + JSRuntime *rt = cx->runtime; + JS_ASSERT(!rt->gcRunning); + + AutoLockGC lock(rt); + AutoGCSession gcsession(cx); +#ifdef JS_THREADSAFE + rt->gcHelperThread.waitBackgroundSweepEnd(); +#endif + AutoUnlockGC unlock(rt); + + AutoCopyFreeListToArenas copy(rt); + for (CompartmentsIter c(rt); !c.done(); c.next()) { + (*compartmentCallback)(cx, data, c); + } +} + void IterateCompartmentsArenasCells(JSContext *cx, void *data, IterateCompartmentCallback compartmentCallback, diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 706d0bc1984..c548730d8a5 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1786,6 +1786,12 @@ typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing, JSGCTraceKind traceKind, size_t thingSize); +/* + * This function calls |compartmentCallback| on every compartment. + */ +extern JS_FRIEND_API(void) +IterateCompartments(JSContext *cx, void *data, + IterateCompartmentCallback compartmentCallback); /* * This function calls |compartmentCallback| on every compartment, * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 3ac3e6f5608..eac36194893 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1259,6 +1259,15 @@ CompartmentMemoryCallback(JSContext *cx, void *vdata, JSCompartment *compartment JS::SizeOfCompartmentShapeTable(compartment, JsMallocSizeOf); } +void +ExplicitNonHeapCompartmentCallback(JSContext *cx, void *data, JSCompartment *compartment) +{ + size_t *n = static_cast(data); +#ifdef JS_METHODJIT + *n += JS::SizeOfCompartmentMjitCode(compartment); +#endif +} + void ChunkCallback(JSContext *cx, void *vdata, js::gc::Chunk *chunk) { @@ -1541,8 +1550,9 @@ CompartmentStats::CompartmentStats(JSContext *cx, JSCompartment *c) } JSBool -CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) +CollectCompartmentStatsForRuntime(JSRuntime *rt, void *vdata) { + IterateData *data = (IterateData *)vdata; JSContext *cx = JS_NewContext(rt, 0); if (!cx) { NS_ERROR("couldn't create context for memory tracing"); @@ -1688,6 +1698,60 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) return true; } +JSBool +GetExplicitNonHeapForRuntime(JSRuntime *rt, void *data) +{ + PRInt64 *amount = (PRInt64 *)data; + + JSContext *cx = JS_NewContext(rt, 0); + if (!cx) { + NS_ERROR("couldn't create context for memory tracing"); + return NS_ERROR_ABORT; + } + + // explicit//gc-heap/* + *amount = PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * + js::gc::ChunkSize; + + { + JSAutoRequest ar(cx); + + // explicit//mjit-code + size_t n = 0; + js::IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback); + *amount += n; + + { + #ifndef JS_THREADSAFE + #error "This code assumes JS_THREADSAFE is defined" + #endif + + // Need the GC lock to call JS_ContextIteratorUnlocked() and to + // access rt->threads. + js::AutoLockGC lock(rt); + + // explicit/runtime/threads/regexp-code + // explicit/runtime/threads/stack-committed + for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) { + JSThread *thread = r.front().value; + size_t regexpCode, stackCommitted; + thread->sizeOfIncludingThis(JsMallocSizeOf, + NULL, + NULL, + ®expCode, + &stackCommitted); + + *amount += regexpCode; + *amount += stackCommitted; + } + } + } + + JS_DestroyContextNoGC(cx); + + return true; +} + #define SLOP_BYTES_STRING \ " The measurement includes slop bytes caused by the heap allocator rounding up request sizes." @@ -1958,7 +2022,7 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix, "Memory on the garbage-collected JavaScript heap, within chunks with at " "least one allocated GC thing, that could be holding useful data but " "currently isn't. Memory here is mutually exclusive with memory reported" - "under gc-heap-decommitted.", + "under 'explicit/js/gc-heap-decommitted'.", callback, closure); ReportGCHeapBytes(pathPrefix + @@ -1967,7 +2031,7 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix, "Memory on the garbage-collected JavaScript heap taken by completely empty " "chunks, that soon will be released unless claimed for new allocations. " "Memory here is mutually exclusive with memory reported under " - "gc-heap-decommitted.", + "'explicit/js/gc-heap-decommitted'.", callback, closure); ReportGCHeapBytes(pathPrefix + @@ -2105,6 +2169,17 @@ public: return NS_OK; } + + NS_IMETHOD + GetExplicitNonHeap(PRInt64 *n) + { + JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime(); + + if (!GetExplicitNonHeapForRuntime(rt, n)) + return NS_ERROR_FAILURE; + + return NS_OK; + } }; NS_IMPL_THREADSAFE_ISUPPORTS1(XPConnectJSCompartmentsMultiReporter diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 10316b72ec9..f8c66bf5810 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -311,7 +311,9 @@ struct IterateData }; JSBool -CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data); +CollectCompartmentStatsForRuntime(JSRuntime *rt, void *data); +JSBool +GetExplicitNonHeapForRuntime(JSRuntime *rt, void *data); void ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix, diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 03fa8bddaa3..49f5fb123a0 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -722,6 +722,13 @@ PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, return NS_OK; } +NS_IMETHODIMP +PresShell::MemoryReporter::GetExplicitNonHeap(PRInt64 *aAmount) { + // This reporter doesn't do any KIND_NONHEAP measurements. + *aAmount = 0; + return NS_OK; +} + class nsAutoCauseReflowNotifier { public: diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index 8a9d90469cd..50be70fb34d 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -236,6 +236,9 @@ Reporter.prototype = { }, treeNameMatches: function(aTreeName) { + // Nb: the '/' must be present, because we have a KIND_OTHER reporter + // called "explicit" which is not part of the "explicit" tree. + aTreeName += "/"; return this._path.slice(0, aTreeName.length) === aTreeName; } }; diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul index cb32c7ff96a..c1cc9ed3a64 100644 --- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul @@ -36,8 +36,10 @@ while (e.hasMoreElements()) { var r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter); // Call collectReports, even though we don't use its results, just to - // test that the multi-reporter doesn't crash or anything. + // test that the multi-reporter doesn't crash or anything. And likewise + // for the |explicitNonHeap| field. r.collectReports(function(){}, null); + dummy += r.explicitNonHeap; mgr.unregisterMultiReporter(r); realMultiReporters.push(r); } @@ -78,8 +80,6 @@ f("", "explicit/b/b", HEAP, 75 * MB), f("", "explicit/b/c/a", HEAP, 70 * MB), f("", "explicit/b/c/b", HEAP, 2 * MB), // omitted - f("", "explicit/c", NONHEAP, 100 * MB), - f("", "explicit/c/d", NONHEAP, 13 * MB), // subsumed by parent f("", "explicit/g", HEAP, 1 * MB), // internal, dup: merge f("", "explicit/g/a", HEAP, 6 * MB), f("", "explicit/g/b", HEAP, 5 * MB), @@ -89,13 +89,16 @@ var fakeMultiReporters = [ { collectReports: function(cbObj, closure) { function f(p, k, u, a) { cbObj.callback("", p, k, u, a, "(desc)", closure); } + f("explicit/c", NONHEAP, BYTES, 100 * MB), + f("explicit/c/d", NONHEAP, BYTES, 13 * MB), // subsumed by parent f("explicit/c/d", NONHEAP, BYTES, 10 * MB), // dup, subsumed by parent f("explicit/cc", NONHEAP, BYTES, 13 * MB); f("explicit/cc", NONHEAP, BYTES, 10 * MB); // dup f("explicit/d", NONHEAP, BYTES, 499 * KB); // omitted f("explicit/e", NONHEAP, BYTES, 100 * KB); // omitted f("explicit/f/g/h/i", HEAP, BYTES, 20 * MB); - } + }, + explicitNonHeap: (100 + 13 + 10)*MB + (499 + 100)*KB }, { collectReports: function(cbObj, closure) { function f(p, k, u, a) { cbObj.callback("", p, k, u, a, "(desc)", closure); } @@ -104,7 +107,8 @@ f("other2", OTHER, BYTES, 222 * MB); f("perc2", OTHER, PERCENTAGE, 10000); f("perc1", OTHER, PERCENTAGE, 4567); - } + }, + explicitNonHeap: 0 }, { collectReports: function(cbObj, closure) { // The amounts are given in pages, so multiply here by 4kb. @@ -115,7 +119,8 @@ f("map/vsize/a", 19); f("map/swap/b/c", 10); f("map/resident/a", 42); - } + }, + explicitNonHeap: 0 } ]; for (var i = 0; i < fakeReporters.length; i++) { diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 609326b5b5d..dce53728b9a 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -57,6 +57,7 @@ const MEM_HISTOGRAMS = { "js-gc-heap": "MEMORY_JS_GC_HEAP", "js-compartments-system": "MEMORY_JS_COMPARTMENTS_SYSTEM", "js-compartments-user": "MEMORY_JS_COMPARTMENTS_USER", + "explicit": "MEMORY_EXPLICIT", "resident": "MEMORY_RESIDENT", "explicit/storage/sqlite": "MEMORY_STORAGE_SQLITE", "explicit/images/content/used/uncompressed": @@ -263,33 +264,36 @@ TelemetryPing.prototype = { while (e.hasMoreElements()) { let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter); let id = MEM_HISTOGRAMS[mr.path]; - if (!id || mr.amount == -1) { + if (!id) { + continue; + } + // mr.amount is expensive to read in some cases, so get it only once. + let amount = mr.amount; + if (amount == -1) { continue; } let val; if (mr.units == Ci.nsIMemoryReporter.UNITS_BYTES) { - val = Math.floor(mr.amount / 1024); + val = Math.floor(amount / 1024); } else if (mr.units == Ci.nsIMemoryReporter.UNITS_COUNT) { - val = mr.amount; + val = amount; } else if (mr.units == Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE) { // If the reporter gives us a cumulative count, we'll report the // difference in its value between now and our previous ping. - // Read mr.amount just once so our arithmetic is consistent. - let curVal = mr.amount; if (!(mr.path in this._prevValues)) { // If this is the first time we're reading this reporter, store its // current value but don't report it in the telemetry ping, so we // ignore the effect startup had on the reporter. - this._prevValues[mr.path] = curVal; + this._prevValues[mr.path] = amount; continue; } - val = curVal - this._prevValues[mr.path]; - this._prevValues[mr.path] = curVal; + val = amount - this._prevValues[mr.path]; + this._prevValues[mr.path] = amount; } else { NS_ASSERT(false, "Can't handle memory reporter with units " + mr.units); @@ -297,11 +301,6 @@ TelemetryPing.prototype = { } this.addValue(mr.path, id, val); } - // "explicit" is found differently. - let explicit = mgr.explicit; // Get it only once, it's reasonably expensive - if (explicit != -1) { - this.addValue("explicit", "MEMORY_EXPLICIT", Math.floor(explicit / 1024)); - } }, /** diff --git a/xpcom/base/MapsMemoryReporter.cpp b/xpcom/base/MapsMemoryReporter.cpp index 39b282474b9..1851fad440f 100644 --- a/xpcom/base/MapsMemoryReporter.cpp +++ b/xpcom/base/MapsMemoryReporter.cpp @@ -152,6 +152,13 @@ public: CollectReports(nsIMemoryMultiReporterCallback *aCallback, nsISupports *aClosure); + NS_IMETHOD + GetExplicitNonHeap(PRInt64 *aAmount) { + // This reporter doesn't do any "explicit" measurements. + *aAmount = 0; + return NS_OK; + } + private: // Search through /proc/self/maps for libxul.so, and set mLibxulDir to the // the directory containing libxul. @@ -223,7 +230,7 @@ MapsReporter::CollectReports(nsIMemoryMultiReporterCallback *aCallback, NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a resident node?"); if (!categoriesSeen.mSeenSwap) { aCallback->Callback(NS_LITERAL_CSTRING(""), - NS_LITERAL_CSTRING("map/swap"), + NS_LITERAL_CSTRING("map/swap/total"), nsIMemoryReporter::KIND_NONHEAP, nsIMemoryReporter::UNITS_BYTES, 0, diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index 50513ef737a..1ef11168712 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -213,11 +213,23 @@ interface nsIMemoryMultiReporterCallback : nsISupports * (Compare and contrast this with nsIMemoryReporter, which allows all * fields except |amount| to be accessed without triggering computation.) */ -[scriptable, uuid(eae277ad-b67d-4389-95f4-03fa11c09d06)] +[scriptable, uuid(61d498d5-b460-4398-a8ea-7f75208534b4)] interface nsIMemoryMultiReporter : nsISupports { void collectReports(in nsIMemoryMultiReporterCallback callback, in nsISupports closure); + + /* + * Return the sum of all this multi-reporter's measurements that have a + * path that starts with "explicit" and are KIND_NONHEAP. + * + * This is a hack that's required to implement + * nsIMemoryReporterManager::explicit efficiently, which is important -- + * multi-reporters can special-case this operation so it's much faster + * than getting all the reports, filtering out the unneeded ones, and + * summing the remainder. + */ + readonly attribute PRInt64 explicitNonHeap; }; [scriptable, uuid(84ba9c85-3372-4423-b7ab-74708b9269a6)] diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 459260c3c82..1add056f619 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -48,6 +48,19 @@ using namespace mozilla; +static PRInt64 GetExplicit() +{ + nsCOMPtr mgr = do_GetService("@mozilla.org/memory-reporter-manager;1"); + if (mgr == nsnull) + return (PRInt64)-1; + + PRInt64 n; + nsresult rv = mgr->GetExplicit(&n); + NS_ENSURE_SUCCESS(rv, rv); + + return n; +} + #if defined(MOZ_MEMORY) # if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX) # define HAVE_JEMALLOC_STATS 1 @@ -320,6 +333,15 @@ NS_MEMORY_REPORTER_IMPLEMENT(PageFaultsHard, "so hard page faults a second.") #endif +NS_MEMORY_REPORTER_IMPLEMENT(Explicit, + "explicit", + KIND_OTHER, + UNITS_BYTES, + GetExplicit, + "This is the same measurement as the root of the 'explicit' tree. " + "However, it is measured at a different time and so gives slightly " + "different results.") + NS_MEMORY_REPORTER_IMPLEMENT(Resident, "resident", KIND_OTHER, @@ -533,6 +555,7 @@ nsMemoryReporterManager::Init() REGISTER(HeapAllocated); REGISTER(HeapUnallocated); + REGISTER(Explicit); REGISTER(Resident); #if defined(XP_LINUX) || defined(XP_MACOSX) || defined(XP_WIN) || defined(SOLARIS) @@ -657,6 +680,7 @@ struct MemoryReport { PRInt64 amount; }; +#ifdef DEBUG // This is just a wrapper for InfallibleTArray that implements // nsISupports, so it can be passed to nsIMemoryMultiReporter::CollectReports. class MemoryReportsWrapper : public nsISupports { @@ -679,8 +703,8 @@ public: { if (aKind == nsIMemoryReporter::KIND_NONHEAP && PromiseFlatCString(aPath).Find("explicit") == 0 && - aAmount != PRInt64(-1)) { - + aAmount != PRInt64(-1)) + { MemoryReportsWrapper *wrappedMRs = static_cast(aWrappedMRs); MemoryReport mr(aPath, aAmount); @@ -693,6 +717,7 @@ NS_IMPL_ISUPPORTS1( MemoryReportCallback , nsIMemoryMultiReporterCallback ) +#endif // Is path1 a prefix, and thus a parent, of path2? Eg. "a/b" is a parent of // "a/b/c", but "a/bb" is not. @@ -709,21 +734,21 @@ isParent(const nsACString &path1, const nsACString &path2) NS_IMETHODIMP nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) { - InfallibleTArray nonheap; - PRInt64 heapUsed = PRInt64(-1); + nsresult rv; - // Get "heap-allocated" and all the KIND_NONHEAP measurements from vanilla - // "explicit" reporters. + // Get "heap-allocated" and all the KIND_NONHEAP measurements from normal + // (i.e. non-multi) "explicit" reporters. + PRInt64 heapAllocated = PRInt64(-1); + InfallibleTArray explicitNonHeapNormalReports; nsCOMPtr e; EnumerateReporters(getter_AddRefs(e)); - bool more; while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) { nsCOMPtr r; e->GetNext(getter_AddRefs(r)); PRInt32 kind; - nsresult rv = r->GetKind(&kind); + rv = r->GetKind(&kind); NS_ENSURE_SUCCESS(rv, rv); nsCString path; @@ -743,57 +768,106 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) // "heap-allocated" is the most important one. if (amount != PRInt64(-1)) { MemoryReport mr(path, amount); - nonheap.AppendElement(mr); + explicitNonHeapNormalReports.AppendElement(mr); } } else if (path.Equals("heap-allocated")) { - rv = r->GetAmount(&heapUsed); + rv = r->GetAmount(&heapAllocated); NS_ENSURE_SUCCESS(rv, rv); - // If "heap-allocated" fails, we give up, because the result - // would be horribly inaccurate. - if (heapUsed == PRInt64(-1)) { - *aExplicit = PRInt64(-1); - return NS_OK; - } } } - // Get KIND_NONHEAP measurements from multi-reporters, too. + // If we don't have "heap-allocated", give up, because the result would be + // horribly inaccurate. + if (heapAllocated == PRInt64(-1)) { + *aExplicit = PRInt64(-1); + return NS_OK; + } + + // Sum all the explicit, NONHEAP reports from normal reporters. + // Ignore (by zeroing its amount) any normal reporter that is a child of + // another normal reporter. Eg. if we have "explicit/a" and + // "explicit/a/b", zero the latter. This is quadratic in the number of + // explicit NONHEAP reporters, but there shouldn't be many. + // + // XXX: bug 700508 will remove the need for this + // + for (PRUint32 i = 0; i < explicitNonHeapNormalReports.Length(); i++) { + const nsCString &iPath = explicitNonHeapNormalReports[i].path; + for (PRUint32 j = i + 1; j < explicitNonHeapNormalReports.Length(); j++) { + const nsCString &jPath = explicitNonHeapNormalReports[j].path; + if (isParent(iPath, jPath)) { + explicitNonHeapNormalReports[j].amount = 0; + } else if (isParent(jPath, iPath)) { + explicitNonHeapNormalReports[i].amount = 0; + } + } + } + PRInt64 explicitNonHeapNormalSize = 0; + for (PRUint32 i = 0; i < explicitNonHeapNormalReports.Length(); i++) { + explicitNonHeapNormalSize += explicitNonHeapNormalReports[i].amount; + } + + // For each multi-reporter we could call CollectReports and filter out the + // non-explicit, non-NONHEAP measurements. But that's lots of wasted work, + // so we instead use GetExplicitNonHeap() which exists purely for this + // purpose. + // + // (Actually, in debug builds we also do it the slow way and compare the + // result to the result obtained from GetExplicitNonHeap(). This + // guarantees the two measurement paths are equivalent. This is wise + // because it's easy for memory reporters to have bugs.) + nsCOMPtr e2; EnumerateMultiReporters(getter_AddRefs(e2)); - nsRefPtr wrappedMRs = - new MemoryReportsWrapper(&nonheap); - - // This callback adds only NONHEAP explicit reporters. - nsRefPtr cb = new MemoryReportCallback(); - + PRInt64 explicitNonHeapMultiSize = 0; while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) { nsCOMPtr r; e2->GetNext(getter_AddRefs(r)); + PRInt64 n; + rv = r->GetExplicitNonHeap(&n); + NS_ENSURE_SUCCESS(rv, rv); + explicitNonHeapMultiSize += n; + } + +#ifdef DEBUG + InfallibleTArray explicitNonHeapMultiReports; + nsRefPtr cb = new MemoryReportCallback(); + nsRefPtr wrappedMRs = + new MemoryReportsWrapper(&explicitNonHeapMultiReports); + nsCOMPtr e3; + EnumerateMultiReporters(getter_AddRefs(e3)); + while (NS_SUCCEEDED(e3->HasMoreElements(&more)) && more) { + nsCOMPtr r; + e3->GetNext(getter_AddRefs(r)); r->CollectReports(cb, wrappedMRs); } - // Ignore (by zeroing its amount) any reporter that is a child of another - // reporter. Eg. if we have "explicit/a" and "explicit/a/b", zero the - // latter. This is quadratic in the number of explicit NONHEAP reporters, - // but there shouldn't be many. - for (PRUint32 i = 0; i < nonheap.Length(); i++) { - const nsCString &iPath = nonheap[i].path; - for (PRUint32 j = i + 1; j < nonheap.Length(); j++) { - const nsCString &jPath = nonheap[j].path; + // Sum all the explicit, NONHEAP reports from multi-reporters. + // XXX: identical to the explicitNonHeapNormalReports case above; bug + // 700508 will remove the need for this + for (PRUint32 i = 0; i < explicitNonHeapMultiReports.Length(); i++) { + const nsCString &iPath = explicitNonHeapMultiReports[i].path; + for (PRUint32 j = i + 1; j < explicitNonHeapMultiReports.Length(); j++) { + const nsCString &jPath = explicitNonHeapMultiReports[j].path; if (isParent(iPath, jPath)) { - nonheap[j].amount = 0; + explicitNonHeapMultiReports[j].amount = 0; } else if (isParent(jPath, iPath)) { - nonheap[i].amount = 0; + explicitNonHeapMultiReports[i].amount = 0; } } } - - // Sum all the nonheap reporters and heapUsed. - *aExplicit = heapUsed; - for (PRUint32 i = 0; i < nonheap.Length(); i++) { - *aExplicit += nonheap[i].amount; + PRInt64 explicitNonHeapMultiSize2 = 0; + for (PRUint32 i = 0; i < explicitNonHeapMultiReports.Length(); i++) { + explicitNonHeapMultiSize2 += explicitNonHeapMultiReports[i].amount; } + // Check the two measurements give the same result. + NS_ASSERTION(explicitNonHeapMultiSize == explicitNonHeapMultiSize2, + "The two measurements of 'explicit' memory usage don't match"); +#endif + + *aExplicit = heapAllocated + explicitNonHeapNormalSize + explicitNonHeapMultiSize; + return NS_OK; }