mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-inbound
This commit is contained in:
commit
d6ab60ebc2
@ -521,7 +521,11 @@ AudioInitTask::Run()
|
||||
MOZ_ASSERT(mThread);
|
||||
if (NS_IsMainThread()) {
|
||||
mThread->Shutdown(); // can't Shutdown from the thread itself, darn
|
||||
mThread = nullptr;
|
||||
// Don't null out mThread!
|
||||
// See bug 999104. We must hold a ref to the thread across Dispatch()
|
||||
// since the internal mThread ref could be released while processing
|
||||
// the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
|
||||
// assumes the caller does.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -445,6 +445,7 @@ public:
|
||||
// Can't add 'this' as the event to run, since mThread may not be set yet
|
||||
nsresult rv = NS_NewNamedThread("CubebInit", getter_AddRefs(mThread));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Note: event must not null out mThread!
|
||||
rv = mThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
return rv;
|
||||
|
@ -25,7 +25,7 @@ const Ci = SpecialPowers.Ci;
|
||||
// not enabled by default yet.
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var policy = setupPolicy();
|
||||
var policy;
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
|
||||
|
||||
@ -88,7 +88,12 @@ function teardownPolicy() {
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
navigator.sendBeacon(beaconUrl, "bacon would have been a better name than beacon");
|
||||
policy = setupPolicy();
|
||||
// Make sure to hit the event loop here in order to ensure that nsContentPolicy
|
||||
// has been notified of the newly registered policy.
|
||||
SimpleTest.executeSoon(function() {
|
||||
navigator.sendBeacon(beaconUrl, "bacon would have been a better name than beacon");
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
@ -1077,7 +1077,7 @@ nsresult AppCallbacks::CreateBrowserWindow(uint32_t aChromeFlags,
|
||||
|
||||
// the interface to return and one addref, which we assume will be
|
||||
// immediately released
|
||||
CallQueryInterface(static_cast<nsIWebBrowserChrome*>(chrome), aNewWindow);
|
||||
*aNewWindow = static_cast<nsIWebBrowserChrome*>(chrome);
|
||||
// now an extra addref; the window owns itself (to be released by
|
||||
// WebBrowserChromeUI::Destroy)
|
||||
NS_ADDREF(*aNewWindow);
|
||||
|
@ -13,6 +13,20 @@
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/Value.h"
|
||||
|
||||
typedef enum JSGCMode {
|
||||
/* Perform only global GCs. */
|
||||
JSGC_MODE_GLOBAL = 0,
|
||||
|
||||
/* Perform per-compartment GCs until too much garbage has accumulated. */
|
||||
JSGC_MODE_COMPARTMENT = 1,
|
||||
|
||||
/*
|
||||
* Collect in short time slices rather than all at once. Implies
|
||||
* JSGC_MODE_COMPARTMENT.
|
||||
*/
|
||||
JSGC_MODE_INCREMENTAL = 2
|
||||
} JSGCMode;
|
||||
|
||||
namespace JS {
|
||||
|
||||
#define GCREASONS(D) \
|
||||
|
@ -6,17 +6,24 @@
|
||||
|
||||
#include "gc/Tracer.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsprf.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsutil.h"
|
||||
#include "NamespaceImports.h"
|
||||
|
||||
#include "gc/GCInternals.h"
|
||||
#include "gc/Marking.h"
|
||||
|
||||
#include "jsgcinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
using mozilla::DebugOnly;
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_CallValueTracer(JSTracer *trc, Value *valuep, const char *name)
|
||||
@ -326,3 +333,342 @@ JSTracer::tracingLocation(void **thingp)
|
||||
return realLocation_ ? (void **)realLocation_ : thingp;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
MarkStack::init(JSGCMode gcMode)
|
||||
{
|
||||
setBaseCapacity(gcMode);
|
||||
|
||||
JS_ASSERT(!stack_);
|
||||
uintptr_t *newStack = js_pod_malloc<uintptr_t>(baseCapacity_);
|
||||
if (!newStack)
|
||||
return false;
|
||||
|
||||
setStack(newStack, 0, baseCapacity_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MarkStack::setBaseCapacity(JSGCMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case JSGC_MODE_GLOBAL:
|
||||
case JSGC_MODE_COMPARTMENT:
|
||||
baseCapacity_ = NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY;
|
||||
break;
|
||||
case JSGC_MODE_INCREMENTAL:
|
||||
baseCapacity_ = INCREMENTAL_MARK_STACK_BASE_CAPACITY;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("bad gc mode");
|
||||
}
|
||||
|
||||
if (baseCapacity_ > maxCapacity_)
|
||||
baseCapacity_ = maxCapacity_;
|
||||
}
|
||||
|
||||
void
|
||||
MarkStack::setMaxCapacity(size_t maxCapacity)
|
||||
{
|
||||
JS_ASSERT(isEmpty());
|
||||
maxCapacity_ = maxCapacity;
|
||||
if (baseCapacity_ > maxCapacity_)
|
||||
baseCapacity_ = maxCapacity_;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void
|
||||
MarkStack::reset()
|
||||
{
|
||||
if (capacity() == baseCapacity_) {
|
||||
// No size change; keep the current stack.
|
||||
setStack(stack_, 0, baseCapacity_);
|
||||
return;
|
||||
}
|
||||
|
||||
uintptr_t *newStack = (uintptr_t *)js_realloc(stack_, sizeof(uintptr_t) * baseCapacity_);
|
||||
if (!newStack) {
|
||||
// If the realloc fails, just keep using the existing stack; it's
|
||||
// not ideal but better than failing.
|
||||
newStack = stack_;
|
||||
baseCapacity_ = capacity();
|
||||
}
|
||||
setStack(newStack, 0, baseCapacity_);
|
||||
}
|
||||
|
||||
bool
|
||||
MarkStack::enlarge(unsigned count)
|
||||
{
|
||||
size_t newCapacity = Min(maxCapacity_, capacity() * 2);
|
||||
if (newCapacity < capacity() + count)
|
||||
return false;
|
||||
|
||||
size_t tosIndex = position();
|
||||
|
||||
uintptr_t *newStack = (uintptr_t *)js_realloc(stack_, sizeof(uintptr_t) * newCapacity);
|
||||
if (!newStack)
|
||||
return false;
|
||||
|
||||
setStack(newStack, tosIndex, newCapacity);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MarkStack::setGCMode(JSGCMode gcMode)
|
||||
{
|
||||
// The mark stack won't be resized until the next call to reset(), but
|
||||
// that will happen at the end of the next GC.
|
||||
setBaseCapacity(gcMode);
|
||||
}
|
||||
|
||||
size_t
|
||||
MarkStack::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
return mallocSizeOf(stack_);
|
||||
}
|
||||
|
||||
/*
|
||||
* DoNotTraceWeakMaps: the GC is recomputing the liveness of WeakMap entries,
|
||||
* so we delay visting entries.
|
||||
*/
|
||||
GCMarker::GCMarker(JSRuntime *rt)
|
||||
: JSTracer(rt, nullptr, DoNotTraceWeakMaps),
|
||||
stack(size_t(-1)),
|
||||
color(BLACK),
|
||||
unmarkedArenaStackTop(nullptr),
|
||||
markLaterArenas(0),
|
||||
grayBufferState(GRAY_BUFFER_UNUSED),
|
||||
started(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
GCMarker::init(JSGCMode gcMode)
|
||||
{
|
||||
return stack.init(gcMode);
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::start()
|
||||
{
|
||||
JS_ASSERT(!started);
|
||||
started = true;
|
||||
color = BLACK;
|
||||
|
||||
JS_ASSERT(!unmarkedArenaStackTop);
|
||||
JS_ASSERT(markLaterArenas == 0);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::stop()
|
||||
{
|
||||
JS_ASSERT(isDrained());
|
||||
|
||||
JS_ASSERT(started);
|
||||
started = false;
|
||||
|
||||
JS_ASSERT(!unmarkedArenaStackTop);
|
||||
JS_ASSERT(markLaterArenas == 0);
|
||||
|
||||
/* Free non-ballast stack memory. */
|
||||
stack.reset();
|
||||
|
||||
resetBufferedGrayRoots();
|
||||
grayBufferState = GRAY_BUFFER_UNUSED;
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::reset()
|
||||
{
|
||||
color = BLACK;
|
||||
|
||||
stack.reset();
|
||||
JS_ASSERT(isMarkStackEmpty());
|
||||
|
||||
while (unmarkedArenaStackTop) {
|
||||
ArenaHeader *aheader = unmarkedArenaStackTop;
|
||||
JS_ASSERT(aheader->hasDelayedMarking);
|
||||
JS_ASSERT(markLaterArenas);
|
||||
unmarkedArenaStackTop = aheader->getNextDelayedMarking();
|
||||
aheader->unsetDelayedMarking();
|
||||
aheader->markOverflow = 0;
|
||||
aheader->allocatedDuringIncremental = 0;
|
||||
markLaterArenas--;
|
||||
}
|
||||
JS_ASSERT(isDrained());
|
||||
JS_ASSERT(!markLaterArenas);
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::markDelayedChildren(ArenaHeader *aheader)
|
||||
{
|
||||
if (aheader->markOverflow) {
|
||||
bool always = aheader->allocatedDuringIncremental;
|
||||
aheader->markOverflow = 0;
|
||||
|
||||
for (CellIterUnderGC i(aheader); !i.done(); i.next()) {
|
||||
Cell *t = i.getCell();
|
||||
if (always || t->isMarked()) {
|
||||
t->markIfUnmarked();
|
||||
JS_TraceChildren(this, t, MapAllocToTraceKind(aheader->getAllocKind()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(aheader->allocatedDuringIncremental);
|
||||
PushArena(this, aheader);
|
||||
}
|
||||
aheader->allocatedDuringIncremental = 0;
|
||||
/*
|
||||
* Note that during an incremental GC we may still be allocating into
|
||||
* aheader. However, prepareForIncrementalGC sets the
|
||||
* allocatedDuringIncremental flag if we continue marking.
|
||||
*/
|
||||
}
|
||||
|
||||
bool
|
||||
GCMarker::markDelayedChildren(SliceBudget &budget)
|
||||
{
|
||||
gcstats::MaybeAutoPhase ap;
|
||||
if (runtime()->gcIncrementalState == MARK)
|
||||
ap.construct(runtime()->gcStats, gcstats::PHASE_MARK_DELAYED);
|
||||
|
||||
JS_ASSERT(unmarkedArenaStackTop);
|
||||
do {
|
||||
/*
|
||||
* If marking gets delayed at the same arena again, we must repeat
|
||||
* marking of its things. For that we pop arena from the stack and
|
||||
* clear its hasDelayedMarking flag before we begin the marking.
|
||||
*/
|
||||
ArenaHeader *aheader = unmarkedArenaStackTop;
|
||||
JS_ASSERT(aheader->hasDelayedMarking);
|
||||
JS_ASSERT(markLaterArenas);
|
||||
unmarkedArenaStackTop = aheader->getNextDelayedMarking();
|
||||
aheader->unsetDelayedMarking();
|
||||
markLaterArenas--;
|
||||
markDelayedChildren(aheader);
|
||||
|
||||
budget.step(150);
|
||||
if (budget.isOverBudget())
|
||||
return false;
|
||||
} while (unmarkedArenaStackTop);
|
||||
JS_ASSERT(!markLaterArenas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
GCMarker::checkZone(void *p)
|
||||
{
|
||||
JS_ASSERT(started);
|
||||
DebugOnly<Cell *> cell = static_cast<Cell *>(p);
|
||||
JS_ASSERT_IF(cell->isTenured(), cell->tenuredZone()->isCollecting());
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
GCMarker::hasBufferedGrayRoots() const
|
||||
{
|
||||
return grayBufferState == GRAY_BUFFER_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::startBufferingGrayRoots()
|
||||
{
|
||||
JS_ASSERT(grayBufferState == GRAY_BUFFER_UNUSED);
|
||||
grayBufferState = GRAY_BUFFER_OK;
|
||||
for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
|
||||
JS_ASSERT(zone->gcGrayRoots.empty());
|
||||
|
||||
JS_ASSERT(!callback);
|
||||
callback = GrayCallback;
|
||||
JS_ASSERT(IS_GC_MARKING_TRACER(this));
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::endBufferingGrayRoots()
|
||||
{
|
||||
JS_ASSERT(callback == GrayCallback);
|
||||
callback = nullptr;
|
||||
JS_ASSERT(IS_GC_MARKING_TRACER(this));
|
||||
JS_ASSERT(grayBufferState == GRAY_BUFFER_OK ||
|
||||
grayBufferState == GRAY_BUFFER_FAILED);
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::resetBufferedGrayRoots()
|
||||
{
|
||||
for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
|
||||
zone->gcGrayRoots.clearAndFree();
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::markBufferedGrayRoots(JS::Zone *zone)
|
||||
{
|
||||
JS_ASSERT(grayBufferState == GRAY_BUFFER_OK);
|
||||
JS_ASSERT(zone->isGCMarkingGray());
|
||||
|
||||
for (GrayRoot *elem = zone->gcGrayRoots.begin(); elem != zone->gcGrayRoots.end(); elem++) {
|
||||
#ifdef DEBUG
|
||||
setTracingDetails(elem->debugPrinter, elem->debugPrintArg, elem->debugPrintIndex);
|
||||
#endif
|
||||
void *tmp = elem->thing;
|
||||
setTracingLocation((void *)&elem->thing);
|
||||
MarkKind(this, &tmp, elem->kind);
|
||||
JS_ASSERT(tmp == elem->thing);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::appendGrayRoot(void *thing, JSGCTraceKind kind)
|
||||
{
|
||||
JS_ASSERT(started);
|
||||
|
||||
if (grayBufferState == GRAY_BUFFER_FAILED)
|
||||
return;
|
||||
|
||||
GrayRoot root(thing, kind);
|
||||
#ifdef DEBUG
|
||||
root.debugPrinter = debugPrinter();
|
||||
root.debugPrintArg = debugPrintArg();
|
||||
root.debugPrintIndex = debugPrintIndex();
|
||||
#endif
|
||||
|
||||
Zone *zone = static_cast<Cell *>(thing)->tenuredZone();
|
||||
if (zone->isCollecting()) {
|
||||
zone->maybeAlive = true;
|
||||
if (!zone->gcGrayRoots.append(root)) {
|
||||
resetBufferedGrayRoots();
|
||||
grayBufferState = GRAY_BUFFER_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::GrayCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind)
|
||||
{
|
||||
JS_ASSERT(thingp);
|
||||
JS_ASSERT(*thingp);
|
||||
GCMarker *gcmarker = static_cast<GCMarker *>(trc);
|
||||
gcmarker->appendGrayRoot(*thingp, kind);
|
||||
}
|
||||
|
||||
size_t
|
||||
GCMarker::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
size_t size = stack.sizeOfExcludingThis(mallocSizeOf);
|
||||
for (ZonesIter zone(runtime(), WithAtoms); !zone.done(); zone.next())
|
||||
size += zone->gcGrayRoots.sizeOfExcludingThis(mallocSizeOf);
|
||||
return size;
|
||||
}
|
||||
|
||||
void
|
||||
js::SetMarkStackLimit(JSRuntime *rt, size_t limit)
|
||||
{
|
||||
JS_ASSERT(!rt->isHeapBusy());
|
||||
AutoStopVerifyingBarriers pauseVerification(rt, false);
|
||||
rt->gcMarker.setMaxCapacity(limit);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,301 @@
|
||||
#ifndef js_Tracer_h
|
||||
#define js_Tracer_h
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/SliceBudget.h"
|
||||
#include "js/TracingAPI.h"
|
||||
|
||||
namespace js {
|
||||
class GCMarker;
|
||||
class ObjectImpl;
|
||||
namespace gc {
|
||||
class ArenaHeader;
|
||||
}
|
||||
namespace jit {
|
||||
class JitCode;
|
||||
}
|
||||
namespace types {
|
||||
class TypeObject;
|
||||
}
|
||||
|
||||
static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
|
||||
static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
|
||||
|
||||
/*
|
||||
* When the native stack is low, the GC does not call JS_TraceChildren to mark
|
||||
* the reachable "children" of the thing. Rather the thing is put aside and
|
||||
* JS_TraceChildren is called later with more space on the C stack.
|
||||
*
|
||||
* To implement such delayed marking of the children with minimal overhead for
|
||||
* the normal case of sufficient native stack, the code adds a field per arena.
|
||||
* The field markingDelay->link links all arenas with delayed things into a
|
||||
* stack list with the pointer to stack top in GCMarker::unmarkedArenaStackTop.
|
||||
* GCMarker::delayMarkingChildren adds arenas to the stack as necessary while
|
||||
* markDelayedChildren pops the arenas from the stack until it empties.
|
||||
*/
|
||||
class MarkStack
|
||||
{
|
||||
friend class GCMarker;
|
||||
|
||||
uintptr_t *stack_;
|
||||
uintptr_t *tos_;
|
||||
uintptr_t *end_;
|
||||
|
||||
// The capacity we start with and reset() to.
|
||||
size_t baseCapacity_;
|
||||
size_t maxCapacity_;
|
||||
|
||||
public:
|
||||
MarkStack(size_t maxCapacity)
|
||||
: stack_(nullptr),
|
||||
tos_(nullptr),
|
||||
end_(nullptr),
|
||||
baseCapacity_(0),
|
||||
maxCapacity_(maxCapacity)
|
||||
{}
|
||||
|
||||
~MarkStack() {
|
||||
js_free(stack_);
|
||||
}
|
||||
|
||||
size_t capacity() { return end_ - stack_; }
|
||||
|
||||
ptrdiff_t position() const { return tos_ - stack_; }
|
||||
|
||||
void setStack(uintptr_t *stack, size_t tosIndex, size_t capacity) {
|
||||
stack_ = stack;
|
||||
tos_ = stack + tosIndex;
|
||||
end_ = stack + capacity;
|
||||
}
|
||||
|
||||
bool init(JSGCMode gcMode);
|
||||
|
||||
void setBaseCapacity(JSGCMode mode);
|
||||
size_t maxCapacity() const { return maxCapacity_; }
|
||||
void setMaxCapacity(size_t maxCapacity);
|
||||
|
||||
bool push(uintptr_t item) {
|
||||
if (tos_ == end_) {
|
||||
if (!enlarge(1))
|
||||
return false;
|
||||
}
|
||||
JS_ASSERT(tos_ < end_);
|
||||
*tos_++ = item;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool push(uintptr_t item1, uintptr_t item2, uintptr_t item3) {
|
||||
uintptr_t *nextTos = tos_ + 3;
|
||||
if (nextTos > end_) {
|
||||
if (!enlarge(3))
|
||||
return false;
|
||||
nextTos = tos_ + 3;
|
||||
}
|
||||
JS_ASSERT(nextTos <= end_);
|
||||
tos_[0] = item1;
|
||||
tos_[1] = item2;
|
||||
tos_[2] = item3;
|
||||
tos_ = nextTos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
return tos_ == stack_;
|
||||
}
|
||||
|
||||
uintptr_t pop() {
|
||||
JS_ASSERT(!isEmpty());
|
||||
return *--tos_;
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
/* Grow the stack, ensuring there is space for at least count elements. */
|
||||
bool enlarge(unsigned count);
|
||||
|
||||
void setGCMode(JSGCMode gcMode);
|
||||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
};
|
||||
|
||||
class GCMarker : public JSTracer
|
||||
{
|
||||
public:
|
||||
explicit GCMarker(JSRuntime *rt);
|
||||
bool init(JSGCMode gcMode);
|
||||
|
||||
void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
|
||||
size_t maxCapacity() const { return stack.maxCapacity(); }
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void reset();
|
||||
|
||||
void pushObject(ObjectImpl *obj) {
|
||||
pushTaggedPtr(ObjectTag, obj);
|
||||
}
|
||||
|
||||
void pushType(types::TypeObject *type) {
|
||||
pushTaggedPtr(TypeTag, type);
|
||||
}
|
||||
|
||||
void pushJitCode(jit::JitCode *code) {
|
||||
pushTaggedPtr(JitCodeTag, code);
|
||||
}
|
||||
|
||||
uint32_t getMarkColor() const {
|
||||
return color;
|
||||
}
|
||||
|
||||
/*
|
||||
* Care must be taken changing the mark color from gray to black. The cycle
|
||||
* collector depends on the invariant that there are no black to gray edges
|
||||
* in the GC heap. This invariant lets the CC not trace through black
|
||||
* objects. If this invariant is violated, the cycle collector may free
|
||||
* objects that are still reachable.
|
||||
*/
|
||||
void setMarkColorGray() {
|
||||
JS_ASSERT(isDrained());
|
||||
JS_ASSERT(color == gc::BLACK);
|
||||
color = gc::GRAY;
|
||||
}
|
||||
|
||||
void setMarkColorBlack() {
|
||||
JS_ASSERT(isDrained());
|
||||
JS_ASSERT(color == gc::GRAY);
|
||||
color = gc::BLACK;
|
||||
}
|
||||
|
||||
inline void delayMarkingArena(gc::ArenaHeader *aheader);
|
||||
void delayMarkingChildren(const void *thing);
|
||||
void markDelayedChildren(gc::ArenaHeader *aheader);
|
||||
bool markDelayedChildren(SliceBudget &budget);
|
||||
bool hasDelayedChildren() const {
|
||||
return !!unmarkedArenaStackTop;
|
||||
}
|
||||
|
||||
bool isDrained() {
|
||||
return isMarkStackEmpty() && !unmarkedArenaStackTop;
|
||||
}
|
||||
|
||||
bool drainMarkStack(SliceBudget &budget);
|
||||
|
||||
/*
|
||||
* Gray marking must be done after all black marking is complete. However,
|
||||
* we do not have write barriers on XPConnect roots. Therefore, XPConnect
|
||||
* roots must be accumulated in the first slice of incremental GC. We
|
||||
* accumulate these roots in the each compartment's gcGrayRoots vector and
|
||||
* then mark them later, after black marking is complete for each
|
||||
* compartment. This accumulation can fail, but in that case we switch to
|
||||
* non-incremental GC.
|
||||
*/
|
||||
bool hasBufferedGrayRoots() const;
|
||||
void startBufferingGrayRoots();
|
||||
void endBufferingGrayRoots();
|
||||
void resetBufferedGrayRoots();
|
||||
void markBufferedGrayRoots(JS::Zone *zone);
|
||||
|
||||
static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind);
|
||||
|
||||
void setGCMode(JSGCMode mode) { stack.setGCMode(mode); }
|
||||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
/* This is public exclusively for ScanRope. */
|
||||
MarkStack stack;
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
void checkZone(void *p);
|
||||
#else
|
||||
void checkZone(void *p) {}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We use a common mark stack to mark GC things of different types and use
|
||||
* the explicit tags to distinguish them when it cannot be deduced from
|
||||
* the context of push or pop operation.
|
||||
*/
|
||||
enum StackTag {
|
||||
ValueArrayTag,
|
||||
ObjectTag,
|
||||
TypeTag,
|
||||
XmlTag,
|
||||
SavedValueArrayTag,
|
||||
JitCodeTag,
|
||||
LastTag = JitCodeTag
|
||||
};
|
||||
|
||||
static const uintptr_t StackTagMask = 7;
|
||||
static_assert(StackTagMask >= uintptr_t(LastTag), "The tag mask must subsume the tags.");
|
||||
static_assert(StackTagMask <= gc::CellMask, "The tag mask must be embeddable in a Cell*.");
|
||||
|
||||
void pushTaggedPtr(StackTag tag, void *ptr) {
|
||||
checkZone(ptr);
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
|
||||
JS_ASSERT(!(addr & StackTagMask));
|
||||
if (!stack.push(addr | uintptr_t(tag)))
|
||||
delayMarkingChildren(ptr);
|
||||
}
|
||||
|
||||
void pushValueArray(JSObject *obj, void *start, void *end) {
|
||||
checkZone(obj);
|
||||
|
||||
JS_ASSERT(start <= end);
|
||||
uintptr_t tagged = reinterpret_cast<uintptr_t>(obj) | GCMarker::ValueArrayTag;
|
||||
uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
|
||||
uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
|
||||
|
||||
/*
|
||||
* Push in the reverse order so obj will be on top. If we cannot push
|
||||
* the array, we trigger delay marking for the whole object.
|
||||
*/
|
||||
if (!stack.push(endAddr, startAddr, tagged))
|
||||
delayMarkingChildren(obj);
|
||||
}
|
||||
|
||||
bool isMarkStackEmpty() {
|
||||
return stack.isEmpty();
|
||||
}
|
||||
|
||||
bool restoreValueArray(JSObject *obj, void **vpp, void **endp);
|
||||
void saveValueRanges();
|
||||
inline void processMarkStackTop(SliceBudget &budget);
|
||||
void processMarkStackOther(uintptr_t tag, uintptr_t addr);
|
||||
|
||||
void appendGrayRoot(void *thing, JSGCTraceKind kind);
|
||||
|
||||
/* The color is only applied to objects and functions. */
|
||||
uint32_t color;
|
||||
|
||||
/* Pointer to the top of the stack of arenas we are delaying marking on. */
|
||||
js::gc::ArenaHeader *unmarkedArenaStackTop;
|
||||
|
||||
/* Count of arenas that are currently in the stack. */
|
||||
mozilla::DebugOnly<size_t> markLaterArenas;
|
||||
|
||||
enum GrayBufferState {
|
||||
GRAY_BUFFER_UNUSED,
|
||||
GRAY_BUFFER_OK,
|
||||
GRAY_BUFFER_FAILED
|
||||
};
|
||||
GrayBufferState grayBufferState;
|
||||
|
||||
/* Assert that start and stop are called with correct ordering. */
|
||||
mozilla::DebugOnly<bool> started;
|
||||
};
|
||||
|
||||
void
|
||||
SetMarkStackLimit(JSRuntime *rt, size_t limit);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
* Macro to test if a traversal is the marking phase of the GC.
|
||||
*/
|
||||
#define IS_GC_MARKING_TRACER(trc) \
|
||||
((trc)->callback == nullptr || (trc)->callback == GCMarker::GrayCallback)
|
||||
|
||||
#endif /* js_Tracer_h */
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include "jit/AsmJSSignalHandlers.h"
|
||||
|
||||
#include "mozilla/BinarySearch.h"
|
||||
|
||||
#include "assembler/assembler/MacroAssembler.h"
|
||||
#include "jit/AsmJSModule.h"
|
||||
|
||||
@ -207,35 +209,29 @@ SetXMMRegToNaN(bool isFloat32, T *xmm_reg)
|
||||
}
|
||||
}
|
||||
|
||||
struct GetHeapAccessOffset
|
||||
{
|
||||
const AsmJSModule &module;
|
||||
explicit GetHeapAccessOffset(const AsmJSModule &module) : module(module) {}
|
||||
uintptr_t operator[](size_t index) const {
|
||||
return module.heapAccess(index).offset();
|
||||
}
|
||||
};
|
||||
|
||||
// Perform a binary search on the projected offsets of the known heap accesses
|
||||
// in the module.
|
||||
static const AsmJSHeapAccess *
|
||||
LookupHeapAccess(const AsmJSModule &module, uint8_t *pc)
|
||||
{
|
||||
JS_ASSERT(module.containsPC(pc));
|
||||
size_t targetOffset = pc - module.codeBase();
|
||||
|
||||
if (module.numHeapAccesses() == 0)
|
||||
uintptr_t pcOff = pc - module.codeBase();
|
||||
|
||||
size_t match;
|
||||
if (!BinarySearch(GetHeapAccessOffset(module), 0, module.numHeapAccesses(), pcOff, &match))
|
||||
return nullptr;
|
||||
|
||||
size_t low = 0;
|
||||
size_t high = module.numHeapAccesses() - 1;
|
||||
while (high - low >= 2) {
|
||||
size_t mid = low + (high - low) / 2;
|
||||
uint32_t midOffset = module.heapAccess(mid).offset();
|
||||
if (targetOffset == midOffset)
|
||||
return &module.heapAccess(mid);
|
||||
if (targetOffset < midOffset)
|
||||
high = mid;
|
||||
else
|
||||
low = mid;
|
||||
}
|
||||
if (targetOffset == module.heapAccess(low).offset())
|
||||
return &module.heapAccess(low);
|
||||
if (targetOffset == module.heapAccess(high).offset())
|
||||
return &module.heapAccess(high);
|
||||
|
||||
return nullptr;
|
||||
return &module.heapAccess(match);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -506,6 +506,7 @@ ValueNumberer::breakClass(MDefinition *def)
|
||||
MDefinition *newRep = findSplit(def);
|
||||
if (!newRep)
|
||||
return;
|
||||
markConsumers(def);
|
||||
ValueNumberData *newdata = newRep->valueNumberData();
|
||||
|
||||
// Right now, |defdata| is at the front of the list, and |newdata| is
|
||||
@ -544,8 +545,10 @@ ValueNumberer::breakClass(MDefinition *def)
|
||||
// make the VN of every member in the class the VN of the new representative number.
|
||||
for (MDefinition *tmp = newRep; tmp != nullptr; tmp = tmp->valueNumberData()->classNext) {
|
||||
// if this instruction is already scheduled to be processed, don't do anything.
|
||||
if (tmp->isInWorklist())
|
||||
if (tmp->isInWorklist()) {
|
||||
IonSpew(IonSpew_GVN, "Defer to a new congruence class: %d", tmp->id());
|
||||
continue;
|
||||
}
|
||||
IonSpew(IonSpew_GVN, "Moving to a new congruence class: %d", tmp->id());
|
||||
tmp->setValueNumber(newRep->id());
|
||||
markConsumers(tmp);
|
||||
|
@ -2120,20 +2120,6 @@ typedef enum JSGCParamKey {
|
||||
JSGC_DECOMMIT_THRESHOLD = 20
|
||||
} JSGCParamKey;
|
||||
|
||||
typedef enum JSGCMode {
|
||||
/* Perform only global GCs. */
|
||||
JSGC_MODE_GLOBAL = 0,
|
||||
|
||||
/* Perform per-compartment GCs until too much garbage has accumulated. */
|
||||
JSGC_MODE_COMPARTMENT = 1,
|
||||
|
||||
/*
|
||||
* Collect in short time slices rather than all at once. Implies
|
||||
* JSGC_MODE_COMPARTMENT.
|
||||
*/
|
||||
JSGC_MODE_INCREMENTAL = 2
|
||||
} JSGCMode;
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value);
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "jsutil.h"
|
||||
|
||||
#include "ds/Sort.h"
|
||||
#include "gc/Heap.h"
|
||||
#include "vm/ArgumentsObject.h"
|
||||
#include "vm/ForkJoin.h"
|
||||
#include "vm/Interpreter.h"
|
||||
|
298
js/src/jsgc.cpp
298
js/src/jsgc.cpp
@ -1338,6 +1338,26 @@ Allocator::Allocator(Zone *zone)
|
||||
: zone_(zone)
|
||||
{}
|
||||
|
||||
inline void
|
||||
GCMarker::delayMarkingArena(ArenaHeader *aheader)
|
||||
{
|
||||
if (aheader->hasDelayedMarking) {
|
||||
/* Arena already scheduled to be marked later */
|
||||
return;
|
||||
}
|
||||
aheader->setNextDelayedMarking(unmarkedArenaStackTop);
|
||||
unmarkedArenaStackTop = aheader;
|
||||
markLaterArenas++;
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::delayMarkingChildren(const void *thing)
|
||||
{
|
||||
const Cell *cell = reinterpret_cast<const Cell *>(thing);
|
||||
cell->arenaHeader()->markOverflow = 1;
|
||||
delayMarkingArena(cell->arenaHeader());
|
||||
}
|
||||
|
||||
inline void
|
||||
ArenaLists::prepareForIncrementalGC(JSRuntime *rt)
|
||||
{
|
||||
@ -1849,284 +1869,6 @@ SliceBudget::checkOverBudget()
|
||||
return over;
|
||||
}
|
||||
|
||||
/*
|
||||
* DoNotTraceWeakMaps: the GC is recomputing the liveness of WeakMap entries,
|
||||
* so we delay visting entries.
|
||||
*/
|
||||
GCMarker::GCMarker(JSRuntime *rt)
|
||||
: JSTracer(rt, nullptr, DoNotTraceWeakMaps),
|
||||
stack(size_t(-1)),
|
||||
color(BLACK),
|
||||
started(false),
|
||||
unmarkedArenaStackTop(nullptr),
|
||||
markLaterArenas(0),
|
||||
grayBufferState(GRAY_BUFFER_UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
GCMarker::init(JSGCMode gcMode)
|
||||
{
|
||||
return stack.init(gcMode);
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::start()
|
||||
{
|
||||
JS_ASSERT(!started);
|
||||
started = true;
|
||||
color = BLACK;
|
||||
|
||||
JS_ASSERT(!unmarkedArenaStackTop);
|
||||
JS_ASSERT(markLaterArenas == 0);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::stop()
|
||||
{
|
||||
JS_ASSERT(isDrained());
|
||||
|
||||
JS_ASSERT(started);
|
||||
started = false;
|
||||
|
||||
JS_ASSERT(!unmarkedArenaStackTop);
|
||||
JS_ASSERT(markLaterArenas == 0);
|
||||
|
||||
/* Free non-ballast stack memory. */
|
||||
stack.reset();
|
||||
|
||||
resetBufferedGrayRoots();
|
||||
grayBufferState = GRAY_BUFFER_UNUSED;
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::reset()
|
||||
{
|
||||
color = BLACK;
|
||||
|
||||
stack.reset();
|
||||
JS_ASSERT(isMarkStackEmpty());
|
||||
|
||||
while (unmarkedArenaStackTop) {
|
||||
ArenaHeader *aheader = unmarkedArenaStackTop;
|
||||
JS_ASSERT(aheader->hasDelayedMarking);
|
||||
JS_ASSERT(markLaterArenas);
|
||||
unmarkedArenaStackTop = aheader->getNextDelayedMarking();
|
||||
aheader->unsetDelayedMarking();
|
||||
aheader->markOverflow = 0;
|
||||
aheader->allocatedDuringIncremental = 0;
|
||||
markLaterArenas--;
|
||||
}
|
||||
JS_ASSERT(isDrained());
|
||||
JS_ASSERT(!markLaterArenas);
|
||||
}
|
||||
|
||||
/*
|
||||
* When the native stack is low, the GC does not call JS_TraceChildren to mark
|
||||
* the reachable "children" of the thing. Rather the thing is put aside and
|
||||
* JS_TraceChildren is called later with more space on the C stack.
|
||||
*
|
||||
* To implement such delayed marking of the children with minimal overhead for
|
||||
* the normal case of sufficient native stack, the code adds a field per
|
||||
* arena. The field markingDelay->link links all arenas with delayed things
|
||||
* into a stack list with the pointer to stack top in
|
||||
* GCMarker::unmarkedArenaStackTop. delayMarkingChildren adds
|
||||
* arenas to the stack as necessary while markDelayedChildren pops the arenas
|
||||
* from the stack until it empties.
|
||||
*/
|
||||
|
||||
inline void
|
||||
GCMarker::delayMarkingArena(ArenaHeader *aheader)
|
||||
{
|
||||
if (aheader->hasDelayedMarking) {
|
||||
/* Arena already scheduled to be marked later */
|
||||
return;
|
||||
}
|
||||
aheader->setNextDelayedMarking(unmarkedArenaStackTop);
|
||||
unmarkedArenaStackTop = aheader;
|
||||
markLaterArenas++;
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::delayMarkingChildren(const void *thing)
|
||||
{
|
||||
const Cell *cell = reinterpret_cast<const Cell *>(thing);
|
||||
cell->arenaHeader()->markOverflow = 1;
|
||||
delayMarkingArena(cell->arenaHeader());
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::markDelayedChildren(ArenaHeader *aheader)
|
||||
{
|
||||
if (aheader->markOverflow) {
|
||||
bool always = aheader->allocatedDuringIncremental;
|
||||
aheader->markOverflow = 0;
|
||||
|
||||
for (CellIterUnderGC i(aheader); !i.done(); i.next()) {
|
||||
Cell *t = i.getCell();
|
||||
if (always || t->isMarked()) {
|
||||
t->markIfUnmarked();
|
||||
JS_TraceChildren(this, t, MapAllocToTraceKind(aheader->getAllocKind()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(aheader->allocatedDuringIncremental);
|
||||
PushArena(this, aheader);
|
||||
}
|
||||
aheader->allocatedDuringIncremental = 0;
|
||||
/*
|
||||
* Note that during an incremental GC we may still be allocating into
|
||||
* aheader. However, prepareForIncrementalGC sets the
|
||||
* allocatedDuringIncremental flag if we continue marking.
|
||||
*/
|
||||
}
|
||||
|
||||
bool
|
||||
GCMarker::markDelayedChildren(SliceBudget &budget)
|
||||
{
|
||||
gcstats::MaybeAutoPhase ap;
|
||||
if (runtime()->gcIncrementalState == MARK)
|
||||
ap.construct(runtime()->gcStats, gcstats::PHASE_MARK_DELAYED);
|
||||
|
||||
JS_ASSERT(unmarkedArenaStackTop);
|
||||
do {
|
||||
/*
|
||||
* If marking gets delayed at the same arena again, we must repeat
|
||||
* marking of its things. For that we pop arena from the stack and
|
||||
* clear its hasDelayedMarking flag before we begin the marking.
|
||||
*/
|
||||
ArenaHeader *aheader = unmarkedArenaStackTop;
|
||||
JS_ASSERT(aheader->hasDelayedMarking);
|
||||
JS_ASSERT(markLaterArenas);
|
||||
unmarkedArenaStackTop = aheader->getNextDelayedMarking();
|
||||
aheader->unsetDelayedMarking();
|
||||
markLaterArenas--;
|
||||
markDelayedChildren(aheader);
|
||||
|
||||
budget.step(150);
|
||||
if (budget.isOverBudget())
|
||||
return false;
|
||||
} while (unmarkedArenaStackTop);
|
||||
JS_ASSERT(!markLaterArenas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
GCMarker::checkZone(void *p)
|
||||
{
|
||||
JS_ASSERT(started);
|
||||
DebugOnly<Cell *> cell = static_cast<Cell *>(p);
|
||||
JS_ASSERT_IF(cell->isTenured(), cell->tenuredZone()->isCollecting());
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
GCMarker::hasBufferedGrayRoots() const
|
||||
{
|
||||
return grayBufferState == GRAY_BUFFER_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::startBufferingGrayRoots()
|
||||
{
|
||||
JS_ASSERT(grayBufferState == GRAY_BUFFER_UNUSED);
|
||||
grayBufferState = GRAY_BUFFER_OK;
|
||||
for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
|
||||
JS_ASSERT(zone->gcGrayRoots.empty());
|
||||
|
||||
JS_ASSERT(!callback);
|
||||
callback = GrayCallback;
|
||||
JS_ASSERT(IS_GC_MARKING_TRACER(this));
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::endBufferingGrayRoots()
|
||||
{
|
||||
JS_ASSERT(callback == GrayCallback);
|
||||
callback = nullptr;
|
||||
JS_ASSERT(IS_GC_MARKING_TRACER(this));
|
||||
JS_ASSERT(grayBufferState == GRAY_BUFFER_OK ||
|
||||
grayBufferState == GRAY_BUFFER_FAILED);
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::resetBufferedGrayRoots()
|
||||
{
|
||||
for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
|
||||
zone->gcGrayRoots.clearAndFree();
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::markBufferedGrayRoots(JS::Zone *zone)
|
||||
{
|
||||
JS_ASSERT(grayBufferState == GRAY_BUFFER_OK);
|
||||
JS_ASSERT(zone->isGCMarkingGray());
|
||||
|
||||
for (GrayRoot *elem = zone->gcGrayRoots.begin(); elem != zone->gcGrayRoots.end(); elem++) {
|
||||
#ifdef DEBUG
|
||||
setTracingDetails(elem->debugPrinter, elem->debugPrintArg, elem->debugPrintIndex);
|
||||
#endif
|
||||
void *tmp = elem->thing;
|
||||
setTracingLocation((void *)&elem->thing);
|
||||
MarkKind(this, &tmp, elem->kind);
|
||||
JS_ASSERT(tmp == elem->thing);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::appendGrayRoot(void *thing, JSGCTraceKind kind)
|
||||
{
|
||||
JS_ASSERT(started);
|
||||
|
||||
if (grayBufferState == GRAY_BUFFER_FAILED)
|
||||
return;
|
||||
|
||||
GrayRoot root(thing, kind);
|
||||
#ifdef DEBUG
|
||||
root.debugPrinter = debugPrinter();
|
||||
root.debugPrintArg = debugPrintArg();
|
||||
root.debugPrintIndex = debugPrintIndex();
|
||||
#endif
|
||||
|
||||
Zone *zone = static_cast<Cell *>(thing)->tenuredZone();
|
||||
if (zone->isCollecting()) {
|
||||
zone->maybeAlive = true;
|
||||
if (!zone->gcGrayRoots.append(root)) {
|
||||
resetBufferedGrayRoots();
|
||||
grayBufferState = GRAY_BUFFER_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::GrayCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind)
|
||||
{
|
||||
JS_ASSERT(thingp);
|
||||
JS_ASSERT(*thingp);
|
||||
GCMarker *gcmarker = static_cast<GCMarker *>(trc);
|
||||
gcmarker->appendGrayRoot(*thingp, kind);
|
||||
}
|
||||
|
||||
size_t
|
||||
GCMarker::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
size_t size = stack.sizeOfExcludingThis(mallocSizeOf);
|
||||
for (ZonesIter zone(runtime(), WithAtoms); !zone.done(); zone.next())
|
||||
size += zone->gcGrayRoots.sizeOfExcludingThis(mallocSizeOf);
|
||||
return size;
|
||||
}
|
||||
|
||||
void
|
||||
js::SetMarkStackLimit(JSRuntime *rt, size_t limit)
|
||||
{
|
||||
JS_ASSERT(!rt->isHeapBusy());
|
||||
AutoStopVerifyingBarriers pauseVerification(rt, false);
|
||||
rt->gcMarker.setMaxCapacity(limit);
|
||||
}
|
||||
|
||||
void
|
||||
js::MarkCompartmentActive(InterpreterFrame *fp)
|
||||
{
|
||||
|
330
js/src/jsgc.h
330
js/src/jsgc.h
@ -15,7 +15,6 @@
|
||||
#include "jslock.h"
|
||||
#include "jsobj.h"
|
||||
|
||||
#include "gc/Tracer.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/SliceBudget.h"
|
||||
#include "js/Vector.h"
|
||||
@ -930,157 +929,6 @@ struct GCChunkHasher {
|
||||
|
||||
typedef HashSet<js::gc::Chunk *, GCChunkHasher, SystemAllocPolicy> GCChunkSet;
|
||||
|
||||
static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
|
||||
static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
|
||||
|
||||
template<class T>
|
||||
struct MarkStack {
|
||||
T *stack_;
|
||||
T *tos_;
|
||||
T *end_;
|
||||
|
||||
// The capacity we start with and reset() to.
|
||||
size_t baseCapacity_;
|
||||
size_t maxCapacity_;
|
||||
|
||||
MarkStack(size_t maxCapacity)
|
||||
: stack_(nullptr),
|
||||
tos_(nullptr),
|
||||
end_(nullptr),
|
||||
baseCapacity_(0),
|
||||
maxCapacity_(maxCapacity)
|
||||
{}
|
||||
|
||||
~MarkStack() {
|
||||
js_free(stack_);
|
||||
}
|
||||
|
||||
size_t capacity() { return end_ - stack_; }
|
||||
|
||||
ptrdiff_t position() const { return tos_ - stack_; }
|
||||
|
||||
void setStack(T *stack, size_t tosIndex, size_t capacity) {
|
||||
stack_ = stack;
|
||||
tos_ = stack + tosIndex;
|
||||
end_ = stack + capacity;
|
||||
}
|
||||
|
||||
void setBaseCapacity(JSGCMode mode) {
|
||||
switch (mode) {
|
||||
case JSGC_MODE_GLOBAL:
|
||||
case JSGC_MODE_COMPARTMENT:
|
||||
baseCapacity_ = NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY;
|
||||
break;
|
||||
case JSGC_MODE_INCREMENTAL:
|
||||
baseCapacity_ = INCREMENTAL_MARK_STACK_BASE_CAPACITY;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("bad gc mode");
|
||||
}
|
||||
|
||||
if (baseCapacity_ > maxCapacity_)
|
||||
baseCapacity_ = maxCapacity_;
|
||||
}
|
||||
|
||||
bool init(JSGCMode gcMode) {
|
||||
setBaseCapacity(gcMode);
|
||||
|
||||
JS_ASSERT(!stack_);
|
||||
T *newStack = js_pod_malloc<T>(baseCapacity_);
|
||||
if (!newStack)
|
||||
return false;
|
||||
|
||||
setStack(newStack, 0, baseCapacity_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setMaxCapacity(size_t maxCapacity) {
|
||||
JS_ASSERT(isEmpty());
|
||||
maxCapacity_ = maxCapacity;
|
||||
if (baseCapacity_ > maxCapacity_)
|
||||
baseCapacity_ = maxCapacity_;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
bool push(T item) {
|
||||
if (tos_ == end_) {
|
||||
if (!enlarge(1))
|
||||
return false;
|
||||
}
|
||||
JS_ASSERT(tos_ < end_);
|
||||
*tos_++ = item;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool push(T item1, T item2, T item3) {
|
||||
T *nextTos = tos_ + 3;
|
||||
if (nextTos > end_) {
|
||||
if (!enlarge(3))
|
||||
return false;
|
||||
nextTos = tos_ + 3;
|
||||
}
|
||||
JS_ASSERT(nextTos <= end_);
|
||||
tos_[0] = item1;
|
||||
tos_[1] = item2;
|
||||
tos_[2] = item3;
|
||||
tos_ = nextTos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
return tos_ == stack_;
|
||||
}
|
||||
|
||||
T pop() {
|
||||
JS_ASSERT(!isEmpty());
|
||||
return *--tos_;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (capacity() == baseCapacity_) {
|
||||
// No size change; keep the current stack.
|
||||
setStack(stack_, 0, baseCapacity_);
|
||||
return;
|
||||
}
|
||||
|
||||
T *newStack = (T *)js_realloc(stack_, sizeof(T) * baseCapacity_);
|
||||
if (!newStack) {
|
||||
// If the realloc fails, just keep using the existing stack; it's
|
||||
// not ideal but better than failing.
|
||||
newStack = stack_;
|
||||
baseCapacity_ = capacity();
|
||||
}
|
||||
setStack(newStack, 0, baseCapacity_);
|
||||
}
|
||||
|
||||
/* Grow the stack, ensuring there is space for at least count elements. */
|
||||
bool enlarge(unsigned count) {
|
||||
size_t newCapacity = Min(maxCapacity_, capacity() * 2);
|
||||
if (newCapacity < capacity() + count)
|
||||
return false;
|
||||
|
||||
size_t tosIndex = position();
|
||||
|
||||
T *newStack = (T *)js_realloc(stack_, sizeof(T) * newCapacity);
|
||||
if (!newStack)
|
||||
return false;
|
||||
|
||||
setStack(newStack, tosIndex, newCapacity);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setGCMode(JSGCMode gcMode) {
|
||||
// The mark stack won't be resized until the next call to reset(), but
|
||||
// that will happen at the end of the next GC.
|
||||
setBaseCapacity(gcMode);
|
||||
}
|
||||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
return mallocSizeOf(stack_);
|
||||
}
|
||||
};
|
||||
|
||||
struct GrayRoot {
|
||||
void *thing;
|
||||
JSGCTraceKind kind;
|
||||
@ -1094,178 +942,6 @@ struct GrayRoot {
|
||||
: thing(thing), kind(kind) {}
|
||||
};
|
||||
|
||||
struct GCMarker : public JSTracer {
|
||||
private:
|
||||
/*
|
||||
* We use a common mark stack to mark GC things of different types and use
|
||||
* the explicit tags to distinguish them when it cannot be deduced from
|
||||
* the context of push or pop operation.
|
||||
*/
|
||||
enum StackTag {
|
||||
ValueArrayTag,
|
||||
ObjectTag,
|
||||
TypeTag,
|
||||
XmlTag,
|
||||
SavedValueArrayTag,
|
||||
JitCodeTag,
|
||||
LastTag = JitCodeTag
|
||||
};
|
||||
|
||||
static const uintptr_t StackTagMask = 7;
|
||||
|
||||
static void staticAsserts() {
|
||||
JS_STATIC_ASSERT(StackTagMask >= uintptr_t(LastTag));
|
||||
JS_STATIC_ASSERT(StackTagMask <= gc::CellMask);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit GCMarker(JSRuntime *rt);
|
||||
bool init(JSGCMode gcMode);
|
||||
|
||||
void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
|
||||
size_t maxCapacity() const { return stack.maxCapacity_; }
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void reset();
|
||||
|
||||
void pushObject(ObjectImpl *obj) {
|
||||
pushTaggedPtr(ObjectTag, obj);
|
||||
}
|
||||
|
||||
void pushType(types::TypeObject *type) {
|
||||
pushTaggedPtr(TypeTag, type);
|
||||
}
|
||||
|
||||
void pushJitCode(jit::JitCode *code) {
|
||||
pushTaggedPtr(JitCodeTag, code);
|
||||
}
|
||||
|
||||
uint32_t getMarkColor() const {
|
||||
return color;
|
||||
}
|
||||
|
||||
/*
|
||||
* Care must be taken changing the mark color from gray to black. The cycle
|
||||
* collector depends on the invariant that there are no black to gray edges
|
||||
* in the GC heap. This invariant lets the CC not trace through black
|
||||
* objects. If this invariant is violated, the cycle collector may free
|
||||
* objects that are still reachable.
|
||||
*/
|
||||
void setMarkColorGray() {
|
||||
JS_ASSERT(isDrained());
|
||||
JS_ASSERT(color == gc::BLACK);
|
||||
color = gc::GRAY;
|
||||
}
|
||||
|
||||
void setMarkColorBlack() {
|
||||
JS_ASSERT(isDrained());
|
||||
JS_ASSERT(color == gc::GRAY);
|
||||
color = gc::BLACK;
|
||||
}
|
||||
|
||||
inline void delayMarkingArena(gc::ArenaHeader *aheader);
|
||||
void delayMarkingChildren(const void *thing);
|
||||
void markDelayedChildren(gc::ArenaHeader *aheader);
|
||||
bool markDelayedChildren(SliceBudget &budget);
|
||||
bool hasDelayedChildren() const {
|
||||
return !!unmarkedArenaStackTop;
|
||||
}
|
||||
|
||||
bool isDrained() {
|
||||
return isMarkStackEmpty() && !unmarkedArenaStackTop;
|
||||
}
|
||||
|
||||
bool drainMarkStack(SliceBudget &budget);
|
||||
|
||||
/*
|
||||
* Gray marking must be done after all black marking is complete. However,
|
||||
* we do not have write barriers on XPConnect roots. Therefore, XPConnect
|
||||
* roots must be accumulated in the first slice of incremental GC. We
|
||||
* accumulate these roots in the each compartment's gcGrayRoots vector and
|
||||
* then mark them later, after black marking is complete for each
|
||||
* compartment. This accumulation can fail, but in that case we switch to
|
||||
* non-incremental GC.
|
||||
*/
|
||||
bool hasBufferedGrayRoots() const;
|
||||
void startBufferingGrayRoots();
|
||||
void endBufferingGrayRoots();
|
||||
void resetBufferedGrayRoots();
|
||||
void markBufferedGrayRoots(JS::Zone *zone);
|
||||
|
||||
static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind);
|
||||
|
||||
void setGCMode(JSGCMode mode) { stack.setGCMode(mode); }
|
||||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
MarkStack<uintptr_t> stack;
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
void checkZone(void *p);
|
||||
#else
|
||||
void checkZone(void *p) {}
|
||||
#endif
|
||||
|
||||
void pushTaggedPtr(StackTag tag, void *ptr) {
|
||||
checkZone(ptr);
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
|
||||
JS_ASSERT(!(addr & StackTagMask));
|
||||
if (!stack.push(addr | uintptr_t(tag)))
|
||||
delayMarkingChildren(ptr);
|
||||
}
|
||||
|
||||
void pushValueArray(JSObject *obj, void *start, void *end) {
|
||||
checkZone(obj);
|
||||
|
||||
JS_ASSERT(start <= end);
|
||||
uintptr_t tagged = reinterpret_cast<uintptr_t>(obj) | GCMarker::ValueArrayTag;
|
||||
uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
|
||||
uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
|
||||
|
||||
/*
|
||||
* Push in the reverse order so obj will be on top. If we cannot push
|
||||
* the array, we trigger delay marking for the whole object.
|
||||
*/
|
||||
if (!stack.push(endAddr, startAddr, tagged))
|
||||
delayMarkingChildren(obj);
|
||||
}
|
||||
|
||||
bool isMarkStackEmpty() {
|
||||
return stack.isEmpty();
|
||||
}
|
||||
|
||||
bool restoreValueArray(JSObject *obj, void **vpp, void **endp);
|
||||
void saveValueRanges();
|
||||
inline void processMarkStackTop(SliceBudget &budget);
|
||||
void processMarkStackOther(uintptr_t tag, uintptr_t addr);
|
||||
|
||||
void appendGrayRoot(void *thing, JSGCTraceKind kind);
|
||||
|
||||
/* The color is only applied to objects and functions. */
|
||||
uint32_t color;
|
||||
|
||||
mozilla::DebugOnly<bool> started;
|
||||
|
||||
/* Pointer to the top of the stack of arenas we are delaying marking on. */
|
||||
js::gc::ArenaHeader *unmarkedArenaStackTop;
|
||||
/* Count of arenas that are currently in the stack. */
|
||||
mozilla::DebugOnly<size_t> markLaterArenas;
|
||||
|
||||
enum GrayBufferState
|
||||
{
|
||||
GRAY_BUFFER_UNUSED,
|
||||
GRAY_BUFFER_OK,
|
||||
GRAY_BUFFER_FAILED
|
||||
};
|
||||
|
||||
GrayBufferState grayBufferState;
|
||||
};
|
||||
|
||||
void
|
||||
SetMarkStackLimit(JSRuntime *rt, size_t limit);
|
||||
|
||||
void
|
||||
MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);
|
||||
|
||||
@ -1320,12 +996,6 @@ IterateScripts(JSRuntime *rt, JSCompartment *compartment,
|
||||
extern void
|
||||
js_FinalizeStringRT(JSRuntime *rt, JSString *str);
|
||||
|
||||
/*
|
||||
* Macro to test if a traversal is the marking phase of the GC.
|
||||
*/
|
||||
#define IS_GC_MARKING_TRACER(trc) \
|
||||
((trc)->callback == nullptr || (trc)->callback == GCMarker::GrayCallback)
|
||||
|
||||
namespace js {
|
||||
|
||||
JSCompartment *
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
#include "gc/Marking.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "vm/ObjectImpl.h"
|
||||
|
@ -34,6 +34,7 @@
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
# include "gc/StoreBuffer.h"
|
||||
#endif
|
||||
#include "gc/Tracer.h"
|
||||
#ifdef XP_MACOSX
|
||||
# include "jit/AsmJSSignalHandlers.h"
|
||||
#endif
|
||||
|
30
media/libopus/Makefile.in
Normal file
30
media/libopus/Makefile.in
Normal file
@ -0,0 +1,30 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
ifdef GNU_AS
|
||||
ifeq ($(CPU_ARCH),arm)
|
||||
|
||||
# These flags are a lie; they're just used to enable the requisite
|
||||
# opcodes; actual arch detection is done at runtime.
|
||||
ASFLAGS = -march=armv7-a -mfpu=neon
|
||||
|
||||
celt_pitch_xcorr_arm-gnu.$(ASM_SUFFIX): celt_arm_dir celt/arm/armopts-gnu.S
|
||||
|
||||
# we need to throw this here because upstream has this path hardcoded
|
||||
celt_arm_dir:
|
||||
mkdir -p celt/arm
|
||||
|
||||
# armopts needs a specific rule, because arm2gnu.pl will always add the .S
|
||||
# suffix when translating the files that include it.
|
||||
celt/arm/armopts-gnu.S: celt/arm/armopts.s
|
||||
$(PERL) $(srcdir)/celt/arm/arm2gnu.pl < $< > $@
|
||||
# For all others, we can use an implicit rule with the configured $(ASM_SUFFIX).
|
||||
%-gnu.$(ASM_SUFFIX): celt/arm/%.s
|
||||
$(PERL) $(srcdir)/celt/arm/arm2gnu.pl < $< > $@
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
316
media/libopus/celt/arm/arm2gnu.pl
Executable file
316
media/libopus/celt/arm/arm2gnu.pl
Executable file
@ -0,0 +1,316 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
my $bigend; # little/big endian
|
||||
my $nxstack;
|
||||
|
||||
$nxstack = 0;
|
||||
|
||||
eval 'exec /usr/local/bin/perl -S $0 ${1+"$@"}'
|
||||
if $running_under_some_shell;
|
||||
|
||||
while ($ARGV[0] =~ /^-/) {
|
||||
$_ = shift;
|
||||
last if /^--/;
|
||||
if (/^-n/) {
|
||||
$nflag++;
|
||||
next;
|
||||
}
|
||||
die "I don't recognize this switch: $_\\n";
|
||||
}
|
||||
$printit++ unless $nflag;
|
||||
|
||||
$\ = "\n"; # automatically add newline on print
|
||||
$n=0;
|
||||
|
||||
$thumb = 0; # ARM mode by default, not Thumb.
|
||||
@proc_stack = ();
|
||||
|
||||
LINE:
|
||||
while (<>) {
|
||||
|
||||
# For ADRLs we need to add a new line after the substituted one.
|
||||
$addPadding = 0;
|
||||
|
||||
# First, we do not dare to touch *anything* inside double quotes, do we?
|
||||
# Second, if you want a dollar character in the string,
|
||||
# insert two of them -- that's how ARM C and assembler treat strings.
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCB[ \t]*\"/$1: .ascii \"/ && do { s/\$\$/\$/g; next };
|
||||
s/\bDCB\b[ \t]*\"/.ascii \"/ && do { s/\$\$/\$/g; next };
|
||||
s/^(\S+)\s+RN\s+(\S+)/$1 .req r$2/ && do { s/\$\$/\$/g; next };
|
||||
# If there's nothing on a line but a comment, don't try to apply any further
|
||||
# substitutions (this is a cheap hack to avoid mucking up the license header)
|
||||
s/^([ \t]*);/$1@/ && do { s/\$\$/\$/g; next };
|
||||
# If substituted -- leave immediately !
|
||||
|
||||
s/@/,:/;
|
||||
s/;/@/;
|
||||
while ( /@.*'/ ) {
|
||||
s/(@.*)'/$1/g;
|
||||
}
|
||||
s/\{FALSE\}/0/g;
|
||||
s/\{TRUE\}/1/g;
|
||||
s/\{(\w\w\w\w+)\}/$1/g;
|
||||
s/\bINCLUDE[ \t]*([^ \t\n]+)/.include \"$1\"/;
|
||||
s/\bGET[ \t]*([^ \t\n]+)/.include \"${ my $x=$1; $x =~ s|\.s|-gnu.S|; \$x }\"/;
|
||||
s/\bIMPORT\b/.extern/;
|
||||
s/\bEXPORT\b/.global/;
|
||||
s/^(\s+)\[/$1IF/;
|
||||
s/^(\s+)\|/$1ELSE/;
|
||||
s/^(\s+)\]/$1ENDIF/;
|
||||
s/IF *:DEF:/ .ifdef/;
|
||||
s/IF *:LNOT: *:DEF:/ .ifndef/;
|
||||
s/ELSE/ .else/;
|
||||
s/ENDIF/ .endif/;
|
||||
|
||||
if( /\bIF\b/ ) {
|
||||
s/\bIF\b/ .if/;
|
||||
s/=/==/;
|
||||
}
|
||||
if ( $n == 2) {
|
||||
s/\$/\\/g;
|
||||
}
|
||||
if ($n == 1) {
|
||||
s/\$//g;
|
||||
s/label//g;
|
||||
$n = 2;
|
||||
}
|
||||
if ( /MACRO/ ) {
|
||||
s/MACRO *\n/.macro/;
|
||||
$n=1;
|
||||
}
|
||||
if ( /\bMEND\b/ ) {
|
||||
s/\bMEND\b/.endm/;
|
||||
$n=0;
|
||||
}
|
||||
|
||||
# ".rdata" doesn't work in 'as' version 2.13.2, as it is ".rodata" there.
|
||||
#
|
||||
if ( /\bAREA\b/ ) {
|
||||
my $align;
|
||||
$align = "2";
|
||||
if ( /ALIGN=(\d+)/ ) {
|
||||
$align = $1;
|
||||
}
|
||||
if ( /CODE/ ) {
|
||||
$nxstack = 1;
|
||||
}
|
||||
s/^(.+)CODE(.+)READONLY(.*)/ .text/;
|
||||
s/^(.+)DATA(.+)READONLY(.*)/ .section .rdata/;
|
||||
s/^(.+)\|\|\.data\|\|(.+)/ .data/;
|
||||
s/^(.+)\|\|\.bss\|\|(.+)/ .bss/;
|
||||
s/$/; .p2align $align/;
|
||||
# Enable NEON instructions but don't produce a binary that requires
|
||||
# ARMv7. RVCT does not have equivalent directives, so we just do this
|
||||
# for all CODE areas.
|
||||
if ( /.text/ ) {
|
||||
# Separating .arch, .fpu, etc., by semicolons does not work (gas
|
||||
# thinks the semicolon is part of the arch name, even when there's
|
||||
# whitespace separating them). Sadly this means our line numbers
|
||||
# won't match the original source file (we could use the .line
|
||||
# directive, which is documented to be obsolete, but then gdb will
|
||||
# show the wrong line in the translated source file).
|
||||
s/$/; .arch armv7-a\n .fpu neon\n .object_arch armv4t/;
|
||||
}
|
||||
}
|
||||
|
||||
s/\|\|\.constdata\$(\d+)\|\|/.L_CONST$1/; # ||.constdata$3||
|
||||
s/\|\|\.bss\$(\d+)\|\|/.L_BSS$1/; # ||.bss$2||
|
||||
s/\|\|\.data\$(\d+)\|\|/.L_DATA$1/; # ||.data$2||
|
||||
s/\|\|([a-zA-Z0-9_]+)\@([a-zA-Z0-9_]+)\|\|/@ $&/;
|
||||
s/^(\s+)\%(\s)/ .space $1/;
|
||||
|
||||
s/\|(.+)\.(\d+)\|/\.$1_$2/; # |L80.123| -> .L80_123
|
||||
s/\bCODE32\b/.code 32/ && do {$thumb = 0};
|
||||
s/\bCODE16\b/.code 16/ && do {$thumb = 1};
|
||||
if (/\bPROC\b/)
|
||||
{
|
||||
my $prefix;
|
||||
my $proc;
|
||||
/^([A-Za-z_\.]\w+)\b/;
|
||||
$proc = $1;
|
||||
$prefix = "";
|
||||
if ($proc)
|
||||
{
|
||||
$prefix = $prefix.sprintf("\t.type\t%s, %%function; ",$proc);
|
||||
push(@proc_stack, $proc);
|
||||
s/^[A-Za-z_\.]\w+/$&:/;
|
||||
}
|
||||
$prefix = $prefix."\t.thumb_func; " if ($thumb);
|
||||
s/\bPROC\b/@ $&/;
|
||||
$_ = $prefix.$_;
|
||||
}
|
||||
s/^(\s*)(S|Q|SH|U|UQ|UH)ASX\b/$1$2ADDSUBX/;
|
||||
s/^(\s*)(S|Q|SH|U|UQ|UH)SAX\b/$1$2SUBADDX/;
|
||||
if (/\bENDP\b/)
|
||||
{
|
||||
my $proc;
|
||||
s/\bENDP\b/@ $&/;
|
||||
$proc = pop(@proc_stack);
|
||||
$_ = "\t.size $proc, .-$proc".$_ if ($proc);
|
||||
}
|
||||
s/\bSUBT\b/@ $&/;
|
||||
s/\bDATA\b/@ $&/; # DATA directive is deprecated -- Asm guide, p.7-25
|
||||
s/\bKEEP\b/@ $&/;
|
||||
s/\bEXPORTAS\b/@ $&/;
|
||||
s/\|\|(.)+\bEQU\b/@ $&/;
|
||||
s/\|\|([\w\$]+)\|\|/$1/;
|
||||
s/\bENTRY\b/@ $&/;
|
||||
s/\bASSERT\b/@ $&/;
|
||||
s/\bGBLL\b/@ $&/;
|
||||
s/\bGBLA\b/@ $&/;
|
||||
s/^\W+OPT\b/@ $&/;
|
||||
s/:OR:/|/g;
|
||||
s/:SHL:/<</g;
|
||||
s/:SHR:/>>/g;
|
||||
s/:AND:/&/g;
|
||||
s/:LAND:/&&/g;
|
||||
s/CPSR/cpsr/;
|
||||
s/SPSR/spsr/;
|
||||
s/ALIGN$/.balign 4/;
|
||||
s/ALIGN\s+([0-9x]+)$/.balign $1/;
|
||||
s/psr_cxsf/psr_all/;
|
||||
s/LTORG/.ltorg/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+EQU/ .set $1,/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+SETL/ .set $1,/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+SETA/ .set $1,/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+\*/ .set $1,/;
|
||||
|
||||
# {PC} + 0xdeadfeed --> . + 0xdeadfeed
|
||||
s/\{PC\} \+/ \. +/;
|
||||
|
||||
# Single hex constant on the line !
|
||||
#
|
||||
# >>> NOTE <<<
|
||||
# Double-precision floats in gcc are always mixed-endian, which means
|
||||
# bytes in two words are little-endian, but words are big-endian.
|
||||
# So, 0x0000deadfeed0000 would be stored as 0x0000dead at low address
|
||||
# and 0xfeed0000 at high address.
|
||||
#
|
||||
s/\bDCFD\b[ \t]+0x([a-fA-F0-9]{8})([a-fA-F0-9]{8})/.long 0x$1, 0x$2/;
|
||||
# Only decimal constants on the line, no hex !
|
||||
s/\bDCFD\b[ \t]+([0-9\.\-]+)/.double $1/;
|
||||
|
||||
# Single hex constant on the line !
|
||||
# s/\bDCFS\b[ \t]+0x([a-f0-9]{8})([a-f0-9]{8})/.long 0x$1, 0x$2/;
|
||||
# Only decimal constants on the line, no hex !
|
||||
# s/\bDCFS\b[ \t]+([0-9\.\-]+)/.double $1/;
|
||||
s/\bDCFS[ \t]+0x/.word 0x/;
|
||||
s/\bDCFS\b/.float/;
|
||||
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCD/$1 .word/;
|
||||
s/\bDCD\b/.word/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCW/$1 .short/;
|
||||
s/\bDCW\b/.short/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCB/$1 .byte/;
|
||||
s/\bDCB\b/.byte/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+\%/.comm $1,/;
|
||||
s/^[A-Za-z_\.]\w+/$&:/;
|
||||
s/^(\d+)/$1:/;
|
||||
s/\%(\d+)/$1b_or_f/;
|
||||
s/\%[Bb](\d+)/$1b/;
|
||||
s/\%[Ff](\d+)/$1f/;
|
||||
s/\%[Ff][Tt](\d+)/$1f/;
|
||||
s/&([\dA-Fa-f]+)/0x$1/;
|
||||
if ( /\b2_[01]+\b/ ) {
|
||||
s/\b2_([01]+)\b/conv$1&&&&/g;
|
||||
while ( /[01][01][01][01]&&&&/ ) {
|
||||
s/0000&&&&/&&&&0/g;
|
||||
s/0001&&&&/&&&&1/g;
|
||||
s/0010&&&&/&&&&2/g;
|
||||
s/0011&&&&/&&&&3/g;
|
||||
s/0100&&&&/&&&&4/g;
|
||||
s/0101&&&&/&&&&5/g;
|
||||
s/0110&&&&/&&&&6/g;
|
||||
s/0111&&&&/&&&&7/g;
|
||||
s/1000&&&&/&&&&8/g;
|
||||
s/1001&&&&/&&&&9/g;
|
||||
s/1010&&&&/&&&&A/g;
|
||||
s/1011&&&&/&&&&B/g;
|
||||
s/1100&&&&/&&&&C/g;
|
||||
s/1101&&&&/&&&&D/g;
|
||||
s/1110&&&&/&&&&E/g;
|
||||
s/1111&&&&/&&&&F/g;
|
||||
}
|
||||
s/000&&&&/&&&&0/g;
|
||||
s/001&&&&/&&&&1/g;
|
||||
s/010&&&&/&&&&2/g;
|
||||
s/011&&&&/&&&&3/g;
|
||||
s/100&&&&/&&&&4/g;
|
||||
s/101&&&&/&&&&5/g;
|
||||
s/110&&&&/&&&&6/g;
|
||||
s/111&&&&/&&&&7/g;
|
||||
s/00&&&&/&&&&0/g;
|
||||
s/01&&&&/&&&&1/g;
|
||||
s/10&&&&/&&&&2/g;
|
||||
s/11&&&&/&&&&3/g;
|
||||
s/0&&&&/&&&&0/g;
|
||||
s/1&&&&/&&&&1/g;
|
||||
s/conv&&&&/0x/g;
|
||||
}
|
||||
|
||||
if ( /commandline/)
|
||||
{
|
||||
if( /-bigend/)
|
||||
{
|
||||
$bigend=1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( /\bDCDU\b/ )
|
||||
{
|
||||
my $cmd=$_;
|
||||
my $value;
|
||||
my $prefix;
|
||||
my $w1;
|
||||
my $w2;
|
||||
my $w3;
|
||||
my $w4;
|
||||
|
||||
s/\s+DCDU\b/@ $&/;
|
||||
|
||||
$cmd =~ /\bDCDU\b\s+0x(\d+)/;
|
||||
$value = $1;
|
||||
$value =~ /(\w\w)(\w\w)(\w\w)(\w\w)/;
|
||||
$w1 = $1;
|
||||
$w2 = $2;
|
||||
$w3 = $3;
|
||||
$w4 = $4;
|
||||
|
||||
if( $bigend ne "")
|
||||
{
|
||||
# big endian
|
||||
$prefix = "\t.byte\t0x".$w1.";".
|
||||
"\t.byte\t0x".$w2.";".
|
||||
"\t.byte\t0x".$w3.";".
|
||||
"\t.byte\t0x".$w4."; ";
|
||||
}
|
||||
else
|
||||
{
|
||||
# little endian
|
||||
$prefix = "\t.byte\t0x".$w4.";".
|
||||
"\t.byte\t0x".$w3.";".
|
||||
"\t.byte\t0x".$w2.";".
|
||||
"\t.byte\t0x".$w1."; ";
|
||||
}
|
||||
$_=$prefix.$_;
|
||||
}
|
||||
|
||||
if ( /\badrl\b/i )
|
||||
{
|
||||
s/\badrl\s+(\w+)\s*,\s*(\w+)/ldr $1,=$2/i;
|
||||
$addPadding = 1;
|
||||
}
|
||||
s/\bEND\b/@ END/;
|
||||
} continue {
|
||||
printf ("%s", $_) if $printit;
|
||||
if ($addPadding != 0)
|
||||
{
|
||||
printf (" mov r0,r0\n");
|
||||
$addPadding = 0;
|
||||
}
|
||||
}
|
||||
#If we had a code section, mark that this object doesn't need an executable
|
||||
# stack.
|
||||
if ($nxstack) {
|
||||
printf (" .section\t.note.GNU-stack,\"\",\%\%progbits\n");
|
||||
}
|
37
media/libopus/celt/arm/armopts.s
Normal file
37
media/libopus/celt/arm/armopts.s
Normal file
@ -0,0 +1,37 @@
|
||||
/* Copyright (C) 2013 Mozilla Corporation */
|
||||
/*
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
; Set the following to 1 if we have EDSP instructions
|
||||
; (LDRD/STRD, etc., ARMv5E and later).
|
||||
OPUS_ARM_MAY_HAVE_EDSP * 1
|
||||
|
||||
; Set the following to 1 if we have ARMv6 media instructions.
|
||||
OPUS_ARM_MAY_HAVE_MEDIA * 1
|
||||
|
||||
; Set the following to 1 if we have NEON (some ARMv7)
|
||||
OPUS_ARM_MAY_HAVE_NEON * 1
|
||||
|
||||
END
|
@ -19,6 +19,15 @@ DEFINES['OPUS_BUILD'] = True
|
||||
DEFINES['OPUS_VERSION'] = '"v1.1-mozilla"'
|
||||
DEFINES['USE_ALLOCA'] = True
|
||||
|
||||
if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_AS']:
|
||||
DEFINES['OPUS_ARM_ASM'] = True
|
||||
DEFINES['OPUS_ARM_EXTERNAL_ASM'] = True
|
||||
DEFINES['OPUS_ARM_INLINE_ASM'] = True
|
||||
DEFINES['OPUS_ARM_INLINE_EDSP'] = True
|
||||
DEFINES['OPUS_ARM_MAY_HAVE_EDSP'] = True
|
||||
DEFINES['OPUS_ARM_MAY_HAVE_MEDIA'] = True
|
||||
DEFINES['OPUS_ARM_MAY_HAVE_NEON'] = True
|
||||
|
||||
if CONFIG['MOZ_DEBUG']:
|
||||
DEFINES['ENABLE_ASSERTIONS'] = True
|
||||
|
||||
@ -68,6 +77,20 @@ else:
|
||||
]
|
||||
SOURCES += silk_sources_fixed
|
||||
|
||||
if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_AS']:
|
||||
SOURCES += celt_sources_arm
|
||||
GENERATED_SOURCES += [ '%s.%s' % (f, CONFIG['ASM_SUFFIX']) for f in [
|
||||
'celt_pitch_xcorr_arm-gnu',
|
||||
]]
|
||||
# -Os is significantly slower, enable -O3 unless optimization is disabled
|
||||
if CONFIG['MOZ_OPTIMIZE']:
|
||||
CFLAGS += [
|
||||
'-O3',
|
||||
]
|
||||
CXXFLAGS += [
|
||||
'-O3',
|
||||
]
|
||||
|
||||
# Suppress warnings in third-party code.
|
||||
if CONFIG['GNU_CC']:
|
||||
CFLAGS += ['-Wno-declaration-after-statement']
|
||||
|
8
media/libopus/update.sh
Normal file → Executable file
8
media/libopus/update.sh
Normal file → Executable file
@ -11,7 +11,7 @@
|
||||
|
||||
TARGET='.'
|
||||
|
||||
STATIC_FILES="COPYING"
|
||||
STATIC_FILES="COPYING celt/arm/arm2gnu.pl"
|
||||
MK_FILES="opus_sources.mk celt_sources.mk silk_sources.mk \
|
||||
opus_headers.mk celt_headers.mk silk_headers.mk"
|
||||
|
||||
@ -49,6 +49,12 @@ for file in ${STATIC_FILES} ${SRC_FILES} ${HDR_FILES}; do
|
||||
${cmd}
|
||||
done
|
||||
|
||||
sed \
|
||||
-e s/@OPUS_ARM_MAY_HAVE_EDSP@/1/g \
|
||||
-e s/@OPUS_ARM_MAY_HAVE_MEDIA@/1/g \
|
||||
-e s/@OPUS_ARM_MAY_HAVE_NEON@/1/g \
|
||||
$1/celt/arm/armopts.s.in > ${TARGET}/celt/arm/armopts.s
|
||||
|
||||
# query git for the revision we're copying from
|
||||
if test -d $1/.git; then
|
||||
version=$(cd $1 && git describe --tags --match 'v*' --dirty)
|
||||
|
66
mfbt/BinarySearch.h
Normal file
66
mfbt/BinarySearch.h
Normal file
@ -0,0 +1,66 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_BinarySearch_h
|
||||
#define mozilla_BinarySearch_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* The algorithm searches the given container 'c' over the sorted index range
|
||||
* [begin, end) for an index 'i' where 'c[i] == target'. If such an index 'i' is
|
||||
* found, BinarySearch returns 'true' and the index is returned via the outparam
|
||||
* 'matchOrInsertionPoint'. If no index is found, BinarySearch returns 'false'
|
||||
* and the outparam returns the first index in [begin, end] where 'target' can
|
||||
* be inserted to maintain sorted order.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Vector<int> sortedInts = ...
|
||||
*
|
||||
* size_t match;
|
||||
* if (BinarySearch(sortedInts, 0, sortedInts.length(), 13, &match))
|
||||
* printf("found 13 at %lu\n", match);
|
||||
*/
|
||||
|
||||
template <typename Container, typename T>
|
||||
bool
|
||||
BinarySearch(const Container &c, size_t begin, size_t end, T target, size_t *matchOrInsertionPoint)
|
||||
{
|
||||
MOZ_ASSERT(begin <= end);
|
||||
|
||||
size_t low = begin;
|
||||
size_t high = end;
|
||||
while (low != high) {
|
||||
size_t middle = low + (high - low) / 2;
|
||||
const T &middleValue = c[middle];
|
||||
|
||||
MOZ_ASSERT(c[low] <= c[middle]);
|
||||
MOZ_ASSERT(c[middle] <= c[high - 1]);
|
||||
MOZ_ASSERT(c[low] <= c[high - 1]);
|
||||
|
||||
if (target == middleValue) {
|
||||
*matchOrInsertionPoint = middle;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target < middleValue)
|
||||
high = middle;
|
||||
else
|
||||
low = middle + 1;
|
||||
}
|
||||
|
||||
*matchOrInsertionPoint = low;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_BinarySearch_h
|
@ -16,6 +16,7 @@ EXPORTS.mozilla = [
|
||||
'Assertions.h',
|
||||
'Atomics.h',
|
||||
'Attributes.h',
|
||||
'BinarySearch.h',
|
||||
'BloomFilter.h',
|
||||
'Casting.h',
|
||||
'ChaosMode.h',
|
||||
|
75
mfbt/tests/TestBinarySearch.cpp
Normal file
75
mfbt/tests/TestBinarySearch.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/BinarySearch.h"
|
||||
#include "mozilla/Vector.h"
|
||||
|
||||
using mozilla::Vector;
|
||||
using mozilla::BinarySearch;
|
||||
|
||||
struct Person
|
||||
{
|
||||
int age;
|
||||
int id;
|
||||
Person(int age, int id) : age(age), id(id) {}
|
||||
};
|
||||
|
||||
struct GetAge
|
||||
{
|
||||
Vector<Person> &v;
|
||||
GetAge(Vector<Person> &v) : v(v) {}
|
||||
int operator[](size_t index) const { return v[index].age; }
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t m;
|
||||
|
||||
Vector<int> v1;
|
||||
v1.append(2);
|
||||
v1.append(4);
|
||||
v1.append(6);
|
||||
v1.append(8);
|
||||
|
||||
MOZ_ASSERT(!BinarySearch(v1, 0, v1.length(), 1, &m) && m == 0);
|
||||
MOZ_ASSERT( BinarySearch(v1, 0, v1.length(), 2, &m) && m == 0);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 0, v1.length(), 3, &m) && m == 1);
|
||||
MOZ_ASSERT( BinarySearch(v1, 0, v1.length(), 4, &m) && m == 1);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 0, v1.length(), 5, &m) && m == 2);
|
||||
MOZ_ASSERT( BinarySearch(v1, 0, v1.length(), 6, &m) && m == 2);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 0, v1.length(), 7, &m) && m == 3);
|
||||
MOZ_ASSERT( BinarySearch(v1, 0, v1.length(), 8, &m) && m == 3);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 0, v1.length(), 9, &m) && m == 4);
|
||||
|
||||
MOZ_ASSERT(!BinarySearch(v1, 1, 3, 1, &m) && m == 1);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 1, 3, 2, &m) && m == 1);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 1, 3, 3, &m) && m == 1);
|
||||
MOZ_ASSERT( BinarySearch(v1, 1, 3, 4, &m) && m == 1);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 1, 3, 5, &m) && m == 2);
|
||||
MOZ_ASSERT( BinarySearch(v1, 1, 3, 6, &m) && m == 2);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 1, 3, 7, &m) && m == 3);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 1, 3, 8, &m) && m == 3);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 1, 3, 9, &m) && m == 3);
|
||||
|
||||
MOZ_ASSERT(!BinarySearch(v1, 0, 0, 0, &m) && m == 0);
|
||||
MOZ_ASSERT(!BinarySearch(v1, 0, 0, 9, &m) && m == 0);
|
||||
|
||||
Vector<int> v2;
|
||||
MOZ_ASSERT(!BinarySearch(v2, 0, 0, 0, &m) && m == 0);
|
||||
MOZ_ASSERT(!BinarySearch(v2, 0, 0, 9, &m) && m == 0);
|
||||
|
||||
Vector<Person> v3;
|
||||
v3.append(Person(2, 42));
|
||||
v3.append(Person(4, 13));
|
||||
v3.append(Person(6, 360));
|
||||
MOZ_ASSERT(!BinarySearch(GetAge(v3), 0, v3.length(), 1, &m) && m == 0);
|
||||
MOZ_ASSERT( BinarySearch(GetAge(v3), 0, v3.length(), 2, &m) && m == 0);
|
||||
MOZ_ASSERT(!BinarySearch(GetAge(v3), 0, v3.length(), 3, &m) && m == 1);
|
||||
MOZ_ASSERT( BinarySearch(GetAge(v3), 0, v3.length(), 4, &m) && m == 1);
|
||||
MOZ_ASSERT(!BinarySearch(GetAge(v3), 0, v3.length(), 5, &m) && m == 2);
|
||||
MOZ_ASSERT( BinarySearch(GetAge(v3), 0, v3.length(), 6, &m) && m == 2);
|
||||
MOZ_ASSERT(!BinarySearch(GetAge(v3), 0, v3.length(), 7, &m) && m == 3);
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
|
||||
CPP_UNIT_TESTS += [
|
||||
'TestAtomics.cpp',
|
||||
'TestBinarySearch.cpp',
|
||||
'TestBloomFilter.cpp',
|
||||
'TestCasting.cpp',
|
||||
'TestCeilingFloor.cpp',
|
||||
|
@ -109,11 +109,13 @@ class PinSiteDialog extends DialogFragment {
|
||||
|
||||
// If the user manually entered a search term or URL, wrap the value in
|
||||
// a special URI until we can get a valid URL for this bookmark.
|
||||
final String text = mSearch.getText().toString();
|
||||
final String url = TopSitesPanel.encodeUserEnteredUrl(text);
|
||||
mOnSiteSelectedListener.onSiteSelected(url, text);
|
||||
final String text = mSearch.getText().toString().trim();
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
final String url = TopSitesPanel.encodeUserEnteredUrl(text);
|
||||
mOnSiteSelectedListener.onSiteSelected(url, text);
|
||||
dismiss();
|
||||
}
|
||||
|
||||
dismiss();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
@ -107,6 +107,13 @@ public class TopSitesGridItemView extends RelativeLayout {
|
||||
return mIsPinned;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true, if this view has no content to show.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return mIsEmpty;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param title The title for this view.
|
||||
*/
|
||||
|
@ -125,7 +125,8 @@ public class TopSitesGridView extends GridView {
|
||||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Cursor cursor = (Cursor) parent.getItemAtPosition(position);
|
||||
|
||||
if (cursor == null) {
|
||||
TopSitesGridItemView gridView = (TopSitesGridItemView) view;
|
||||
if (cursor == null || gridView.isEmpty()) {
|
||||
mContextMenuInfo = null;
|
||||
return false;
|
||||
}
|
||||
|
@ -92,6 +92,7 @@ nsFtpState::nsFtpState()
|
||||
, mPort(21)
|
||||
, mAddressChecked(false)
|
||||
, mServerIsIPv6(false)
|
||||
, mUseUTF8(false)
|
||||
, mControlStatus(NS_OK)
|
||||
, mDeferredCallbackPending(false)
|
||||
{
|
||||
@ -276,7 +277,12 @@ nsFtpState::EstablishControlConnection()
|
||||
mServerType = mControlConnection->mServerType;
|
||||
mPassword = mControlConnection->mPassword;
|
||||
mPwd = mControlConnection->mPwd;
|
||||
mUseUTF8 = mControlConnection->mUseUTF8;
|
||||
mTryingCachedControl = true;
|
||||
|
||||
// we have to set charset to connection if server supports utf-8
|
||||
if (mUseUTF8)
|
||||
mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
|
||||
|
||||
// we're already connected to this server, skip login.
|
||||
mState = FTP_S_PASV;
|
||||
@ -646,7 +652,43 @@ nsFtpState::Process()
|
||||
mInternalError = NS_ERROR_FTP_PWD;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
// FEAT for RFC2640 support
|
||||
case FTP_S_FEAT:
|
||||
rv = S_feat();
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
mInternalError = rv;
|
||||
|
||||
MoveToNextState(FTP_R_FEAT);
|
||||
break;
|
||||
|
||||
case FTP_R_FEAT:
|
||||
mState = R_feat();
|
||||
|
||||
// Don't want to overwrite a more explicit status code
|
||||
if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
|
||||
mInternalError = NS_ERROR_FAILURE;
|
||||
break;
|
||||
|
||||
// OPTS for some non-RFC2640-compliant servers support
|
||||
case FTP_S_OPTS:
|
||||
rv = S_opts();
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
mInternalError = rv;
|
||||
|
||||
MoveToNextState(FTP_R_OPTS);
|
||||
break;
|
||||
|
||||
case FTP_R_OPTS:
|
||||
mState = R_opts();
|
||||
|
||||
// Don't want to overwrite a more explicit status code
|
||||
if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
|
||||
mInternalError = NS_ERROR_FAILURE;
|
||||
break;
|
||||
|
||||
default:
|
||||
;
|
||||
|
||||
@ -923,7 +965,7 @@ nsFtpState::R_syst() {
|
||||
return FTP_ERROR;
|
||||
}
|
||||
|
||||
return FTP_S_PWD;
|
||||
return FTP_S_FEAT;
|
||||
}
|
||||
|
||||
if (mResponseCode/100 == 5) {
|
||||
@ -931,7 +973,7 @@ nsFtpState::R_syst() {
|
||||
// No clue. We will just hope it is UNIX type server.
|
||||
mServerType = FTP_UNIX_TYPE;
|
||||
|
||||
return FTP_S_PWD;
|
||||
return FTP_S_FEAT;
|
||||
}
|
||||
return FTP_ERROR;
|
||||
}
|
||||
@ -1135,6 +1177,10 @@ nsFtpState::S_list() {
|
||||
serverType.AppendInt(mServerType);
|
||||
mCacheEntry->SetMetaDataElement("servertype", serverType.get());
|
||||
|
||||
nsAutoCString useUTF8;
|
||||
useUTF8.AppendInt(mUseUTF8);
|
||||
mCacheEntry->SetMetaDataElement("useUTF8", useUTF8.get());
|
||||
|
||||
// open cache entry for writing, and configure it to receive data.
|
||||
if (NS_FAILED(InstallCacheListener())) {
|
||||
mCacheEntry->AsyncDoom(nullptr);
|
||||
@ -1551,6 +1597,39 @@ nsFtpState::R_pasv() {
|
||||
return FTP_S_SIZE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFtpState::S_feat() {
|
||||
return SendFTPCommand(NS_LITERAL_CSTRING("FEAT" CRLF));
|
||||
}
|
||||
|
||||
FTP_STATE
|
||||
nsFtpState::R_feat() {
|
||||
if (mResponseCode/100 == 2) {
|
||||
if (mResponseMsg.Find(NS_LITERAL_CSTRING(CRLF " UTF8" CRLF), true) > -1) {
|
||||
// This FTP server supports UTF-8 encoding
|
||||
mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
|
||||
mUseUTF8 = true;
|
||||
return FTP_S_OPTS;
|
||||
}
|
||||
}
|
||||
|
||||
mUseUTF8 = false;
|
||||
return FTP_S_PWD;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFtpState::S_opts() {
|
||||
// This command is for compatibility of old FTP spec (IETF Draft)
|
||||
return SendFTPCommand(NS_LITERAL_CSTRING("OPTS UTF8 ON" CRLF));
|
||||
}
|
||||
|
||||
FTP_STATE
|
||||
nsFtpState::R_opts() {
|
||||
// Ignore error code because "OPTS UTF8 ON" is for compatibility of
|
||||
// FTP server using IETF draft
|
||||
return FTP_S_PWD;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsIRequest methods:
|
||||
|
||||
@ -1847,6 +1926,7 @@ nsFtpState::KillControlConnection()
|
||||
mControlConnection->mServerType = mServerType;
|
||||
mControlConnection->mPassword = mPassword;
|
||||
mControlConnection->mPwd = mPwd;
|
||||
mControlConnection->mUseUTF8 = mUseUTF8;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
// Don't cache controlconnection if anonymous (bug #473371)
|
||||
@ -1865,7 +1945,7 @@ nsFtpState::KillControlConnection()
|
||||
class nsFtpAsyncAlert : public nsRunnable
|
||||
{
|
||||
public:
|
||||
nsFtpAsyncAlert(nsIPrompt *aPrompter, nsACString& aResponseMsg)
|
||||
nsFtpAsyncAlert(nsIPrompt *aPrompter, nsString aResponseMsg)
|
||||
: mPrompter(aPrompter)
|
||||
, mResponseMsg(aResponseMsg)
|
||||
{
|
||||
@ -1878,15 +1958,15 @@ public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mPrompter) {
|
||||
mPrompter->Alert(nullptr, NS_ConvertASCIItoUTF16(mResponseMsg).get());
|
||||
mPrompter->Alert(nullptr, mResponseMsg.get());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsCOMPtr<nsIPrompt> mPrompter;
|
||||
nsCString mResponseMsg;
|
||||
nsString mResponseMsg;
|
||||
};
|
||||
|
||||
|
||||
|
||||
nsresult
|
||||
nsFtpState::StopProcessing()
|
||||
@ -1910,8 +1990,14 @@ nsFtpState::StopProcessing()
|
||||
nsCOMPtr<nsIPrompt> prompter;
|
||||
mChannel->GetCallback(prompter);
|
||||
if (prompter) {
|
||||
nsCOMPtr<nsIRunnable> alertEvent =
|
||||
new nsFtpAsyncAlert(prompter, mResponseMsg);
|
||||
nsCOMPtr<nsIRunnable> alertEvent;
|
||||
if (mUseUTF8) {
|
||||
alertEvent = new nsFtpAsyncAlert(prompter,
|
||||
NS_ConvertUTF8toUTF16(mResponseMsg));
|
||||
} else {
|
||||
alertEvent = new nsFtpAsyncAlert(prompter,
|
||||
NS_ConvertASCIItoUTF16(mResponseMsg));
|
||||
}
|
||||
NS_DispatchToMainThread(alertEvent, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
}
|
||||
@ -2372,6 +2458,12 @@ nsFtpState::ReadCacheEntry()
|
||||
nsAutoCString serverNum(serverType.get());
|
||||
nsresult err;
|
||||
mServerType = serverNum.ToInteger(&err);
|
||||
|
||||
nsXPIDLCString charset;
|
||||
mCacheEntry->GetMetaDataElement("useUTF8", getter_Copies(charset));
|
||||
const char *useUTF8 = charset.get();
|
||||
if (useUTF8 && atoi(useUTF8) == 1)
|
||||
mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
|
||||
|
||||
mChannel->PushStreamConverter("text/ftp-dir",
|
||||
APPLICATION_HTTP_INDEX_FORMAT);
|
||||
|
@ -57,7 +57,9 @@ typedef enum _FTP_STATE {
|
||||
FTP_S_STOR, FTP_R_STOR,
|
||||
FTP_S_LIST, FTP_R_LIST,
|
||||
FTP_S_PASV, FTP_R_PASV,
|
||||
FTP_S_PWD, FTP_R_PWD
|
||||
FTP_S_PWD, FTP_R_PWD,
|
||||
FTP_S_FEAT, FTP_R_FEAT,
|
||||
FTP_S_OPTS, FTP_R_OPTS
|
||||
} FTP_STATE;
|
||||
|
||||
// higher level ftp actions
|
||||
@ -129,6 +131,8 @@ private:
|
||||
nsresult S_stor(); FTP_STATE R_stor();
|
||||
nsresult S_pasv(); FTP_STATE R_pasv();
|
||||
nsresult S_pwd(); FTP_STATE R_pwd();
|
||||
nsresult S_feat(); FTP_STATE R_feat();
|
||||
nsresult S_opts(); FTP_STATE R_opts();
|
||||
// END: STATE METHODS
|
||||
///////////////////////////////////
|
||||
|
||||
@ -243,6 +247,7 @@ private:
|
||||
nsCOMPtr<nsIRequest> mUploadRequest;
|
||||
bool mAddressChecked;
|
||||
bool mServerIsIPv6;
|
||||
bool mUseUTF8;
|
||||
|
||||
static uint32_t mSessionStartTime;
|
||||
|
||||
|
@ -65,8 +65,8 @@ nsFtpControlConnection::OnInputStreamReady(nsIAsyncInputStream *stream)
|
||||
|
||||
nsFtpControlConnection::nsFtpControlConnection(const nsCSubstring& host,
|
||||
uint32_t port)
|
||||
: mServerType(0), mSessionId(gFtpHandler->GetSessionId()), mHost(host)
|
||||
, mPort(port)
|
||||
: mServerType(0), mSessionId(gFtpHandler->GetSessionId())
|
||||
, mUseUTF8(false), mHost(host), mPort(port)
|
||||
{
|
||||
LOG_ALWAYS(("FTP:CC created @%p", this));
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ public:
|
||||
int32_t mSuspendedWrite;
|
||||
nsCString mPwd;
|
||||
uint32_t mSessionId;
|
||||
bool mUseUTF8;
|
||||
|
||||
private:
|
||||
nsCString mHost;
|
||||
|
@ -46,7 +46,11 @@ CryptoTask::Run()
|
||||
if (mThread) {
|
||||
// Don't leak threads!
|
||||
mThread->Shutdown(); // can't Shutdown from the thread itself, darn
|
||||
mThread = nullptr;
|
||||
// Don't null out mThread!
|
||||
// See bug 999104. We must hold a ref to the thread across Dispatch()
|
||||
// since the internal mThread ref could be released while processing
|
||||
// the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
|
||||
// assumes the caller does.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ public:
|
||||
// Can't add 'this' as the event to run, since mThread may not be set yet
|
||||
nsresult rv = NS_NewNamedThread(taskThreadName, getter_AddRefs(mThread));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Note: event must not null out mThread!
|
||||
rv = mThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
return rv;
|
||||
|
@ -190,7 +190,7 @@ class MochitestRunner(MozbuildObject):
|
||||
jsdebugger=False, debug_on_failure=False, start_at=None, end_at=None,
|
||||
e10s=False, dmd=False, dump_output_directory=None,
|
||||
dump_about_memory_after_test=False, dump_dmd_after_test=False,
|
||||
install_extension=None, quiet=False, **kwargs):
|
||||
install_extension=None, quiet=False, environment=[], **kwargs):
|
||||
"""Runs a mochitest.
|
||||
|
||||
test_paths are path to tests. They can be a relative path from the
|
||||
@ -314,6 +314,7 @@ class MochitestRunner(MozbuildObject):
|
||||
options.dumpDMDAfterTest = dump_dmd_after_test
|
||||
options.dumpOutputDirectory = dump_output_directory
|
||||
options.quiet = quiet
|
||||
options.environment = environment
|
||||
|
||||
options.failureFile = failure_file_path
|
||||
if install_extension != None:
|
||||
@ -515,6 +516,11 @@ def MochitestCommand(func):
|
||||
help='Do not print test log lines unless a failure occurs.')
|
||||
func = quiet(func)
|
||||
|
||||
setenv = CommandArgument('--setenv', default=[], action='append',
|
||||
metavar='NAME=VALUE', dest='environment',
|
||||
help="Sets the given variable in the application's environment")
|
||||
func = setenv(func)
|
||||
|
||||
return func
|
||||
|
||||
def B2GCommand(func):
|
||||
|
@ -304,43 +304,56 @@ class Permissions(object):
|
||||
proxy.update(user_proxy)
|
||||
|
||||
# TODO: this should live in a template!
|
||||
# TODO: So changing the 5th line of the regex below from (\\\\\\\\d+)
|
||||
# to (\\\\d+) makes this code work. Not sure why there would be this
|
||||
# difference between automation.py.in and this file.
|
||||
# If you must escape things in this string with backslashes, be aware
|
||||
# of the multiple layers of escaping at work:
|
||||
#
|
||||
# - Python will unescape backslashes;
|
||||
# - Writing out the prefs will escape things via JSON serialization;
|
||||
# - The prefs file reader will unescape backslashes;
|
||||
# - The JS engine parser will unescape backslashes.
|
||||
pacURL = """data:text/plain,
|
||||
var knownOrigins = (function () {
|
||||
return [%(origins)s].reduce(function(t, h) { t[h] = true; return t; }, {})
|
||||
})();
|
||||
var uriRegex = new RegExp('^([a-z][-a-z0-9+.]*)' +
|
||||
'://' +
|
||||
'(?:[^/@]*@)?' +
|
||||
'(.*?)' +
|
||||
'(?::(\\\\d+))?/');
|
||||
var defaultPortsForScheme = {
|
||||
'http': 80,
|
||||
'ws': 80,
|
||||
'https': 443,
|
||||
'wss': 443
|
||||
};
|
||||
var originSchemesRemap = {
|
||||
'ws': 'http',
|
||||
'wss': 'https'
|
||||
};
|
||||
var proxyForScheme = {
|
||||
'http': 'PROXY %(remote)s:%(http)s',
|
||||
'https': 'PROXY %(remote)s:%(https)s',
|
||||
'ws': 'PROXY %(remote)s:%(ws)s',
|
||||
'wss': 'PROXY %(remote)s:%(wss)s'
|
||||
};
|
||||
|
||||
function FindProxyForURL(url, host)
|
||||
{
|
||||
var origins = [%(origins)s];
|
||||
var regex = new RegExp('^([a-z][-a-z0-9+.]*)' +
|
||||
'://' +
|
||||
'(?:[^/@]*@)?' +
|
||||
'(.*?)' +
|
||||
'(?::(\\\\d+))?/');
|
||||
var matches = regex.exec(url);
|
||||
var matches = uriRegex.exec(url);
|
||||
if (!matches)
|
||||
return 'DIRECT';
|
||||
var isHttp = matches[1] == 'http';
|
||||
var isHttps = matches[1] == 'https';
|
||||
var isWebSocket = matches[1] == 'ws';
|
||||
var isWebSocketSSL = matches[1] == 'wss';
|
||||
if (!matches[3])
|
||||
{
|
||||
if (isHttp | isWebSocket) matches[3] = '80';
|
||||
if (isHttps | isWebSocketSSL) matches[3] = '443';
|
||||
var originalScheme = matches[1];
|
||||
var host = matches[2];
|
||||
var port = matches[3];
|
||||
if (!port && originalScheme in defaultPortsForScheme) {
|
||||
port = defaultPortsForScheme[originalScheme];
|
||||
}
|
||||
if (isWebSocket)
|
||||
matches[1] = 'http';
|
||||
if (isWebSocketSSL)
|
||||
matches[1] = 'https';
|
||||
var schemeForOriginChecking = originSchemesRemap[originalScheme] || originalScheme;
|
||||
|
||||
var origin = matches[1] + '://' + matches[2] + ':' + matches[3];
|
||||
if (origins.indexOf(origin) < 0)
|
||||
var origin = schemeForOriginChecking + '://' + host + ':' + port;
|
||||
if (!(origin in knownOrigins))
|
||||
return 'DIRECT';
|
||||
if (isHttp) return 'PROXY %(remote)s:%(http)s';
|
||||
if (isHttps) return 'PROXY %(remote)s:%(https)s';
|
||||
if (isWebSocket) return 'PROXY %(remote)s:%(ws)s';
|
||||
if (isWebSocketSSL) return 'PROXY %(remote)s:%(wss)s';
|
||||
return 'DIRECT';
|
||||
return proxyForScheme[originalScheme] || 'DIRECT';
|
||||
}""" % proxy
|
||||
pacURL = "".join(pacURL.splitlines())
|
||||
|
||||
|
10
testing/mozbase/mozprofile/tests/permissions.py
Normal file → Executable file
10
testing/mozbase/mozprofile/tests/permissions.py
Normal file → Executable file
@ -123,13 +123,13 @@ http://127.0.0.1:8888 privileged
|
||||
self.assertEqual(user_prefs[0], ('network.proxy.type', 2))
|
||||
self.assertEqual(user_prefs[1][0], 'network.proxy.autoconfig_url')
|
||||
|
||||
origins_decl = "var origins = ['http://mochi.test:8888', 'http://127.0.0.1:80', 'http://127.0.0.1:8888'];"
|
||||
origins_decl = "var knownOrigins = (function () { return ['http://mochi.test:8888', 'http://127.0.0.1:80', 'http://127.0.0.1:8888'].reduce"
|
||||
self.assertTrue(origins_decl in user_prefs[1][1])
|
||||
|
||||
proxy_check = ("if (isHttp) return 'PROXY mochi.test:8888';",
|
||||
"if (isHttps) return 'PROXY mochi.test:4443';",
|
||||
"if (isWebSocket) return 'PROXY mochi.test:4443';",
|
||||
"if (isWebSocketSSL) return 'PROXY mochi.test:4443';")
|
||||
proxy_check = ("'http': 'PROXY mochi.test:8888'",
|
||||
"'https': 'PROXY mochi.test:4443'",
|
||||
"'ws': 'PROXY mochi.test:4443'",
|
||||
"'wss': 'PROXY mochi.test:4443'")
|
||||
self.assertTrue(all(c in user_prefs[1][1] for c in proxy_check))
|
||||
|
||||
def verify_user_version(self, version):
|
||||
|
@ -298,7 +298,7 @@ PendingDBLookup::HandleEvent(const nsACString& tables)
|
||||
// Blocklisting trumps allowlisting.
|
||||
nsAutoCString blockList;
|
||||
Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blockList);
|
||||
if (!mAllowlistOnly && FindInReadable(tables, blockList)) {
|
||||
if (!mAllowlistOnly && FindInReadable(blockList, tables)) {
|
||||
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST);
|
||||
LOG(("Found principal %s on blocklist [this = %p]", mSpec.get(), this));
|
||||
return mPendingLookup->OnComplete(true, NS_OK);
|
||||
@ -306,7 +306,7 @@ PendingDBLookup::HandleEvent(const nsACString& tables)
|
||||
|
||||
nsAutoCString allowList;
|
||||
Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowList);
|
||||
if (FindInReadable(tables, allowList)) {
|
||||
if (FindInReadable(allowList, tables)) {
|
||||
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST);
|
||||
LOG(("Found principal %s on allowlist [this = %p]", mSpec.get(), this));
|
||||
return mPendingLookup->OnComplete(false, NS_OK);
|
||||
|
@ -463,8 +463,9 @@ nsThread::DispatchInternal(nsIRunnable *event, uint32_t flags,
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Allows waiting; ensure no locks are held that would deadlock us!
|
||||
while (wrapper->IsPending())
|
||||
NS_ProcessNextEvent(thread);
|
||||
NS_ProcessNextEvent(thread, true);
|
||||
return wrapper->Result();
|
||||
}
|
||||
|
||||
@ -539,8 +540,9 @@ nsThread::Shutdown()
|
||||
// after setting mShutdownContext just before exiting.
|
||||
|
||||
// Process events on the current thread until we receive a shutdown ACK.
|
||||
// Allows waiting; ensure no locks are held that would deadlock us!
|
||||
while (!context.shutdownAck)
|
||||
NS_ProcessNextEvent(context.joiningThread);
|
||||
NS_ProcessNextEvent(context.joiningThread, true);
|
||||
|
||||
// Now, it should be safe to join without fear of dead-locking.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user