author | Phil Ringnalda <philringnalda@gmail.com> |
Sun, 02 Oct 2016 22:32:54 -0700 | |
changeset 316154 | 955840bfd3c20eb24dd5a01be27bdc55c489a285 |
parent 316144 | d856f453dcd6c10aa5a0724824cc3b9b685db419 (current diff) |
parent 316153 | d2193462357aa9c9ac3acf6e01501f0dcc3cb749 (diff) |
child 316159 | b65e4cc8e326a0cfaf51cafe7e99893bc2d6e929 |
child 316212 | f30756ff80e65180522ef15fbbabc6caf90f29b4 |
push id | 30764 |
push user | philringnalda@gmail.com |
push date | Mon, 03 Oct 2016 05:33:02 +0000 |
treeherder | mozilla-central@955840bfd3c2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 52.0a1 |
first release with | nightly linux32
955840bfd3c2
/
52.0a1
/
20161003030438
/
files
nightly linux64
955840bfd3c2
/
52.0a1
/
20161003030438
/
files
nightly mac
955840bfd3c2
/
52.0a1
/
20161003030438
/
files
nightly win32
955840bfd3c2
/
52.0a1
/
20161003030438
/
files
nightly win64
955840bfd3c2
/
52.0a1
/
20161003030438
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
52.0a1
/
20161003030438
/
pushlog to previous
nightly linux64
52.0a1
/
20161003030438
/
pushlog to previous
nightly mac
52.0a1
/
20161003030438
/
pushlog to previous
nightly win32
52.0a1
/
20161003030438
/
pushlog to previous
nightly win64
52.0a1
/
20161003030438
/
pushlog to previous
|
--- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -1442,45 +1442,66 @@ MediaStreamGraphImpl::ApplyStreamUpdate( } } void MediaStreamGraphImpl::ForceShutDown(ShutdownTicket* aShutdownTicket) { NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread"); STREAM_LOG(LogLevel::Debug, ("MediaStreamGraph %p ForceShutdown", this)); - { - MonitorAutoLock lock(mMonitor); - mForceShutDown = true; - mForceShutdownTicket = aShutdownTicket; - if (mLifecycleState == LIFECYCLE_THREAD_NOT_STARTED) { - // We *could* have just sent this a message to start up, so don't - // yank the rug out from under it. Tell it to startup and let it - // shut down. - RefPtr<GraphDriver> driver = CurrentDriver(); - MonitorAutoUnlock unlock(mMonitor); - driver->Start(); + + MonitorAutoLock lock(mMonitor); + if (aShutdownTicket) { + MOZ_ASSERT(!mForceShutdownTicket); + // Avoid waiting forever for a graph to shut down + // synchronously. Reports are that some 3rd-party audio drivers + // occasionally hang in shutdown (both for us and Chrome). + mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + if (!mShutdownTimer) { + return; } - EnsureNextIterationLocked(); + mShutdownTimer->InitWithCallback(this, + MediaStreamGraph::AUDIO_CALLBACK_DRIVER_SHUTDOWN_TIMEOUT, + nsITimer::TYPE_ONE_SHOT); + } + mForceShutDown = true; + mForceShutdownTicket = aShutdownTicket; + if (mLifecycleState == LIFECYCLE_THREAD_NOT_STARTED) { + // We *could* have just sent this a message to start up, so don't + // yank the rug out from under it. Tell it to startup and let it + // shut down. + RefPtr<GraphDriver> driver = CurrentDriver(); + MonitorAutoUnlock unlock(mMonitor); + driver->Start(); } + EnsureNextIterationLocked(); } +NS_IMETHODIMP +MediaStreamGraphImpl::Notify(nsITimer* aTimer) +{ + MonitorAutoLock lock(mMonitor); + NS_ASSERTION(!mForceShutdownTicket, "MediaStreamGraph took too long to shut down!"); + // Sigh, graph took too long to shut down. Stop blocking system + // shutdown and hope all is well. + mForceShutdownTicket = nullptr; + return NS_OK; +} + + /* static */ StaticRefPtr<nsIAsyncShutdownBlocker> gMediaStreamGraphShutdownBlocker; namespace { -class MediaStreamGraphShutDownRunnable : public Runnable - , public nsITimerCallback { +class MediaStreamGraphShutDownRunnable : public Runnable { public: explicit MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl* aGraph) : mGraph(aGraph) {} - NS_DECL_ISUPPORTS_INHERITED - - NS_IMETHOD Run() override + NS_IMETHOD Run() { NS_ASSERTION(mGraph->mDetectedNotRunning, "We should know the graph thread control loop isn't running!"); LIFECYCLE_LOG("Shutting down graph %p", mGraph.get()); // We've asserted the graph isn't running. Use mDriver instead of CurrentDriver // to avoid thread-safety checks @@ -1488,43 +1509,30 @@ public: // XXX a better test would be have setting mDetectedNotRunning make sure // any current callback has finished and block future ones -- or just // handle it all in Shutdown()! if (mGraph->mDriver->AsAudioCallbackDriver()) { MOZ_ASSERT(!mGraph->mDriver->AsAudioCallbackDriver()->InCallback()); } #endif - if (mGraph->mForceShutdownTicket) { - // Avoid waiting forever for a callback driver to shut down - // synchronously. Reports are that some 3rd-party audio drivers - // occasionally hang in shutdown (both for us and Chrome). - mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); - if (!mTimer) { - return NS_ERROR_FAILURE; - } - mTimer->InitWithCallback(this, - MediaStreamGraph::AUDIO_CALLBACK_DRIVER_SHUTDOWN_TIMEOUT, - nsITimer::TYPE_ONE_SHOT); - } - mGraph->mDriver->Shutdown(); // This will wait until it's shutdown since // we'll start tearing down the graph after this + // Safe to access these without the monitor since the graph isn't running. // We may be one of several graphs. Drop ticket to eventually unblock shutdown. - if (mTimer && !mGraph->mForceShutdownTicket) { + if (mGraph->mShutdownTimer && !mGraph->mForceShutdownTicket) { MOZ_ASSERT(false, "AudioCallbackDriver took too long to shut down and we let shutdown" " continue - freezing and leaking"); // The timer fired, so we may be deeper in shutdown now. Block any further // teardown and just leak, for safety. return NS_OK; } - mTimer = nullptr; mGraph->mForceShutdownTicket = nullptr; // We can't block past the final LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION // stage, since completion of that stage requires all streams to be freed, // which requires shutdown to proceed. // mGraph's thread is not running so it's OK to do whatever here if (mGraph->IsEmpty()) { @@ -1547,40 +1555,20 @@ public: stream->GetStreamTracks().Clear(); } mGraph->mLifecycleState = MediaStreamGraphImpl::LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION; } return NS_OK; } - - NS_IMETHOD Notify(nsITimer* aTimer) override - { - // Sigh, driver took too long to shut down. Stop blocking system - // shutdown and hope all is well. Shutdown of this graph will proceed - // if the driver eventually comes back. - NS_ASSERTION(!(mGraph->mForceShutdownTicket), - "AudioCallbackDriver took too long to shut down - probably hung"); - - mGraph->mForceShutdownTicket = nullptr; - return NS_OK; - } - private: - ~MediaStreamGraphShutDownRunnable() {} - - nsCOMPtr<nsITimer> mTimer; RefPtr<MediaStreamGraphImpl> mGraph; }; -NS_IMPL_ISUPPORTS_INHERITED(MediaStreamGraphShutDownRunnable, Runnable, nsITimerCallback) - - - class MediaStreamGraphStableStateRunnable : public Runnable { public: explicit MediaStreamGraphStableStateRunnable(MediaStreamGraphImpl* aGraph, bool aSourceIsMSG) : mGraph(aGraph) , mSourceIsMSG(aSourceIsMSG) { } @@ -3439,27 +3427,25 @@ MediaStreamGraph::CreateNonRealtimeInsta void MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph) { NS_ASSERTION(NS_IsMainThread(), "Main thread only"); MOZ_ASSERT(aGraph->IsNonRealtime(), "Should not destroy the global graph here"); MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph); - if (graph->mForceShutDown) - return; // already done if (!graph->mNonRealtimeProcessing) { // Start the graph, but don't produce anything graph->StartNonRealtimeProcessing(0); } graph->ForceShutDown(nullptr); } -NS_IMPL_ISUPPORTS(MediaStreamGraphImpl, nsIMemoryReporter) +NS_IMPL_ISUPPORTS(MediaStreamGraphImpl, nsIMemoryReporter, nsITimerCallback) NS_IMETHODIMP MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) { if (mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN) { // Shutting down, nothing to report. FinishCollectReports(aHandleReport, aData, nsTArray<AudioNodeSizes>());
--- a/dom/media/MediaStreamGraphImpl.h +++ b/dom/media/MediaStreamGraphImpl.h @@ -5,16 +5,17 @@ #ifndef MOZILLA_MEDIASTREAMGRAPHIMPL_H_ #define MOZILLA_MEDIASTREAMGRAPHIMPL_H_ #include "MediaStreamGraph.h" #include "nsDataHashtable.h" +#include "nsITimer.h" #include "mozilla/Monitor.h" #include "mozilla/TimeStamp.h" #include "nsIMemoryReporter.h" #include "nsIThread.h" #include "nsIRunnable.h" #include "nsIAsyncShutdown.h" #include "Latency.h" #include "mozilla/Services.h" @@ -92,21 +93,23 @@ public: * file. It's not in the anonymous namespace because MediaStream needs to * be able to friend it. * * There can be multiple MediaStreamGraph per process: one per AudioChannel. * Additionaly, each OfflineAudioContext object creates its own MediaStreamGraph * object too. */ class MediaStreamGraphImpl : public MediaStreamGraph, - public nsIMemoryReporter + public nsIMemoryReporter, + public nsITimerCallback { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIMEMORYREPORTER + NS_DECL_NSITIMERCALLBACK /** * Use aGraphDriverRequested with SYSTEM_THREAD_DRIVER or AUDIO_THREAD_DRIVER * to create a MediaStreamGraph which provides support for real-time audio * and/or video. Set it to OFFLINE_THREAD_DRIVER in order to create a * non-realtime instance which just churns through its inputs and produces * output. Those objects currently only support audio, and are used to * implement OfflineAudioContext. They do not support MediaStream inputs. @@ -803,16 +806,19 @@ public: RefPtr<AsyncLatencyLogger> mLatencyLog; AudioMixer mMixer; #ifdef MOZ_WEBRTC RefPtr<AudioOutputObserver> mFarendObserverRef; #endif dom::AudioChannel AudioChannel() const { return mAudioChannel; } + // used to limit graph shutdown time + nsCOMPtr<nsITimer> mShutdownTimer; + private: virtual ~MediaStreamGraphImpl(); MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) /** * This class uses manual memory management, and all pointers to it are raw * pointers. However, in order for it to implement nsIMemoryReporter, it needs
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/compression-random-data.js @@ -0,0 +1,26 @@ +// Deterministic Math.random, from Octane. +var seed = 49734321; +function rand() { + // Robert Jenkins' 32 bit integer hash function. + seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff; + seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff; + seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff; + seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff; + seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff; + seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff; + return (seed & 0xfffffff) / 0x10000000; +} +var randomS = ""; +function test(len) { + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var s = "var data = '"; + while (randomS.length < len) { + randomS += chars.charAt((rand() * chars.length)|0); + } + s += randomS; + s += "'; function f() {}; assertEq(f.toSource(), 'function f() {}');"; + eval(s); +} +for (var i=32740; i<32780; i += 5) { + test(i); +}
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2259,16 +2259,19 @@ IonCompile(JSContext* cx, JSScript* scri } // If possible, compile the script off thread. if (options.offThreadCompilationAvailable()) { JitSpew(JitSpew_IonSyncLogs, "Can't log script %s:%" PRIuSIZE ". (Compiled on background thread.)", builderScript->filename(), builderScript->lineno()); + if (!CreateMIRRootList(*builder)) + return AbortReason_Alloc; + if (!StartOffThreadIonCompile(cx, builder)) { JitSpew(JitSpew_IonAbort, "Unable to start off-thread ion compilation."); builder->graphSpewer().endFunction(); return AbortReason_Alloc; } if (!recompile) builderScript->setIonScript(cx->runtime(), ION_COMPILING_SCRIPT);
--- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -4586,8 +4586,69 @@ jit::MakeLoopsContiguous(MIRGraph& graph // Move all blocks between header and backedge that aren't marked to // the end of the loop, making the loop itself contiguous. MakeLoopContiguous(graph, header, numMarked); } return true; } + +MRootList::MRootList(TempAllocator& alloc) +{ +#define INIT_VECTOR(name, _0, _1) \ + roots_[JS::RootKind::name].emplace(alloc); +JS_FOR_EACH_TRACEKIND(INIT_VECTOR) +#undef INIT_VECTOR +} + +template <typename T> +static void +TraceVector(JSTracer* trc, const MRootList::RootVector& vector, const char* name) +{ + for (auto ptr : vector) { + T ptrT = static_cast<T>(ptr); + TraceManuallyBarrieredEdge(trc, &ptrT, name); + MOZ_ASSERT(ptr == ptrT, "Shouldn't move without updating MIR pointers"); + } +} + +void +MRootList::trace(JSTracer* trc) +{ +#define TRACE_ROOTS(name, type, _) \ + TraceVector<type*>(trc, *roots_[JS::RootKind::name], "mir-root-" #name); +JS_FOR_EACH_TRACEKIND(TRACE_ROOTS) +#undef TRACE_ROOTS +} + +MOZ_MUST_USE bool +jit::CreateMIRRootList(IonBuilder& builder) +{ + MOZ_ASSERT(!builder.info().isAnalysis()); + + TempAllocator& alloc = builder.alloc(); + MIRGraph& graph = builder.graph(); + + MRootList* roots = new(alloc.fallible()) MRootList(alloc); + if (!roots) + return false; + + JSScript* prevScript = nullptr; + + for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) { + + JSScript* script = block->info().script(); + if (script != prevScript) { + if (!roots->append(script)) + return false; + prevScript = script; + } + + for (MInstructionIterator iter(block->begin()), end(block->end()); iter != end; iter++) { + if (!iter->appendRoots(*roots)) + return false; + } + } + + builder.setRootList(*roots); + return true; +}
--- a/js/src/jit/IonAnalysis.h +++ b/js/src/jit/IonAnalysis.h @@ -61,16 +61,19 @@ RenumberBlocks(MIRGraph& graph); MOZ_MUST_USE bool AccountForCFGChanges(MIRGenerator* mir, MIRGraph& graph, bool updateAliasAnalysis, bool underValueNumberer = false); MOZ_MUST_USE bool RemoveUnmarkedBlocks(MIRGenerator* mir, MIRGraph& graph, uint32_t numMarkedBlocks); +MOZ_MUST_USE bool +CreateMIRRootList(IonBuilder& builder); + void ClearDominatorTree(MIRGraph& graph); MOZ_MUST_USE bool BuildDominatorTree(MIRGraph& graph); MOZ_MUST_USE bool BuildPhiReverseMapping(MIRGraph& graph);
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -120,16 +120,17 @@ IonBuilder::IonBuilder(JSContext* analys const OptimizationInfo* optimizationInfo, BaselineFrameInspector* baselineFrame, size_t inliningDepth, uint32_t loopDepth) : MIRGenerator(comp, options, temp, graph, info, optimizationInfo), backgroundCodegen_(nullptr), actionableAbortScript_(nullptr), actionableAbortPc_(nullptr), actionableAbortMessage_(nullptr), + rootList_(nullptr), analysisContext(analysisContext), baselineFrame_(baselineFrame), constraints_(constraints), analysis_(*temp, info->script()), thisTypes(nullptr), argTypes(nullptr), typeArray(nullptr), typeArrayHint(0), @@ -14630,12 +14631,11 @@ IonBuilder::convertToBoolean(MDefinition } void IonBuilder::trace(JSTracer* trc) { if (!compartment->runtime()->runtimeMatches(trc->runtime())) return; - TraceManuallyBarrieredEdge(trc, &script_, "IonBuiler::script_"); - if (actionableAbortScript_) - TraceManuallyBarrieredEdge(trc, &actionableAbortScript_, "IonBuiler::actionableAbortScript_"); -} + MOZ_ASSERT(rootList_); + rootList_->trace(trc); +}
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -1109,17 +1109,23 @@ class IonBuilder // Some aborts are actionable (e.g., using an unsupported bytecode). When // optimization tracking is enabled, the location and message of the abort // are recorded here so they may be propagated to the script's // corresponding JitcodeGlobalEntry::BaselineEntry. JSScript* actionableAbortScript_; jsbytecode* actionableAbortPc_; const char* actionableAbortMessage_; + MRootList* rootList_; + public: + void setRootList(MRootList& rootList) { + MOZ_ASSERT(!rootList_); + rootList_ = &rootList; + } void clearForBackEnd(); JSObject* checkNurseryObject(JSObject* obj); JSScript* script() const { return script_; } bool scriptHasIonScript() const { return scriptHasIonScript_; } CodeGenerator* backgroundCodegen() const { return backgroundCodegen_; } void setBackgroundCodegen(CodeGenerator* codegen) { backgroundCodegen_ = codegen; }
--- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -5289,16 +5289,26 @@ InlinePropertyTable::buildTypeSetForFunc return nullptr; for (size_t i = 0; i < numEntries(); i++) { if (entries_[i]->func == func) types->addType(TypeSet::ObjectType(entries_[i]->group), alloc); } return types; } +bool +InlinePropertyTable::appendRoots(MRootList& roots) const +{ + for (const Entry* entry : entries_) { + if (!entry->appendRoots(roots)) + return false; + } + return true; +} + SharedMem<void*> MLoadTypedArrayElementStatic::base() const { return someTypedArray_->as<TypedArrayObject>().viewDataEither(); } size_t MLoadTypedArrayElementStatic::length() const @@ -5374,16 +5384,105 @@ MGetPropertyPolymorphic::mightAlias(cons } return AliasType::MayAlias; } return AliasType::NoAlias; } +bool +MGetPropertyPolymorphic::appendRoots(MRootList& roots) const +{ + if (!roots.append(name_)) + return false; + + for (const PolymorphicEntry& entry : receivers_) { + if (!entry.appendRoots(roots)) + return false; + } + + return true; +} + +bool +MSetPropertyPolymorphic::appendRoots(MRootList& roots) const +{ + if (!roots.append(name_)) + return false; + + for (const PolymorphicEntry& entry : receivers_) { + if (!entry.appendRoots(roots)) + return false; + } + + return true; +} + +bool +MGuardReceiverPolymorphic::appendRoots(MRootList& roots) const +{ + for (const ReceiverGuard& guard : receivers_) { + if (!roots.append(guard)) + return false; + } + return true; +} + +bool +MDispatchInstruction::appendRoots(MRootList& roots) const +{ + for (const Entry& entry : map_) { + if (!entry.appendRoots(roots)) + return false; + } + return true; +} + +bool +MObjectGroupDispatch::appendRoots(MRootList& roots) const +{ + if (inlinePropertyTable_ && !inlinePropertyTable_->appendRoots(roots)) + return false; + return MDispatchInstruction::appendRoots(roots); +} + +bool +MFunctionDispatch::appendRoots(MRootList& roots) const +{ + return MDispatchInstruction::appendRoots(roots); +} + +bool +MConstant::appendRoots(MRootList& roots) const +{ + switch (type()) { + case MIRType::String: + return roots.append(toString()); + case MIRType::Symbol: + return roots.append(toSymbol()); + case MIRType::Object: + return roots.append(&toObject()); + case MIRType::Undefined: + case MIRType::Null: + case MIRType::Boolean: + case MIRType::Int32: + case MIRType::Double: + case MIRType::Float32: + case MIRType::MagicOptimizedArguments: + case MIRType::MagicOptimizedOut: + case MIRType::MagicHole: + case MIRType::MagicIsConstructing: + case MIRType::MagicUninitializedLexical: + return true; + default: + MOZ_CRASH("Unexpected type"); + } +} + void MGetPropertyCache::setBlock(MBasicBlock* block) { MDefinition::setBlock(block); // Track where we started. if (!location_.pc) { location_.pc = block->trackedPc(); location_.script = block->info().script();
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1020,16 +1020,92 @@ class MUseDefIterator MUse* use() const { return *current_; } MDefinition* def() const { return current_->consumer()->toDefinition(); } }; +#ifdef DEBUG +bool +IonCompilationCanUseNurseryPointers(); +#endif + +// Helper class to check that GC pointers embedded in MIR instructions are in +// in the nursery only when the store buffer has been marked as needing to +// cancel all ion compilations. Otherwise, off-thread Ion compilation and +// nursery GCs can happen in parallel, so it's invalid to store pointers to +// nursery things. There's no need to root these pointers, as GC is suppressed +// during compilation and off-thread compilations are canceled on major GCs. +template <typename T> +class CompilerGCPointer +{ + js::gc::Cell* ptr_; + + public: + explicit CompilerGCPointer(T ptr) + : ptr_(ptr) + { + MOZ_ASSERT_IF(IsInsideNursery(ptr), IonCompilationCanUseNurseryPointers()); +#ifdef DEBUG + PerThreadData* pt = TlsPerThreadData.get(); + MOZ_ASSERT_IF(pt->runtimeIfOnOwnerThread(), pt->suppressGC); +#endif + } + + operator T() const { return static_cast<T>(ptr_); } + T operator->() const { return static_cast<T>(ptr_); } + + private: + CompilerGCPointer() = delete; + CompilerGCPointer(const CompilerGCPointer<T>&) = delete; + CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete; +}; + +typedef CompilerGCPointer<JSObject*> CompilerObject; +typedef CompilerGCPointer<NativeObject*> CompilerNativeObject; +typedef CompilerGCPointer<JSFunction*> CompilerFunction; +typedef CompilerGCPointer<JSScript*> CompilerScript; +typedef CompilerGCPointer<PropertyName*> CompilerPropertyName; +typedef CompilerGCPointer<Shape*> CompilerShape; +typedef CompilerGCPointer<ObjectGroup*> CompilerObjectGroup; + +class MRootList : public TempObject +{ + public: + using RootVector = Vector<void*, 0, JitAllocPolicy>; + + private: + mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit, mozilla::Maybe<RootVector>> roots_; + + MRootList(const MRootList&) = delete; + void operator=(const MRootList&) = delete; + + public: + explicit MRootList(TempAllocator& alloc); + + void trace(JSTracer* trc); + + template <typename T> + MOZ_MUST_USE bool append(T ptr) { + if (ptr) + return roots_[JS::MapTypeToRootKind<T>::kind]->append(ptr); + return true; + } + + template <typename T> + MOZ_MUST_USE bool append(const CompilerGCPointer<T>& ptr) { + return append(static_cast<T>(ptr)); + } + MOZ_MUST_USE bool append(const ReceiverGuard& guard) { + return append(guard.group) && append(guard.shape); + } +}; + // An instruction is an SSA name that is inserted into a basic block's IR // stream. class MInstruction : public MDefinition, public InlineListNode<MInstruction> { MResumePoint* resumePoint_; @@ -1081,16 +1157,22 @@ class MInstruction // resume points. virtual bool canClone() const { return false; } virtual MInstruction* clone(TempAllocator& alloc, const MDefinitionVector& inputs) const { MOZ_CRASH(); } + // MIR instructions containing GC pointers should override this to append + // these pointers to the root list. + virtual bool appendRoots(MRootList& roots) const { + return true; + } + // Instructions needing to hook into type analysis should return a // TypePolicy. virtual TypePolicy* typePolicy() = 0; virtual MIRType typePolicySpecialization() = 0; }; #define INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode) \ static const Opcode classOpcode = MDefinition::Op_##opcode; \ @@ -1613,16 +1695,18 @@ class MConstant : public MNullaryInstruc return toDouble(); return toFloat32(); } // Convert this constant to a js::Value. Float32 constants will be stored // as DoubleValue and NaNs are canonicalized. Callers must be careful: not // all constants can be represented by js::Value (wasm supports int64). Value toJSValue() const; + + bool appendRoots(MRootList& roots) const override; }; // Generic constructor of SIMD valuesX4. class MSimdValueX4 : public MQuaternaryInstruction, public Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>, SimdScalarPolicy<2>, SimdScalarPolicy<3> >::Data { @@ -3114,60 +3198,16 @@ TypeSetIncludes(TypeSet* types, MIRType bool EqualTypes(MIRType type1, TemporaryTypeSet* typeset1, MIRType type2, TemporaryTypeSet* typeset2); bool CanStoreUnboxedType(TempAllocator& alloc, JSValueType unboxedType, MIRType input, TypeSet* inputTypes); -#ifdef DEBUG -bool -IonCompilationCanUseNurseryPointers(); -#endif - -// Helper class to check that GC pointers embedded in MIR instructions are in -// in the nursery only when the store buffer has been marked as needing to -// cancel all ion compilations. Otherwise, off-thread Ion compilation and -// nursery GCs can happen in parallel, so it's invalid to store pointers to -// nursery things. There's no need to root these pointers, as GC is suppressed -// during compilation and off-thread compilations are canceled on major GCs. -template <typename T> -class CompilerGCPointer -{ - js::gc::Cell* ptr_; - - public: - explicit CompilerGCPointer(T ptr) - : ptr_(ptr) - { - MOZ_ASSERT_IF(IsInsideNursery(ptr), IonCompilationCanUseNurseryPointers()); -#ifdef DEBUG - PerThreadData* pt = TlsPerThreadData.get(); - MOZ_ASSERT_IF(pt->runtimeIfOnOwnerThread(), pt->suppressGC); -#endif - } - - operator T() const { return static_cast<T>(ptr_); } - T operator->() const { return static_cast<T>(ptr_); } - - private: - CompilerGCPointer() = delete; - CompilerGCPointer(const CompilerGCPointer<T>&) = delete; - CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete; -}; - -typedef CompilerGCPointer<JSObject*> CompilerObject; -typedef CompilerGCPointer<NativeObject*> CompilerNativeObject; -typedef CompilerGCPointer<JSFunction*> CompilerFunction; -typedef CompilerGCPointer<JSScript*> CompilerScript; -typedef CompilerGCPointer<PropertyName*> CompilerPropertyName; -typedef CompilerGCPointer<Shape*> CompilerShape; -typedef CompilerGCPointer<ObjectGroup*> CompilerObjectGroup; - class MNewArray : public MUnaryInstruction, public NoTypePolicy::Data { private: // Number of elements to allocate for the array. uint32_t length_; @@ -3262,16 +3302,20 @@ class MNewArrayCopyOnWrite : public MNul gc::InitialHeap initialHeap() const { return initialHeap_; } virtual AliasSet getAliasSet() const override { return AliasSet::None(); } + + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObject_); + } }; class MNewArrayDynamicLength : public MUnaryInstruction, public IntPolicy<0>::Data { CompilerObject templateObject_; gc::InitialHeap initialHeap_; @@ -3298,16 +3342,20 @@ class MNewArrayDynamicLength } gc::InitialHeap initialHeap() const { return initialHeap_; } virtual AliasSet getAliasSet() const override { return AliasSet::None(); } + + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObject_); + } }; class MNewTypedArray : public MNullaryInstruction { CompilerGCPointer<TypedArrayObject*> templateObject_; gc::InitialHeap initialHeap_; MNewTypedArray(CompilerConstraintList* constraints, TypedArrayObject* templateObject, @@ -3337,16 +3385,20 @@ class MNewTypedArray : public MNullaryIn gc::InitialHeap initialHeap() const { return initialHeap_; } virtual AliasSet getAliasSet() const override { return AliasSet::None(); } + + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObject_); + } }; class MNewTypedArrayDynamicLength : public MUnaryInstruction, public IntPolicy<0>::Data { CompilerObject templateObject_; gc::InitialHeap initialHeap_; @@ -3381,16 +3433,20 @@ class MNewTypedArrayDynamicLength } gc::InitialHeap initialHeap() const { return initialHeap_; } virtual AliasSet getAliasSet() const override { return AliasSet::None(); } + + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObject_); + } }; class MNewObject : public MUnaryInstruction, public NoTypePolicy::Data { public: enum Mode { ObjectLiteral, ObjectCreate }; @@ -3482,16 +3538,20 @@ class MNewTypedObject : public MNullaryI gc::InitialHeap initialHeap() const { return initialHeap_; } virtual AliasSet getAliasSet() const override { return AliasSet::None(); } + + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObject_); + } }; class MTypedObjectDescr : public MUnaryInstruction, public SingleObjectPolicy::Data { private: explicit MTypedObjectDescr(MDefinition* object) @@ -3575,16 +3635,20 @@ class MSimdBox return AliasSet::None(); } void printOpcode(GenericPrinter& out) const override; MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override; bool canRecoverOnBailout() const override { return true; } + + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObject_); + } }; class MSimdUnbox : public MUnaryInstruction, public SingleObjectPolicy::Data { protected: SimdType simdType_; @@ -3871,16 +3935,20 @@ class MInitProp PropertyName* propertyName() const { return name_; } bool possiblyCalls() const override { return true; } + + bool appendRoots(MRootList& roots) const override { + return roots.append(name_); + } }; class MInitPropGetterSetter : public MBinaryInstruction, public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data { CompilerPropertyName name_; @@ -3892,16 +3960,20 @@ class MInitPropGetterSetter public: INSTRUCTION_HEADER(InitPropGetterSetter) TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, object), (1, value)) PropertyName* name() const { return name_; } + + bool appendRoots(MRootList& roots) const override { + return roots.append(name_); + } }; class MInitElem : public MAryInstruction<3>, public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >::Data { MInitElem(MDefinition* obj, MDefinition* id, MDefinition* value) { @@ -3958,16 +4030,20 @@ class WrappedFunction : public TempObjec bool isSelfHostedBuiltin() const { return isSelfHostedBuiltin_; } // fun->native() and fun->jitInfo() can safely be called off-thread: these // fields never change. JSNative native() const { return fun_->native(); } const JSJitInfo* jitInfo() const { return fun_->jitInfo(); } JSFunction* rawJSFunction() const { return fun_; } + + bool appendRoots(MRootList& roots) const { + return roots.append(fun_); + } }; class MCall : public MVariadicInstruction, public CallPolicy::Data { private: // An MCall uses the MPrepareCall, MDefinition for the function, and @@ -4066,16 +4142,22 @@ class MCall } // A method that can be called to tell the MCall to figure out whether it's // movable or not. This can't be done in the constructor, because it // depends on the arguments to the call, and those aren't passed to the // constructor but are set up later via addArg. virtual void computeMovable() { } + + bool appendRoots(MRootList& roots) const override { + if (target_) + return target_->appendRoots(roots); + return true; + } }; class MCallDOMNative : public MCall { // A helper class for MCalls for DOM natives. Note that this is NOT // actually a separate MIR op from MCall, because all sorts of places use // isCall() to check for calls and all we really want is to overload a few // virtual things from MCall. @@ -4158,16 +4240,22 @@ class MApplyArgs // For TI-informed monomorphic callsites. WrappedFunction* getSingleTarget() const { return target_; } bool possiblyCalls() const override { return true; } + + bool appendRoots(MRootList& roots) const override { + if (target_) + return target_->appendRoots(roots); + return true; + } }; // fun.apply(fn, array) class MApplyArray : public MAryInstruction<3>, public Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >::Data { protected: @@ -4191,16 +4279,22 @@ class MApplyArray // For TI-informed monomorphic callsites. WrappedFunction* getSingleTarget() const { return target_; } bool possiblyCalls() const override { return true; } + + bool appendRoots(MRootList& roots) const override { + if (target_) + return target_->appendRoots(roots); + return true; + } }; class MBail : public MNullaryInstruction { protected: explicit MBail(BailoutKind kind) : MNullaryInstruction() { @@ -4945,16 +5039,20 @@ class MCreateArgumentsObject AliasSet getAliasSet() const override { return AliasSet::None(); } bool possiblyCalls() const override { return true; } + + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObj_); + } }; class MGetArgumentsObjectArg : public MUnaryInstruction, public ObjectPolicy<0>::Data { size_t argno_; @@ -7864,16 +7962,19 @@ class MDefVar } unsigned attrs() const { return attrs_; } bool possiblyCalls() const override { return true; } + bool appendRoots(MRootList& roots) const override { + return roots.append(name_); + } }; class MDefLexical : public MNullaryInstruction { CompilerPropertyName name_; // Target name to be defined. unsigned attrs_; // Attributes to be set. @@ -7888,16 +7989,19 @@ class MDefLexical TRIVIAL_NEW_WRAPPERS PropertyName* name() const { return name_; } unsigned attrs() const { return attrs_; } + bool appendRoots(MRootList& roots) const override { + return roots.append(name_); + } }; class MDefFun : public MUnaryInstruction, public NoTypePolicy::Data { CompilerFunction fun_; @@ -7913,16 +8017,19 @@ class MDefFun NAMED_OPERANDS((0, environmentChain)) JSFunction* fun() const { return fun_; } bool possiblyCalls() const override { return true; } + bool appendRoots(MRootList& roots) const override { + return roots.append(fun_); + } }; class MRegExp : public MNullaryInstruction { CompilerGCPointer<RegExpObject*> source_; bool mustClone_; MRegExp(CompilerConstraintList* constraints, RegExpObject* source) @@ -7947,16 +8054,19 @@ class MRegExp : public MNullaryInstructi return source_; } AliasSet getAliasSet() const override { return AliasSet::None(); } bool possiblyCalls() const override { return true; } + bool appendRoots(MRootList& roots) const override { + return roots.append(source_); + } }; class MRegExpMatcher : public MAryInstruction<3>, public Mix3Policy<ObjectPolicy<0>, StringPolicy<1>, IntPolicy<2> >::Data { @@ -8218,16 +8328,24 @@ struct LambdaFunctionInfo : fun(fun), flags(fun->flags()), nargs(fun->nargs()), scriptOrLazyScript(fun->hasScript() ? (gc::Cell*) fun->nonLazyScript() : (gc::Cell*) fun->lazyScript()), singletonType(fun->isSingleton()), useSingletonForClone(ObjectGroup::useSingletonForClone(fun)) {} + bool appendRoots(MRootList& roots) const { + if (!roots.append(fun)) + return false; + if (fun->hasScript()) + return roots.append(fun->nonLazyScript()); + return roots.append(fun->lazyScript()); + } + private: LambdaFunctionInfo(const LambdaFunctionInfo&) = delete; void operator=(const LambdaFunctionInfo&) = delete; }; class MLambda : public MBinaryInstruction, public SingleObjectPolicy::Data @@ -8252,16 +8370,19 @@ class MLambda } const LambdaFunctionInfo& info() const { return info_; } MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override; bool canRecoverOnBailout() const override { return true; } + bool appendRoots(MRootList& roots) const override { + return info_.appendRoots(roots); + } }; class MLambdaArrow : public MBinaryInstruction, public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data { const LambdaFunctionInfo info_; @@ -8278,16 +8399,19 @@ class MLambdaArrow public: INSTRUCTION_HEADER(LambdaArrow) TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, environmentChain), (1, newTargetDef)) const LambdaFunctionInfo& info() const { return info_; } + bool appendRoots(MRootList& roots) const override { + return info_.appendRoots(roots); + } }; // Returns obj->slots. class MSlots : public MUnaryInstruction, public SingleObjectPolicy::Data { explicit MSlots(MDefinition* object) @@ -9564,16 +9688,19 @@ class MConvertUnboxedObjectToNative // in this opcode). // // Later accesses can assume the object has a native representation // and optimize accordingly. Those accesses cannot be reordered before // this instruction, however. This is prevented by chaining this // instruction with the object itself, in the same way as MBoundsCheck. return AliasSet::None(); } + bool appendRoots(MRootList& roots) const override { + return roots.append(group_); + } }; // Array.prototype.pop or Array.prototype.shift on a dense array. class MArrayPopShift : public MUnaryInstruction, public SingleObjectPolicy::Data { public: @@ -9688,16 +9815,19 @@ class MArraySlice AliasSet getAliasSet() const override { return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) | AliasSet::ObjectFields); } bool possiblyCalls() const override { return true; } + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObj_); + } }; class MArrayJoin : public MBinaryInstruction, public MixPolicy<ObjectPolicy<0>, StringPolicy<1> >::Data { MArrayJoin(MDefinition* array, MDefinition* sep) : MBinaryInstruction(array, sep) @@ -9950,16 +10080,20 @@ class MLoadTypedArrayElementStatic void setInfallible() { fallible_ = false; } void computeRange(TempAllocator& alloc) override; bool needTruncation(TruncateKind kind) override; bool canProduceFloat32() const override { return accessType() == Scalar::Float32; } void collectRangeInfoPreTrunc() override; + + bool appendRoots(MRootList& roots) const override { + return roots.append(someTypedArray_); + } }; // Base class for MIR ops that write unboxed scalar values. class StoreUnboxedScalarBase { Scalar::Type writeType_; protected: @@ -10163,16 +10297,20 @@ class MStoreTypedArrayElementStatic : return AliasSet::Store(AliasSet::UnboxedElement); } TruncateKind operandTruncateKind(size_t index) const override; bool canConsumeFloat32(MUse* use) const override { return use == getUseFor(1) && accessType() == Scalar::Float32; } void collectRangeInfoPreTrunc() override; + + bool appendRoots(MRootList& roots) const override { + return roots.append(someTypedArray_); + } }; // Compute an "effective address", i.e., a compound computation of the form: // base + index * scale + displacement class MEffectiveAddress : public MBinaryInstruction, public NoTypePolicy::Data { @@ -10386,16 +10524,19 @@ class InlinePropertyTable : public TempO { struct Entry : public TempObject { CompilerObjectGroup group; CompilerFunction func; Entry(ObjectGroup* group, JSFunction* func) : group(group), func(func) { } + bool appendRoots(MRootList& roots) const { + return roots.append(group) && roots.append(func); + } }; jsbytecode* pc_; MResumePoint* priorResumePoint_; Vector<Entry*, 4, JitAllocPolicy> entries_; public: InlinePropertyTable(TempAllocator& alloc, jsbytecode* pc) @@ -10439,16 +10580,18 @@ class InlinePropertyTable : public TempO TemporaryTypeSet* buildTypeSetForFunction(JSFunction* func) const; // Remove targets that vetoed inlining from the InlinePropertyTable. void trimTo(const ObjectVector& targets, const BoolVector& choiceSet); // Ensure that the InlinePropertyTable's domain is a subset of |targets|. void trimToTargets(const ObjectVector& targets); + + bool appendRoots(MRootList& roots) const; }; class CacheLocationList : public InlineConcatList<CacheLocationList> { public: CacheLocationList() : pc(nullptr), script(nullptr) @@ -10533,33 +10676,43 @@ class MGetPropertyCache } return AliasSet::Store(AliasSet::Any); } void setBlock(MBasicBlock* block) override; MOZ_MUST_USE bool updateForReplacement(MDefinition* ins) override; bool allowDoubleResult() const; + + bool appendRoots(MRootList& roots) const override { + if (inlinePropertyTable_) + return inlinePropertyTable_->appendRoots(roots); + return true; + } +}; + +struct PolymorphicEntry { + // The group and/or shape to guard against. + ReceiverGuard receiver; + + // The property to load, null for loads from unboxed properties. + Shape* shape; + + bool appendRoots(MRootList& roots) const { + return roots.append(receiver) && roots.append(shape); + } }; // Emit code to load a value from an object if it matches one of the receivers // observed by the baseline IC, else bails out. class MGetPropertyPolymorphic : public MUnaryInstruction, public SingleObjectPolicy::Data { - struct Entry { - // The group and/or shape to guard against. - ReceiverGuard receiver; - - // The property to load, null for loads from unboxed properties. - Shape* shape; - }; - - Vector<Entry, 4, JitAllocPolicy> receivers_; + Vector<PolymorphicEntry, 4, JitAllocPolicy> receivers_; CompilerPropertyName name_; MGetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, PropertyName* name) : MUnaryInstruction(obj), receivers_(alloc), name_(name) { setGuard(); @@ -10579,17 +10732,17 @@ class MGetPropertyPolymorphic if (!ins->isGetPropertyPolymorphic()) return false; if (name() != ins->toGetPropertyPolymorphic()->name()) return false; return congruentIfOperandsEqual(ins); } MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver, Shape* shape) { - Entry entry; + PolymorphicEntry entry; entry.receiver = receiver; entry.shape = shape; return receivers_.append(entry); } size_t numReceivers() const { return receivers_.length(); } const ReceiverGuard receiver(size_t i) const { @@ -10611,33 +10764,27 @@ class MGetPropertyPolymorphic } return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot | (hasUnboxedLoad ? AliasSet::UnboxedElement : 0)); } AliasType mightAlias(const MDefinition* store) const override; + + bool appendRoots(MRootList& roots) const override; }; // Emit code to store a value to an object's slots if its shape/group matches // one of the shapes/groups observed by the baseline IC, else bails out. class MSetPropertyPolymorphic : public MBinaryInstruction, public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data { - struct Entry { - // The group and/or shape to guard against. - ReceiverGuard receiver; - - // The property to store, null for stores to unboxed properties. - Shape* shape; - }; - - Vector<Entry, 4, JitAllocPolicy> receivers_; + Vector<PolymorphicEntry, 4, JitAllocPolicy> receivers_; CompilerPropertyName name_; bool needsBarrier_; MSetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, MDefinition* value, PropertyName* name) : MBinaryInstruction(obj, value), receivers_(alloc), name_(name), @@ -10650,17 +10797,17 @@ class MSetPropertyPolymorphic NAMED_OPERANDS((0, object), (1, value)) static MSetPropertyPolymorphic* New(TempAllocator& alloc, MDefinition* obj, MDefinition* value, PropertyName* name) { return new(alloc) MSetPropertyPolymorphic(alloc, obj, value, name); } MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver, Shape* shape) { - Entry entry; + PolymorphicEntry entry; entry.receiver = receiver; entry.shape = shape; return receivers_.append(entry); } size_t numReceivers() const { return receivers_.length(); } const ReceiverGuard& receiver(size_t i) const { @@ -10686,16 +10833,17 @@ class MSetPropertyPolymorphic break; } } return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot | (hasUnboxedStore ? AliasSet::UnboxedElement : 0)); } + bool appendRoots(MRootList& roots) const override; }; class MDispatchInstruction : public MControlInstruction, public SingleObjectPolicy::Data { // Map from JSFunction* -> MBasicBlock. struct Entry { @@ -10704,16 +10852,19 @@ class MDispatchInstruction // |funcGroup| holds the ObjectGroup for |func|, and dispatch guards // on the group instead of directly on the function. ObjectGroup* funcGroup; MBasicBlock* block; Entry(JSFunction* func, ObjectGroup* funcGroup, MBasicBlock* block) : func(func), funcGroup(funcGroup), block(block) { } + bool appendRoots(MRootList& roots) const { + return roots.append(func) && roots.append(funcGroup); + } }; Vector<Entry, 4, JitAllocPolicy> map_; // An optional fallback path that uses MCall. MBasicBlock* fallback_; MUse operand_; void initOperand(size_t index, MDefinition* operand) { @@ -10798,16 +10949,17 @@ class MDispatchInstruction void addFallback(MBasicBlock* block) { MOZ_ASSERT(!hasFallback()); fallback_ = block; } MBasicBlock* getFallback() const { MOZ_ASSERT(hasFallback()); return fallback_; } + bool appendRoots(MRootList& roots) const override; }; // Polymorphic dispatch for inlining, keyed off incoming ObjectGroup. class MObjectGroupDispatch : public MDispatchInstruction { // Map ObjectGroup (of CallProp's Target Object) -> JSFunction (yielded by the CallProp). InlinePropertyTable* inlinePropertyTable_; @@ -10823,31 +10975,33 @@ class MObjectGroupDispatch : public MDis InlinePropertyTable* table) { return new(alloc) MObjectGroupDispatch(alloc, ins, table); } InlinePropertyTable* propTable() const { return inlinePropertyTable_; } + bool appendRoots(MRootList& roots) const override; }; // Polymorphic dispatch for inlining, keyed off incoming JSFunction*. class MFunctionDispatch : public MDispatchInstruction { MFunctionDispatch(TempAllocator& alloc, MDefinition* input) : MDispatchInstruction(alloc, input) { } public: INSTRUCTION_HEADER(FunctionDispatch) static MFunctionDispatch* New(TempAllocator& alloc, MDefinition* ins) { return new(alloc) MFunctionDispatch(alloc, ins); } + bool appendRoots(MRootList& roots) const override; }; class MBindNameCache : public MUnaryInstruction, public SingleObjectPolicy::Data { CompilerPropertyName name_; CompilerScript script_; @@ -10868,16 +11022,20 @@ class MBindNameCache return name_; } JSScript* script() const { return script_; } jsbytecode* pc() const { return pc_; } + bool appendRoots(MRootList& roots) const override { + // Don't append the script, all scripts are added anyway. + return roots.append(name_); + } }; class MCallBindVar : public MUnaryInstruction, public SingleObjectPolicy::Data { explicit MCallBindVar(MDefinition* envChain) : MUnaryInstruction(envChain) @@ -10944,16 +11102,19 @@ class MGuardShape return false; if (bailoutKind() != ins->toGuardShape()->bailoutKind()) return false; return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::ObjectFields); } + bool appendRoots(MRootList& roots) const override { + return roots.append(shape_); + } }; // Bail if the object's shape or unboxed group is not in the input list. class MGuardReceiverPolymorphic : public MUnaryInstruction, public SingleObjectPolicy::Data { Vector<ReceiverGuard, 4, JitAllocPolicy> receivers_; @@ -10986,16 +11147,19 @@ class MGuardReceiverPolymorphic return receivers_[i]; } bool congruentTo(const MDefinition* ins) const override; AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::ObjectFields); } + + bool appendRoots(MRootList& roots) const override; + }; // Guard on an object's group, inclusively or exclusively. class MGuardObjectGroup : public MUnaryInstruction, public SingleObjectPolicy::Data { CompilerObjectGroup group_; @@ -11042,16 +11206,19 @@ class MGuardObjectGroup return false; if (bailoutKind() != ins->toGuardObjectGroup()->bailoutKind()) return false; return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::ObjectFields); } + bool appendRoots(MRootList& roots) const override { + return roots.append(group_); + } }; // Guard on an object's identity, inclusively or exclusively. class MGuardObjectIdentity : public MBinaryInstruction, public SingleObjectPolicy::Data { bool bailOnEquality_; @@ -11343,16 +11510,19 @@ class MGetNameCache NAMED_OPERANDS((0, envObj)) PropertyName* name() const { return name_; } AccessKind accessKind() const { return kind_; } + bool appendRoots(MRootList& roots) const override { + return roots.append(name_); + } }; class MCallGetIntrinsicValue : public MNullaryInstruction { CompilerPropertyName name_; explicit MCallGetIntrinsicValue(PropertyName* name) : name_(name) @@ -11368,16 +11538,19 @@ class MCallGetIntrinsicValue : public MN return name_; } AliasSet getAliasSet() const override { return AliasSet::None(); } bool possiblyCalls() const override { return true; } + bool appendRoots(MRootList& roots) const override { + return roots.append(name_); + } }; class MSetPropertyInstruction : public MBinaryInstruction { CompilerPropertyName name_; bool strict_; protected: @@ -11390,16 +11563,19 @@ class MSetPropertyInstruction : public M public: NAMED_OPERANDS((0, object), (1, value)) PropertyName* name() const { return name_; } bool strict() const { return strict_; } + bool appendRoots(MRootList& roots) const override { + return roots.append(name_); + } }; class MSetElementInstruction : public MTernaryInstruction { bool strict_; protected: MSetElementInstruction(MDefinition* object, MDefinition* index, MDefinition* value, bool strict) @@ -11437,16 +11613,19 @@ class MDeleteProperty NAMED_OPERANDS((0, value)) PropertyName* name() const { return name_; } bool strict() const { return strict_; } + bool appendRoots(MRootList& roots) const override { + return roots.append(name_); + } }; class MDeleteElement : public MBinaryInstruction, public BoxInputsPolicy::Data { bool strict_; @@ -11554,16 +11733,19 @@ class MCallGetProperty AliasSet getAliasSet() const override { if (!idempotent_) return AliasSet::Store(AliasSet::Any); return AliasSet::None(); } bool possiblyCalls() const override { return true; } + bool appendRoots(MRootList& roots) const override { + return roots.append(name_); + } }; // Inline call to handle lhs[rhs]. The first input is a Value so that this // instruction can handle both objects and strings. class MCallGetElement : public MBinaryInstruction, public BoxInputsPolicy::Data { @@ -12137,16 +12319,20 @@ class MInstanceOf public: INSTRUCTION_HEADER(InstanceOf) TRIVIAL_NEW_WRAPPERS JSObject* prototypeObject() { return protoObj_; } + + bool appendRoots(MRootList& roots) const override { + return roots.append(protoObj_); + } }; // Implementation for instanceof operator with unknown rhs. class MCallInstanceOf : public MBinaryInstruction, public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >::Data { MCallInstanceOf(MDefinition* obj, MDefinition* proto) @@ -12311,16 +12497,19 @@ class MRest NAMED_OPERANDS((0, numActuals)) AliasSet getAliasSet() const override { return AliasSet::None(); } bool possiblyCalls() const override { return true; } + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObject()); + } }; class MFilterTypeSet : public MUnaryInstruction, public FilterTypeSetPolicy::Data { MFilterTypeSet(MDefinition* def, TemporaryTypeSet* types) : MUnaryInstruction(def) @@ -12529,16 +12718,19 @@ class MNewNamedLambdaObject : public MNu TRIVIAL_NEW_WRAPPERS LexicalEnvironmentObject* templateObj() { return templateObj_; } AliasSet getAliasSet() const override { return AliasSet::None(); } + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObj_); + } }; class MNewCallObjectBase : public MNullaryInstruction { CompilerGCPointer<CallObject*> templateObj_; protected: explicit MNewCallObjectBase(CallObject* templateObj) @@ -12550,16 +12742,19 @@ class MNewCallObjectBase : public MNulla public: CallObject* templateObject() { return templateObj_; } AliasSet getAliasSet() const override { return AliasSet::None(); } + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObj_); + } }; class MNewCallObject : public MNewCallObjectBase { public: INSTRUCTION_HEADER(NewCallObject) explicit MNewCallObject(CallObject* templateObj) @@ -12604,16 +12799,20 @@ class MNewStringObject : setResultType(MIRType::Object); } public: INSTRUCTION_HEADER(NewStringObject) TRIVIAL_NEW_WRAPPERS StringObject* templateObj() const; + + bool appendRoots(MRootList& roots) const override { + return roots.append(templateObj_); + } }; // This is an alias for MLoadFixedSlot. class MEnclosingEnvironment : public MLoadFixedSlot { explicit MEnclosingEnvironment(MDefinition* obj) : MLoadFixedSlot(obj, EnvironmentObject::enclosingEnvironmentSlot()) {
--- a/js/src/vm/Compression.cpp +++ b/js/src/vm/Compression.cpp @@ -28,16 +28,17 @@ zlib_free(void* cx, void* addr) { js_free(addr); } Compressor::Compressor(const unsigned char* inp, size_t inplen) : inp(inp), inplen(inplen), initialized(false), + finished(false), currentChunkSize(0), chunkOffsets() { MOZ_ASSERT(inplen > 0); zs.opaque = nullptr; zs.next_in = (Bytef*)inp; zs.avail_in = 0; zs.next_out = nullptr; @@ -51,17 +52,17 @@ Compressor::Compressor(const unsigned ch Compressor::~Compressor() { if (initialized) { int ret = deflateEnd(&zs); if (ret != Z_OK) { // If we finished early, we can get a Z_DATA_ERROR. MOZ_ASSERT(ret == Z_DATA_ERROR); - MOZ_ASSERT(uInt(zs.next_in - inp) < inplen || !zs.avail_out); + MOZ_ASSERT(!finished); } } } // According to the zlib docs, the default value for windowBits is 15. Passing // -15 is treated the same, but it also forces 'raw deflate' (no zlib header or // trailer). Raw deflate is necessary for chunked decompression. static const int WindowBits = -15; @@ -91,45 +92,49 @@ Compressor::setOutput(unsigned char* out zs.avail_out = outlen - outbytes; } Compressor::Status Compressor::compressMore() { MOZ_ASSERT(zs.next_out); uInt left = inplen - (zs.next_in - inp); - bool done = left <= MAX_INPUT_SIZE; - if (done) + if (left <= MAX_INPUT_SIZE) zs.avail_in = left; else if (zs.avail_in == 0) zs.avail_in = MAX_INPUT_SIZE; // Finish the current chunk if needed. bool flush = false; MOZ_ASSERT(currentChunkSize <= CHUNK_SIZE); if (currentChunkSize + zs.avail_in >= CHUNK_SIZE) { // Adjust avail_in, so we don't get chunks that are larger than // CHUNK_SIZE. zs.avail_in = CHUNK_SIZE - currentChunkSize; MOZ_ASSERT(currentChunkSize + zs.avail_in == CHUNK_SIZE); flush = true; } + MOZ_ASSERT(zs.avail_in <= left); + bool done = zs.avail_in == left; + Bytef* oldin = zs.next_in; Bytef* oldout = zs.next_out; int ret = deflate(&zs, done ? Z_FINISH : (flush ? Z_FULL_FLUSH : Z_NO_FLUSH)); outbytes += zs.next_out - oldout; currentChunkSize += zs.next_in - oldin; MOZ_ASSERT(currentChunkSize <= CHUNK_SIZE); if (ret == Z_MEM_ERROR) { zs.avail_out = 0; return OOM; } - if (ret == Z_BUF_ERROR || (done && ret == Z_OK)) { + if (ret == Z_BUF_ERROR || (ret == Z_OK && zs.avail_out == 0)) { + // We have to resize the output buffer. Note that we're not done yet + // because ret != Z_STREAM_END. MOZ_ASSERT(zs.avail_out == 0); return MOREOUTPUT; } if (done || currentChunkSize == CHUNK_SIZE) { MOZ_ASSERT_IF(!done, flush); MOZ_ASSERT(chunkSize(inplen, chunkOffsets.length()) == currentChunkSize); if (!chunkOffsets.append(outbytes)) @@ -145,32 +150,34 @@ Compressor::compressMore() size_t Compressor::totalBytesNeeded() const { return AlignBytes(outbytes, sizeof(uint32_t)) + sizeOfChunkOffsets(); } void -Compressor::finish(char* dest, size_t destBytes) const +Compressor::finish(char* dest, size_t destBytes) { MOZ_ASSERT(!chunkOffsets.empty()); CompressedDataHeader* compressedHeader = reinterpret_cast<CompressedDataHeader*>(dest); compressedHeader->compressedBytes = outbytes; size_t outbytesAligned = AlignBytes(outbytes, sizeof(uint32_t)); // Zero the padding bytes, the ImmutableStringsCache will hash them. mozilla::PodZero(dest + outbytes, outbytesAligned - outbytes); uint32_t* destArr = reinterpret_cast<uint32_t*>(dest + outbytesAligned); MOZ_ASSERT(uintptr_t(dest + destBytes) == uintptr_t(destArr + chunkOffsets.length())); mozilla::PodCopy(destArr, chunkOffsets.begin(), chunkOffsets.length()); + + finished = true; } bool js::DecompressString(const unsigned char* inp, size_t inplen, unsigned char* out, size_t outlen) { MOZ_ASSERT(inplen <= UINT32_MAX); // Mark the memory we pass to zlib as initialized for MSan.
--- a/js/src/vm/Compression.h +++ b/js/src/vm/Compression.h @@ -32,16 +32,17 @@ class Compressor // Number of bytes we should hand to zlib each compressMore() call. static const size_t MAX_INPUT_SIZE = 2 * 1024; z_stream zs; const unsigned char* inp; size_t inplen; size_t outbytes; bool initialized; + bool finished; // The number of uncompressed bytes written for the current chunk. When this // reaches CHUNK_SIZE, we finish the current chunk and start a new chunk. uint32_t currentChunkSize; // At the end of each chunk (and the end of the uncompressed data if it's // not a chunk boundary), we record the offset in the compressed data. js::Vector<uint32_t, 8, SystemAllocPolicy> chunkOffsets; @@ -62,17 +63,17 @@ class Compressor Status compressMore(); size_t sizeOfChunkOffsets() const { return chunkOffsets.length() * sizeof(chunkOffsets[0]); } // Returns the number of bytes needed to store the data currently written + // the chunk offsets. size_t totalBytesNeeded() const; // Append the chunk offsets to |dest|. - void finish(char* dest, size_t destBytes) const; + void finish(char* dest, size_t destBytes); static void toChunkOffset(size_t uncompressedOffset, size_t* chunk, size_t* chunkOffset) { *chunk = uncompressedOffset / CHUNK_SIZE; *chunkOffset = uncompressedOffset % CHUNK_SIZE; } static size_t chunkSize(size_t uncompressedBytes, size_t chunk) { MOZ_ASSERT(uncompressedBytes > 0); size_t lastChunk = (uncompressedBytes - 1) / CHUNK_SIZE;
--- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -1776,16 +1776,30 @@ GlobalHelperThreadState::compressionTask void GlobalHelperThreadState::trace(JSTracer* trc) { AutoLockHelperThreadState lock; for (auto builder : ionWorklist(lock)) builder->trace(trc); for (auto builder : ionFinishedList(lock)) builder->trace(trc); + + if (HelperThreadState().threads) { + for (auto& helper : *HelperThreadState().threads) { + if (auto builder = helper.ionBuilder()) + builder->trace(trc); + } + } + + jit::IonBuilder* builder = trc->runtime()->ionLazyLinkList().getFirst(); + while (builder) { + builder->trace(trc); + builder = builder->getNext(); + } + for (auto parseTask : parseWorklist_) parseTask->trace(trc); for (auto parseTask : parseFinishedList_) parseTask->trace(trc); for (auto parseTask : parseWaitingOnGC_) parseTask->trace(trc); }
--- a/layout/reftests/w3c-css/submitted/masking/clip-path-mix-blend-mode-1.html +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-mix-blend-mode-1.html @@ -1,17 +1,17 @@ <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>CSS Masking: clip-path with mix-blend-mode</title> <link rel="author" title="CJ Ku" href="mailto:cku@mozilla.com"> <link rel="author" title="Mozilla" href="https://www.mozilla.org"> <link rel="help" href="https://www.w3.org/TR/css-masking-1/#the-clip-path"> - <link rel="match" href="clip-path-mix-blend-mode-ref.html"> + <link rel="match" href="clip-path-mix-blend-mode-1-ref.html"> <meta name="assert" content="Test checks whether clip-path works with mix-blend-mode correctly or not."> <style type="text/css"> div { position: absolute; width: 100px; height: 100px; }
--- a/media/webrtc/signaling/src/jsep/JsepSession.h +++ b/media/webrtc/signaling/src/jsep/JsepSession.h @@ -90,18 +90,20 @@ public: virtual nsresult SetBundlePolicy(JsepBundlePolicy policy) = 0; virtual bool RemoteIsIceLite() const = 0; virtual bool RemoteIceIsRestarting() const = 0; virtual std::vector<std::string> GetIceOptions() const = 0; virtual nsresult AddDtlsFingerprint(const std::string& algorithm, const std::vector<uint8_t>& value) = 0; - virtual nsresult AddAudioRtpExtension(const std::string& extensionName) = 0; - virtual nsresult AddVideoRtpExtension(const std::string& extensionName) = 0; + virtual nsresult AddAudioRtpExtension(const std::string& extensionName, + SdpDirectionAttribute::Direction direction) = 0; + virtual nsresult AddVideoRtpExtension(const std::string& extensionName, + SdpDirectionAttribute::Direction direction) = 0; // Kinda gross to be locking down the data structure type like this, but // returning by value is problematic due to the lack of stl move semantics in // our build config, since we can't use UniquePtr in the container. The // alternative is writing a raft of accessor functions that allow arbitrary // manipulation (which will be unwieldy), or allowing functors to be injected // that manipulate the data structure (still pretty unwieldy). virtual std::vector<JsepCodecDescription*>& Codecs() = 0;
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp +++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp @@ -193,54 +193,50 @@ JsepSessionImpl::AddDtlsFingerprint(cons fp.mValue = value; mDtlsFingerprints.push_back(fp); return NS_OK; } nsresult -JsepSessionImpl::AddAudioRtpExtension(const std::string& extensionName) +JsepSessionImpl::AddRtpExtension(std::vector<SdpExtmapAttributeList::Extmap>& extensions, + const std::string& extensionName, + SdpDirectionAttribute::Direction direction) { mLastError.clear(); - if (mAudioRtpExtensions.size() + 1 > UINT16_MAX) { - JSEP_SET_ERROR("Too many audio rtp extensions have been added"); + if (extensions.size() + 1 > UINT16_MAX) { + JSEP_SET_ERROR("Too many rtp extensions have been added"); return NS_ERROR_FAILURE; } SdpExtmapAttributeList::Extmap extmap = - { static_cast<uint16_t>(mAudioRtpExtensions.size() + 1), - SdpDirectionAttribute::kSendrecv, - false, // don't actually specify direction + { static_cast<uint16_t>(extensions.size() + 1), + direction, + direction != SdpDirectionAttribute::kSendrecv, // do we want to specify direction? extensionName, "" }; - mAudioRtpExtensions.push_back(extmap); + extensions.push_back(extmap); return NS_OK; } nsresult -JsepSessionImpl::AddVideoRtpExtension(const std::string& extensionName) +JsepSessionImpl::AddAudioRtpExtension(const std::string& extensionName, + SdpDirectionAttribute::Direction direction) { - mLastError.clear(); - - if (mVideoRtpExtensions.size() + 1 > UINT16_MAX) { - JSEP_SET_ERROR("Too many video rtp extensions have been added"); - return NS_ERROR_FAILURE; - } + return AddRtpExtension(mAudioRtpExtensions, extensionName, direction); +} - SdpExtmapAttributeList::Extmap extmap = - { static_cast<uint16_t>(mVideoRtpExtensions.size() + 1), - SdpDirectionAttribute::kSendrecv, - false, // don't actually specify direction - extensionName, "" }; - - mVideoRtpExtensions.push_back(extmap); - return NS_OK; +nsresult +JsepSessionImpl::AddVideoRtpExtension(const std::string& extensionName, + SdpDirectionAttribute::Direction direction) +{ + return AddRtpExtension(mVideoRtpExtensions, extensionName, direction); } template<class T> std::vector<RefPtr<JsepTrack>> GetTracks(const std::vector<T>& wrappedTracks) { std::vector<RefPtr<JsepTrack>> result; for (auto i = wrappedTracks.begin(); i != wrappedTracks.end(); ++i) { @@ -282,16 +278,35 @@ JsepSessionImpl::SetParameters(const std const std::vector<JsepTrack::JsConstraints>& constraints) { auto it = FindTrackByIds(mLocalTracks, streamId, trackId); if (it == mLocalTracks.end()) { JSEP_SET_ERROR("Track " << streamId << "/" << trackId << " was never added."); return NS_ERROR_INVALID_ARG; } + + // Add RtpStreamId Extmap + // SdpDirectionAttribute::Direction is a bitmask + SdpDirectionAttribute::Direction addVideoExt = SdpDirectionAttribute::kInactive; + for (auto constraintEntry: constraints) { + if (constraintEntry.rid != "") { + switch (it->mTrack->GetMediaType()) { + case SdpMediaSection::kVideo: { + addVideoExt = static_cast<SdpDirectionAttribute::Direction>(addVideoExt + | it->mTrack->GetDirection()); + break; + } + } + } + } + if (addVideoExt != SdpDirectionAttribute::kInactive) { + AddVideoRtpExtension("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id", addVideoExt); + } + it->mTrack->SetJsConstraints(constraints); return NS_OK; } nsresult JsepSessionImpl::GetParameters(const std::string& streamId, const std::string& trackId, std::vector<JsepTrack::JsConstraints>* outConstraints) @@ -2203,19 +2218,18 @@ JsepSessionImpl::SetupDefaultCodecs() // Update the redundant encodings for the RED codec with the supported // codecs. Note: only uses the video codecs. red->UpdateRedundantEncodings(mSupportedCodecs.values); } void JsepSessionImpl::SetupDefaultRtpExtensions() { - AddAudioRtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level"); - AddAudioRtpExtension("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"); - AddVideoRtpExtension("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"); + AddAudioRtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", + SdpDirectionAttribute::Direction::kSendonly); } void JsepSessionImpl::SetState(JsepSignalingState state) { if (state == mState) return;
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h +++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h @@ -74,21 +74,28 @@ public: GetIceOptions() const override { return mIceOptions; } virtual nsresult AddDtlsFingerprint(const std::string& algorithm, const std::vector<uint8_t>& value) override; + nsresult AddRtpExtension(std::vector<SdpExtmapAttributeList::Extmap>& extensions, + const std::string& extensionName, + SdpDirectionAttribute::Direction direction); virtual nsresult AddAudioRtpExtension( - const std::string& extensionName) override; + const std::string& extensionName, + SdpDirectionAttribute::Direction direction = + SdpDirectionAttribute::Direction::kSendrecv) override; virtual nsresult AddVideoRtpExtension( - const std::string& extensionName) override; + const std::string& extensionName, + SdpDirectionAttribute::Direction direction = + SdpDirectionAttribute::Direction::kSendrecv) override; virtual std::vector<JsepCodecDescription*>& Codecs() override { return mSupportedCodecs.values; } virtual nsresult ReplaceTrack(const std::string& oldStreamId,
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp +++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp @@ -709,16 +709,29 @@ WebrtcVideoConduit::ConfigureSendMediaCo if(codecFound == false) { CSFLogError(logTag, "%s Codec Mismatch ", __FUNCTION__); return kMediaConduitInvalidSendCodec; } // Note: only for overriding parameters from GetCodec()! CodecConfigToWebRTCCodec(codecConfig, video_codec); + if (mSendingWidth != 0) { + // We're already in a call and are reconfiguring (perhaps due to + // ReplaceTrack). Set to match the last frame we sent. + + // We could also set mLastWidth to 0, to force immediate reconfig - + // more expensive, but perhaps less risk of missing something. Really + // on ReplaceTrack we should just call ConfigureCodecMode(), and if the + // mode changed, we re-configure. + // Do this after CodecConfigToWebRTCCodec() to avoid messing up simulcast + video_codec.width = mSendingWidth; + video_codec.height = mSendingHeight; + video_codec.maxFramerate = mSendingFramerate; + } video_codec.mode = mCodecMode; if(mPtrViECodec->SetSendCodec(mChannel, video_codec) == -1) { error = mPtrViEBase->LastError(); if(error == kViECodecInvalidCodec) { CSFLogError(logTag, "%s Invalid Send Codec", __FUNCTION__); @@ -1563,17 +1576,17 @@ WebrtcVideoConduit::SendVideoFrame(unsig return SendVideoFrame(i420_frame); } MediaConduitErrorCode WebrtcVideoConduit::SendVideoFrame(webrtc::I420VideoFrame& frame) { CSFLogDebug(logTag, "%s ", __FUNCTION__); // See if we need to recalculate what we're sending. - // Don't compate mSendingWidth/Height, since those may not be the same as the input. + // Don't compare mSendingWidth/Height, since those may not be the same as the input. { MutexAutoLock lock(mCodecMutex); if (mInReconfig) { // Waiting for it to finish return kMediaConduitNoError; } if (frame.width() != mLastWidth || frame.height() != mLastHeight) { CSFLogDebug(logTag, "%s: call SelectSendResolution with %ux%u",
--- a/media/webrtc/signaling/src/sdp/SdpHelper.cpp +++ b/media/webrtc/signaling/src/sdp/SdpHelper.cpp @@ -741,18 +741,35 @@ SdpHelper::AddCommonExtmaps( SdpAttribute::kExtmapAttribute)) { return; } UniquePtr<SdpExtmapAttributeList> localExtmap(new SdpExtmapAttributeList); auto& theirExtmap = remoteMsection.GetAttributeList().GetExtmap().mExtmaps; for (auto i = theirExtmap.begin(); i != theirExtmap.end(); ++i) { for (auto j = localExtensions.begin(); j != localExtensions.end(); ++j) { - if (i->extensionname == j->extensionname) { - localExtmap->mExtmaps.push_back(*i); + // verify we have a valid combination of directions. For kInactive + // we'll just not add the response + if (i->extensionname == j->extensionname && + (((i->direction == SdpDirectionAttribute::Direction::kSendrecv || + i->direction == SdpDirectionAttribute::Direction::kSendonly) && + (j->direction == SdpDirectionAttribute::Direction::kSendrecv || + j->direction == SdpDirectionAttribute::Direction::kRecvonly)) || + + ((i->direction == SdpDirectionAttribute::Direction::kSendrecv || + i->direction == SdpDirectionAttribute::Direction::kRecvonly) && + (j->direction == SdpDirectionAttribute::Direction::kSendrecv || + j->direction == SdpDirectionAttribute::Direction::kSendonly)))) { + auto k = *i; // we need to modify it + if (j->direction == SdpDirectionAttribute::Direction::kSendonly) { + k.direction = SdpDirectionAttribute::Direction::kRecvonly; + } else if (j->direction == SdpDirectionAttribute::Direction::kRecvonly) { + k.direction = SdpDirectionAttribute::Direction::kSendonly; + } + localExtmap->mExtmaps.push_back(k); // RFC 5285 says that ids >= 4096 can be used by the offerer to // force the answerer to pick, otherwise the value in the offer is // used. if (localExtmap->mExtmaps.back().entry >= 4096) { localExtmap->mExtmaps.back().entry = j->entry; } }
--- a/media/webrtc/signaling/test/jsep_session_unittest.cpp +++ b/media/webrtc/signaling/test/jsep_session_unittest.cpp @@ -3569,61 +3569,51 @@ TEST_F(JsepSessionTest, TestIceOptions) ASSERT_EQ("trickle", mSessionAns.GetIceOptions()[0]); } TEST_F(JsepSessionTest, TestExtmap) { AddTracks(mSessionOff, "audio"); AddTracks(mSessionAns, "audio"); // ssrc-audio-level will be extmap 1 for both - // rtp-stream-id will be extmap 2 for both - mSessionOff.AddAudioRtpExtension("foo"); // Default mapping of 3 - mSessionOff.AddAudioRtpExtension("bar"); // Default mapping of 4 - mSessionAns.AddAudioRtpExtension("bar"); // Default mapping of 3 + mSessionOff.AddAudioRtpExtension("foo"); // Default mapping of 2 + mSessionOff.AddAudioRtpExtension("bar"); // Default mapping of 3 + mSessionAns.AddAudioRtpExtension("bar"); // Default mapping of 2 std::string offer = CreateOffer(); SetLocalOffer(offer, CHECK_SUCCESS); SetRemoteOffer(offer, CHECK_SUCCESS); std::string answer = CreateAnswer(); SetLocalAnswer(answer, CHECK_SUCCESS); SetRemoteAnswer(answer, CHECK_SUCCESS); UniquePtr<Sdp> parsedOffer(Parse(offer)); ASSERT_EQ(1U, parsedOffer->GetMediaSectionCount()); auto& offerMediaAttrs = parsedOffer->GetMediaSection(0).GetAttributeList(); ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); auto& offerExtmap = offerMediaAttrs.GetExtmap().mExtmaps; - ASSERT_EQ(4U, offerExtmap.size()); + ASSERT_EQ(3U, offerExtmap.size()); ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", offerExtmap[0].extensionname); ASSERT_EQ(1U, offerExtmap[0].entry); - ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id", - offerExtmap[1].extensionname); + ASSERT_EQ("foo", offerExtmap[1].extensionname); ASSERT_EQ(2U, offerExtmap[1].entry); - ASSERT_EQ("foo", offerExtmap[2].extensionname); + ASSERT_EQ("bar", offerExtmap[2].extensionname); ASSERT_EQ(3U, offerExtmap[2].entry); - ASSERT_EQ("bar", offerExtmap[3].extensionname); - ASSERT_EQ(4U, offerExtmap[3].entry); UniquePtr<Sdp> parsedAnswer(Parse(answer)); ASSERT_EQ(1U, parsedAnswer->GetMediaSectionCount()); auto& answerMediaAttrs = parsedAnswer->GetMediaSection(0).GetAttributeList(); ASSERT_TRUE(answerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); auto& answerExtmap = answerMediaAttrs.GetExtmap().mExtmaps; - ASSERT_EQ(3U, answerExtmap.size()); - ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", - answerExtmap[0].extensionname); - ASSERT_EQ(1U, answerExtmap[0].entry); - ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id", - answerExtmap[1].extensionname); - ASSERT_EQ(2U, answerExtmap[1].entry); + ASSERT_EQ(1U, answerExtmap.size()); // We ensure that the entry for "bar" matches what was in the offer - ASSERT_EQ("bar", answerExtmap[2].extensionname); - ASSERT_EQ(4U, answerExtmap[2].entry); + ASSERT_EQ("bar", answerExtmap[0].extensionname); + ASSERT_EQ(3U, answerExtmap[0].entry); } TEST_F(JsepSessionTest, TestRtcpFbStar) { AddTracks(mSessionOff, "video"); AddTracks(mSessionAns, "video"); std::string offer = CreateOffer();
--- a/testing/web-platform/meta/dom/historical.html.ini +++ b/testing/web-platform/meta/dom/historical.html.ini @@ -5,10 +5,10 @@ [Historical DOM features must be removed: DOMError] expected: FAIL [Historical DOM features must be removed: createCDATASection] expected: FAIL [Node member must be nuked: rootNode] - expected: FAIL + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1303802