merge mozilla-central to autoland. r=merge a=merge

This commit is contained in:
Sebastian Hengst 2017-04-15 00:08:12 +02:00
Родитель a432540bb2 d8496d0a1f
Коммит 4c8fa6d5da
52 изменённых файлов: 716 добавлений и 1224 удалений

Просмотреть файл

@ -3294,7 +3294,9 @@ dump(`callFromJSON: < ${JSON.stringify(call)}\n`);
PPB_KeyboardInputEvent_GetCharacterText: function(json) { PPB_KeyboardInputEvent_GetCharacterText: function(json) {
let event = PP_Resource.lookup(json.character_event); let event = PP_Resource.lookup(json.character_event);
let charCode = event.domEvent.charCode; let charCode = event.domEvent.charCode;
if (charCode === 0) { if (charCode === 0 ||
event.domEvent.getModifierState("Control") ||
event.domEvent.getModifierState("Meta")) {
return new PP_Var(); return new PP_Var();
} }
return new String_PP_Var(String.fromCharCode(charCode)); return new String_PP_Var(String.fromCharCode(charCode));

Просмотреть файл

@ -570,6 +570,9 @@ Promise::PerformWorkerMicroTaskCheckpoint()
MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!"); MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
CycleCollectedJSContext* context = CycleCollectedJSContext::Get(); CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
if (!context) {
return;
}
for (;;) { for (;;) {
// For a normal microtask checkpoint, we try to use the debugger microtask // For a normal microtask checkpoint, we try to use the debugger microtask
@ -604,6 +607,9 @@ Promise::PerformWorkerDebuggerMicroTaskCheckpoint()
MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!"); MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
CycleCollectedJSContext* context = CycleCollectedJSContext::Get(); CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
if (!context) {
return;
}
for (;;) { for (;;) {
// For a debugger microtask checkpoint, we always use the debugger microtask // For a debugger microtask checkpoint, we always use the debugger microtask

Просмотреть файл

@ -170,6 +170,27 @@ public:
return true; return true;
} }
// This returns true if scrolling info is equivalent for the purposes of
// APZ hit testing.
bool HitTestingInfoIsEqual(const SimpleLayerAttributes& aOther) const {
if (mIsScrollbarContainer != aOther.mIsScrollbarContainer) {
return false;
}
if (mScrollbarTargetContainerId != aOther.mScrollbarTargetContainerId) {
return false;
}
if (mScrollbarDirection != aOther.mScrollbarDirection) {
return false;
}
if (FixedPositionScrollContainerId() != aOther.FixedPositionScrollContainerId()) {
return false;
}
if (mTransform != aOther.mTransform) {
return false;
}
return true;
}
// //
// Getters. // Getters.
// //

Просмотреть файл

@ -480,6 +480,9 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
RefPtr<HitTestingTreeNode> node = nullptr; RefPtr<HitTestingTreeNode> node = nullptr;
if (!needsApzc) { if (!needsApzc) {
// Note: if layer properties must be propagated to nodes, RecvUpdate in
// LayerTransactionParent.cpp must ensure that APZ will be notified
// when those properties change.
node = RecycleOrCreateNode(aState, nullptr, aLayersId); node = RecycleOrCreateNode(aState, nullptr, aLayersId);
AttachNodeToTree(node, aParent, aNextSibling); AttachNodeToTree(node, aParent, aNextSibling);
node->SetHitTestData( node->SetHitTestData(
@ -674,6 +677,9 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
GetEventRegionsOverride(aParent, aLayer)); GetEventRegionsOverride(aParent, aLayer));
} }
// Note: if layer properties must be propagated to nodes, RecvUpdate in
// LayerTransactionParent.cpp must ensure that APZ will be notified
// when those properties change.
node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(), node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
aLayer.GetScrollbarDirection(), aLayer.GetScrollbarDirection(),
aLayer.GetScrollThumbLength(), aLayer.GetScrollThumbLength(),

Просмотреть файл

@ -172,8 +172,8 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
layer_manager()->BeginTransaction(); layer_manager()->BeginTransaction();
} }
// not all edits require an update to the hit testing tree // Not all edits require an update to the hit testing tree.
bool updateHitTestingTree = false; mUpdateHitTestingTree = false;
for (EditArray::index_type i = 0; i < aInfo.cset().Length(); ++i) { for (EditArray::index_type i = 0; i < aInfo.cset().Length(); ++i) {
const Edit& edit = const_cast<Edit&>(aInfo.cset()[i]); const Edit& edit = const_cast<Edit&>(aInfo.cset()[i]);
@ -188,7 +188,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "CreatePaintedLayer");
break; break;
} }
case Edit::TOpCreateContainerLayer: { case Edit::TOpCreateContainerLayer: {
@ -199,7 +199,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "CreateContainerLayer");
break; break;
} }
case Edit::TOpCreateImageLayer: { case Edit::TOpCreateImageLayer: {
@ -210,7 +210,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "CreateImageLayer");
break; break;
} }
case Edit::TOpCreateColorLayer: { case Edit::TOpCreateColorLayer: {
@ -221,7 +221,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "CreateColorLayer");
break; break;
} }
case Edit::TOpCreateTextLayer: { case Edit::TOpCreateTextLayer: {
@ -232,18 +232,18 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "CreateTextLayer");
break; break;
} }
case Edit::TOpCreateBorderLayer: { case Edit::TOpCreateBorderLayer: {
MOZ_LAYERS_LOG(("[ParentSide] CreateTextLayer")); MOZ_LAYERS_LOG(("[ParentSide] CreateBorderLayer"));
RefPtr<BorderLayer> layer = layer_manager()->CreateBorderLayer(); RefPtr<BorderLayer> layer = layer_manager()->CreateBorderLayer();
if (!BindLayer(layer, edit.get_OpCreateBorderLayer())) { if (!BindLayer(layer, edit.get_OpCreateBorderLayer())) {
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "CreateBorderLayer");
break; break;
} }
case Edit::TOpCreateCanvasLayer: { case Edit::TOpCreateCanvasLayer: {
@ -254,7 +254,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "CreateCanvasLayer");
break; break;
} }
case Edit::TOpCreateRefLayer: { case Edit::TOpCreateRefLayer: {
@ -265,7 +265,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "CreateRefLayer");
break; break;
} }
case Edit::TOpSetDiagnosticTypes: { case Edit::TOpSetDiagnosticTypes: {
@ -291,7 +291,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
} }
mRoot = newRoot; mRoot = newRoot;
updateHitTestingTree = true; UpdateHitTestingTree(mRoot, "SetRoot");
break; break;
} }
case Edit::TOpInsertAfter: { case Edit::TOpInsertAfter: {
@ -309,7 +309,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "InsertAfter");
break; break;
} }
case Edit::TOpPrependChild: { case Edit::TOpPrependChild: {
@ -326,7 +326,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "PrependChild");
break; break;
} }
case Edit::TOpRemoveChild: { case Edit::TOpRemoveChild: {
@ -343,7 +343,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "RemoveChild");
break; break;
} }
case Edit::TOpRepositionChild: { case Edit::TOpRepositionChild: {
@ -361,7 +361,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "RepositionChild");
break; break;
} }
case Edit::TOpRaiseToTopChild: { case Edit::TOpRaiseToTopChild: {
@ -381,7 +381,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true; UpdateHitTestingTree(layer, "RaiseToTopChild");
break; break;
} }
case Edit::TCompositableOperation: { case Edit::TCompositableOperation: {
@ -446,8 +446,12 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
if (!layer) { if (!layer) {
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
const SimpleLayerAttributes& attrs = op.attrs();
const SimpleLayerAttributes& orig = layer->GetSimpleAttributes();
if (!attrs.HitTestingInfoIsEqual(orig)) {
UpdateHitTestingTree(layer, "scrolling info changed");
}
layer->SetSimpleAttributes(op.attrs()); layer->SetSimpleAttributes(op.attrs());
updateHitTestingTree = true;
} }
// Process attribute updates. // Process attribute updates.
@ -456,7 +460,6 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
if (!SetLayerAttributes(op)) { if (!SetLayerAttributes(op)) {
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
updateHitTestingTree = true;
} }
// Process paints separately, after all normal edits. // Process paints separately, after all normal edits.
@ -466,7 +469,7 @@ LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
} }
} }
mCompositorBridge->ShadowLayersUpdated(this, aInfo, updateHitTestingTree); mCompositorBridge->ShadowLayersUpdated(this, aInfo, mUpdateHitTestingTree);
{ {
AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this)); AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this));
@ -521,16 +524,29 @@ LayerTransactionParent::SetLayerAttributes(const OpSetLayerAttributes& aOp)
const LayerAttributes& attrs = aOp.attrs(); const LayerAttributes& attrs = aOp.attrs();
const CommonLayerAttributes& common = attrs.common(); const CommonLayerAttributes& common = attrs.common();
layer->SetVisibleRegion(common.visibleRegion()); if (common.visibleRegion() != layer->GetVisibleRegion()) {
layer->SetEventRegions(common.eventRegions()); UpdateHitTestingTree(layer, "visible region changed");
layer->SetClipRect(common.useClipRect() ? Some(common.clipRect()) : Nothing()); layer->SetVisibleRegion(common.visibleRegion());
}
if (common.eventRegions() != layer->GetEventRegions()) {
UpdateHitTestingTree(layer, "event regions changed");
layer->SetEventRegions(common.eventRegions());
}
Maybe<ParentLayerIntRect> clipRect = common.useClipRect() ? Some(common.clipRect()) : Nothing();
if (clipRect != layer->GetClipRect()) {
UpdateHitTestingTree(layer, "clip rect changed");
layer->SetClipRect(clipRect);
}
if (LayerHandle maskLayer = common.maskLayer()) { if (LayerHandle maskLayer = common.maskLayer()) {
layer->SetMaskLayer(AsLayer(maskLayer)); layer->SetMaskLayer(AsLayer(maskLayer));
} else { } else {
layer->SetMaskLayer(nullptr); layer->SetMaskLayer(nullptr);
} }
layer->SetCompositorAnimations(common.compositorAnimations()); layer->SetCompositorAnimations(common.compositorAnimations());
layer->SetScrollMetadata(common.scrollMetadata()); if (common.scrollMetadata() != layer->GetAllScrollMetadata()) {
UpdateHitTestingTree(layer, "scroll metadata changed");
layer->SetScrollMetadata(common.scrollMetadata());
}
layer->SetDisplayListLog(common.displayListLog().get()); layer->SetDisplayListLog(common.displayListLog().get());
// The updated invalid region is added to the existing one, since we can // The updated invalid region is added to the existing one, since we can
@ -579,8 +595,10 @@ LayerTransactionParent::SetLayerAttributes(const OpSetLayerAttributes& aOp)
containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale()); containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale());
containerLayer->SetScaleToResolution(attrs.scaleToResolution(), containerLayer->SetScaleToResolution(attrs.scaleToResolution(),
attrs.presShellResolution()); attrs.presShellResolution());
containerLayer->SetEventRegionsOverride(attrs.eventRegionsOverride()); if (attrs.eventRegionsOverride() != containerLayer->GetEventRegionsOverride()) {
UpdateHitTestingTree(layer, "event regions override changed");
containerLayer->SetEventRegionsOverride(attrs.eventRegionsOverride());
}
break; break;
} }
case Specific::TColorLayerAttributes: { case Specific::TColorLayerAttributes: {
@ -640,6 +658,7 @@ LayerTransactionParent::SetLayerAttributes(const OpSetLayerAttributes& aOp)
} }
refLayer->SetReferentId(specific.get_RefLayerAttributes().id()); refLayer->SetReferentId(specific.get_RefLayerAttributes().id());
refLayer->SetEventRegionsOverride(specific.get_RefLayerAttributes().eventRegionsOverride()); refLayer->SetEventRegionsOverride(specific.get_RefLayerAttributes().eventRegionsOverride());
UpdateHitTestingTree(layer, "event regions override changed");
break; break;
} }
case Specific::TImageLayerAttributes: { case Specific::TImageLayerAttributes: {

Просмотреть файл

@ -171,6 +171,13 @@ protected:
friend class CrossProcessCompositorBridgeParent; friend class CrossProcessCompositorBridgeParent;
friend class layout::RenderFrameParent; friend class layout::RenderFrameParent;
private:
// This is a function so we can log or breakpoint on why hit
// testing tree changes are made.
void UpdateHitTestingTree(Layer* aLayer, const char* aWhy) {
mUpdateHitTestingTree = true;
}
private: private:
RefPtr<HostLayerManager> mLayerManager; RefPtr<HostLayerManager> mLayerManager;
CompositorBridgeParentBase* mCompositorBridge; CompositorBridgeParentBase* mCompositorBridge;
@ -216,8 +223,11 @@ private:
// transactions posted by the child. // transactions posted by the child.
bool mDestroyed; bool mDestroyed;
bool mIPCOpen; bool mIPCOpen;
// This is set during RecvUpdate to track whether we'll need to update
// APZ's hit test regions.
bool mUpdateHitTestingTree;
}; };
} // namespace layers } // namespace layers

Просмотреть файл

@ -246,7 +246,6 @@ gc::GCRuntime::startVerifyPreBarriers()
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
MOZ_ASSERT(!zone->usedByHelperThread()); MOZ_ASSERT(!zone->usedByHelperThread());
PurgeJITCaches(zone);
zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit); zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit);
zone->arenas.purge(); zone->arenas.purge();
} }
@ -342,7 +341,6 @@ gc::GCRuntime::endVerifyPreBarriers()
compartmentCreated = true; compartmentCreated = true;
zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit); zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit);
PurgeJITCaches(zone);
} }
/* /*

Просмотреть файл

@ -192,69 +192,67 @@ Zone::discardJitCode(FreeOp* fop, bool discardBaselineCode)
if (!jitZone()) if (!jitZone())
return; return;
if (isPreservingCode()) { if (isPreservingCode())
PurgeJITCaches(this); return;
} else {
if (discardBaselineCode) { if (discardBaselineCode) {
#ifdef DEBUG #ifdef DEBUG
/* Assert no baseline scripts are marked as active. */ /* Assert no baseline scripts are marked as active. */
for (auto script = cellIter<JSScript>(); !script.done(); script.next()) for (auto script = cellIter<JSScript>(); !script.done(); script.next())
MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active()); MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active());
#endif #endif
/* Mark baseline scripts on the stack as active. */ /* Mark baseline scripts on the stack as active. */
jit::MarkActiveBaselineScripts(this); jit::MarkActiveBaselineScripts(this);
}
/* Only mark OSI points if code is being discarded. */
jit::InvalidateAll(fop, this);
for (auto script = cellIter<JSScript>(); !script.done(); script.next()) {
jit::FinishInvalidation(fop, script);
/*
* Discard baseline script if it's not marked as active. Note that
* this also resets the active flag.
*/
if (discardBaselineCode)
jit::FinishDiscardBaselineScript(fop, script);
/*
* Warm-up counter for scripts are reset on GC. After discarding code we
* need to let it warm back up to get information such as which
* opcodes are setting array holes or accessing getter properties.
*/
script->resetWarmUpCounter();
/*
* Make it impossible to use the control flow graphs cached on the
* BaselineScript. They get deleted.
*/
if (script->hasBaselineScript())
script->baselineScript()->setControlFlowGraph(nullptr);
}
/*
* When scripts contains pointers to nursery things, the store buffer
* can contain entries that point into the optimized stub space. Since
* this method can be called outside the context of a GC, this situation
* could result in us trying to mark invalid store buffer entries.
*
* Defer freeing any allocated blocks until after the next minor GC.
*/
if (discardBaselineCode) {
jitZone()->optimizedStubSpace()->freeAllAfterMinorGC(this);
jitZone()->purgeIonCacheIRStubInfo();
}
/*
* Free all control flow graphs that are cached on BaselineScripts.
* Assuming this happens on the active thread and all control flow
* graph reads happen on the active thread, this is safe.
*/
jitZone()->cfgSpace()->lifoAlloc().freeAll();
} }
/* Only mark OSI points if code is being discarded. */
jit::InvalidateAll(fop, this);
for (auto script = cellIter<JSScript>(); !script.done(); script.next()) {
jit::FinishInvalidation(fop, script);
/*
* Discard baseline script if it's not marked as active. Note that
* this also resets the active flag.
*/
if (discardBaselineCode)
jit::FinishDiscardBaselineScript(fop, script);
/*
* Warm-up counter for scripts are reset on GC. After discarding code we
* need to let it warm back up to get information such as which
* opcodes are setting array holes or accessing getter properties.
*/
script->resetWarmUpCounter();
/*
* Make it impossible to use the control flow graphs cached on the
* BaselineScript. They get deleted.
*/
if (script->hasBaselineScript())
script->baselineScript()->setControlFlowGraph(nullptr);
}
/*
* When scripts contains pointers to nursery things, the store buffer
* can contain entries that point into the optimized stub space. Since
* this method can be called outside the context of a GC, this situation
* could result in us trying to mark invalid store buffer entries.
*
* Defer freeing any allocated blocks until after the next minor GC.
*/
if (discardBaselineCode) {
jitZone()->optimizedStubSpace()->freeAllAfterMinorGC(this);
jitZone()->purgeIonCacheIRStubInfo();
}
/*
* Free all control flow graphs that are cached on BaselineScripts.
* Assuming this happens on the active thread and all control flow
* graph reads happen on the active thread, this is safe.
*/
jitZone()->cfgSpace()->lifoAlloc().freeAll();
} }
#ifdef JSGC_HASH_TABLE_CHECKS #ifdef JSGC_HASH_TABLE_CHECKS

Просмотреть файл

@ -0,0 +1,26 @@
const x = 1;
function testConst() {
for (var i = 0; i < 20; i++) {
try {
x = 2;
} catch (e) {
continue;
}
throw "Fail1";
}
assertEq(x, 1);
}
testConst();
function testUninit() {
for (var i = 0; i < 20; i++) {
try {
y = 2;
} catch (e) {
continue;
}
throw "Fail2";
}
}
testUninit();
let y;

Просмотреть файл

@ -1855,10 +1855,11 @@ BaselineCacheIRCompiler::init(CacheKind kind)
allocator.initInputLocation(2, BaselineFrameSlot(0)); allocator.initInputLocation(2, BaselineFrameSlot(0));
break; break;
case CacheKind::GetName: case CacheKind::GetName:
case CacheKind::BindName:
MOZ_ASSERT(numInputs == 1); MOZ_ASSERT(numInputs == 1);
allocator.initInputLocation(0, R0.scratchReg(), JSVAL_TYPE_OBJECT); allocator.initInputLocation(0, R0.scratchReg(), JSVAL_TYPE_OBJECT);
#if defined(JS_NUNBOX32) #if defined(JS_NUNBOX32)
// availableGeneralRegs can't know that GetName is only using // availableGeneralRegs can't know that GetName/BindName is only using
// the payloadReg and not typeReg on x86. // the payloadReg and not typeReg on x86.
available.add(R0.typeReg()); available.add(R0.typeReg());
#endif #endif
@ -1900,6 +1901,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
switch (kind) { switch (kind) {
case CacheKind::In: case CacheKind::In:
case CacheKind::HasOwn: case CacheKind::HasOwn:
case CacheKind::BindName:
stubDataOffset = sizeof(ICCacheIR_Regular); stubDataOffset = sizeof(ICCacheIR_Regular);
stubKind = CacheIRStubKind::Regular; stubKind = CacheIRStubKind::Regular;
break; break;

Просмотреть файл

@ -1426,6 +1426,24 @@ DoBindNameFallback(JSContext* cx, BaselineFrame* frame, ICBindName_Fallback* stu
RootedPropertyName name(cx, frame->script()->getName(pc)); RootedPropertyName name(cx, frame->script()->getName(pc));
if (stub->state().maybeTransition())
stub->discardStubs(cx);
if (stub->state().canAttachStub()) {
bool attached = false;
RootedScript script(cx, frame->script());
BindNameIRGenerator gen(cx, script, pc, stub->state().mode(), envChain, name);
if (gen.tryAttachStub()) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
ICStubEngine::Baseline, script, stub,
&attached);
if (newStub)
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
}
if (!attached)
stub->state().trackNotAttached();
}
RootedObject scope(cx); RootedObject scope(cx);
if (!LookupNameUnqualified(cx, name, envChain, &scope)) if (!LookupNameUnqualified(cx, name, envChain, &scope))
return false; return false;

Просмотреть файл

@ -1857,6 +1857,130 @@ GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
return true; return true;
} }
BindNameIRGenerator::BindNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
ICState::Mode mode, HandleObject env,
HandlePropertyName name)
: IRGenerator(cx, script, pc, CacheKind::BindName, mode),
env_(env),
name_(name)
{}
bool
BindNameIRGenerator::tryAttachStub()
{
MOZ_ASSERT(cacheKind_ == CacheKind::BindName);
AutoAssertNoPendingException aanpe(cx_);
ObjOperandId envId(writer.setInputOperandId(0));
RootedId id(cx_, NameToId(name_));
if (tryAttachGlobalName(envId, id))
return true;
if (tryAttachEnvironmentName(envId, id))
return true;
return false;
}
bool
BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId, HandleId id)
{
if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
return false;
Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
MOZ_ASSERT(globalLexical->isGlobal());
JSObject* result = nullptr;
if (Shape* shape = globalLexical->lookup(cx_, id)) {
// If this is an uninitialized lexical or a const, we need to return a
// RuntimeLexicalErrorObject.
if (globalLexical->getSlot(shape->slot()).isMagic() || !shape->writable())
return false;
result = globalLexical;
} else {
result = &globalLexical->global();
}
if (result == globalLexical) {
// Lexical bindings are non-configurable so we can just return the
// global lexical.
writer.loadObjectResult(objId);
} else {
// If the property exists on the global and is non-configurable, it cannot be
// shadowed by the lexical scope so we can just return the global without a
// shape guard.
Shape* shape = result->as<GlobalObject>().lookup(cx_, id);
if (!shape || shape->configurable())
writer.guardShape(objId, globalLexical->lastProperty());
ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
writer.loadObjectResult(globalId);
}
writer.returnFromIC();
return true;
}
bool
BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
{
if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
return false;
RootedObject env(cx_, env_);
RootedShape shape(cx_);
while (true) {
if (!env->is<GlobalObject>() && !env->is<EnvironmentObject>())
return false;
if (env->is<WithEnvironmentObject>())
return false;
MOZ_ASSERT(!env->hasUncacheableProto());
// When we reach an unqualified variables object (like the global) we
// have to stop looking and return that object.
if (env->isUnqualifiedVarObj())
break;
// Check for an 'own' property on the env. There is no need to
// check the prototype as non-with scopes do not inherit properties
// from any prototype.
shape = env->as<NativeObject>().lookup(cx_, id);
if (shape)
break;
env = env->enclosingEnvironment();
}
// If this is an uninitialized lexical or a const, we need to return a
// RuntimeLexicalErrorObject.
RootedNativeObject holder(cx_, &env->as<NativeObject>());
if (shape &&
holder->is<EnvironmentObject>() &&
(holder->getSlot(shape->slot()).isMagic() || !shape->writable()))
{
return false;
}
ObjOperandId lastObjId = objId;
env = env_;
while (env) {
if (NeedEnvironmentShapeGuard(env) && !env->is<GlobalObject>())
writer.guardShape(lastObjId, env->maybeShape());
if (env == holder)
break;
lastObjId = writer.loadEnclosingEnvironment(lastObjId);
env = env->enclosingEnvironment();
}
writer.loadObjectResult(lastObjId);
writer.returnFromIC();
return true;
}
InIRGenerator::InIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, InIRGenerator::InIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
ICState::Mode mode, HandleValue key, HandleObject obj) ICState::Mode mode, HandleValue key, HandleObject obj)
: IRGenerator(cx, script, pc, CacheKind::In, mode), : IRGenerator(cx, script, pc, CacheKind::In, mode),

Просмотреть файл

@ -139,6 +139,7 @@ class TypedOperandId : public OperandId
_(GetName) \ _(GetName) \
_(SetProp) \ _(SetProp) \
_(SetElem) \ _(SetElem) \
_(BindName) \
_(In) \ _(In) \
_(HasOwn) _(HasOwn)
@ -236,6 +237,7 @@ extern const char* CacheKindNames[];
_(LoadFrameArgumentResult) \ _(LoadFrameArgumentResult) \
_(LoadEnvironmentFixedSlotResult) \ _(LoadEnvironmentFixedSlotResult) \
_(LoadEnvironmentDynamicSlotResult) \ _(LoadEnvironmentDynamicSlotResult) \
_(LoadObjectResult) \
_(CallScriptedGetterResult) \ _(CallScriptedGetterResult) \
_(CallNativeGetterResult) \ _(CallNativeGetterResult) \
_(CallProxyGetResult) \ _(CallProxyGetResult) \
@ -891,6 +893,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
writeOpWithOperandId(CacheOp::LoadEnvironmentDynamicSlotResult, obj); writeOpWithOperandId(CacheOp::LoadEnvironmentDynamicSlotResult, obj);
addStubField(offset, StubField::Type::RawWord); addStubField(offset, StubField::Type::RawWord);
} }
void loadObjectResult(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::LoadObjectResult, obj);
}
void typeMonitorResult() { void typeMonitorResult() {
writeOp(CacheOp::TypeMonitorResult); writeOp(CacheOp::TypeMonitorResult);
@ -1108,6 +1113,22 @@ class MOZ_RAII GetNameIRGenerator : public IRGenerator
bool tryAttachStub(); bool tryAttachStub();
}; };
// BindNameIRGenerator generates CacheIR for a BindName IC.
class MOZ_RAII BindNameIRGenerator : public IRGenerator
{
HandleObject env_;
HandlePropertyName name_;
bool tryAttachGlobalName(ObjOperandId objId, HandleId id);
bool tryAttachEnvironmentName(ObjOperandId objId, HandleId id);
public:
BindNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
HandleObject env, HandlePropertyName name);
bool tryAttachStub();
};
// Information used by SetProp/SetElem stubs to check/update property types. // Information used by SetProp/SetElem stubs to check/update property types.
class MOZ_RAII PropertyTypeCheckInfo class MOZ_RAII PropertyTypeCheckInfo
{ {

Просмотреть файл

@ -2109,6 +2109,20 @@ CacheIRCompiler::emitLoadTypedObjectResultShared(const Address& fieldAddr, Regis
} }
} }
bool
CacheIRCompiler::emitLoadObjectResult()
{
AutoOutputRegister output(*this);
Register obj = allocator.useRegister(masm, reader.objOperandId());
if (output.hasValue())
masm.tagValue(JSVAL_TYPE_OBJECT, obj, output.valueReg());
else
masm.mov(obj, output.typedReg().gpr());
return true;
}
void void
CacheIRCompiler::emitStoreTypedObjectReferenceProp(ValueOperand val, ReferenceTypeDescr::Type type, CacheIRCompiler::emitStoreTypedObjectReferenceProp(ValueOperand val, ReferenceTypeDescr::Type type,
const Address& dest, Register scratch) const Address& dest, Register scratch)

Просмотреть файл

@ -51,6 +51,7 @@ namespace jit {
_(LoadDenseElementHoleExistsResult) \ _(LoadDenseElementHoleExistsResult) \
_(LoadUnboxedArrayElementResult) \ _(LoadUnboxedArrayElementResult) \
_(LoadTypedElementResult) \ _(LoadTypedElementResult) \
_(LoadObjectResult) \
_(MegamorphicLoadSlotByValueResult) \ _(MegamorphicLoadSlotByValueResult) \
_(MegamorphicHasOwnResult) \ _(MegamorphicHasOwnResult) \
_(WrapResult) _(WrapResult)

Просмотреть файл

@ -70,53 +70,6 @@ using JS::GenericNaN;
namespace js { namespace js {
namespace jit { namespace jit {
// This out-of-line cache is used to do a double dispatch including it-self and
// the wrapped IonCache.
class OutOfLineUpdateCache :
public OutOfLineCodeBase<CodeGenerator>,
public IonCacheVisitor
{
private:
LInstruction* lir_;
size_t cacheIndex_;
RepatchLabel entry_;
public:
OutOfLineUpdateCache(LInstruction* lir, size_t cacheIndex)
: lir_(lir),
cacheIndex_(cacheIndex)
{ }
void bind(MacroAssembler* masm) {
// The binding of the initial jump is done in
// CodeGenerator::visitOutOfLineCache.
}
size_t getCacheIndex() const {
return cacheIndex_;
}
LInstruction* lir() const {
return lir_;
}
RepatchLabel& entry() {
return entry_;
}
void accept(CodeGenerator* codegen) {
codegen->visitOutOfLineCache(this);
}
// ICs' visit functions delegating the work to the CodeGen visit funtions.
#define VISIT_CACHE_FUNCTION(op) \
void visit##op##IC(CodeGenerator* codegen) { \
CodeGenerator::DataPtr<op##IC> ic(codegen, getCacheIndex()); \
codegen->visit##op##IC(this, ic); \
}
IONCACHE_KIND_LIST(VISIT_CACHE_FUNCTION)
#undef VISIT_CACHE_FUNCTION
};
class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator> class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator>
{ {
private: private:
@ -151,33 +104,6 @@ class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator>
} }
}; };
// This function is declared here because it needs to instantiate an
// OutOfLineUpdateCache, but we want to keep it visible inside the
// CodeGeneratorShared such as we can specialize inline caches in function of
// the architecture.
void
CodeGeneratorShared::addCache(LInstruction* lir, size_t cacheIndex)
{
if (cacheIndex == SIZE_MAX) {
masm.setOOM();
return;
}
DataPtr<IonCache> cache(this, cacheIndex);
MInstruction* mir = lir->mirRaw()->toInstruction();
if (mir->resumePoint())
cache->setScriptedLocation(mir->block()->info().script(),
mir->resumePoint()->pc());
else
cache->setIdempotent();
OutOfLineUpdateCache* ool = new(alloc()) OutOfLineUpdateCache(lir, cacheIndex);
addOutOfLineCode(ool, mir);
cache->emitInitialJump(masm, ool->entry());
masm.bind(ool->rejoin());
}
void void
CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex) CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex)
{ {
@ -208,19 +134,6 @@ CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex)
cache->setRejoinLabel(CodeOffset(ool->rejoin()->offset())); cache->setRejoinLabel(CodeOffset(ool->rejoin()->offset()));
} }
void
CodeGenerator::visitOutOfLineCache(OutOfLineUpdateCache* ool)
{
DataPtr<IonCache> cache(this, ool->getCacheIndex());
// Register the location of the OOL path in the IC.
cache->setFallbackLabel(masm.labelForPatch());
masm.bind(&ool->entry());
// Dispatch to ICs' accept functions.
cache->accept(this, ool);
}
typedef bool (*IonGetPropertyICFn)(JSContext*, HandleScript, IonGetPropertyIC*, HandleValue, HandleValue, typedef bool (*IonGetPropertyICFn)(JSContext*, HandleScript, IonGetPropertyIC*, HandleValue, HandleValue,
MutableHandleValue); MutableHandleValue);
static const VMFunction IonGetPropertyICInfo = static const VMFunction IonGetPropertyICInfo =
@ -241,6 +154,10 @@ typedef bool (*IonHasOwnICFn)(JSContext*, HandleScript, IonHasOwnIC*, HandleValu
static const VMFunction IonHasOwnICInfo = static const VMFunction IonHasOwnICInfo =
FunctionInfo<IonHasOwnICFn>(IonHasOwnIC::update, "IonHasOwnIC::update"); FunctionInfo<IonHasOwnICFn>(IonHasOwnIC::update, "IonHasOwnIC::update");
typedef JSObject* (*IonBindNameICFn)(JSContext*, HandleScript, IonBindNameIC*, HandleObject);
static const VMFunction IonBindNameICInfo =
FunctionInfo<IonBindNameICFn>(IonBindNameIC::update, "IonBindNameIC::update");
void void
CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
{ {
@ -309,6 +226,23 @@ CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
masm.jump(ool->rejoin()); masm.jump(ool->rejoin());
return; return;
} }
case CacheKind::BindName: {
IonBindNameIC* bindNameIC = ic->asBindNameIC();
saveLive(lir);
pushArg(bindNameIC->environment());
icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
pushArg(ImmGCPtr(gen->info().script()));
callVM(IonBindNameICInfo, lir);
StoreRegisterTo(bindNameIC->output()).generate(this);
restoreLiveIgnore(lir, StoreRegisterTo(bindNameIC->output()).clobbered());
masm.jump(ool->rejoin());
return;
}
case CacheKind::In: case CacheKind::In:
MOZ_CRASH("Baseline-specific for now"); MOZ_CRASH("Baseline-specific for now");
case CacheKind::HasOwn: { case CacheKind::HasOwn: {
@ -9625,7 +9559,6 @@ CodeGenerator::generateWasm(wasm::SigIdDesc sigId, wasm::BytecodeOffset trapOffs
MOZ_ASSERT(graph.numConstants() == 0); MOZ_ASSERT(graph.numConstants() == 0);
MOZ_ASSERT(safepointIndices_.empty()); MOZ_ASSERT(safepointIndices_.empty());
MOZ_ASSERT(osiIndices_.empty()); MOZ_ASSERT(osiIndices_.empty());
MOZ_ASSERT(cacheList_.empty());
MOZ_ASSERT(icList_.empty()); MOZ_ASSERT(icList_.empty());
MOZ_ASSERT(safepoints_.size() == 0); MOZ_ASSERT(safepoints_.size() == 0);
MOZ_ASSERT(!scriptCounts_); MOZ_ASSERT(!scriptCounts_);
@ -9847,7 +9780,7 @@ CodeGenerator::link(JSContext* cx, CompilerConstraintList* constraints)
snapshots_.listSize(), snapshots_.RVATableSize(), snapshots_.listSize(), snapshots_.RVATableSize(),
recovers_.size(), bailouts_.length(), graph.numConstants(), recovers_.size(), bailouts_.length(), graph.numConstants(),
safepointIndices_.length(), osiIndices_.length(), safepointIndices_.length(), osiIndices_.length(),
cacheList_.length(), icList_.length(), runtimeData_.length(), icList_.length(), runtimeData_.length(),
safepoints_.size(), patchableBackedges_.length(), safepoints_.size(), patchableBackedges_.length(),
sharedStubs_.length(), optimizationLevel); sharedStubs_.length(), optimizationLevel);
if (!ionScript) if (!ionScript)
@ -10012,8 +9945,6 @@ CodeGenerator::link(JSContext* cx, CompilerConstraintList* constraints)
// for generating inline caches during the execution. // for generating inline caches during the execution.
if (runtimeData_.length()) if (runtimeData_.length())
ionScript->copyRuntimeData(&runtimeData_[0]); ionScript->copyRuntimeData(&runtimeData_[0]);
if (cacheList_.length())
ionScript->copyCacheEntries(&cacheList_[0], masm);
if (icList_.length()) if (icList_.length())
ionScript->copyICEntries(&icList_[0], masm); ionScript->copyICEntries(&icList_[0], masm);
@ -10411,32 +10342,13 @@ CodeGenerator::visitGetPropertyCacheT(LGetPropertyCacheT* ins)
void void
CodeGenerator::visitBindNameCache(LBindNameCache* ins) CodeGenerator::visitBindNameCache(LBindNameCache* ins)
{ {
LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
Register envChain = ToRegister(ins->environmentChain()); Register envChain = ToRegister(ins->environmentChain());
Register output = ToRegister(ins->output()); Register output = ToRegister(ins->output());
BindNameIC cache(envChain, ins->mir()->name(), output); Register temp = ToRegister(ins->temp());
cache.setProfilerLeavePC(ins->mir()->profilerLeavePc());
addCache(ins, allocateCache(cache)); IonBindNameIC ic(liveRegs, envChain, output, temp);
} addIC(ins, allocateIC(ic));
typedef JSObject* (*BindNameICFn)(JSContext*, HandleScript, size_t, HandleObject);
const VMFunction BindNameIC::UpdateInfo =
FunctionInfo<BindNameICFn>(BindNameIC::update, "BindNameIC::update");
void
CodeGenerator::visitBindNameIC(OutOfLineUpdateCache* ool, DataPtr<BindNameIC>& ic)
{
LInstruction* lir = ool->lir();
saveLive(lir);
pushArg(ic->environmentChainReg());
pushArg(Imm32(ool->getCacheIndex()));
pushArg(ImmGCPtr(gen->info().script()));
callVM(BindNameIC::UpdateInfo, lir);
StoreRegisterTo(ic->outputReg()).generate(this);
restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered());
masm.jump(ool->rejoin());
} }
void void

Просмотреть файл

@ -412,8 +412,6 @@ class CodeGenerator final : public CodeGeneratorSpecific
void loadJSScriptForBlock(MBasicBlock* block, Register reg); void loadJSScriptForBlock(MBasicBlock* block, Register reg);
void loadOutermostJSScript(Register reg); void loadOutermostJSScript(Register reg);
// Inline caches visitors.
void visitOutOfLineCache(OutOfLineUpdateCache* ool);
void visitOutOfLineICFallback(OutOfLineICFallback* ool); void visitOutOfLineICFallback(OutOfLineICFallback* ool);
void visitGetPropertyCacheV(LGetPropertyCacheV* ins); void visitGetPropertyCacheV(LGetPropertyCacheV* ins);
@ -424,8 +422,6 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitGetNameCache(LGetNameCache* ins); void visitGetNameCache(LGetNameCache* ins);
void visitHasOwnCache(LHasOwnCache* ins); void visitHasOwnCache(LHasOwnCache* ins);
void visitBindNameIC(OutOfLineUpdateCache* ool, DataPtr<BindNameIC>& ic);
void visitAssertRangeI(LAssertRangeI* ins); void visitAssertRangeI(LAssertRangeI* ins);
void visitAssertRangeD(LAssertRangeD* ins); void visitAssertRangeD(LAssertRangeD* ins);
void visitAssertRangeF(LAssertRangeF* ins); void visitAssertRangeF(LAssertRangeF* ins);

Просмотреть файл

@ -859,8 +859,6 @@ IonScript::IonScript()
recompiling_(false), recompiling_(false),
runtimeData_(0), runtimeData_(0),
runtimeSize_(0), runtimeSize_(0),
cacheIndex_(0),
cacheEntries_(0),
icIndex_(0), icIndex_(0),
icEntries_(0), icEntries_(0),
safepointIndexOffset_(0), safepointIndexOffset_(0),
@ -893,7 +891,7 @@ IonScript::New(JSContext* cx, RecompileInfo recompileInfo,
size_t snapshotsListSize, size_t snapshotsRVATableSize, size_t snapshotsListSize, size_t snapshotsRVATableSize,
size_t recoversSize, size_t bailoutEntries, size_t recoversSize, size_t bailoutEntries,
size_t constants, size_t safepointIndices, size_t constants, size_t safepointIndices,
size_t osiIndices, size_t cacheEntries, size_t icEntries, size_t osiIndices, size_t icEntries,
size_t runtimeSize, size_t safepointsSize, size_t runtimeSize, size_t safepointsSize,
size_t backedgeEntries, size_t sharedStubEntries, size_t backedgeEntries, size_t sharedStubEntries,
OptimizationLevel optimizationLevel) OptimizationLevel optimizationLevel)
@ -916,7 +914,6 @@ IonScript::New(JSContext* cx, RecompileInfo recompileInfo,
size_t paddedConstantsSize = AlignBytes(constants * sizeof(Value), DataAlignment); size_t paddedConstantsSize = AlignBytes(constants * sizeof(Value), DataAlignment);
size_t paddedSafepointIndicesSize = AlignBytes(safepointIndices * sizeof(SafepointIndex), DataAlignment); size_t paddedSafepointIndicesSize = AlignBytes(safepointIndices * sizeof(SafepointIndex), DataAlignment);
size_t paddedOsiIndicesSize = AlignBytes(osiIndices * sizeof(OsiIndex), DataAlignment); size_t paddedOsiIndicesSize = AlignBytes(osiIndices * sizeof(OsiIndex), DataAlignment);
size_t paddedCacheEntriesSize = AlignBytes(cacheEntries * sizeof(uint32_t), DataAlignment);
size_t paddedICEntriesSize = AlignBytes(icEntries * sizeof(uint32_t), DataAlignment); size_t paddedICEntriesSize = AlignBytes(icEntries * sizeof(uint32_t), DataAlignment);
size_t paddedRuntimeSize = AlignBytes(runtimeSize, DataAlignment); size_t paddedRuntimeSize = AlignBytes(runtimeSize, DataAlignment);
size_t paddedSafepointSize = AlignBytes(safepointsSize, DataAlignment); size_t paddedSafepointSize = AlignBytes(safepointsSize, DataAlignment);
@ -929,7 +926,6 @@ IonScript::New(JSContext* cx, RecompileInfo recompileInfo,
paddedConstantsSize + paddedConstantsSize +
paddedSafepointIndicesSize + paddedSafepointIndicesSize +
paddedOsiIndicesSize + paddedOsiIndicesSize +
paddedCacheEntriesSize +
paddedICEntriesSize + paddedICEntriesSize +
paddedRuntimeSize + paddedRuntimeSize +
paddedSafepointSize + paddedSafepointSize +
@ -946,10 +942,6 @@ IonScript::New(JSContext* cx, RecompileInfo recompileInfo,
script->runtimeSize_ = runtimeSize; script->runtimeSize_ = runtimeSize;
offsetCursor += paddedRuntimeSize; offsetCursor += paddedRuntimeSize;
script->cacheIndex_ = offsetCursor;
script->cacheEntries_ = cacheEntries;
offsetCursor += paddedCacheEntriesSize;
script->icIndex_ = offsetCursor; script->icIndex_ = offsetCursor;
script->icEntries_ = icEntries; script->icEntries_ = icEntries;
offsetCursor += paddedICEntriesSize; offsetCursor += paddedICEntriesSize;
@ -1028,9 +1020,6 @@ IonScript::trace(JSTracer* trc)
} }
// Trace caches so that the JSScript pointer can be updated if moved. // Trace caches so that the JSScript pointer can be updated if moved.
for (size_t i = 0; i < numCaches(); i++)
getCacheFromIndex(i).trace(trc);
for (size_t i = 0; i < numICs(); i++) for (size_t i = 0; i < numICs(); i++)
getICFromIndex(i).trace(trc); getICFromIndex(i).trace(trc);
} }
@ -1133,18 +1122,6 @@ IonScript::copyRuntimeData(const uint8_t* data)
memcpy(runtimeData(), data, runtimeSize()); memcpy(runtimeData(), data, runtimeSize());
} }
void
IonScript::copyCacheEntries(const uint32_t* caches, MacroAssembler& masm)
{
memcpy(cacheIndex(), caches, numCaches() * sizeof(uint32_t));
// Jumps in the caches reflect the offset of those jumps in the compiled
// code, not the absolute positions of the jumps. Update according to the
// final code address now.
for (size_t i = 0; i < numCaches(); i++)
getCacheFromIndex(i).updateBaseAddress(method_, masm);
}
void void
IonScript::copyICEntries(const uint32_t* icEntries, MacroAssembler& masm) IonScript::copyICEntries(const uint32_t* icEntries, MacroAssembler& masm)
{ {
@ -1331,25 +1308,6 @@ IonScript::purgeOptimizedStubs(Zone* zone)
#endif #endif
} }
void
IonScript::purgeCaches()
{
// Don't reset any ICs if we're invalidated, otherwise, repointing the
// inline jump could overwrite an invalidation marker. These ICs can
// no longer run, however, the IC slow paths may be active on the stack.
// ICs therefore are required to check for invalidation before patching,
// to ensure the same invariant.
if (invalidated())
return;
if (numCaches() == 0)
return;
AutoWritableJitCode awjc(method());
for (size_t i = 0; i < numCaches(); i++)
getCacheFromIndex(i).reset(DontReprotect);
}
void void
IonScript::purgeICs(Zone* zone) IonScript::purgeICs(Zone* zone)
{ {
@ -3122,7 +3080,6 @@ InvalidateActivation(FreeOp* fop, const JitActivationIterator& activations, bool
// Purge ICs before we mark this script as invalidated. This will // Purge ICs before we mark this script as invalidated. This will
// prevent lastJump_ from appearing to be a bogus pointer, just // prevent lastJump_ from appearing to be a bogus pointer, just
// in case anyone tries to read it. // in case anyone tries to read it.
ionScript->purgeCaches();
ionScript->purgeICs(script->zone()); ionScript->purgeICs(script->zone());
ionScript->purgeOptimizedStubs(script->zone()); ionScript->purgeOptimizedStubs(script->zone());
@ -3531,13 +3488,6 @@ AutoFlushICache::~AutoFlushICache()
#endif #endif
} }
void
jit::PurgeCaches(JSScript* script)
{
if (script->hasIonScript())
script->ionScript()->purgeCaches();
}
size_t size_t
jit::SizeOfIonData(JSScript* script, mozilla::MallocSizeOf mallocSizeOf) jit::SizeOfIonData(JSScript* script, mozilla::MallocSizeOf mallocSizeOf)
{ {

Просмотреть файл

@ -214,7 +214,6 @@ bool OffThreadCompilationAvailable(JSContext* cx);
void ForbidCompilation(JSContext* cx, JSScript* script); void ForbidCompilation(JSContext* cx, JSScript* script);
void PurgeCaches(JSScript* script);
size_t SizeOfIonData(JSScript* script, mozilla::MallocSizeOf mallocSizeOf); size_t SizeOfIonData(JSScript* script, mozilla::MallocSizeOf mallocSizeOf);
void DestroyJitScripts(FreeOp* fop, JSScript* script); void DestroyJitScripts(FreeOp* fop, JSScript* script);
void TraceJitScripts(JSTracer* trc, JSScript* script); void TraceJitScripts(JSTracer* trc, JSScript* script);

Просмотреть файл

@ -426,6 +426,20 @@ IonCacheIRCompiler::init()
allocator.initInputLocation(0, ic->environment(), JSVAL_TYPE_OBJECT); allocator.initInputLocation(0, ic->environment(), JSVAL_TYPE_OBJECT);
break; break;
} }
case CacheKind::BindName: {
IonBindNameIC* ic = ic_->asBindNameIC();
Register output = ic->output();
available.add(output);
available.add(ic->temp());
liveRegs_.emplace(ic->liveRegs());
outputUnchecked_.emplace(TypedOrValueRegister(MIRType::Object, AnyRegister(output)));
MOZ_ASSERT(numInputs == 1);
allocator.initInputLocation(0, ic->environment(), JSVAL_TYPE_OBJECT);
break;
}
case CacheKind::In: case CacheKind::In:
MOZ_CRASH("Invalid cache"); MOZ_CRASH("Invalid cache");
case CacheKind::HasOwn: { case CacheKind::HasOwn: {

Просмотреть файл

@ -38,8 +38,6 @@ using namespace js::jit;
using mozilla::tl::FloorLog2; using mozilla::tl::FloorLog2;
typedef Rooted<TypedArrayObject*> RootedTypedArrayObject;
void void
CodeLocationJump::repoint(JitCode* code, MacroAssembler* masm) CodeLocationJump::repoint(JitCode* code, MacroAssembler* masm)
{ {
@ -89,308 +87,6 @@ CodeOffsetJump::fixup(MacroAssembler* masm)
#endif #endif
} }
const char*
IonCache::CacheName(IonCache::Kind kind)
{
static const char * const names[] =
{
#define NAME(x) #x,
IONCACHE_KIND_LIST(NAME)
#undef NAME
};
return names[kind];
}
const size_t IonCache::MAX_STUBS = 16;
// Helper class which encapsulates logic to attach a stub to an IC by hooking
// up rejoins and next stub jumps.
//
// The simplest stubs have a single jump to the next stub and look like the
// following:
//
// branch guard NEXTSTUB
// ... IC-specific code ...
// jump REJOIN
//
// This corresponds to:
//
// attacher.branchNextStub(masm, ...);
// ... emit IC-specific code ...
// attacher.jumpRejoin(masm);
//
// Whether the stub needs multiple next stub jumps look like:
//
// branch guard FAILURES
// ... IC-specific code ...
// branch another-guard FAILURES
// ... IC-specific code ...
// jump REJOIN
// FAILURES:
// jump NEXTSTUB
//
// This corresponds to:
//
// Label failures;
// masm.branchX(..., &failures);
// ... emit IC-specific code ...
// masm.branchY(..., failures);
// ... emit more IC-specific code ...
// attacher.jumpRejoin(masm);
// masm.bind(&failures);
// attacher.jumpNextStub(masm);
//
// A convenience function |branchNextStubOrLabel| is provided in the case that
// the stub sometimes has multiple next stub jumps and sometimes a single
// one. If a non-nullptr label is passed in, a |branchPtr| will be made to
// that label instead of a |branchPtrWithPatch| to the next stub.
class IonCache::StubAttacher
{
protected:
bool hasNextStubOffset_ : 1;
bool hasStubCodePatchOffset_ : 1;
IonCache& cache_;
CodeLocationLabel rejoinLabel_;
CodeOffsetJump nextStubOffset_;
CodeOffsetJump rejoinOffset_;
CodeOffset stubCodePatchOffset_;
public:
explicit StubAttacher(IonCache& cache)
: hasNextStubOffset_(false),
hasStubCodePatchOffset_(false),
cache_(cache),
rejoinLabel_(cache.rejoinLabel_),
nextStubOffset_(),
rejoinOffset_(),
stubCodePatchOffset_()
{ }
// Value used instead of the JitCode self-reference of generated
// stubs. This value is needed for marking calls made inside stubs. This
// value would be replaced by the attachStub function after the allocation
// of the JitCode. The self-reference is used to keep the stub path alive
// even if the IonScript is invalidated or if the IC is flushed.
static const void* const STUB_ADDR;
template <class T1, class T2>
void branchNextStub(MacroAssembler& masm, Assembler::Condition cond, T1 op1, T2 op2) {
MOZ_ASSERT(!hasNextStubOffset_);
RepatchLabel nextStub;
nextStubOffset_ = masm.branchPtrWithPatch(cond, op1, op2, &nextStub);
hasNextStubOffset_ = true;
masm.bind(&nextStub);
}
template <class T1, class T2>
void branchNextStubOrLabel(MacroAssembler& masm, Assembler::Condition cond, T1 op1, T2 op2,
Label* label)
{
if (label != nullptr)
masm.branchPtr(cond, op1, op2, label);
else
branchNextStub(masm, cond, op1, op2);
}
void jumpRejoin(MacroAssembler& masm) {
RepatchLabel rejoin;
rejoinOffset_ = masm.jumpWithPatch(&rejoin);
masm.bind(&rejoin);
}
void jumpNextStub(MacroAssembler& masm) {
MOZ_ASSERT(!hasNextStubOffset_);
RepatchLabel nextStub;
nextStubOffset_ = masm.jumpWithPatch(&nextStub);
hasNextStubOffset_ = true;
masm.bind(&nextStub);
}
void pushStubCodePointer(MacroAssembler& masm) {
// Push the JitCode pointer for the stub we're generating.
// WARNING:
// WARNING: If JitCode ever becomes relocatable, the following code is incorrect.
// WARNING: Note that we're not marking the pointer being pushed as an ImmGCPtr.
// WARNING: This location will be patched with the pointer of the generated stub,
// WARNING: such as it can be marked when a call is made with this stub. Be aware
// WARNING: that ICs are not marked and so this stub will only be kept alive iff
// WARNING: it is on the stack at the time of the GC. No ImmGCPtr is needed as the
// WARNING: stubs are flushed on GC.
// WARNING:
MOZ_ASSERT(!hasStubCodePatchOffset_);
stubCodePatchOffset_ = masm.PushWithPatch(ImmPtr(STUB_ADDR));
hasStubCodePatchOffset_ = true;
}
void patchRejoinJump(MacroAssembler& masm, JitCode* code) {
rejoinOffset_.fixup(&masm);
CodeLocationJump rejoinJump(code, rejoinOffset_);
PatchJump(rejoinJump, rejoinLabel_);
}
void patchStubCodePointer(JitCode* code) {
if (hasStubCodePatchOffset_) {
Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, stubCodePatchOffset_),
ImmPtr(code), ImmPtr(STUB_ADDR));
}
}
void patchNextStubJump(MacroAssembler& masm, JitCode* code) {
// If this path is not taken, we are producing an entry which can no
// longer go back into the update function.
if (hasNextStubOffset_) {
nextStubOffset_.fixup(&masm);
CodeLocationJump nextStubJump(code, nextStubOffset_);
PatchJump(nextStubJump, cache_.fallbackLabel_);
// When the last stub fails, it fallback to the ool call which can
// produce a stub. Next time we generate a stub, we will patch the
// nextStub jump to try the new stub.
cache_.lastJump_ = nextStubJump;
}
}
};
const void* const IonCache::StubAttacher::STUB_ADDR = (void*)0xdeadc0de;
void
IonCache::emitInitialJump(MacroAssembler& masm, RepatchLabel& entry)
{
initialJump_ = masm.jumpWithPatch(&entry);
lastJump_ = initialJump_;
Label label;
masm.bind(&label);
rejoinLabel_ = CodeOffset(label.offset());
}
void
IonCache::attachStub(MacroAssembler& masm, StubAttacher& attacher, CodeLocationJump lastJump,
Handle<JitCode*> code)
{
MOZ_ASSERT(canAttachStub());
incrementStubCount();
// Patch the previous nextStubJump of the last stub, or the jump from the
// codeGen, to jump into the newly allocated code.
PatchJump(lastJump, CodeLocationLabel(code), Reprotect);
}
IonCache::LinkStatus
IonCache::linkCode(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher, IonScript* ion,
JitCode** code)
{
Linker linker(masm);
*code = linker.newCode<CanGC>(cx, ION_CODE);
if (!*code)
return LINK_ERROR;
if (ion->invalidated())
return CACHE_FLUSHED;
// Update the success path to continue after the IC initial jump.
attacher.patchRejoinJump(masm, *code);
// Replace the STUB_ADDR constant by the address of the generated stub, such
// as it can be kept alive even if the cache is flushed (see
// MarkJitExitFrame).
attacher.patchStubCodePointer(*code);
// Update the failure path.
attacher.patchNextStubJump(masm, *code);
return LINK_GOOD;
}
bool
IonCache::linkAndAttachStub(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher,
IonScript* ion, const char* attachKind,
JS::TrackedOutcome trackedOutcome)
{
CodeLocationJump lastJumpBefore = lastJump_;
Rooted<JitCode*> code(cx);
{
// Need to exit the AutoFlushICache context to flush the cache
// before attaching the stub below.
AutoFlushICache afc("IonCache");
LinkStatus status = linkCode(cx, masm, attacher, ion, code.address());
if (status != LINK_GOOD)
return status != LINK_ERROR;
}
if (pc_) {
JitSpew(JitSpew_IonIC, "Cache %p(%s:%" PRIuSIZE "/%" PRIuSIZE ") generated %s %s stub at %p",
this, script_->filename(), script_->lineno(), script_->pcToOffset(pc_),
attachKind, CacheName(kind()), code->raw());
} else {
JitSpew(JitSpew_IonIC, "Cache %p generated %s %s stub at %p",
this, attachKind, CacheName(kind()), code->raw());
}
#ifdef JS_ION_PERF
writePerfSpewerJitCodeProfile(code, "IonCache");
#endif
attachStub(masm, attacher, lastJumpBefore, code);
// Add entry to native => bytecode mapping for this stub if needed.
if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) {
JitcodeGlobalEntry::IonCacheEntry entry;
entry.init(code, code->raw(), code->rawEnd(), rejoinAddress(), trackedOutcome);
// Add entry to the global table.
JitcodeGlobalTable* globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
if (!globalTable->addEntry(entry, cx->runtime())) {
entry.destroy();
ReportOutOfMemory(cx);
return false;
}
// Mark the jitcode as having a bytecode map.
code->setHasBytecodeMap();
} else {
JitcodeGlobalEntry::DummyEntry entry;
entry.init(code, code->raw(), code->rawEnd());
// Add entry to the global table.
JitcodeGlobalTable* globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
if (!globalTable->addEntry(entry, cx->runtime())) {
entry.destroy();
ReportOutOfMemory(cx);
return false;
}
// Mark the jitcode as having a bytecode map.
code->setHasBytecodeMap();
}
// Report masm OOM errors here, so all our callers can:
// return linkAndAttachStub(...);
if (masm.oom()) {
ReportOutOfMemory(cx);
return false;
}
return true;
}
void
IonCache::updateBaseAddress(JitCode* code, MacroAssembler& masm)
{
fallbackLabel_.repoint(code, &masm);
initialJump_.repoint(code, &masm);
lastJump_.repoint(code, &masm);
rejoinLabel_.repoint(code, &masm);
}
void
IonCache::trace(JSTracer* trc)
{
if (script_)
TraceManuallyBarrieredEdge(trc, &script_, "IonCache::script_");
}
void* void*
jit::GetReturnAddressToIonCode(JSContext* cx) jit::GetReturnAddressToIonCode(JSContext* cx)
{ {
@ -523,21 +219,6 @@ jit::ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
return true; return true;
} }
void
IonCache::disable()
{
reset(Reprotect);
this->disabled_ = 1;
}
void
IonCache::reset(ReprotectCode reprotect)
{
this->stubCount_ = 0;
PatchJump(initialJump_, fallbackLabel_, reprotect);
lastJump_ = initialJump_;
}
bool bool
jit::IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape) jit::IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
{ {
@ -642,171 +323,3 @@ jit::EmitIonStoreDenseElement(MacroAssembler& masm, const ConstantOrRegister& va
masm.bind(&done); masm.bind(&done);
} }
bool
BindNameIC::attachGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject envChain)
{
MOZ_ASSERT(envChain->is<GlobalObject>());
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
StubAttacher attacher(*this);
// Guard on the env chain.
attacher.branchNextStub(masm, Assembler::NotEqual, environmentChainReg(),
ImmGCPtr(envChain));
masm.movePtr(ImmGCPtr(envChain), outputReg());
attacher.jumpRejoin(masm);
return linkAndAttachStub(cx, masm, attacher, ion, "global");
}
static inline void
GenerateEnvironmentChainGuard(MacroAssembler& masm, JSObject* envObj,
Register envObjReg, Shape* shape, Label* failures)
{
if (envObj->is<CallObject>()) {
// We can skip a guard on the call object if the script's bindings are
// guaranteed to be immutable (and thus cannot introduce shadowing
// variables).
CallObject* callObj = &envObj->as<CallObject>();
JSFunction* fun = &callObj->callee();
// The function might have been relazified under rare conditions.
// In that case, we pessimistically create the guard, as we'd
// need to root various pointers to delazify,
if (fun->hasScript()) {
JSScript* script = fun->nonLazyScript();
if (!script->funHasExtensibleScope())
return;
}
} else if (envObj->is<GlobalObject>()) {
// If this is the last object on the scope walk, and the property we've
// found is not configurable, then we don't need a shape guard because
// the shape cannot be removed.
if (shape && !shape->configurable())
return;
}
Address shapeAddr(envObjReg, ShapedObject::offsetOfShape());
masm.branchPtr(Assembler::NotEqual, shapeAddr,
ImmGCPtr(envObj->as<NativeObject>().lastProperty()), failures);
}
static void
GenerateEnvironmentChainGuards(MacroAssembler& masm, JSObject* envChain, JSObject* holder,
Register outputReg, Label* failures, bool skipLastGuard = false)
{
JSObject* tobj = envChain;
// Walk up the env chain. Note that IsCacheableEnvironmentChain guarantees the
// |tobj == holder| condition terminates the loop.
while (true) {
MOZ_ASSERT(IsCacheableEnvironment(tobj) || tobj->is<GlobalObject>());
if (skipLastGuard && tobj == holder)
break;
GenerateEnvironmentChainGuard(masm, tobj, outputReg, nullptr, failures);
if (tobj == holder)
break;
// Load the next link.
tobj = &tobj->as<EnvironmentObject>().enclosingEnvironment();
masm.extractObject(Address(outputReg, EnvironmentObject::offsetOfEnclosingEnvironment()),
outputReg);
}
}
bool
BindNameIC::attachNonGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject envChain, HandleObject holder)
{
MOZ_ASSERT(IsCacheableEnvironment(envChain));
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
StubAttacher attacher(*this);
// Guard on the shape of the env chain.
Label failures;
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(environmentChainReg(), ShapedObject::offsetOfShape()),
ImmGCPtr(envChain->as<NativeObject>().lastProperty()),
holder != envChain ? &failures : nullptr);
if (holder != envChain) {
JSObject* parent = &envChain->as<EnvironmentObject>().enclosingEnvironment();
masm.extractObject(Address(environmentChainReg(),
EnvironmentObject::offsetOfEnclosingEnvironment()),
outputReg());
GenerateEnvironmentChainGuards(masm, parent, holder, outputReg(), &failures);
} else {
masm.movePtr(environmentChainReg(), outputReg());
}
// At this point outputReg holds the object on which the property
// was found, so we're done.
attacher.jumpRejoin(masm);
// All failures flow to here, so there is a common point to patch.
if (holder != envChain) {
masm.bind(&failures);
attacher.jumpNextStub(masm);
}
return linkAndAttachStub(cx, masm, attacher, ion, "non-global");
}
static bool
IsCacheableNonGlobalEnvironmentChain(JSObject* envChain, JSObject* holder)
{
while (true) {
if (!IsCacheableEnvironment(envChain)) {
JitSpew(JitSpew_IonIC, "Non-cacheable object on env chain");
return false;
}
if (envChain == holder)
return true;
envChain = &envChain->as<EnvironmentObject>().enclosingEnvironment();
if (!envChain) {
JitSpew(JitSpew_IonIC, "env chain indirect hit");
return false;
}
}
MOZ_CRASH("Invalid env chain");
}
JSObject*
BindNameIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
HandleObject envChain)
{
IonScript* ion = outerScript->ionScript();
BindNameIC& cache = ion->getCache(cacheIndex).toBindName();
HandlePropertyName name = cache.name();
RootedObject holder(cx);
if (!LookupNameUnqualified(cx, name, envChain, &holder))
return nullptr;
// Stop generating new stubs once we hit the stub count limit, see
// GetPropertyCache.
if (cache.canAttachStub()) {
if (envChain->is<GlobalObject>()) {
if (!cache.attachGlobal(cx, outerScript, ion, envChain))
return nullptr;
} else if (IsCacheableNonGlobalEnvironmentChain(envChain, holder)) {
if (!cache.attachNonGlobal(cx, outerScript, ion, envChain, holder))
return nullptr;
} else {
JitSpew(JitSpew_IonIC, "BINDNAME uncacheable env chain");
}
}
return holder;
}

Просмотреть файл

@ -26,392 +26,6 @@
namespace js { namespace js {
namespace jit { namespace jit {
class LInstruction;
#define IONCACHE_KIND_LIST(_) \
_(BindName)
// Forward declarations of Cache kinds.
#define FORWARD_DECLARE(kind) class kind##IC;
IONCACHE_KIND_LIST(FORWARD_DECLARE)
#undef FORWARD_DECLARE
class IonCacheVisitor
{
public:
#define VISIT_INS(op) \
virtual void visit##op##IC(CodeGenerator* codegen) { \
MOZ_CRASH("NYI: " #op "IC"); \
}
IONCACHE_KIND_LIST(VISIT_INS)
#undef VISIT_INS
};
// Common structure encoding the state of a polymorphic inline cache contained
// in the code for an IonScript. IonCaches are used for polymorphic operations
// where multiple implementations may be required.
//
// Roughly speaking, the cache initially jumps to an out of line fragment
// which invokes a cache function to perform the operation. The cache function
// may generate a stub to perform the operation in certain cases (e.g. a
// particular shape for an input object) and attach the stub to existing
// stubs, forming a daisy chain of tests for how to perform the operation in
// different circumstances.
//
// Eventually, if too many stubs are generated the cache function may disable
// the cache, by generating a stub to make a call and perform the operation
// within the VM.
//
// The caches initially generate a patchable jump to an out of line call
// to the cache function. Stubs are attached by appending: when attaching a
// new stub, we patch the any failure conditions in last generated stub to
// jump to the new stub. Failure conditions in the new stub jump to the cache
// function which may generate new stubs.
//
// Control flow Pointers
// =======# ----. .---->
// # | |
// #======> \-----/
//
// Initial state:
//
// JIT Code
// +--------+ .---------------.
// | | | |
// |========| v +----------+ |
// |== IC ==|====>| Cache Fn | |
// |========| +----------+ |
// | |<=# # |
// | | #=======# |
// +--------+ Rejoin path |
// |________ |
// | |
// IC | |
// Entry | |
// +------------+ |
// | lastJump_ |---------------/
// +------------+
// | ... |
// +------------+
//
// Attaching stubs:
//
// Patch the jump pointed to by lastJump_ to jump to the new stub. Update
// lastJump_ to be the new stub's failure jump. The failure jump of the new
// stub goes to the fallback label, which is the cache function. In this
// fashion, new stubs are _appended_ to the chain of stubs, as lastJump_
// points to the _tail_ of the stub chain.
//
// JIT Code
// +--------+ #=======================#
// | | # v
// |========| # +----------+ +------+
// |== IC ==|=# | Cache Fn |<====| Stub |
// |========| +----------+ ^ +------+
// | |<=# # | #
// | | #======#=========|=====#
// +--------+ Rejoin path |
// |________ |
// | |
// IC | |
// Entry | |
// +------------+ |
// | lastJump_ |---------------/
// +------------+
// | ... |
// +------------+
//
// While calls may be made to the cache function and other VM functions, the
// cache may still be treated as pure during optimization passes, such that
// LICM and GVN may be performed on operations around the cache as if the
// operation cannot reenter scripted code through an Invoke() or otherwise have
// unexpected behavior. This restricts the sorts of stubs which the cache can
// generate or the behaviors which called functions can have, and if a called
// function performs a possibly impure operation then the operation will be
// marked as such and the calling script will be recompiled.
//
// Similarly, despite the presence of functions and multiple stubs generated
// for a cache, the cache itself may be marked as idempotent and become hoisted
// or coalesced by LICM or GVN. This also constrains the stubs which can be
// generated for the cache.
//
// * IonCache usage
//
// IonCache is the base structure of an inline cache, which generates code stubs
// dynamically and attaches them to an IonScript.
//
// A cache must at least provide a static update function which will usualy have
// a JSContext*, followed by the cache index. The rest of the arguments of the
// update function are usualy corresponding to the register inputs of the cache,
// as it must perform the same operation as any of the stubs that it might
// produce. The update function call is handled by the visit function of
// CodeGenerator corresponding to this IC.
//
// The CodeGenerator visit function, as opposed to other visit functions, has
// two arguments. The first one is the OutOfLineUpdateCache which stores the LIR
// instruction. The second one is the IC object. This function would be called
// once the IC is registered with the addCache function of CodeGeneratorShared.
//
// To register a cache, you must call the addCache function as follow:
//
// MyCodeIC cache(inputReg1, inputValueReg2, outputReg);
// if (!addCache(lir, allocateCache(cache)))
// return false;
//
// Once the cache is allocated with the allocateCache function, any modification
// made to the cache would be ignored.
//
// The addCache function will produce a patchable jump at the location where
// it is called. This jump will execute generated stubs and fallback on the code
// of the visitMyCodeIC function if no stub match.
//
// Warning: As the addCache function fallback on a VMCall, calls to
// addCache should not be in the same path as another VMCall or in the same
// path of another addCache as this is not supported by the invalidation
// procedure.
class IonCache
{
public:
class StubAttacher;
enum Kind {
# define DEFINE_CACHEKINDS(ickind) Cache_##ickind,
IONCACHE_KIND_LIST(DEFINE_CACHEKINDS)
# undef DEFINE_CACHEKINDS
Cache_Invalid
};
// Cache testing and cast.
# define CACHEKIND_CASTS(ickind) \
bool is##ickind() const { \
return kind() == Cache_##ickind; \
} \
inline ickind##IC& to##ickind(); \
inline const ickind##IC& to##ickind() const;
IONCACHE_KIND_LIST(CACHEKIND_CASTS)
# undef CACHEKIND_CASTS
virtual Kind kind() const = 0;
virtual void accept(CodeGenerator* codegen, IonCacheVisitor* visitor) = 0;
public:
static const char* CacheName(Kind kind);
protected:
bool pure_ : 1;
bool idempotent_ : 1;
bool disabled_ : 1;
size_t stubCount_ : 5;
CodeLocationLabel fallbackLabel_;
// Location of this operation, nullptr for idempotent caches.
JSScript* script_;
jsbytecode* pc_;
// Location to use when updating profiler pseudostack when leaving this
// IC code to enter a callee.
jsbytecode* profilerLeavePc_;
CodeLocationJump initialJump_;
CodeLocationJump lastJump_;
CodeLocationLabel rejoinLabel_;
private:
static const size_t MAX_STUBS;
void incrementStubCount() {
// The IC should stop generating stubs before wrapping stubCount.
stubCount_++;
MOZ_ASSERT(stubCount_);
}
public:
IonCache()
: pure_(false),
idempotent_(false),
disabled_(false),
stubCount_(0),
fallbackLabel_(),
script_(nullptr),
pc_(nullptr),
profilerLeavePc_(nullptr),
initialJump_(),
lastJump_(),
rejoinLabel_()
{
}
void disable();
inline bool isDisabled() const {
return disabled_;
}
// Set the initial 'out-of-line' jump state of the cache. The fallbackLabel is
// the location of the out-of-line update (slow) path. This location will
// be set to the exitJump of the last generated stub.
void setFallbackLabel(CodeOffset fallbackLabel) {
fallbackLabel_ = fallbackLabel;
}
void setProfilerLeavePC(jsbytecode* pc) {
MOZ_ASSERT(pc != nullptr);
profilerLeavePc_ = pc;
}
// Get the address at which IC rejoins the mainline jitcode.
void* rejoinAddress() const {
return rejoinLabel_.raw();
}
void emitInitialJump(MacroAssembler& masm, RepatchLabel& entry);
void updateBaseAddress(JitCode* code, MacroAssembler& masm);
// Reset the cache around garbage collection.
virtual void reset(ReprotectCode reprotect);
bool canAttachStub() const {
return stubCount_ < MAX_STUBS;
}
bool empty() const {
return stubCount_ == 0;
}
enum LinkStatus {
LINK_ERROR,
CACHE_FLUSHED,
LINK_GOOD
};
// Use the Linker to link the generated code and check if any
// monitoring/allocation caused an invalidation of the running ion script,
// this function returns CACHE_FLUSHED. In case of allocation issue this
// function returns LINK_ERROR.
LinkStatus linkCode(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher, IonScript* ion,
JitCode** code);
// Fixup variables and update jumps in the list of stubs. Increment the
// number of attached stubs accordingly.
void attachStub(MacroAssembler& masm, StubAttacher& attacher, CodeLocationJump lastJump,
Handle<JitCode*> code);
// Combine both linkStub and attachStub into one function. In addition, it
// produces a spew augmented with the attachKind string.
MOZ_MUST_USE bool linkAndAttachStub(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher,
IonScript* ion, const char* attachKind,
JS::TrackedOutcome = JS::TrackedOutcome::ICOptStub_GenericSuccess);
#ifdef DEBUG
bool isAllocated() {
return fallbackLabel_.isSet();
}
#endif
bool pure() const {
return pure_;
}
bool idempotent() const {
return idempotent_;
}
void setIdempotent() {
MOZ_ASSERT(!idempotent_);
MOZ_ASSERT(!script_);
MOZ_ASSERT(!pc_);
idempotent_ = true;
}
void setScriptedLocation(JSScript* script, jsbytecode* pc) {
MOZ_ASSERT(!idempotent_);
script_ = script;
pc_ = pc;
}
void getScriptedLocation(MutableHandleScript pscript, jsbytecode** ppc) const {
pscript.set(script_);
*ppc = pc_;
}
jsbytecode* pc() const {
MOZ_ASSERT(pc_);
return pc_;
}
void trace(JSTracer* trc);
};
// Define the cache kind and pre-declare data structures used for calling inline
// caches.
#define CACHE_HEADER(ickind) \
Kind kind() const { \
return IonCache::Cache_##ickind; \
} \
\
void accept(CodeGenerator* codegen, IonCacheVisitor* visitor) { \
visitor->visit##ickind##IC(codegen); \
} \
\
static const VMFunction UpdateInfo;
// Subclasses of IonCache for the various kinds of caches. These do not define
// new data members; all caches must be of the same size.
class BindNameIC : public IonCache
{
protected:
Register environmentChain_;
PropertyName* name_;
Register output_;
public:
BindNameIC(Register envChain, PropertyName* name, Register output)
: environmentChain_(envChain),
name_(name),
output_(output)
{
}
CACHE_HEADER(BindName)
Register environmentChainReg() const {
return environmentChain_;
}
HandlePropertyName name() const {
return HandlePropertyName::fromMarkedLocation(&name_);
}
Register outputReg() const {
return output_;
}
MOZ_MUST_USE bool attachGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject envChain);
MOZ_MUST_USE bool attachNonGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject envChain, HandleObject holder);
static JSObject*
update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain);
};
#undef CACHE_HEADER
// Implement cache casts now that the compiler can see the inheritance.
#define CACHE_CASTS(ickind) \
ickind##IC& IonCache::to##ickind() \
{ \
MOZ_ASSERT(is##ickind()); \
return *static_cast<ickind##IC*>(this); \
} \
const ickind##IC& IonCache::to##ickind() const \
{ \
MOZ_ASSERT(is##ickind()); \
return *static_cast<const ickind##IC*>(this); \
}
IONCACHE_KIND_LIST(CACHE_CASTS)
#undef OPCODE_CASTS
bool IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder); bool IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder);
bool IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder, bool IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder,
PropertyResult prop); PropertyResult prop);

Просмотреть файл

@ -168,10 +168,8 @@ class RecoverWriter;
class SafepointWriter; class SafepointWriter;
class SafepointIndex; class SafepointIndex;
class OsiIndex; class OsiIndex;
class IonCache;
class IonIC; class IonIC;
struct PatchableBackedgeInfo; struct PatchableBackedgeInfo;
struct CacheLocation;
// An IonScript attaches Ion-generated information to a JSScript. // An IonScript attaches Ion-generated information to a JSScript.
struct IonScript struct IonScript
@ -216,12 +214,6 @@ struct IonScript
uint32_t runtimeData_; uint32_t runtimeData_;
uint32_t runtimeSize_; uint32_t runtimeSize_;
// State for polymorphic caches in the compiled code. All caches are stored
// in the runtimeData buffer and indexed by the cacheIndex which gives a
// relative offset in the runtimeData array.
uint32_t cacheIndex_;
uint32_t cacheEntries_;
// State for polymorphic caches in the compiled code. All caches are stored // State for polymorphic caches in the compiled code. All caches are stored
// in the runtimeData buffer and indexed by the icIndex which gives a // in the runtimeData buffer and indexed by the icIndex which gives a
// relative offset in the runtimeData array. // relative offset in the runtimeData array.
@ -322,9 +314,6 @@ struct IonScript
OsiIndex* osiIndices() { OsiIndex* osiIndices() {
return (OsiIndex*) &bottomBuffer()[osiIndexOffset_]; return (OsiIndex*) &bottomBuffer()[osiIndexOffset_];
} }
uint32_t* cacheIndex() {
return (uint32_t*) &bottomBuffer()[cacheIndex_];
}
uint32_t* icIndex() { uint32_t* icIndex() {
return (uint32_t*) &bottomBuffer()[icIndex_]; return (uint32_t*) &bottomBuffer()[icIndex_];
} }
@ -353,7 +342,7 @@ struct IonScript
size_t snapshotsListSize, size_t snapshotsRVATableSize, size_t snapshotsListSize, size_t snapshotsRVATableSize,
size_t recoversSize, size_t bailoutEntries, size_t recoversSize, size_t bailoutEntries,
size_t constants, size_t safepointIndexEntries, size_t constants, size_t safepointIndexEntries,
size_t osiIndexEntries, size_t cacheEntries, size_t icEntries, size_t osiIndexEntries, size_t icEntries,
size_t runtimeSize, size_t safepointsSize, size_t runtimeSize, size_t safepointsSize,
size_t backedgeEntries, size_t sharedStubEntries, size_t backedgeEntries, size_t sharedStubEntries,
OptimizationLevel optimizationLevel); OptimizationLevel optimizationLevel);
@ -501,18 +490,7 @@ struct IonScript
} }
const OsiIndex* getOsiIndex(uint32_t disp) const; const OsiIndex* getOsiIndex(uint32_t disp) const;
const OsiIndex* getOsiIndex(uint8_t* retAddr) const; const OsiIndex* getOsiIndex(uint8_t* retAddr) const;
inline IonCache& getCacheFromIndex(uint32_t index) {
MOZ_ASSERT(index < cacheEntries_);
uint32_t offset = cacheIndex()[index];
return getCache(offset);
}
inline IonCache& getCache(uint32_t offset) {
MOZ_ASSERT(offset < runtimeSize_);
return *(IonCache*) &runtimeData()[offset];
}
size_t numCaches() const {
return cacheEntries_;
}
IonIC& getICFromIndex(uint32_t index) { IonIC& getICFromIndex(uint32_t index) {
MOZ_ASSERT(index < icEntries_); MOZ_ASSERT(index < icEntries_);
uint32_t offset = icIndex()[index]; uint32_t offset = icIndex()[index];
@ -534,12 +512,7 @@ struct IonScript
size_t runtimeSize() const { size_t runtimeSize() const {
return runtimeSize_; return runtimeSize_;
} }
CacheLocation* getCacheLocs(uint32_t locIndex) {
MOZ_ASSERT(locIndex < runtimeSize_);
return (CacheLocation*) &runtimeData()[locIndex];
}
void toggleBarriers(bool enabled, ReprotectCode reprotect = Reprotect); void toggleBarriers(bool enabled, ReprotectCode reprotect = Reprotect);
void purgeCaches();
void purgeICs(Zone* zone); void purgeICs(Zone* zone);
void unlinkFromRuntime(FreeOp* fop); void unlinkFromRuntime(FreeOp* fop);
void copySnapshots(const SnapshotWriter* writer); void copySnapshots(const SnapshotWriter* writer);
@ -549,7 +522,6 @@ struct IonScript
void copySafepointIndices(const SafepointIndex* firstSafepointIndex, MacroAssembler& masm); void copySafepointIndices(const SafepointIndex* firstSafepointIndex, MacroAssembler& masm);
void copyOsiIndices(const OsiIndex* firstOsiIndex, MacroAssembler& masm); void copyOsiIndices(const OsiIndex* firstOsiIndex, MacroAssembler& masm);
void copyRuntimeData(const uint8_t* data); void copyRuntimeData(const uint8_t* data);
void copyCacheEntries(const uint32_t* caches, MacroAssembler& masm);
void copyICEntries(const uint32_t* caches, MacroAssembler& masm); void copyICEntries(const uint32_t* caches, MacroAssembler& masm);
void copySafepoints(const SafepointWriter* writer); void copySafepoints(const SafepointWriter* writer);
void copyPatchableBackedges(JSContext* cx, JitCode* code, void copyPatchableBackedges(JSContext* cx, JitCode* code,

Просмотреть файл

@ -46,6 +46,8 @@ IonIC::scratchRegisterForEntryJump()
return asSetPropertyIC()->temp(); return asSetPropertyIC()->temp();
case CacheKind::GetName: case CacheKind::GetName:
return asGetNameIC()->temp(); return asGetNameIC()->temp();
case CacheKind::BindName:
return asBindNameIC()->temp();
case CacheKind::In: case CacheKind::In:
MOZ_CRASH("Baseline-specific for now"); MOZ_CRASH("Baseline-specific for now");
case CacheKind::HasOwn: case CacheKind::HasOwn:
@ -285,13 +287,12 @@ IonGetNameIC::update(JSContext* cx, HandleScript outerScript, IonGetNameIC* ic,
HandleObject envChain, MutableHandleValue res) HandleObject envChain, MutableHandleValue res)
{ {
IonScript* ionScript = outerScript->ionScript(); IonScript* ionScript = outerScript->ionScript();
jsbytecode* pc = ic->pc();
RootedPropertyName name(cx, ic->script()->getName(pc));
if (ic->state().maybeTransition()) if (ic->state().maybeTransition())
ic->discardStubs(cx->zone()); ic->discardStubs(cx->zone());
jsbytecode* pc = ic->pc();
RootedPropertyName name(cx, ic->script()->getName(pc));
if (ic->state().canAttachStub()) { if (ic->state().canAttachStub()) {
bool attached = false; bool attached = false;
RootedScript script(cx, ic->script()); RootedScript script(cx, ic->script());
@ -323,6 +324,35 @@ IonGetNameIC::update(JSContext* cx, HandleScript outerScript, IonGetNameIC* ic,
return true; return true;
} }
/* static */ JSObject*
IonBindNameIC::update(JSContext* cx, HandleScript outerScript, IonBindNameIC* ic,
HandleObject envChain)
{
IonScript* ionScript = outerScript->ionScript();
jsbytecode* pc = ic->pc();
RootedPropertyName name(cx, ic->script()->getName(pc));
if (ic->state().maybeTransition())
ic->discardStubs(cx->zone());
if (ic->state().canAttachStub()) {
bool attached = false;
RootedScript script(cx, ic->script());
BindNameIRGenerator gen(cx, script, pc, ic->state().mode(), envChain, name);
if (gen.tryAttachStub())
ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
if (!attached)
ic->state().trackNotAttached();
}
RootedObject holder(cx);
if (!LookupNameUnqualified(cx, name, envChain, &holder))
return nullptr;
return holder;
}
/* static */ bool /* static */ bool
IonHasOwnIC::update(JSContext* cx, HandleScript outerScript, IonHasOwnIC* ic, IonHasOwnIC::update(JSContext* cx, HandleScript outerScript, IonHasOwnIC* ic,
HandleValue val, HandleValue idVal, int32_t* res) HandleValue val, HandleValue idVal, int32_t* res)

Просмотреть файл

@ -59,6 +59,7 @@ class IonICStub
class IonGetPropertyIC; class IonGetPropertyIC;
class IonSetPropertyIC; class IonSetPropertyIC;
class IonGetNameIC; class IonGetNameIC;
class IonBindNameIC;
class IonHasOwnIC; class IonHasOwnIC;
class IonIC class IonIC
@ -145,6 +146,10 @@ class IonIC
MOZ_ASSERT(kind_ == CacheKind::GetName); MOZ_ASSERT(kind_ == CacheKind::GetName);
return (IonGetNameIC*)this; return (IonGetNameIC*)this;
} }
IonBindNameIC* asBindNameIC() {
MOZ_ASSERT(kind_ == CacheKind::BindName);
return (IonBindNameIC*)this;
}
IonHasOwnIC* asHasOwnIC() { IonHasOwnIC* asHasOwnIC() {
MOZ_ASSERT(kind_ == CacheKind::HasOwn); MOZ_ASSERT(kind_ == CacheKind::HasOwn);
return (IonHasOwnIC*)this; return (IonHasOwnIC*)this;
@ -280,6 +285,32 @@ class IonGetNameIC : public IonIC
HandleObject envChain, MutableHandleValue res); HandleObject envChain, MutableHandleValue res);
}; };
class IonBindNameIC : public IonIC
{
LiveRegisterSet liveRegs_;
Register environment_;
Register output_;
Register temp_;
public:
IonBindNameIC(LiveRegisterSet liveRegs, Register environment, Register output, Register temp)
: IonIC(CacheKind::BindName),
liveRegs_(liveRegs),
environment_(environment),
output_(output),
temp_(temp)
{ }
Register environment() const { return environment_; }
Register output() const { return output_; }
Register temp() const { return temp_; }
LiveRegisterSet liveRegs() const { return liveRegs_; }
static JSObject* update(JSContext* cx, HandleScript outerScript, IonBindNameIC* ic,
HandleObject envChain);
};
class IonHasOwnIC : public IonIC class IonHasOwnIC : public IonIC
{ {
LiveRegisterSet liveRegs_; LiveRegisterSet liveRegs_;

Просмотреть файл

@ -3817,7 +3817,7 @@ LIRGenerator::visitBindNameCache(MBindNameCache* ins)
MOZ_ASSERT(ins->environmentChain()->type() == MIRType::Object); MOZ_ASSERT(ins->environmentChain()->type() == MIRType::Object);
MOZ_ASSERT(ins->type() == MIRType::Object); MOZ_ASSERT(ins->type() == MIRType::Object);
LBindNameCache* lir = new(alloc()) LBindNameCache(useRegister(ins->environmentChain())); LBindNameCache* lir = new(alloc()) LBindNameCache(useRegister(ins->environmentChain()), temp());
define(lir, ins); define(lir, ins);
assignSafepoint(lir, ins); assignSafepoint(lir, ins);
} }

Просмотреть файл

@ -27,7 +27,6 @@ namespace jit {
class OutOfLineCode; class OutOfLineCode;
class CodeGenerator; class CodeGenerator;
class MacroAssembler; class MacroAssembler;
class IonCache;
class IonIC; class IonIC;
template <class ArgSeq, class StoreOutputTo> template <class ArgSeq, class StoreOutputTo>
@ -102,9 +101,6 @@ class CodeGeneratorShared : public LElementVisitor
// Allocated data space needed at runtime. // Allocated data space needed at runtime.
js::Vector<uint8_t, 0, SystemAllocPolicy> runtimeData_; js::Vector<uint8_t, 0, SystemAllocPolicy> runtimeData_;
// Vector of information about generated polymorphic inline caches.
js::Vector<uint32_t, 0, SystemAllocPolicy> cacheList_;
// Vector mapping each IC index to its offset in runtimeData_. // Vector mapping each IC index to its offset in runtimeData_.
js::Vector<uint32_t, 0, SystemAllocPolicy> icList_; js::Vector<uint32_t, 0, SystemAllocPolicy> icList_;
@ -289,23 +285,6 @@ class CodeGeneratorShared : public LElementVisitor
return !masm.oom(); return !masm.oom();
} }
// Ensure the cache is an IonCache while expecting the size of the derived
// class. We only need the cache list at GC time. Everyone else can just take
// runtimeData offsets.
template <typename T>
inline size_t allocateCache(const T& cache) {
static_assert(mozilla::IsBaseOf<IonCache, T>::value, "T must inherit from IonCache");
size_t index;
masm.propagateOOM(allocateData(sizeof(mozilla::AlignedStorage2<T>), &index));
masm.propagateOOM(cacheList_.append(index));
if (masm.oom())
return SIZE_MAX;
// Use the copy constructor on the allocated space.
MOZ_ASSERT(index == cacheList_.back());
new (&runtimeData_[index]) T(cache);
return index;
}
template <typename T> template <typename T>
inline size_t allocateIC(const T& cache) { inline size_t allocateIC(const T& cache) {
static_assert(mozilla::IsBaseOf<IonIC, T>::value, "T must inherit from IonIC"); static_assert(mozilla::IsBaseOf<IonIC, T>::value, "T must inherit from IonIC");
@ -497,7 +476,6 @@ class CodeGeneratorShared : public LElementVisitor
inline OutOfLineCode* oolCallVM(const VMFunction& fun, LInstruction* ins, const ArgSeq& args, inline OutOfLineCode* oolCallVM(const VMFunction& fun, LInstruction* ins, const ArgSeq& args,
const StoreOutputTo& out); const StoreOutputTo& out);
void addCache(LInstruction* lir, size_t cacheIndex);
void addIC(LInstruction* lir, size_t cacheIndex); void addIC(LInstruction* lir, size_t cacheIndex);
ReciprocalMulConstants computeDivisionConstants(uint32_t d, int maxLog); ReciprocalMulConstants computeDivisionConstants(uint32_t d, int maxLog);

Просмотреть файл

@ -6850,17 +6850,21 @@ class LSetPropertyPolymorphicT : public LInstructionHelper<0, 2, 1>
} }
}; };
class LBindNameCache : public LInstructionHelper<1, 1, 0> class LBindNameCache : public LInstructionHelper<1, 1, 1>
{ {
public: public:
LIR_HEADER(BindNameCache) LIR_HEADER(BindNameCache)
explicit LBindNameCache(const LAllocation& envChain) { LBindNameCache(const LAllocation& envChain, const LDefinition& temp) {
setOperand(0, envChain); setOperand(0, envChain);
setTemp(0, temp);
} }
const LAllocation* environmentChain() { const LAllocation* environmentChain() {
return getOperand(0); return getOperand(0);
} }
const LDefinition* temp() {
return getTemp(0);
}
const MBindNameCache* mir() const { const MBindNameCache* mir() const {
return mir_->toBindNameCache(); return mir_->toBindNameCache();
} }

Просмотреть файл

@ -7243,14 +7243,6 @@ js::ReleaseAllJITCode(FreeOp* fop)
} }
} }
void
js::PurgeJITCaches(Zone* zone)
{
/* Discard Ion caches. */
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next())
jit::PurgeCaches(script);
}
void void
ArenaLists::normalizeBackgroundFinalizeState(AllocKind thingKind) ArenaLists::normalizeBackgroundFinalizeState(AllocKind thingKind)
{ {

Просмотреть файл

@ -1405,9 +1405,6 @@ struct MOZ_RAII AutoDisableCompactingGC
JSContext* cx; JSContext* cx;
}; };
void
PurgeJITCaches(JS::Zone* zone);
// This is the same as IsInsideNursery, but not inlined. // This is the same as IsInsideNursery, but not inlined.
bool bool
UninlinedIsInsideNursery(const gc::Cell* cell); UninlinedIsInsideNursery(const gc::Cell* cell);

Просмотреть файл

@ -138,7 +138,6 @@ struct MOZ_STACK_CLASS BidiParagraphData
bool mIsVisual; bool mIsVisual;
nsBidiLevel mParaLevel; nsBidiLevel mParaLevel;
nsIContent* mPrevContent; nsIContent* mPrevContent;
nsBidi mBidiEngine;
nsIFrame* mPrevFrame; nsIFrame* mPrevFrame;
#ifdef DEBUG #ifdef DEBUG
// Only used for NOISY debug output. // Only used for NOISY debug output.
@ -184,8 +183,9 @@ struct MOZ_STACK_CLASS BidiParagraphData
nsresult SetPara() nsresult SetPara()
{ {
return mBidiEngine.SetPara(mBuffer.get(), BufferLength(), return mPresContext->GetBidiEngine()
mParaLevel); .SetPara(mBuffer.get(), BufferLength(),
mParaLevel);
} }
/** /**
@ -197,7 +197,7 @@ struct MOZ_STACK_CLASS BidiParagraphData
{ {
nsBidiLevel paraLevel = mParaLevel; nsBidiLevel paraLevel = mParaLevel;
if (paraLevel == NSBIDI_DEFAULT_LTR || paraLevel == NSBIDI_DEFAULT_RTL) { if (paraLevel == NSBIDI_DEFAULT_LTR || paraLevel == NSBIDI_DEFAULT_RTL) {
mBidiEngine.GetParaLevel(&paraLevel); mPresContext->GetBidiEngine().GetParaLevel(&paraLevel);
} }
return paraLevel; return paraLevel;
} }
@ -205,18 +205,22 @@ struct MOZ_STACK_CLASS BidiParagraphData
nsBidiDirection GetDirection() nsBidiDirection GetDirection()
{ {
nsBidiDirection dir; nsBidiDirection dir;
mBidiEngine.GetDirection(&dir); mPresContext->GetBidiEngine().GetDirection(&dir);
return dir; return dir;
} }
nsresult CountRuns(int32_t *runCount){ return mBidiEngine.CountRuns(runCount); } nsresult CountRuns(int32_t *runCount)
{
return mPresContext->GetBidiEngine().CountRuns(runCount);
}
nsresult GetLogicalRun(int32_t aLogicalStart, nsresult GetLogicalRun(int32_t aLogicalStart,
int32_t* aLogicalLimit, int32_t* aLogicalLimit,
nsBidiLevel* aLevel) nsBidiLevel* aLevel)
{ {
nsresult rv = mBidiEngine.GetLogicalRun(aLogicalStart, nsresult rv =
aLogicalLimit, aLevel); mPresContext->GetBidiEngine().GetLogicalRun(aLogicalStart,
aLogicalLimit, aLevel);
if (mIsVisual || NS_FAILED(rv)) if (mIsVisual || NS_FAILED(rv))
*aLevel = GetParaLevel(); *aLevel = GetParaLevel();
return rv; return rv;
@ -2152,7 +2156,8 @@ nsresult nsBidiPresUtils::ProcessText(const char16_t* aText,
const char16_t* visualLeftPart; const char16_t* visualLeftPart;
const char16_t* visualRightSide; const char16_t* visualRightSide;
if (dir == NSBIDI_RTL) { if (dir == NSBIDI_RTL) {
// One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ... // One day, son, this could all be replaced with
// mPresContext->GetBidiEngine().GetVisualIndex() ...
posResolve->visualIndex = visualStart + (subRunLength - (posResolve->logicalIndex + 1 - start)); posResolve->visualIndex = visualStart + (subRunLength - (posResolve->logicalIndex + 1 - start));
// Skipping to the "left part". // Skipping to the "left part".
visualLeftPart = text + posResolve->logicalIndex + 1; visualLeftPart = text + posResolve->logicalIndex + 1;
@ -2275,9 +2280,9 @@ nsresult nsBidiPresUtils::ProcessTextForRenderingContext(const char16_t* a
aTextRunConstructionDrawTarget, aTextRunConstructionDrawTarget,
&aFontMetrics, &aFontMetrics,
nsPoint(aX, aY)); nsPoint(aX, aY));
nsBidi bidiEngine;
return ProcessText(aText, aLength, aBaseLevel, aPresContext, processor, return ProcessText(aText, aLength, aBaseLevel, aPresContext, processor,
aMode, aPosResolve, aPosResolveCount, aWidth, &bidiEngine); aMode, aPosResolve, aPosResolveCount, aWidth,
&aPresContext->GetBidiEngine());
} }
/* static */ /* static */

Просмотреть файл

@ -183,6 +183,10 @@ public:
static nsresult ReorderVisual(const nsBidiLevel* aLevels, int32_t aLength, static nsresult ReorderVisual(const nsBidiLevel* aLevels, int32_t aLength,
int32_t* aIndexMap); int32_t* aIndexMap);
private:
nsBidi(const nsBidi&) = delete;
void operator=(const nsBidi&) = delete;
protected: protected:
UBiDi* mBiDi; UBiDi* mBiDi;
}; };

Просмотреть файл

@ -704,6 +704,9 @@ private:
void ReorderLine(nsBidiLevel aMinLevel, nsBidiLevel aMaxLevel); void ReorderLine(nsBidiLevel aMinLevel, nsBidiLevel aMaxLevel);
static bool PrepareReorder(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap, nsBidiLevel *aMinLevel, nsBidiLevel *aMaxLevel); static bool PrepareReorder(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap, nsBidiLevel *aMinLevel, nsBidiLevel *aMaxLevel);
nsBidi(const nsBidi&) = delete;
void operator=(const nsBidi&) = delete;
}; };
#endif // _nsBidi_noICU_h_ #endif // _nsBidi_noICU_h_

Просмотреть файл

@ -89,6 +89,7 @@
#include "nsCSSParser.h" #include "nsCSSParser.h"
#include "nsBidiUtils.h" #include "nsBidiUtils.h"
#include "nsServiceManagerUtils.h" #include "nsServiceManagerUtils.h"
#include "nsBidi.h"
#include "mozilla/dom/URL.h" #include "mozilla/dom/URL.h"
@ -3012,6 +3013,17 @@ nsPresContext::GetRestyleGeneration() const
return mRestyleManager->GetRestyleGeneration(); return mRestyleManager->GetRestyleGeneration();
} }
nsBidi&
nsPresContext::GetBidiEngine()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mBidiEngine) {
mBidiEngine.reset(new nsBidi());
}
return *mBidiEngine;
}
nsRootPresContext::nsRootPresContext(nsIDocument* aDocument, nsRootPresContext::nsRootPresContext(nsIDocument* aDocument,
nsPresContextType aType) nsPresContextType aType)
: nsPresContext(aDocument, aType), : nsPresContext(aDocument, aType),

Просмотреть файл

@ -9,6 +9,7 @@
#define nsPresContext_h___ #define nsPresContext_h___
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WeakPtr.h" #include "mozilla/WeakPtr.h"
#include "nsColor.h" #include "nsColor.h"
#include "nsCoord.h" #include "nsCoord.h"
@ -45,6 +46,7 @@
#include "mozilla/StyleBackendType.h" #include "mozilla/StyleBackendType.h"
class nsAString; class nsAString;
class nsBidi;
class nsIPrintSettings; class nsIPrintSettings;
class nsDocShell; class nsDocShell;
class nsIDocShell; class nsIDocShell;
@ -1193,6 +1195,8 @@ public:
mHasWarnedAboutTooLargeDashedOrDottedRadius = true; mHasWarnedAboutTooLargeDashedOrDottedRadius = true;
} }
nsBidi& GetBidiEngine();
protected: protected:
friend class nsRunnableMethod<nsPresContext>; friend class nsRunnableMethod<nsPresContext>;
void ThemeChangedInternal(); void ThemeChangedInternal();
@ -1349,6 +1353,8 @@ protected:
nsCOMPtr<nsIPrintSettings> mPrintSettings; nsCOMPtr<nsIPrintSettings> mPrintSettings;
nsCOMPtr<nsITimer> mPrefChangedTimer; nsCOMPtr<nsITimer> mPrefChangedTimer;
mozilla::UniquePtr<nsBidi> mBidiEngine;
FramePropertyTable mPropertyTable; FramePropertyTable mPropertyTable;
struct TransactionInvalidations { struct TransactionInvalidations {

Просмотреть файл

@ -29,11 +29,11 @@ skip-if = toolkit == 'android' # autocomplete
[test_basic_form_autocomplete.html] [test_basic_form_autocomplete.html]
skip-if = toolkit == 'android' # android:autocomplete. skip-if = toolkit == 'android' # android:autocomplete.
[test_insecure_form_field_autocomplete.html] [test_insecure_form_field_autocomplete.html]
skip-if = toolkit == 'android' # android:autocomplete. skip-if = toolkit == 'android' || os == 'linux' # android:autocomplete., linux: bug 1325778
[test_password_field_autocomplete.html] [test_password_field_autocomplete.html]
skip-if = toolkit == 'android' # android:autocomplete. skip-if = toolkit == 'android' # android:autocomplete.
[test_insecure_form_field_no_saved_login.html] [test_insecure_form_field_no_saved_login.html]
skip-if = toolkit == 'android' || os == 'linux' # android:autocomplete., linux: bug 1325778 skip-if = toolkit == 'android' # android:autocomplete.
[test_basic_form_html5.html] [test_basic_form_html5.html]
[test_basic_form_pwevent.html] [test_basic_form_pwevent.html]
[test_basic_form_pwonly.html] [test_basic_form_pwonly.html]

Просмотреть файл

@ -246,19 +246,35 @@ Summariser::Rule(uintptr_t aAddress, int aNewReg,
// is the heart of the summarisation process. // is the heart of the summarisation process.
switch (aNewReg) { switch (aNewReg) {
case DW_REG_CFA: case DW_REG_CFA: {
// This is a rule that defines the CFA. The only forms we can // This is a rule that defines the CFA. The only forms we choose to
// represent are: = SP+offset or = FP+offset. // represent are: = SP+offset, = FP+offset, or =prefix-expr.
if (how != NODEREF) { switch (how) {
reason1 = "rule for DW_REG_CFA: invalid |how|"; case NODEREF:
goto cant_summarise; if (oldReg != DW_REG_INTEL_XSP && oldReg != DW_REG_INTEL_XBP) {
} reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
if (oldReg != DW_REG_INTEL_XSP && oldReg != DW_REG_INTEL_XBP) { goto cant_summarise;
reason1 = "rule for DW_REG_CFA: invalid |oldReg|"; }
goto cant_summarise; break;
case DEREF:
reason1 = "rule for DW_REG_CFA: invalid |how|";
goto cant_summarise;
case PFXEXPR: {
// Check that the prefix expression only mentions tracked registers.
const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
reason2 = checkPfxExpr(pfxInstrs, offset);
if (reason2) {
reason1 = "rule for CFA: ";
goto cant_summarise;
}
break;
}
default:
goto cant_summarise;
} }
mCurrRules.mCfaExpr = LExpr(how, oldReg, offset); mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
break; break;
}
case DW_REG_INTEL_XSP: case DW_REG_INTEL_XBP: case DW_REG_INTEL_XIP: { case DW_REG_INTEL_XSP: case DW_REG_INTEL_XBP: case DW_REG_INTEL_XIP: {
// This is a new rule for XSP, XBP or XIP (the return address). // This is a new rule for XSP, XBP or XIP (the return address).

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Firefox for Android", "Graphics, Panning and Zooming")
# List of stems to generate .cpp and .h files for. To add a stem, add it to # List of stems to generate .cpp and .h files for. To add a stem, add it to
# this list and ensure that $(stem)-classes.txt exists in this directory. # this list and ensure that $(stem)-classes.txt exists in this directory.
generated = [ generated = [

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Firefox for Android", "GeckoView")
EXPORTS += [ EXPORTS += [
'FennecJNINatives.h', 'FennecJNINatives.h',
'FennecJNIWrappers.h', 'FennecJNIWrappers.h',

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Firefox for Android", "General")
EXPORTS.mozilla.jni += [ EXPORTS.mozilla.jni += [
'Accessors.h', 'Accessors.h',
'Natives.h', 'Natives.h',

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget: Android")
DIRS += [ DIRS += [
'bindings', 'bindings',
'fennec', 'fennec',

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
XPIDL_SOURCES += [ XPIDL_SOURCES += [
'nsPIWidgetCocoa.idl', 'nsPIWidgetCocoa.idl',
] ]

Просмотреть файл

@ -14,6 +14,9 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget: Gonk")
EXPORTS += [ EXPORTS += [
'GeckoTouchDispatcher.h', 'GeckoTouchDispatcher.h',
'GonkPermission.h', 'GonkPermission.h',

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget: Gtk")
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk3': if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk3':
DIRS += ['mozgtk'] DIRS += ['mozgtk']

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget: Gtk")
EXPORTS += [ EXPORTS += [
'gtk2xtbin.h', 'gtk2xtbin.h',
] ]

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget")
DIRS += ['tests'] DIRS += ['tests']
LOCAL_INCLUDES += [ LOCAL_INCLUDES += [

Просмотреть файл

@ -4,6 +4,30 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget")
with Files("crashtests/*1128214*"):
BUG_COMPONENT = ("Core", "Layout")
with Files("crashtests/*303901*"):
BUG_COMPONENT = ("Core", "Graphics")
with Files("crashtests/*380359*"):
BUG_COMPONENT = ("Core", "Widget")
with Files("reftests/**"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("reftests/*fallback*"):
BUG_COMPONENT = ("Core", "Layout: Form Controls")
with Files("*CompositorWidget*"):
BUG_COMPONENT = ("Core", "Graphics")
with Files("*FontRange*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
toolkit = CONFIG['MOZ_WIDGET_TOOLKIT'] toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
if toolkit in ('cocoa', 'android', 'gonk', 'uikit'): if toolkit in ('cocoa', 'android', 'gonk', 'uikit'):

Просмотреть файл

@ -4,6 +4,121 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget")
with Files("unit/*macwebapputils*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("unit/*taskbar_jumplistitems*"):
BUG_COMPONENT = ("Core", "Widget: Win32")
with Files("TestAppShellSteadyState.cpp"):
BUG_COMPONENT = ("Core", "DOM: IndexedDB")
with Files("TestChromeMargin.cpp"):
BUG_COMPONENT = ("Core", "Widget: win32")
with Files("*1151186*"):
BUG_COMPONENT = ("Core", "Event Handling")
with Files("*413277*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*428405*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*429954*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*444800*"):
BUG_COMPONENT = ("Core", "Widget: Win32")
with Files("*466599*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*478536*"):
BUG_COMPONENT = ("Core", "Event Handling")
with Files("*485118*"):
BUG_COMPONENT = ("Toolkit", "XUL Widgets")
with Files("*517396*"):
BUG_COMPONENT = ("Toolkit", "XUL Widgets")
with Files("*522217*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*538242*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*565392*"):
BUG_COMPONENT = ("Core", "Serializers")
with Files("*586713*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*593307*"):
BUG_COMPONENT = ("Core", "Widget: Win32")
with Files("*596600*"):
BUG_COMPONENT = ("Core", "Event Handling")
with Files("*673301*"):
BUG_COMPONENT = ("Firefox", "Bookmarks & History")
with Files("test_assign_event_data.html"):
BUG_COMPONENT = ("Core", "Event Handling")
with Files("test_input_events_on_deactive_window.xul"):
BUG_COMPONENT = ("Core", "Event Handling")
with Files("*chrome_context_menus_win*"):
BUG_COMPONENT = ("Core", "General")
with Files("*composition_text_querycontent*"):
BUG_COMPONENT = ("Core", "Internationalization")
with Files("*key_event_counts*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*imestate*"):
BUG_COMPONENT = ("Core", "Internationalization")
with Files("*mouse_scroll*"):
BUG_COMPONENT = ("Core", "Widget: Win32")
with Files("*native*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*panel_mouse_coords*"):
BUG_COMPONENT = ("Core", "Widget: Gtk")
with Files("*picker_no_crash*"):
BUG_COMPONENT = ("Core", "Widget: Win32")
with Files("*platform_colors*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*plugin*"):
BUG_COMPONENT = ("Core", "Plug-ins")
with Files("*position_on_resize*"):
BUG_COMPONENT = ("Core", "Widget: Gtk")
with Files("test_sizemode_events.xul"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*system_status_bar*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*taskbar_progress*"):
BUG_COMPONENT = ("Core", "Widget: Win32")
with Files("*wheeltransaction*"):
BUG_COMPONENT = ("Core", "Event Handling")
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini'] XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
MOCHITEST_MANIFESTS += ['mochitest.ini'] MOCHITEST_MANIFESTS += ['mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini'] MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget")
SOURCES += [ SOURCES += [
'GfxInfo.cpp', 'GfxInfo.cpp',
'nsAppShell.mm', 'nsAppShell.mm',

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget: Win32")
TEST_DIRS += ['tests'] TEST_DIRS += ['tests']
EXPORTS += [ EXPORTS += [

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget: Gtk")
SOURCES += [ SOURCES += [
'keysym2ucs.c', 'keysym2ucs.c',
] ]

Просмотреть файл

@ -4,6 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Widget")
FINAL_LIBRARY = 'xul' FINAL_LIBRARY = 'xul'
SOURCES += [ SOURCES += [