From fa611df2beae7c365cada003b56dad3f968c8467 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 18 Apr 2013 15:43:17 -0400 Subject: [PATCH 01/90] Bug 833964 - Don't keep a dangling pointer to a task that is about to get deleted. r=drs --- gfx/layers/ipc/AsyncPanZoomController.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index f466d1d4b8db..893fbe8c5c91 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -1473,6 +1473,7 @@ void AsyncPanZoomController::SetState(PanZoomState aState) { } void AsyncPanZoomController::TimeoutTouchListeners() { + mTouchListenerTimeoutTask = nullptr; ContentReceivedTouch(false); } From 762cc5aba3d1e54bfe3b36a42df6ff350f77252e Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 18 Apr 2013 13:33:13 -0700 Subject: [PATCH 02/90] Bug 860913 part 1: Use ALLOW_THIS_IN_INITIALIZER_LIST macro to silence existing instances of MSVC warning C4355 in content/html/content/src/. r=mounir --- content/html/content/src/HTMLAnchorElement.h | 3 ++- content/html/content/src/HTMLAreaElement.cpp | 3 ++- content/html/content/src/HTMLLinkElement.cpp | 3 ++- content/html/content/src/HTMLSelectElement.cpp | 3 ++- content/html/content/src/HTMLTextAreaElement.cpp | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/content/html/content/src/HTMLAnchorElement.h b/content/html/content/src/HTMLAnchorElement.h index 17b2eb369222..c5fc60dd88e6 100644 --- a/content/html/content/src/HTMLAnchorElement.h +++ b/content/html/content/src/HTMLAnchorElement.h @@ -11,6 +11,7 @@ #include "nsIDOMHTMLAnchorElement.h" #include "nsILink.h" #include "Link.h" +#include "base/compiler_specific.h" namespace mozilla { namespace dom { @@ -26,7 +27,7 @@ public: HTMLAnchorElement(already_AddRefed aNodeInfo) : nsGenericHTMLElement(aNodeInfo) - , Link(this) + , ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)) { SetIsDOMBinding(); } diff --git a/content/html/content/src/HTMLAreaElement.cpp b/content/html/content/src/HTMLAreaElement.cpp index 48839c50edef..f51b9ac78c5b 100644 --- a/content/html/content/src/HTMLAreaElement.cpp +++ b/content/html/content/src/HTMLAreaElement.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/HTMLAreaElement.h" #include "mozilla/dom/HTMLAreaElementBinding.h" +#include "base/compiler_specific.h" NS_IMPL_NS_NEW_HTML_ELEMENT(Area) @@ -14,7 +15,7 @@ namespace dom { HTMLAreaElement::HTMLAreaElement(already_AddRefed aNodeInfo) : nsGenericHTMLElement(aNodeInfo), - Link(this) + ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)) { SetIsDOMBinding(); } diff --git a/content/html/content/src/HTMLLinkElement.cpp b/content/html/content/src/HTMLLinkElement.cpp index fe3e3adff185..8de7375cd833 100644 --- a/content/html/content/src/HTMLLinkElement.cpp +++ b/content/html/content/src/HTMLLinkElement.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/HTMLLinkElement.h" #include "mozilla/dom/HTMLLinkElementBinding.h" +#include "base/compiler_specific.h" #include "nsGenericHTMLElement.h" #include "nsILink.h" #include "nsGkAtoms.h" @@ -31,7 +32,7 @@ namespace dom { HTMLLinkElement::HTMLLinkElement(already_AddRefed aNodeInfo) : nsGenericHTMLElement(aNodeInfo), - Link(this) + ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)) { SetIsDOMBinding(); } diff --git a/content/html/content/src/HTMLSelectElement.cpp b/content/html/content/src/HTMLSelectElement.cpp index 044a0e887a6b..75b9f07f0298 100644 --- a/content/html/content/src/HTMLSelectElement.cpp +++ b/content/html/content/src/HTMLSelectElement.cpp @@ -11,6 +11,7 @@ #include "mozilla/dom/HTMLOptionElement.h" #include "mozilla/dom/HTMLSelectElementBinding.h" #include "mozilla/Util.h" +#include "base/compiler_specific.h" #include "nsContentCreatorFunctions.h" #include "nsError.h" #include "nsEventDispatcher.h" @@ -104,7 +105,7 @@ SafeOptionListMutation::~SafeOptionListMutation() HTMLSelectElement::HTMLSelectElement(already_AddRefed aNodeInfo, FromParser aFromParser) : nsGenericHTMLFormElement(aNodeInfo), - mOptions(new HTMLOptionsCollection(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(mOptions(new HTMLOptionsCollection(this))), mIsDoneAddingChildren(!aFromParser), mDisabledChanged(false), mMutating(false), diff --git a/content/html/content/src/HTMLTextAreaElement.cpp b/content/html/content/src/HTMLTextAreaElement.cpp index 974ffbb4877c..4e1ec5fdfc81 100644 --- a/content/html/content/src/HTMLTextAreaElement.cpp +++ b/content/html/content/src/HTMLTextAreaElement.cpp @@ -7,6 +7,7 @@ #include "mozilla/dom/HTMLTextAreaElement.h" #include "mozilla/dom/HTMLTextAreaElementBinding.h" #include "mozilla/Util.h" +#include "base/compiler_specific.h" #include "nsIControllers.h" #include "nsFocusManager.h" @@ -59,7 +60,7 @@ HTMLTextAreaElement::HTMLTextAreaElement(already_AddRefed aNodeInfo mDisabledChanged(false), mCanShowInvalidUI(true), mCanShowValidUI(true), - mState(this) + ALLOW_THIS_IN_INITIALIZER_LIST(mState(this)) { AddMutationObserver(this); From 639048a535b19820e6c39a3e273049f23c368a2d Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 18 Apr 2013 13:33:14 -0700 Subject: [PATCH 03/90] Bug 860913 part 2: Replace arg-less MOZ_NOT_REACHED with MOZ_ASSERT in HTMLInputElement.cpp, to fix MSVC build warning and match intended semantics. r=mounir --- content/html/content/src/HTMLInputElement.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/content/html/content/src/HTMLInputElement.cpp b/content/html/content/src/HTMLInputElement.cpp index 70062b936465..8eb74b322a55 100644 --- a/content/html/content/src/HTMLInputElement.cpp +++ b/content/html/content/src/HTMLInputElement.cpp @@ -1225,11 +1225,9 @@ HTMLInputElement::ConvertStringToNumber(nsAString& aValue, aResultValue = static_cast(milliseconds); return true; default: + MOZ_ASSERT(false, "Unrecognized input type"); return false; } - - MOZ_NOT_REACHED(); - return false; } double @@ -1429,7 +1427,7 @@ HTMLInputElement::ConvertNumberToString(double aValue, return true; } default: - MOZ_NOT_REACHED(); + MOZ_ASSERT(false, "Unrecognized input type"); return false; } } @@ -1488,7 +1486,7 @@ HTMLInputElement::GetValueAsDate(JSContext* aCx, ErrorResult& aRv) } } - MOZ_NOT_REACHED(); + MOZ_ASSERT(false, "Unrecognized input type"); aRv.Throw(NS_ERROR_UNEXPECTED); return JS::NullValue(); } @@ -6076,7 +6074,7 @@ HTMLInputElement::GetStepScaleFactor() const case NS_FORM_INPUT_TIME: return kStepScaleFactorTime; default: - MOZ_NOT_REACHED(); + MOZ_ASSERT(false, "Unrecognized input type"); return MOZ_DOUBLE_NaN(); } } @@ -6094,7 +6092,7 @@ HTMLInputElement::GetDefaultStep() const case NS_FORM_INPUT_TIME: return kDefaultStepTime; default: - MOZ_NOT_REACHED(); + MOZ_ASSERT(false, "Unrecognized input type"); return MOZ_DOUBLE_NaN(); } } From 15aad4c86f5d5b4bd8b751b3881c00783291f81e Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 18 Apr 2013 13:33:19 -0700 Subject: [PATCH 04/90] Bug 860913 part 3: Mark content/html/content/src/ as FAIL_ON_WARNINGS. r=mounir --- content/html/content/src/Makefile.in | 1 + 1 file changed, 1 insertion(+) diff --git a/content/html/content/src/Makefile.in b/content/html/content/src/Makefile.in index 6b4f81d1addf..cbb4a7010861 100644 --- a/content/html/content/src/Makefile.in +++ b/content/html/content/src/Makefile.in @@ -12,6 +12,7 @@ include $(DEPTH)/config/autoconf.mk LIBRARY_NAME = gkconhtmlcon_s LIBXUL_LIBRARY = 1 +FAIL_ON_WARNINGS = 1 CPPSRCS = \ HTMLPropertiesCollection.cpp \ From 35cdaaf1dc2faa532bb8293fa441fb6005ad150f Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 18 Apr 2013 16:47:25 -0400 Subject: [PATCH 05/90] Bug 860145 - Allow Ion to compile functions which require heavyweight arguments-object construction. r=jandem r=nbp --- js/src/ion/Bailouts.cpp | 52 +++-- js/src/ion/BaselineBailouts.cpp | 47 +++- js/src/ion/BaselineCompiler.cpp | 2 +- js/src/ion/BaselineFrame.h | 7 +- js/src/ion/BaselineJIT.cpp | 5 +- js/src/ion/CodeGenerator.cpp | 74 +++++- js/src/ion/CodeGenerator.h | 3 + js/src/ion/CompileInfo.h | 57 ++++- js/src/ion/Ion.cpp | 23 +- js/src/ion/Ion.h | 2 +- js/src/ion/IonAnalysis.cpp | 11 +- js/src/ion/IonBuilder.cpp | 216 ++++++++++++------ js/src/ion/IonBuilder.h | 2 + js/src/ion/IonFrameIterator-inl.h | 23 +- js/src/ion/IonFrameIterator.h | 3 +- js/src/ion/IonFrames.cpp | 2 +- js/src/ion/LIR-Common.h | 65 ++++++ js/src/ion/LOpcodes.h | 3 + js/src/ion/Lowering.cpp | 28 +++ js/src/ion/Lowering.h | 3 + js/src/ion/MIR.h | 108 +++++++++ js/src/ion/MIRGraph.cpp | 14 ++ js/src/ion/MIRGraph.h | 2 + js/src/ion/MOpcodes.h | 3 + js/src/ion/ParallelArrayAnalysis.cpp | 3 + js/src/ion/Snapshots.cpp | 9 +- js/src/ion/shared/BaselineCompiler-shared.cpp | 3 +- js/src/ion/shared/BaselineCompiler-shared.h | 1 + .../jit-test/tests/ion/arguments-objects.js | 19 ++ js/src/vm/ArgumentsObject.cpp | 59 ++++- js/src/vm/ArgumentsObject.h | 4 + js/src/vm/Stack.cpp | 2 +- 32 files changed, 705 insertions(+), 150 deletions(-) create mode 100644 js/src/jit-test/tests/ion/arguments-objects.js diff --git a/js/src/ion/Bailouts.cpp b/js/src/ion/Bailouts.cpp index aa4f2d14a458..1b23b04540f3 100644 --- a/js/src/ion/Bailouts.cpp +++ b/js/src/ion/Bailouts.cpp @@ -95,18 +95,32 @@ StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter) if (iter.bailoutKind() == Bailout_ArgumentCheck) { // Temporary hack -- skip the (unused) scopeChain, because it could be // bogus (we can fail before the scope chain slot is set). Strip the - // hasScopeChain flag and we'll check this later to run prologue(). + // hasScopeChain flag. If a call object is needed, it will get handled later + // by |ThunkToInterpreter| which call |EnsureHasScopeObjects|. iter.skip(); flags_ &= ~StackFrame::HAS_SCOPECHAIN; + + // If the script binds arguments, then skip the snapshot slot reserved to hold + // its value. + if (script()->argumentsHasVarBinding()) + iter.skip(); + flags_ &= ~StackFrame::HAS_ARGS_OBJ; } else { - Value v = iter.read(); - if (v.isObject()) { - scopeChain_ = &v.toObject(); + Value scopeChain = iter.read(); + JS_ASSERT(scopeChain.isObject() || scopeChain.isUndefined()); + if (scopeChain.isObject()) { + scopeChain_ = &scopeChain.toObject(); flags_ |= StackFrame::HAS_SCOPECHAIN; if (isFunctionFrame() && fun()->isHeavyweight()) flags_ |= StackFrame::HAS_CALL_OBJ; - } else { - JS_ASSERT(v.isUndefined()); + } + + // The second slot will be an arguments object if the script needs one. + if (script()->argumentsHasVarBinding()) { + Value argsObj = iter.read(); + JS_ASSERT(argsObj.isObject() || argsObj.isUndefined()); + if (argsObj.isObject()) + initArgsObj(argsObj.toObject().asArguments()); } } @@ -125,7 +139,7 @@ StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter) if (isConstructing()) JS_ASSERT(!thisv.isPrimitive()); - JS_ASSERT(iter.slots() >= CountArgSlots(fun())); + JS_ASSERT(iter.slots() >= CountArgSlots(script(), fun())); IonSpew(IonSpew_Bailouts, " frame slots %u, nargs %u, nfixed %u", iter.slots(), fun()->nargs, script()->nfixed); @@ -134,7 +148,7 @@ StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter) formals()[i] = arg; } } - exprStackSlots -= CountArgSlots(maybeFun()); + exprStackSlots -= CountArgSlots(script(), maybeFun()); for (uint32_t i = 0; i < script()->nfixed; i++) { Value slot = iter.read(); @@ -498,7 +512,7 @@ ion::ReflowTypeInfo(uint32_t bailoutResult) return true; } -// Initialize the decl env Object and the call object of the current frame. +// Initialize the decl env Object, call object, and any arguments obj of the current frame. bool ion::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp) { @@ -603,20 +617,22 @@ ion::ThunkToInterpreter(Value *vp) fp = iter.interpFrame(); script = iter.script(); if (script->needsArgsObj()) { - // Currently IonMonkey does not compile if the script needs an - // arguments object, so the frame should not have any argument - // object yet. - JS_ASSERT(!fp->hasArgsObj()); - ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, fp); - if (!argsobj) { - resumeMode = JSINTERP_RETHROW; - break; + ArgumentsObject *argsObj; + if (fp->hasArgsObj()) { + argsObj = &fp->argsObj(); + } else { + argsObj = ArgumentsObject::createExpected(cx, fp); + if (!argsObj) { + resumeMode = JSINTERP_RETHROW; + break; + } } + // The arguments is a local binding and needsArgsObj does not // check if it is clobbered. Ensure that the local binding // restored during bailout before storing the arguments object // to the slot. - SetFrameArgumentsObject(cx, fp, script, argsobj); + SetFrameArgumentsObject(cx, fp, script, argsObj); } ++iter; } while (fp != br->entryfp()); diff --git a/js/src/ion/BaselineBailouts.cpp b/js/src/ion/BaselineBailouts.cpp index 53b7628f2f15..6f59d9f6a917 100644 --- a/js/src/ion/BaselineBailouts.cpp +++ b/js/src/ion/BaselineBailouts.cpp @@ -447,7 +447,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, bool invalidate, BaselineStackBuilder &builder, MutableHandleFunction nextCallee, jsbytecode **callPC) { - uint32_t exprStackSlots = iter.slots() - (script->nfixed + CountArgSlots(fun)); + uint32_t exprStackSlots = iter.slots() - (script->nfixed + CountArgSlots(script, fun)); builder.resetFramePushed(); @@ -508,15 +508,25 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME; } - // Initialize BaselineFrame::scopeChain + // Initialize BaselineFrame's scopeChain and argsObj JSObject *scopeChain = NULL; + ArgumentsObject *argsObj = NULL; BailoutKind bailoutKind = iter.bailoutKind(); if (bailoutKind == Bailout_ArgumentCheck) { // Temporary hack -- skip the (unused) scopeChain, because it could be // bogus (we can fail before the scope chain slot is set). Strip the - // hasScopeChain flag and we'll check this later to run prologue(). + // hasScopeChain flag and this will be fixed up later in |FinishBailoutToBaseline|, + // which calls |EnsureHasScopeObjects|. IonSpew(IonSpew_BaselineBailouts, " Bailout_ArgumentCheck! (no valid scopeChain)"); iter.skip(); + + // Scripts with |argumentsHasVarBinding| have an extra slot. + if (script->argumentsHasVarBinding()) { + IonSpew(IonSpew_BaselineBailouts, + " Bailout_ArgumentCheck for script with argumentsHasVarBinding!" + "Using empty arguments object"); + iter.skip(); + } } else { Value v = iter.read(); if (v.isObject()) { @@ -544,9 +554,19 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, scopeChain = &(script->global()); } } + + // If script maybe has an arguments object, the second slot will hold it. + if (script->argumentsHasVarBinding()) { + v = iter.read(); + JS_ASSERT(v.isObject() || v.isUndefined()); + if (v.isObject()) + argsObj = &v.toObject().asArguments(); + } } IonSpew(IonSpew_BaselineBailouts, " ScopeChain=%p", scopeChain); blFrame->setScopeChain(scopeChain); + if (argsObj) + blFrame->initArgsObjUnchecked(*argsObj); // Do not need to initialize scratchValue or returnValue fields in BaselineFrame. // No flags are set. @@ -566,7 +586,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, size_t thisvOffset = builder.framePushed() + IonJSFrameLayout::offsetOfThis(); *builder.valuePointerAtStackOffset(thisvOffset) = thisv; - JS_ASSERT(iter.slots() >= CountArgSlots(fun)); + JS_ASSERT(iter.slots() >= CountArgSlots(script, fun)); IonSpew(IonSpew_BaselineBailouts, " frame slots %u, nargs %u, nfixed %u", iter.slots(), fun->nargs, script->nfixed); @@ -1190,19 +1210,26 @@ ion::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo) if (iter.isBaselineJS()) { BaselineFrame *frame = iter.baselineFrame(); - JS_ASSERT(!frame->hasArgsObj()); - if (frame->script()->needsArgsObj()) { - ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, frame); - if (!argsobj) - return false; + // If the frame doesn't even have a scope chain set yet, then it's resuming + // into the the prologue before the scope chain is initialized. Any + // necessary args object will also be initialized there. + if (frame->scopeChain() && frame->script()->needsArgsObj()) { + ArgumentsObject *argsObj; + if (frame->hasArgsObj()) { + argsObj = &frame->argsObj(); + } else { + argsObj = ArgumentsObject::createExpected(cx, frame); + if (!argsObj) + return false; + } // The arguments is a local binding and needsArgsObj does not // check if it is clobbered. Ensure that the local binding // restored during bailout before storing the arguments object // to the slot. RootedScript script(cx, frame->script()); - SetFrameArgumentsObject(cx, frame, script, argsobj); + SetFrameArgumentsObject(cx, frame, script, argsObj); } if (frameno == 0) diff --git a/js/src/ion/BaselineCompiler.cpp b/js/src/ion/BaselineCompiler.cpp index 15f392397a97..ac951ff34810 100644 --- a/js/src/ion/BaselineCompiler.cpp +++ b/js/src/ion/BaselineCompiler.cpp @@ -411,7 +411,7 @@ BaselineCompiler::emitUseCountIncrement() // Emit no use count increments or bailouts if Ion is not // enabled, or if the script will never be Ion-compileable - if (!ionCompileable_) + if (!ionCompileable_ && !ionOSRCompileable_) return true; Register scriptReg = R2.scratchReg(); diff --git a/js/src/ion/BaselineFrame.h b/js/src/ion/BaselineFrame.h index 7d9642b0d12a..5655cb1db850 100644 --- a/js/src/ion/BaselineFrame.h +++ b/js/src/ion/BaselineFrame.h @@ -251,11 +251,14 @@ class BaselineFrame bool heavyweightFunPrologue(JSContext *cx); bool initFunctionScopeObjects(JSContext *cx); - void initArgsObj(ArgumentsObject &argsobj) { - JS_ASSERT(script()->needsArgsObj()); + void initArgsObjUnchecked(ArgumentsObject &argsobj) { flags_ |= HAS_ARGS_OBJ; argsObj_ = &argsobj; } + void initArgsObj(ArgumentsObject &argsobj) { + JS_ASSERT(script()->needsArgsObj()); + initArgsObjUnchecked(argsobj); + } bool hasArgsObj() const { return flags_ & HAS_ARGS_OBJ; } diff --git a/js/src/ion/BaselineJIT.cpp b/js/src/ion/BaselineJIT.cpp index 8061224465aa..af0708875bb9 100644 --- a/js/src/ion/BaselineJIT.cpp +++ b/js/src/ion/BaselineJIT.cpp @@ -94,8 +94,9 @@ EnterBaseline(JSContext *cx, StackFrame *fp, void *jitcode, bool osr) void *calleeToken; if (fp->isNonEvalFunctionFrame()) { - // CountArgSlot include |this| and the |scopeChain|. - maxArgc = CountArgSlots(fp->fun()) - 1; // -1 = discard |scopeChain| + // CountArgSlot include |this| and the |scopeChain|, and maybe |argumentsObj| + // Want to keep including this, but remove the scopeChain and any argumentsObj. + maxArgc = CountArgSlots(fp->script(), fp->fun()) - StartArgSlot(fp->script(), fp->fun()); maxArgv = fp->formals() - 1; // -1 = include |this| // Formal arguments are the argument corresponding to the function diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index 506db97a3863..92bea9259955 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -1807,21 +1807,19 @@ CodeGenerator::generateArgumentsChecks() CompileInfo &info = gen->info(); - // Indexes need to be shifted by one, to skip the scope chain slot. - JS_ASSERT(info.scopeChainSlot() == 0); - static const uint32_t START_SLOT = 1; - Label miss; - for (uint32_t i = START_SLOT; i < CountArgSlots(info.fun()); i++) { + for (uint32_t i = info.startArgSlot(); i < info.endArgSlot(); i++) { // All initial parameters are guaranteed to be MParameters. MParameter *param = rp->getOperand(i)->toParameter(); const types::TypeSet *types = param->typeSet(); if (!types || types->unknown()) continue; - // Use ReturnReg as a scratch register here, since not all platforms - // have an actual ScratchReg. - int32_t offset = ArgToStackOffset((i - START_SLOT) * sizeof(Value)); + // Calculate the offset on the stack of the argument. + // (i - info.startArgSlot()) - Compute index of arg within arg vector. + // ... * sizeof(Value) - Scale by value size. + // ArgToStackOffset(...) - Compute displacement within arg vector. + int32_t offset = ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value)); Label matched; masm.guardTypeSet(Address(StackPointer, offset), types, temp, &matched, &miss); masm.jump(&miss); @@ -2850,6 +2848,66 @@ CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir) return true; } +typedef JSObject *(*NewIonArgumentsObjectFn)(JSContext *cx, IonJSFrameLayout *frame, HandleObject); +static const VMFunction NewIonArgumentsObjectInfo = + FunctionInfo((NewIonArgumentsObjectFn) ArgumentsObject::createForIon); + +bool +CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject *lir) +{ + // This should be getting constructed in the first block only, and not any OSR entry blocks. + JS_ASSERT(lir->mir()->block()->id() == 0); + + const LAllocation *callObj = lir->getCallObject(); + Register temp = ToRegister(lir->getTemp(0)); + + masm.movePtr(StackPointer, temp); + masm.addPtr(Imm32(frameSize()), temp); + + pushArg(ToRegister(callObj)); + pushArg(temp); + return callVM(NewIonArgumentsObjectInfo, lir); +} + +bool +CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg *lir) +{ + Register temp = ToRegister(lir->getTemp(0)); + Register argsObj = ToRegister(lir->getArgsObject()); + ValueOperand out = ToOutValue(lir); + + masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp); + Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value)); + masm.loadValue(argAddr, out); +#ifdef DEBUG + Label success; + masm.branchTestMagic(Assembler::NotEqual, out, &success); + masm.breakpoint(); + masm.bind(&success); +#endif + return true; +} + +bool +CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir) +{ + Register temp = ToRegister(lir->getTemp(0)); + Register argsObj = ToRegister(lir->getArgsObject()); + ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex); + + masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp); + Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value)); + emitPreBarrier(argAddr, MIRType_Value); +#ifdef DEBUG + Label success; + masm.branchTestMagic(Assembler::NotEqual, argAddr, &success); + masm.breakpoint(); + masm.bind(&success); +#endif + masm.storeValue(value, argAddr); + return true; +} + bool CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir) { diff --git a/js/src/ion/CodeGenerator.h b/js/src/ion/CodeGenerator.h index b48e613a9490..302720394c3b 100644 --- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -121,6 +121,9 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitCreateThis(LCreateThis *lir); bool visitCreateThisWithProto(LCreateThisWithProto *lir); bool visitCreateThisWithTemplate(LCreateThisWithTemplate *lir); + bool visitCreateArgumentsObject(LCreateArgumentsObject *lir); + bool visitGetArgumentsObjectArg(LGetArgumentsObjectArg *lir); + bool visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir); bool visitReturnFromCtor(LReturnFromCtor *lir); bool visitArrayLength(LArrayLength *lir); bool visitTypedArrayLength(LTypedArrayLength *lir); diff --git a/js/src/ion/CompileInfo.h b/js/src/ion/CompileInfo.h index 2288b20460f6..0edf264db05b 100644 --- a/js/src/ion/CompileInfo.h +++ b/js/src/ion/CompileInfo.h @@ -13,9 +13,17 @@ namespace js { namespace ion { inline unsigned -CountArgSlots(JSFunction *fun) +StartArgSlot(RawScript script, JSFunction *fun) { - return fun ? fun->nargs + 2 : 1; // +2 for |scopeChain| and |this|, or +1 for |scopeChain| + // First slot is for scope chain. + // Second one may be for arguments object. + return 1 + (script->argumentsHasVarBinding() ? 1 : 0); +} + +inline unsigned +CountArgSlots(RawScript script, JSFunction *fun) +{ + return StartArgSlot(script, fun) + (fun ? fun->nargs + 1 : 0); } enum ExecutionMode { @@ -37,7 +45,8 @@ class CompileInfo executionMode_(executionMode) { JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY); - nimplicit_ = 1 /* scope chain */ + (fun ? 1 /* this */: 0); + nimplicit_ = StartArgSlot(script, fun) /* scope chain and argument obj */ + + (fun ? 1 : 0); /* this */ nargs_ = fun ? fun->nargs : 0; nlocals_ = script->nfixed; nstack_ = script->nslots - script->nfixed; @@ -117,17 +126,30 @@ class CompileInfo JS_ASSERT(script()); return 0; } - uint32_t thisSlot() const { - JS_ASSERT(fun()); + uint32_t argsObjSlot() const { + JS_ASSERT(hasArguments()); return 1; } - uint32_t firstArgSlot() const { + uint32_t thisSlot() const { + JS_ASSERT(fun()); + return hasArguments() ? 2 : 1; + } + uint32_t firstActualArgSlot() const { return nimplicit_; } - uint32_t argSlot(uint32_t i) const { + uint32_t argSlotUnchecked(uint32_t i) const { + // During initialization, some routines need to get at arg + // slots regardless of how regular argument access is done. JS_ASSERT(i < nargs_); return nimplicit_ + i; } + uint32_t argSlot(uint32_t i) const { + // This should only be accessed when compiling functions for + // which argument accesses don't need to go through the + // argument object. + JS_ASSERT(!argsObjAliasesFormals()); + return argSlotUnchecked(i); + } uint32_t firstLocalSlot() const { return nimplicit_ + nargs_; } @@ -141,9 +163,28 @@ class CompileInfo return firstStackSlot() + i; } - bool hasArguments() { + uint32_t startArgSlot() const { + JS_ASSERT(scopeChainSlot() == 0); + return StartArgSlot(script(), fun()); + } + uint32_t endArgSlot() const { + JS_ASSERT(scopeChainSlot() == 0); + return CountArgSlots(script(), fun()); + } + + uint32_t totalSlots() const { + return 2 + (hasArguments() ? 1 : 0) + nargs() + nlocals(); + } + + bool hasArguments() const { return script()->argumentsHasVarBinding(); } + bool needsArgsObj() const { + return script()->needsArgsObj(); + } + bool argsObjAliasesFormals() const { + return script()->argsObjAliasesFormals(); + } ExecutionMode executionMode() const { return executionMode_; diff --git a/js/src/ion/Ion.cpp b/js/src/ion/Ion.cpp index 9a227fa73aeb..d1a4321d821c 100644 --- a/js/src/ion/Ion.cpp +++ b/js/src/ion/Ion.cpp @@ -1448,11 +1448,11 @@ CheckFrame(AbstractFramePtr fp) } static bool -CheckScript(RawScript script) +CheckScript(RawScript script, bool osr) { - if (script->needsArgsObj()) { - // Functions with arguments objects, are not supported yet. - IonSpew(IonSpew_Abort, "script has argsobj"); + if (osr && script->needsArgsObj()) { + // OSR-ing into functions with arguments objects is not supported. + IonSpew(IonSpew_Abort, "OSR script has argsobj"); return false; } @@ -1508,9 +1508,9 @@ SequentialCompileContext::checkScriptSize(JSContext *cx, RawScript script) } bool -CanIonCompileScript(JSContext *cx, HandleScript script) +CanIonCompileScript(JSContext *cx, HandleScript script, bool osr) { - if (!script->canIonCompile() || !CheckScript(script)) + if (!script->canIonCompile() || !CheckScript(script, osr)) return false; SequentialCompileContext compileContext; @@ -1519,8 +1519,8 @@ CanIonCompileScript(JSContext *cx, HandleScript script) template static MethodStatus -Compile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing, - CompileContext &compileContext) +Compile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, + bool constructing, CompileContext &compileContext) { JS_ASSERT(ion::IsEnabled(cx)); JS_ASSERT_IF(osrPc != NULL, (JSOp)*osrPc == JSOP_LOOPENTRY); @@ -1540,7 +1540,7 @@ Compile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrP return Method_CantCompile; } - if (!CheckScript(script)) { + if (!CheckScript(script, bool(osrPc))) { IonSpew(IonSpew_Abort, "Aborted compilation of %s:%d", script->filename(), script->lineno); return Method_CantCompile; } @@ -1922,8 +1922,9 @@ EnterIon(JSContext *cx, StackFrame *fp, void *jitcode) if (fp->isFunctionFrame()) { fp->cleanupTornValues(); - // CountArgSlot include |this| and the |scopeChain|. - maxArgc = CountArgSlots(fp->fun()) - 1; // -1 = discard |scopeChain| + // CountArgSlot include |this| and the |scopeChain| and maybe |argumentsObj|. + // Keep |this|, but discard the others. + maxArgc = CountArgSlots(fp->script(), fp->fun()) - StartArgSlot(fp->script(), fp->fun()); maxArgv = fp->formals() - 1; // -1 = include |this| // Formal arguments are the argument corresponding to the function diff --git a/js/src/ion/Ion.h b/js/src/ion/Ion.h index d11485db6fe6..16d339c8314d 100644 --- a/js/src/ion/Ion.h +++ b/js/src/ion/Ion.h @@ -282,7 +282,7 @@ IonContext *GetIonContext(); bool SetIonContext(IonContext *ctx); -bool CanIonCompileScript(JSContext *cx, HandleScript script); +bool CanIonCompileScript(JSContext *cx, HandleScript script, bool osr); MethodStatus CanEnterAtBranch(JSContext *cx, JSScript *script, AbstractFramePtr fp, jsbytecode *pc, bool isConstructing); diff --git a/js/src/ion/IonAnalysis.cpp b/js/src/ion/IonAnalysis.cpp index 58a070c42954..94bdcbe3f2bf 100644 --- a/js/src/ion/IonAnalysis.cpp +++ b/js/src/ion/IonAnalysis.cpp @@ -196,17 +196,20 @@ IsPhiObservable(MPhi *phi, Observability observe) // If the Phi is of the |this| value, it must always be observable. uint32_t slot = phi->slot(); - if (slot == 1) + CompileInfo &info = phi->block()->info(); + if (info.fun() && slot == info.thisSlot()) return true; // If the Phi is one of the formal argument, and we are using an argument // object in the function. The phi might be observable after a bailout. // For inlined frames this is not needed, as they are captured in the inlineResumePoint. - CompileInfo &info = phi->block()->info(); if (info.fun() && info.hasArguments()) { - uint32_t first = info.firstArgSlot(); - if (first <= slot && slot - first < info.nargs()) + uint32_t first = info.firstActualArgSlot(); + if (first <= slot && slot - first < info.nargs()) { + // If arguments obj aliases formals, then no arguments slots should ever be phis. + JS_ASSERT(!info.argsObjAliasesFormals()); return true; + } } return false; } diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index f4e080bdd360..ca6fca65af28 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -335,6 +335,13 @@ IonBuilder::build() current->initSlot(info().scopeChainSlot(), scope); } + // Initialize the arguments object slot to undefined if necessary. + if (info().hasArguments()) { + MInstruction *argsObj = MConstant::New(UndefinedValue()); + current->add(argsObj); + current->initSlot(info().argsObjSlot(), argsObj); + } + // Emit the start instruction, so we can begin real instructions. current->makeStart(MStart::New(MStart::StartType_Default)); if (instrumentedProfiling()) @@ -348,6 +355,9 @@ IonBuilder::build() if (!initScopeChain()) return false; + if (info().needsArgsObj() && !initArgumentsObject()) + return false; + // Guard against over-recursion. MCheckOverRecursed *check = new MCheckOverRecursed; current->add(check); @@ -372,13 +382,14 @@ IonBuilder::build() // So we attach the initial resume point to each parameter, which the type // analysis explicitly checks (this is the same mechanism used for // effectful operations). - for (uint32_t i = 0; i < CountArgSlots(info().fun()); i++) { + for (uint32_t i = 0; i < info().endArgSlot(); i++) { MInstruction *ins = current->getEntrySlot(i)->toInstruction(); if (ins->type() == MIRType_Value) ins->setResumePoint(current->entryResumePoint()); } - if (script()->argumentsHasVarBinding()) { + // lazyArguments should never be accessed in |argsObjAliasesFormals| scripts. + if (info().hasArguments() && !info().argsObjAliasesFormals()) { lazyArguments_ = MConstant::New(MagicValue(JS_OPTIMIZED_ARGUMENTS)); current->add(lazyArguments_); } @@ -480,15 +491,25 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi if (!inlinedArgumentTypes_.append(callInfo.argvType().begin(), callInfo.argvType().end())) return false; - // The Oracle ensures that the inlined script does not use the scope chain. + // The Oracle ensures that the inlined script does not use the scope chain, or need + // an arguments object. JS_ASSERT(!script()->analysis()->usesScopeChain()); MInstruction *scope = MConstant::New(UndefinedValue()); current->add(scope); current->initSlot(info().scopeChainSlot(), scope); + if (info().hasArguments()) { + MInstruction *argsObj = MConstant::New(UndefinedValue()); + current->add(argsObj); + current->initSlot(info().argsObjSlot(), argsObj); + } current->initSlot(info().thisSlot(), callInfo.thisArg()); IonSpew(IonSpew_Inlining, "Initializing %u arg slots", info().nargs()); + // NB: Ion does not inline functions which |needsArgsObj|. So using argSlot() + // instead of argSlotUnchecked() below is OK + JS_ASSERT(!info().needsArgsObj()); + // Initialize actually set arguments. uint32_t existing_args = Min(callInfo.argc(), info().nargs()); for (size_t i = 0; i < existing_args; ++i) { @@ -515,8 +536,8 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi IonSpew(IonSpew_Inlining, "Inline entry block MResumePoint %p, %u operands", (void *) current->entryResumePoint(), current->entryResumePoint()->numOperands()); - // +2 for the scope chain and |this|. - JS_ASSERT(current->entryResumePoint()->numOperands() == info().nargs() + info().nlocals() + 2); + // +2 for the scope chain and |this|, maybe another +1 for arguments object slot. + JS_ASSERT(current->entryResumePoint()->numOperands() == info().totalSlots()); if (script_->argumentsHasVarBinding()) { lazyArguments_ = MConstant::New(MagicValue(JS_OPTIMIZED_ARGUMENTS)); @@ -526,6 +547,55 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi return traverseBytecode(); } +void +IonBuilder::rewriteParameter(uint32_t slotIdx, MDefinition *param, int32_t argIndex) +{ + JS_ASSERT(param->isParameter() || param->isGetArgumentsObjectArg()); + + // Find the original (not cloned) type set for the MParameter, as we + // will be adding constraints to it. + types::StackTypeSet *types; + if (argIndex == MParameter::THIS_SLOT) + types = oracle->thisTypeSet(script()); + else + types = oracle->parameterTypeSet(script(), argIndex); + if (!types) + return; + + JSValueType definiteType = types->getKnownTypeTag(); + if (definiteType == JSVAL_TYPE_UNKNOWN) + return; + + MInstruction *actual = NULL; + switch (definiteType) { + case JSVAL_TYPE_UNDEFINED: + param->setFoldedUnchecked(); + actual = MConstant::New(UndefinedValue()); + break; + + case JSVAL_TYPE_NULL: + param->setFoldedUnchecked(); + actual = MConstant::New(NullValue()); + break; + + default: + actual = MUnbox::New(param, MIRTypeFromValueType(definiteType), MUnbox::Infallible); + break; + } + + // Careful! We leave the original MParameter in the entry resume point. The + // arguments still need to be checked unless proven otherwise at the call + // site, and these checks can bailout. We can end up: + // v0 = Parameter(0) + // v1 = Unbox(v0, INT32) + // -- ResumePoint(v0) + // + // As usual, it would be invalid for v1 to be captured in the initial + // resume point, rather than v0. + current->add(actual); + current->rewriteSlot(slotIdx, actual); +} + // Apply Type Inference information to parameters early on, unboxing them if // they have a definitive type. The actual guards will be emitted by the code // generator, explicitly, as part of the function prologue. @@ -533,53 +603,13 @@ void IonBuilder::rewriteParameters() { JS_ASSERT(info().scopeChainSlot() == 0); - static const uint32_t START_SLOT = 1; - for (uint32_t i = START_SLOT; i < CountArgSlots(info().fun()); i++) { - MParameter *param = current->getSlot(i)->toParameter(); + if (!info().fun()) + return; - // Find the original (not cloned) type set for the MParameter, as we - // will be adding constraints to it. - types::StackTypeSet *types; - if (param->index() == MParameter::THIS_SLOT) - types = oracle->thisTypeSet(script()); - else - types = oracle->parameterTypeSet(script(), param->index()); - if (!types) - continue; - - JSValueType definiteType = types->getKnownTypeTag(); - if (definiteType == JSVAL_TYPE_UNKNOWN) - continue; - - MInstruction *actual = NULL; - switch (definiteType) { - case JSVAL_TYPE_UNDEFINED: - param->setFoldedUnchecked(); - actual = MConstant::New(UndefinedValue()); - break; - - case JSVAL_TYPE_NULL: - param->setFoldedUnchecked(); - actual = MConstant::New(NullValue()); - break; - - default: - actual = MUnbox::New(param, MIRTypeFromValueType(definiteType), MUnbox::Infallible); - break; - } - - // Careful! We leave the original MParameter in the entry resume point. The - // arguments still need to be checked unless proven otherwise at the call - // site, and these checks can bailout. We can end up: - // v0 = Parameter(0) - // v1 = Unbox(v0, INT32) - // -- ResumePoint(v0) - // - // As usual, it would be invalid for v1 to be captured in the initial - // resume point, rather than v0. - current->add(actual); - current->rewriteSlot(i, actual); + for (uint32_t i = info().startArgSlot(); i < info().endArgSlot(); i++) { + MDefinition *param = current->getSlot(i); + rewriteParameter(i, param, param->toParameter()->index()); } } @@ -597,7 +627,7 @@ IonBuilder::initParameters() for (uint32_t i = 0; i < info().nargs(); i++) { param = MParameter::New(i, cloneTypeSet(oracle->parameterTypeSet(script(), i))); current->add(param); - current->initSlot(info().argSlot(i), param); + current->initSlot(info().argSlotUnchecked(i), param); } return true; @@ -609,8 +639,10 @@ IonBuilder::initScopeChain() MInstruction *scope = NULL; // If the script doesn't use the scopechain, then it's already initialized - // from earlier. - if (!script()->analysis()->usesScopeChain()) + // from earlier. However, always make a scope chain when |needsArgsObj| is true + // for the script, since arguments object construction requires the scope chain + // to be passed in. + if (!info().needsArgsObj() && !script()->analysis()->usesScopeChain()) return true; // The scope chain is only tracked in scripts that have NAME opcodes which @@ -648,6 +680,18 @@ IonBuilder::initScopeChain() return true; } +bool +IonBuilder::initArgumentsObject() +{ + IonSpew(IonSpew_MIR, "%s:%d - Emitting code to initialize arguments object! block=%p", + script()->filename(), script()->lineno, current); + JS_ASSERT(info().needsArgsObj()); + MCreateArgumentsObject *argsObj = MCreateArgumentsObject::New(current->scopeChain()); + current->add(argsObj); + current->setArgumentsObject(argsObj); + return true; +} + // We try to build a control-flow graph in the order that it would be built as // if traversing the AST. This leads to a nice ordering and lets us build SSA // in one pass, since the bytecode is structured. @@ -901,7 +945,14 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_GETARG: case JSOP_CALLARG: - current->pushArg(GET_SLOTNO(pc)); + if (info().argsObjAliasesFormals()) { + MGetArgumentsObjectArg *getArg = MGetArgumentsObjectArg::New(current->argumentsObject(), + GET_SLOTNO(pc)); + current->add(getArg); + current->push(getArg); + } else { + current->pushArg(GET_SLOTNO(pc)); + } return true; case JSOP_SETARG: @@ -910,9 +961,16 @@ IonBuilder::inspectOpcode(JSOp op) // to wrap the spilling action, we don't want the spilling to be // captured by the GETARG and by the resume point, only by // MGetArgument. - if (info().hasArguments()) - return abort("NYI: arguments & setarg."); - current->setArg(GET_SLOTNO(pc)); + if (info().argsObjAliasesFormals()) { + current->add(MSetArgumentsObjectArg::New(current->argumentsObject(), GET_SLOTNO(pc), + current->peek(-1))); + } else { + // TODO: if hasArguments() is true, and the script has a JSOP_SETARG, then + // convert all arg accesses to go through the arguments object. + if (info().hasArguments()) + return abort("NYI: arguments & setarg."); + current->setArg(GET_SLOTNO(pc)); + } return true; case JSOP_GETLOCAL: @@ -3955,7 +4013,7 @@ IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope) for (AliasedFormalIter i(script()); i; i++) { unsigned slot = i.scopeSlot(); unsigned formal = i.frameIndex(); - MDefinition *param = current->getSlot(info().argSlot(formal)); + MDefinition *param = current->getSlot(info().argSlotUnchecked(formal)); if (slot >= templateObj->numFixedSlots()) current->add(MStoreSlot::New(slots, slot - templateObj->numFixedSlots(), param)); else @@ -4987,6 +5045,15 @@ IonBuilder::newOsrPreheader(MBasicBlock *predecessor, jsbytecode *loopEntry) osrBlock->initSlot(slot, scopev); } + // Initialize arguments object. Ion will not allow OSR-ing into scripts + // with |needsArgsObj| set, so this can be undefined. + JS_ASSERT(!info().needsArgsObj()); + if (info().hasArguments()) { + MInstruction *argsObj = MConstant::New(UndefinedValue()); + osrBlock->add(argsObj); + osrBlock->initSlot(info().argsObjSlot(), argsObj); + } + if (info().fun()) { // Initialize |this| parameter. uint32_t slot = info().thisSlot(); @@ -4998,6 +5065,8 @@ IonBuilder::newOsrPreheader(MBasicBlock *predecessor, jsbytecode *loopEntry) // Initialize arguments. for (uint32_t i = 0; i < info().nargs(); i++) { + // NB: Ion does not OSR into any function which |needsArgsObj|, so + // using argSlot() here instead of argSlotUnchecked() is ok. uint32_t slot = info().argSlot(i); ptrdiff_t offset = StackFrame::offsetOfFormalArg(info().fun(), i); @@ -5018,8 +5087,8 @@ IonBuilder::newOsrPreheader(MBasicBlock *predecessor, jsbytecode *loopEntry) } // Initialize stack. - uint32_t numSlots = preheader->stackDepth() - CountArgSlots(info().fun()) - info().nlocals(); - for (uint32_t i = 0; i < numSlots; i++) { + uint32_t numStackSlots = preheader->stackDepth() - info().firstStackSlot(); + for (uint32_t i = 0; i < numStackSlots; i++) { uint32_t slot = info().stackSlot(i); ptrdiff_t offset = StackFrame::offsetOfFixed(info().nlocals() + i); @@ -5050,19 +5119,26 @@ IonBuilder::newOsrPreheader(MBasicBlock *predecessor, jsbytecode *loopEntry) JS_ASSERT(info().scopeChainSlot() == 0); JS_ASSERT(osrBlock->scopeChain()->type() == MIRType_Object); + // When compiling functions which |hasArguments|, an extra slot is used to hold the + // potential arguments object. In OSR-compiled functions this object is always undefined, + // but the slot still exists. + bool argsSlotAdj = info().hasArguments() ? 1 : 0; Vector slotTypes(cx); - if (!slotTypes.growByUninitialized(osrBlock->stackDepth())) + if (!slotTypes.growByUninitialized(osrBlock->stackDepth() - argsSlotAdj)) return NULL; // Fill slotTypes with the types of the predecessor block. - for (uint32_t i = 0; i < osrBlock->stackDepth(); i++) + for (uint32_t i = 0; i < osrBlock->stackDepth() - argsSlotAdj; i++) slotTypes[i] = MIRType_Value; // Update slotTypes for slots that may have a different type at this join point. if (!oracle->getOsrTypes(loopEntry, slotTypes)) return NULL; - for (uint32_t i = 1; i < osrBlock->stackDepth(); i++) { + // Skip 0 - no type checks on scopeChain slot. + for (uint32_t i = 1; i < slotTypes.length(); i++) { + uint32_t slotNo = i + argsSlotAdj; + // Unbox the MOsrValue if it is known to be unboxable. switch (slotTypes[i]) { case MIRType_Boolean: @@ -5071,12 +5147,12 @@ IonBuilder::newOsrPreheader(MBasicBlock *predecessor, jsbytecode *loopEntry) case MIRType_String: case MIRType_Object: { - MDefinition *def = osrBlock->getSlot(i); + MDefinition *def = osrBlock->getSlot(slotNo); JS_ASSERT(def->type() == MIRType_Value); MInstruction *actual = MUnbox::New(def, slotTypes[i], MUnbox::Infallible); osrBlock->add(actual); - osrBlock->rewriteSlot(i, actual); + osrBlock->rewriteSlot(slotNo, actual); break; } @@ -5084,7 +5160,7 @@ IonBuilder::newOsrPreheader(MBasicBlock *predecessor, jsbytecode *loopEntry) { MConstant *c = MConstant::New(NullValue()); osrBlock->add(c); - osrBlock->rewriteSlot(i, c); + osrBlock->rewriteSlot(slotNo, c); break; } @@ -5092,13 +5168,13 @@ IonBuilder::newOsrPreheader(MBasicBlock *predecessor, jsbytecode *loopEntry) { MConstant *c = MConstant::New(UndefinedValue()); osrBlock->add(c); - osrBlock->rewriteSlot(i, c); + osrBlock->rewriteSlot(slotNo, c); break; } case MIRType_Magic: JS_ASSERT(lazyArguments_); - osrBlock->rewriteSlot(i, lazyArguments_); + osrBlock->rewriteSlot(slotNo, lazyArguments_); break; default: @@ -6211,6 +6287,10 @@ IonBuilder::jsop_length_fastPath() bool IonBuilder::jsop_arguments() { + if (info().needsArgsObj()) { + current->push(current->argumentsObject()); + return true; + } JS_ASSERT(lazyArguments_); current->push(lazyArguments_); return true; diff --git a/js/src/ion/IonBuilder.h b/js/src/ion/IonBuilder.h index 91fdee673034..0bc2664583b8 100644 --- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -273,8 +273,10 @@ class IonBuilder : public MIRGenerator bool maybeInsertResume(); bool initParameters(); + void rewriteParameter(uint32_t slotIdx, MDefinition *param, int32_t argIndex); void rewriteParameters(); bool initScopeChain(); + bool initArgumentsObject(); bool pushConstant(const Value &v); // Add a guard which ensure that the set of type which goes through this diff --git a/js/src/ion/IonFrameIterator-inl.h b/js/src/ion/IonFrameIterator-inl.h index 2740d9432b5b..73bc2604eff2 100644 --- a/js/src/ion/IonFrameIterator-inl.h +++ b/js/src/ion/IonFrameIterator-inl.h @@ -18,7 +18,8 @@ namespace ion { template inline void SnapshotIterator::readFrameArgs(Op &op, const Value *argv, Value *scopeChain, Value *thisv, - unsigned start, unsigned formalEnd, unsigned iterEnd) + unsigned start, unsigned formalEnd, unsigned iterEnd, + RawScript script) { if (scopeChain) *scopeChain = read(); @@ -30,6 +31,10 @@ SnapshotIterator::readFrameArgs(Op &op, const Value *argv, Value *scopeChain, Va else skip(); + // Skip slot for arguments object. + if (script->argumentsHasVarBinding()) + skip(); + unsigned i = 0; if (formalEnd < start) i = start; @@ -103,26 +108,28 @@ InlineFrameIteratorMaybeGC::forEachCanonicalActualArg( // Get the non overflown arguments unsigned formal_end = (end < nformal) ? end : nformal; SnapshotIterator s(si_); - s.readFrameArgs(op, NULL, NULL, NULL, start, nformal, formal_end); + s.readFrameArgs(op, NULL, NULL, NULL, start, nformal, formal_end, script()); // The overflown arguments are not available in current frame. // They are the last pushed arguments in the parent frame of this inlined frame. InlineFrameIteratorMaybeGC it(cx, this); - SnapshotIterator parent_s((++it).snapshotIterator()); + ++it; + unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0; + SnapshotIterator parent_s(it.snapshotIterator()); // Skip over all slots untill we get to the last slots (= arguments slots of callee) - // the +2 is for [this] and [scopechain] - JS_ASSERT(parent_s.slots() >= nactual + 2); - unsigned skip = parent_s.slots() - nactual - 2; + // the +2 is for [this] and [scopechain], and maybe +1 for [argsObj] + JS_ASSERT(parent_s.slots() >= nactual + 2 + argsObjAdj); + unsigned skip = parent_s.slots() - nactual - 2 - argsObjAdj; for (unsigned j = 0; j < skip; j++) parent_s.skip(); // Get the overflown arguments - parent_s.readFrameArgs(op, NULL, NULL, NULL, nformal, nactual, end); + parent_s.readFrameArgs(op, NULL, NULL, NULL, nformal, nactual, end, it.script()); } else { SnapshotIterator s(si_); Value *argv = frame_->actualArgs(); - s.readFrameArgs(op, argv, NULL, NULL, start, nformal, end); + s.readFrameArgs(op, argv, NULL, NULL, start, nformal, end, script()); } } diff --git a/js/src/ion/IonFrameIterator.h b/js/src/ion/IonFrameIterator.h index fb0a339ae79d..42c6cda6fc80 100644 --- a/js/src/ion/IonFrameIterator.h +++ b/js/src/ion/IonFrameIterator.h @@ -269,7 +269,8 @@ class SnapshotIterator : public SnapshotReader template inline void readFrameArgs(Op &op, const Value *argv, Value *scopeChain, Value *thisv, - unsigned start, unsigned formalEnd, unsigned iterEnd); + unsigned start, unsigned formalEnd, unsigned iterEnd, + RawScript script); Value maybeReadSlotByIndex(size_t index) { while (index--) { diff --git a/js/src/ion/IonFrames.cpp b/js/src/ion/IonFrames.cpp index b218d05d802c..92c663737431 100644 --- a/js/src/ion/IonFrames.cpp +++ b/js/src/ion/IonFrames.cpp @@ -287,7 +287,7 @@ CloseLiveIterator(JSContext *cx, const InlineFrameIterator &frame, uint32_t loca SnapshotIterator si = frame.snapshotIterator(); // Skip stack slots until we reach the iterator object. - uint32_t base = CountArgSlots(frame.maybeCallee()) + frame.script()->nfixed; + uint32_t base = CountArgSlots(frame.script(), frame.maybeCallee()) + frame.script()->nfixed; uint32_t skipSlots = base + localSlot - 1; for (unsigned i = 0; i < skipSlots; i++) diff --git a/js/src/ion/LIR-Common.h b/js/src/ion/LIR-Common.h index a758e6a00d28..542403b2586b 100644 --- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -710,6 +710,71 @@ class LCreateThisWithTemplate : public LInstructionHelper<1, 0, 0> } }; +// Allocate a new arguments object for the frame. +class LCreateArgumentsObject : public LCallInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(CreateArgumentsObject) + + LCreateArgumentsObject(const LAllocation &callObj, const LDefinition &temp) + { + setOperand(0, callObj); + setTemp(0, temp); + } + + const LAllocation *getCallObject() { + return getOperand(0); + } + + MCreateArgumentsObject *mir() const { + return mir_->toCreateArgumentsObject(); + } +}; + +// Get argument from arguments object. +class LGetArgumentsObjectArg : public LInstructionHelper +{ + public: + LIR_HEADER(GetArgumentsObjectArg) + + LGetArgumentsObjectArg(const LAllocation &argsObj, const LDefinition &temp) + { + setOperand(0, argsObj); + setTemp(0, temp); + } + + const LAllocation *getArgsObject() { + return getOperand(0); + } + + MGetArgumentsObjectArg *mir() const { + return mir_->toGetArgumentsObjectArg(); + } +}; + +// Set argument on arguments object. +class LSetArgumentsObjectArg : public LInstructionHelper<0, 1 + BOX_PIECES, 1> +{ + public: + LIR_HEADER(SetArgumentsObjectArg) + + LSetArgumentsObjectArg(const LAllocation &argsObj, const LDefinition &temp) + { + setOperand(0, argsObj); + setTemp(0, temp); + } + + const LAllocation *getArgsObject() { + return getOperand(0); + } + + MSetArgumentsObjectArg *mir() const { + return mir_->toSetArgumentsObjectArg(); + } + + static const size_t ValueIndex = 1; +}; + // If the Value is an Object, return unbox(Value). // Otherwise, return the other Object. class LReturnFromCtor : public LInstructionHelper<1, BOX_PIECES + 1, 0> diff --git a/js/src/ion/LOpcodes.h b/js/src/ion/LOpcodes.h index 88142926f1c5..02d63fde4de6 100644 --- a/js/src/ion/LOpcodes.h +++ b/js/src/ion/LOpcodes.h @@ -50,6 +50,9 @@ _(CreateThis) \ _(CreateThisWithProto) \ _(CreateThisWithTemplate) \ + _(CreateArgumentsObject) \ + _(GetArgumentsObjectArg) \ + _(SetArgumentsObjectArg) \ _(ReturnFromCtor) \ _(BitNotI) \ _(BitNotV) \ diff --git a/js/src/ion/Lowering.cpp b/js/src/ion/Lowering.cpp index 748d9ae84c06..b29b50e27ca0 100644 --- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -320,6 +320,34 @@ LIRGenerator::visitCreateThis(MCreateThis *ins) return defineReturn(lir, ins) && assignSafepoint(lir, ins); } +bool +LIRGenerator::visitCreateArgumentsObject(MCreateArgumentsObject *ins) +{ + // LAllocation callObj = useRegisterAtStart(ins->getCallObject()); + LAllocation callObj = useFixed(ins->getCallObject(), CallTempReg0); + LCreateArgumentsObject *lir = new LCreateArgumentsObject(callObj, tempFixed(CallTempReg1)); + return defineReturn(lir, ins) && assignSafepoint(lir, ins); +} + +bool +LIRGenerator::visitGetArgumentsObjectArg(MGetArgumentsObjectArg *ins) +{ + LAllocation argsObj = useRegister(ins->getArgsObject()); + LGetArgumentsObjectArg *lir = new LGetArgumentsObjectArg(argsObj, temp()); + return defineBox(lir, ins); +} + +bool +LIRGenerator::visitSetArgumentsObjectArg(MSetArgumentsObjectArg *ins) +{ + LAllocation argsObj = useRegister(ins->getArgsObject()); + LSetArgumentsObjectArg *lir = new LSetArgumentsObjectArg(argsObj, temp()); + if (!useBox(lir, LSetArgumentsObjectArg::ValueIndex, ins->getValue())) + return false; + + return add(lir, ins); +} + bool LIRGenerator::visitReturnFromCtor(MReturnFromCtor *ins) { diff --git a/js/src/ion/Lowering.h b/js/src/ion/Lowering.h index 6e11bcdbf050..d2733b9a4242 100644 --- a/js/src/ion/Lowering.h +++ b/js/src/ion/Lowering.h @@ -105,6 +105,9 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitCreateThisWithTemplate(MCreateThisWithTemplate *ins); bool visitCreateThisWithProto(MCreateThisWithProto *ins); bool visitCreateThis(MCreateThis *ins); + bool visitCreateArgumentsObject(MCreateArgumentsObject *ins); + bool visitGetArgumentsObjectArg(MGetArgumentsObjectArg *ins); + bool visitSetArgumentsObjectArg(MSetArgumentsObjectArg *ins); bool visitReturnFromCtor(MReturnFromCtor *ins); bool visitCall(MCall *call); bool visitApplyArgs(MApplyArgs *apply); diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 58437d641a54..7550f543e9f7 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -2118,6 +2118,114 @@ class MCreateThis } }; +// Eager initialization of arguments object. +class MCreateArgumentsObject + : public MUnaryInstruction, + public ObjectPolicy<0> +{ + MCreateArgumentsObject(MDefinition *callObj) + : MUnaryInstruction(callObj) + { + setResultType(MIRType_Object); + setGuard(); + } + + public: + INSTRUCTION_HEADER(CreateArgumentsObject) + static MCreateArgumentsObject *New(MDefinition *callObj) { + return new MCreateArgumentsObject(callObj); + } + + MDefinition *getCallObject() const { + return getOperand(0); + } + + AliasSet getAliasSet() const { + return AliasSet::None(); + } + + TypePolicy *typePolicy() { + return this; + } +}; + +class MGetArgumentsObjectArg + : public MUnaryInstruction, + public ObjectPolicy<0> +{ + size_t argno_; + + MGetArgumentsObjectArg(MDefinition *argsObject, size_t argno) + : MUnaryInstruction(argsObject), + argno_(argno) + { + setResultType(MIRType_Value); + } + + public: + INSTRUCTION_HEADER(GetArgumentsObjectArg) + static MGetArgumentsObjectArg *New(MDefinition *argsObj, size_t argno) + { + return new MGetArgumentsObjectArg(argsObj, argno); + } + + MDefinition *getArgsObject() const { + return getOperand(0); + } + + size_t argno() const { + return argno_; + } + + AliasSet getAliasSet() const { + return AliasSet::Load(AliasSet::Any); + } + + TypePolicy *typePolicy() { + return this; + } +}; + +class MSetArgumentsObjectArg + : public MBinaryInstruction, + public MixPolicy, BoxPolicy<1> > +{ + size_t argno_; + + MSetArgumentsObjectArg(MDefinition *argsObj, size_t argno, MDefinition *value) + : MBinaryInstruction(argsObj, value), + argno_(argno) + { + } + + public: + INSTRUCTION_HEADER(SetArgumentsObjectArg) + static MSetArgumentsObjectArg *New(MDefinition *argsObj, size_t argno, MDefinition *value) + { + return new MSetArgumentsObjectArg(argsObj, argno, value); + } + + MDefinition *getArgsObject() const { + return getOperand(0); + } + + size_t argno() const { + return argno_; + } + + MDefinition *getValue() const { + return getOperand(1); + } + + AliasSet getAliasSet() const { + return AliasSet::Store(AliasSet::Any); + } + + TypePolicy *typePolicy() { + return this; + } +}; + // Given a MIRType_Value A and a MIRType_Object B: // If the Value may be safely unboxed to an Object, return Object(A). // Otherwise, return B. diff --git a/js/src/ion/MIRGraph.cpp b/js/src/ion/MIRGraph.cpp index addbda66aecd..5e797430c8ed 100644 --- a/js/src/ion/MIRGraph.cpp +++ b/js/src/ion/MIRGraph.cpp @@ -348,6 +348,8 @@ MBasicBlock::linkOsrValues(MStart *start) MDefinition *def = slots_[i]; if (i == info().scopeChainSlot()) def->toOsrScopeChain()->setResumePoint(res); + else if (info().hasArguments() && i == info().argsObjSlot()) + JS_ASSERT(def->isConstant() && def->toConstant()->value() == UndefinedValue()); else def->toOsrValue()->setResumePoint(res); } @@ -450,12 +452,24 @@ MBasicBlock::scopeChain() return getSlot(info().scopeChainSlot()); } +MDefinition * +MBasicBlock::argumentsObject() +{ + return getSlot(info().argsObjSlot()); +} + void MBasicBlock::setScopeChain(MDefinition *scopeObj) { setSlot(info().scopeChainSlot(), scopeObj); } +void +MBasicBlock::setArgumentsObject(MDefinition *argsObj) +{ + setSlot(info().argsObjSlot(), argsObj); +} + void MBasicBlock::pick(int32_t depth) { diff --git a/js/src/ion/MIRGraph.h b/js/src/ion/MIRGraph.h index f5b62e11623e..e1bbb3e50248 100644 --- a/js/src/ion/MIRGraph.h +++ b/js/src/ion/MIRGraph.h @@ -101,6 +101,7 @@ class MBasicBlock : public TempObject, public InlineListNode MDefinition *peek(int32_t depth); MDefinition *scopeChain(); + MDefinition *argumentsObject(); // Increase the number of slots available bool increaseSlots(size_t num); @@ -136,6 +137,7 @@ class MBasicBlock : public TempObject, public InlineListNode void pushLocal(uint32_t local); void pushSlot(uint32_t slot); void setScopeChain(MDefinition *ins); + void setArgumentsObject(MDefinition *ins); // Returns the top of the stack, then decrements the virtual stack pointer. MDefinition *pop(); diff --git a/js/src/ion/MOpcodes.h b/js/src/ion/MOpcodes.h index 06630fd7ae24..c73995d35e9b 100644 --- a/js/src/ion/MOpcodes.h +++ b/js/src/ion/MOpcodes.h @@ -33,6 +33,9 @@ namespace ion { _(CreateThis) \ _(CreateThisWithProto) \ _(CreateThisWithTemplate) \ + _(CreateArgumentsObject) \ + _(GetArgumentsObjectArg) \ + _(SetArgumentsObjectArg) \ _(PrepareCall) \ _(PassArg) \ _(Call) \ diff --git a/js/src/ion/ParallelArrayAnalysis.cpp b/js/src/ion/ParallelArrayAnalysis.cpp index 3fe70a523437..0d86fe700b90 100644 --- a/js/src/ion/ParallelArrayAnalysis.cpp +++ b/js/src/ion/ParallelArrayAnalysis.cpp @@ -130,6 +130,9 @@ class ParallelArrayVisitor : public MInstructionVisitor UNSAFE_OP(CreateThis) UNSAFE_OP(CreateThisWithTemplate) UNSAFE_OP(CreateThisWithProto) + UNSAFE_OP(CreateArgumentsObject) + UNSAFE_OP(GetArgumentsObjectArg) + UNSAFE_OP(SetArgumentsObjectArg) SAFE_OP(PrepareCall) SAFE_OP(PassArg) CUSTOM_OP(Call) diff --git a/js/src/ion/Snapshots.cpp b/js/src/ion/Snapshots.cpp index 3605148521c0..074669bb5774 100644 --- a/js/src/ion/Snapshots.cpp +++ b/js/src/ion/Snapshots.cpp @@ -298,15 +298,16 @@ SnapshotWriter::startSnapshot(uint32_t frameCount, BailoutKind kind, bool resume void SnapshotWriter::startFrame(JSFunction *fun, RawScript script, jsbytecode *pc, uint32_t exprStack) { - JS_ASSERT(CountArgSlots(fun) < SNAPSHOT_MAX_NARGS); + JS_ASSERT(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS); - uint32_t formalArgs = CountArgSlots(fun); + uint32_t implicit = StartArgSlot(script, fun); + uint32_t formalArgs = CountArgSlots(script, fun); nslots_ = formalArgs + script->nfixed + exprStack; slotsWritten_ = 0; - IonSpew(IonSpew_Snapshots, "Starting frame; formals %u, fixed %u, exprs %u", - formalArgs, script->nfixed, exprStack); + IonSpew(IonSpew_Snapshots, "Starting frame; implicit %u, formals %u, fixed %u, exprs %u", + implicit, formalArgs - implicit, script->nfixed, exprStack); JS_ASSERT(script->code <= pc && pc <= script->code + script->length); diff --git a/js/src/ion/shared/BaselineCompiler-shared.cpp b/js/src/ion/shared/BaselineCompiler-shared.cpp index 47831a5c82cc..d02160f15ee8 100644 --- a/js/src/ion/shared/BaselineCompiler-shared.cpp +++ b/js/src/ion/shared/BaselineCompiler-shared.cpp @@ -15,7 +15,8 @@ BaselineCompilerShared::BaselineCompilerShared(JSContext *cx, HandleScript scrip : cx(cx), script(cx, script), pc(script->code), - ionCompileable_(ion::IsEnabled(cx) && CanIonCompileScript(cx, script)), + ionCompileable_(ion::IsEnabled(cx) && CanIonCompileScript(cx, script, false)), + ionOSRCompileable_(ion::IsEnabled(cx) && CanIonCompileScript(cx, script, true)), debugMode_(cx->compartment->debugMode()), frame(cx, script, masm), stubSpace_(), diff --git a/js/src/ion/shared/BaselineCompiler-shared.h b/js/src/ion/shared/BaselineCompiler-shared.h index 2a23e44ed56b..98bdd2d8154f 100644 --- a/js/src/ion/shared/BaselineCompiler-shared.h +++ b/js/src/ion/shared/BaselineCompiler-shared.h @@ -25,6 +25,7 @@ class BaselineCompilerShared jsbytecode *pc; MacroAssembler masm; bool ionCompileable_; + bool ionOSRCompileable_; bool debugMode_; FrameInfo frame; diff --git a/js/src/jit-test/tests/ion/arguments-objects.js b/js/src/jit-test/tests/ion/arguments-objects.js new file mode 100644 index 000000000000..e0cbbc5995ac --- /dev/null +++ b/js/src/jit-test/tests/ion/arguments-objects.js @@ -0,0 +1,19 @@ +// Make sure arguments object handlig deals well with aliasing of args. +var arr = []; +function f(x) { + var args = arguments; + arr.push(arguments); + arguments[0] = 0; + return { + f: function () { return x; }, + g: function () { return args[0]; } + }; +} + +// Check that aliased arguments are correctly set to the callObject +for (var i = 0; i < 2000; i++) + assertEq(f(1).f(), 0); + +// Check that aliased arguments are correctly read from the callObject +for (var i = 0; i < 2000; i++) + assertEq(f(1).g(), 0); diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index 73ad153f2464..600df4aa1a49 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -18,6 +18,8 @@ #include "vm/Stack-inl.h" #include "vm/ArgumentsObject-inl.h" +#include "ion/IonFrames.h" + using namespace js; using namespace js::gc; @@ -45,7 +47,8 @@ CopyStackFrameArguments(const AbstractFramePtr frame, HeapValue *dst) } /* static */ void -ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, ArgumentsData *data) +ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, + ArgumentsData *data) { RawScript script = frame.script(); if (frame.fun()->isHeavyweight() && script->argsObjAliasesFormals()) { @@ -55,6 +58,20 @@ ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, } } +/* static */ void +ArgumentsObject::MaybeForwardToCallObject(ion::IonJSFrameLayout *frame, HandleObject callObj, + JSObject *obj, ArgumentsData *data) +{ + RawFunction callee = ion::CalleeTokenToFunction(frame->calleeToken()); + RawScript script = callee->nonLazyScript(); + if (callee->isHeavyweight() && script->argsObjAliasesFormals()) { + JS_ASSERT(callObj && callObj->isCall()); + obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(*callObj.get())); + for (AliasedFormalIter fi(script); fi; fi++) + data->args[fi.frameIndex()] = MagicValue(JS_FORWARD_TO_CALL_OBJECT); + } +} + struct CopyFrameArgs { AbstractFramePtr frame_; @@ -76,6 +93,34 @@ struct CopyFrameArgs } }; +struct CopyIonJSFrameArgs +{ + ion::IonJSFrameLayout *frame_; + HandleObject callObj_; + + CopyIonJSFrameArgs(ion::IonJSFrameLayout *frame, HandleObject callObj) + : frame_(frame), callObj_(callObj) + { } + + void copyArgs(JSContext *, HeapValue *dst) const { + unsigned numActuals = frame_->numActualArgs(); + + /* Copy all arguments. */ + Value *src = frame_->argv() + 1; /* +1 to skip this. */ + Value *end = src + numActuals; + while (src != end) + (dst++)->init(*src++); + } + + /* + * If a call object exists and the arguments object aliases formals, the + * call object is the canonical location for formals. + */ + void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) { + ArgumentsObject::MaybeForwardToCallObject(frame_, callObj_, obj, data); + } +}; + struct CopyStackIterArgs { StackIter &iter_; @@ -208,6 +253,18 @@ ArgumentsObject::createUnexpected(JSContext *cx, AbstractFramePtr frame) return create(cx, script, callee, frame.numActualArgs(), copy); } +ArgumentsObject * +ArgumentsObject::createForIon(JSContext *cx, ion::IonJSFrameLayout *frame, HandleObject scopeChain) +{ + ion::CalleeToken token = frame->calleeToken(); + JS_ASSERT(ion::CalleeTokenIsFunction(token)); + RootedScript script(cx, ion::ScriptFromCalleeToken(token)); + RootedFunction callee(cx, ion::CalleeTokenToFunction(token)); + RootedObject callObj(cx, scopeChain->isCall() ? scopeChain.get() : NULL); + CopyIonJSFrameArgs copy(frame, callObj); + return create(cx, script, callee, frame->numActualArgs(), copy); +} + static JSBool args_delProperty(JSContext *cx, HandleObject obj, HandleId id, JSBool *succeeded) { diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index fbb70f0b84f4..67b342f63abc 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -123,6 +123,8 @@ class ArgumentsObject : public JSObject */ static ArgumentsObject *createUnexpected(JSContext *cx, StackIter &iter); static ArgumentsObject *createUnexpected(JSContext *cx, AbstractFramePtr frame); + static ArgumentsObject *createForIon(JSContext *cx, ion::IonJSFrameLayout *frame, + HandleObject scopeChain); /* * Return the initial length of the arguments. This may differ from the @@ -202,6 +204,8 @@ class ArgumentsObject : public JSObject } static void MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, ArgumentsData *data); + static void MaybeForwardToCallObject(ion::IonJSFrameLayout *frame, HandleObject callObj, + JSObject *obj, ArgumentsData *data); }; class NormalArgumentsObject : public ArgumentsObject diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index db4c42ac0d87..dd638712454d 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1132,7 +1132,7 @@ ContextStack::pushBailoutArgs(JSContext *cx, const ion::IonBailoutIterator &it, CopyTo dst(iag->array()); Value *src = it.actualArgs(); Value thisv = iag->thisv(); - s.readFrameArgs(dst, src, NULL, &thisv, 0, fun->nargs, argc); + s.readFrameArgs(dst, src, NULL, &thisv, 0, fun->nargs, argc, it.script()); return true; } From 32f9fa805175c61f57ccf8961700689aa0bd781c Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 18 Apr 2013 17:12:39 -0400 Subject: [PATCH 06/90] Bug 860145 - Fix bustage on no-ion compiles. r=tbpl-bustage. CLOSED TREE --- js/src/vm/ArgumentsObject.cpp | 6 ++++++ js/src/vm/ArgumentsObject.h | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index 600df4aa1a49..748417f02cc8 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -58,6 +58,7 @@ ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, } } +#if defined(JS_ION) /* static */ void ArgumentsObject::MaybeForwardToCallObject(ion::IonJSFrameLayout *frame, HandleObject callObj, JSObject *obj, ArgumentsData *data) @@ -71,6 +72,7 @@ ArgumentsObject::MaybeForwardToCallObject(ion::IonJSFrameLayout *frame, HandleOb data->args[fi.frameIndex()] = MagicValue(JS_FORWARD_TO_CALL_OBJECT); } } +#endif struct CopyFrameArgs { @@ -93,6 +95,7 @@ struct CopyFrameArgs } }; +#if defined(JS_ION) struct CopyIonJSFrameArgs { ion::IonJSFrameLayout *frame_; @@ -120,6 +123,7 @@ struct CopyIonJSFrameArgs ArgumentsObject::MaybeForwardToCallObject(frame_, callObj_, obj, data); } }; +#endif struct CopyStackIterArgs { @@ -253,6 +257,7 @@ ArgumentsObject::createUnexpected(JSContext *cx, AbstractFramePtr frame) return create(cx, script, callee, frame.numActualArgs(), copy); } +#if defined(JS_ION) ArgumentsObject * ArgumentsObject::createForIon(JSContext *cx, ion::IonJSFrameLayout *frame, HandleObject scopeChain) { @@ -264,6 +269,7 @@ ArgumentsObject::createForIon(JSContext *cx, ion::IonJSFrameLayout *frame, Handl CopyIonJSFrameArgs copy(frame, callObj); return create(cx, script, callee, frame->numActualArgs(), copy); } +#endif static JSBool args_delProperty(JSContext *cx, HandleObject obj, HandleId id, JSBool *succeeded) diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index 67b342f63abc..7e8b90c64f2b 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -123,8 +123,10 @@ class ArgumentsObject : public JSObject */ static ArgumentsObject *createUnexpected(JSContext *cx, StackIter &iter); static ArgumentsObject *createUnexpected(JSContext *cx, AbstractFramePtr frame); +#if defined(JS_ION) static ArgumentsObject *createForIon(JSContext *cx, ion::IonJSFrameLayout *frame, HandleObject scopeChain); +#endif /* * Return the initial length of the arguments. This may differ from the @@ -204,8 +206,10 @@ class ArgumentsObject : public JSObject } static void MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, ArgumentsData *data); +#if defined(JS_ION) static void MaybeForwardToCallObject(ion::IonJSFrameLayout *frame, HandleObject callObj, JSObject *obj, ArgumentsData *data); +#endif }; class NormalArgumentsObject : public ArgumentsObject From 74ae8776ed15ce04a57784fbea4be1813683e306 Mon Sep 17 00:00:00 2001 From: Tim Abraldes Date: Thu, 18 Apr 2013 14:48:24 -0700 Subject: [PATCH 07/90] bug 855587. Modify a default profile link to point to the URL that it will redirect to anyway. DONTBUILD. r=jimm --- browser/metro/locales/generic/profile/bookmarks.json.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/metro/locales/generic/profile/bookmarks.json.in b/browser/metro/locales/generic/profile/bookmarks.json.in index 3a51d98c4c88..0304e1ec836f 100644 --- a/browser/metro/locales/generic/profile/bookmarks.json.in +++ b/browser/metro/locales/generic/profile/bookmarks.json.in @@ -9,7 +9,7 @@ {"index":2,"title":"@getting_started@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/firefox/central/", "iconUri":"chrome://branding/content/favicon32.png" }, - {"index":3,"title":"@firefox_community@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/firefox/community/", + {"index":3,"title":"@firefox_community@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/contribute/", "iconUri":"chrome://branding/content/favicon32.png" }, ] From 6174808648886e4ee942537e297d05ad992be6b0 Mon Sep 17 00:00:00 2001 From: Tim Abraldes Date: Wed, 17 Apr 2013 10:40:38 -0700 Subject: [PATCH 08/90] bug 855581. Wait until we've actually closed the last tab to create a new one. DONTBUILD. r=fryn --- browser/metro/base/content/browser-ui.js | 24 +++++----------------- browser/metro/base/content/browser.js | 26 +++++++++++------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/browser/metro/base/content/browser-ui.js b/browser/metro/base/content/browser-ui.js index caa15188ae1d..f8446f0d7eab 100644 --- a/browser/metro/base/content/browser-ui.js +++ b/browser/metro/base/content/browser-ui.js @@ -393,29 +393,15 @@ var BrowserUI = { }, closeTab: function closeTab(aTab) { - // If we only have one tab, open a new one - if (Browser.tabs.length === 1 && !StartUI.isStartURI()) - Browser.addTab(Browser.getHomePage()); - - // We only have the start tab - if (Browser.tabs.length === 1) - return; - // If no tab is passed in, assume the current tab let tab = aTab || Browser.selectedTab; - let tabToClose = tab instanceof XULElement ? Browser.getTabFromChrome(tab) : tab; + Browser.closeTab(tab); + }, + animateClosingTab: function animateClosingTab(tabToClose) { if (this.isTabsOnly) { - Browser.closeTab(tabToClose); + Browser.closeTab(tabToClose, { forceClose: true } ); } else { - let nextTab = Browser.getNextTab(tabToClose); - - if (!nextTab) - return; - - if (nextTab) - Browser.selectedTab = nextTab; - // Trigger closing animation tabToClose.chromeTab.setAttribute("closing", "true"); @@ -425,7 +411,7 @@ var BrowserUI = { } this.setOnTabAnimationEnd(function() { - Browser.closeTab(tabToClose); + Browser.closeTab(tabToClose, { forceClose: true } ); if (wasCollapsed) ContextUI.dismissWithDelay(kNewTabAnimationDelayMsec); }); diff --git a/browser/metro/base/content/browser.js b/browser/metro/base/content/browser.js index 1006a2d88cf5..ba18b5f8756a 100644 --- a/browser/metro/base/content/browser.js +++ b/browser/metro/base/content/browser.js @@ -453,11 +453,12 @@ var Browser = { closeTab: function closeTab(aTab, aOptions) { let tab = aTab instanceof XULElement ? this.getTabFromChrome(aTab) : aTab; - if (!tab || !this.getNextTab(tab)) + if (!tab) { return; + } if (aOptions && "forceClose" in aOptions && aOptions.forceClose) { - this._doCloseTab(aTab); + this._doCloseTab(tab); return; } @@ -469,9 +470,11 @@ var Browser = { }, _doCloseTab: function _doCloseTab(aTab) { + if (this._tabs.length === 1) { + Browser.addTab(this.getHomePage()); + } + let nextTab = this.getNextTab(aTab); - if (!nextTab) - return; // Tabs owned by the closed tab are now orphaned. this._tabs.forEach(function(item, index, array) { @@ -918,16 +921,11 @@ var Browser = { break; case "Browser:CanUnload:Return": { - if (!json.permit) - return; - - // Allow a little delay to not close the target tab while processing - // a message for this particular tab - setTimeout(function(self) { - let tab = self.getTabForBrowser(browser); - self._doCloseTab(tab); - }, 0, this); - break; + if (json.permit) { + let tab = this.getTabForBrowser(browser); + BrowserUI.animateClosingTab(tab); + } + break; } case "Browser:ZoomToPoint:Return": if (json.zoomTo) { From 3eeee69bed7c2596bbd6f49441dd35fd8d3f0325 Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 18 Apr 2013 18:00:23 -0400 Subject: [PATCH 09/90] Bug 861596 - Add Baseline stubs to handle GetProp(length) and GetElem(int32) operations on arguments objects. r=bhackett --- js/src/ion/BaselineIC.cpp | 271 +++++++++++++++++++++++++++- js/src/ion/BaselineIC.h | 93 ++++++++++ js/src/ion/arm/MacroAssembler-arm.h | 11 ++ js/src/ion/x64/MacroAssembler-x64.h | 17 ++ js/src/ion/x86/MacroAssembler-x86.h | 18 ++ js/src/vm/ArgumentsObject.h | 5 + 6 files changed, 414 insertions(+), 1 deletion(-) diff --git a/js/src/ion/BaselineIC.cpp b/js/src/ion/BaselineIC.cpp index 4403911b76a8..4455609df9fa 100644 --- a/js/src/ion/BaselineIC.cpp +++ b/js/src/ion/BaselineIC.cpp @@ -3346,6 +3346,18 @@ TypedArrayGetElemStubExists(ICGetElem_Fallback *stub, HandleObject obj) return false; } +static bool +ArgumentsGetElemStubExists(ICGetElem_Fallback *stub, ICGetElem_Arguments::Which which) +{ + for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) { + if (!iter->isGetElem_Arguments()) + continue; + if (iter->toGetElem_Arguments()->which() == which) + return true; + } + return false; +} + static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, ICGetElem_Fallback *stub, HandleObject obj, @@ -3417,11 +3429,43 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, ICGetElem_Fallback *stu return true; } + if (lhs.isMagic(JS_OPTIMIZED_ARGUMENTS) && rhs.isInt32() && + !ArgumentsGetElemStubExists(stub, ICGetElem_Arguments::Magic)) + { + IonSpew(IonSpew_BaselineIC, " Generating GetElem(MagicArgs[Int32]) stub"); + ICGetElem_Arguments::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(), + ICGetElem_Arguments::Magic); + ICStub *argsStub = compiler.getStub(compiler.getStubSpace(script)); + if (!argsStub) + return false; + + stub->addNewStub(argsStub); + return true; + } + // Otherwise, GetElem is only optimized on objects. if (!lhs.isObject()) return true; RootedObject obj(cx, &lhs.toObject()); + // Check for ArgumentsObj[int] accesses + if (obj->isArguments() && rhs.isInt32()) { + ICGetElem_Arguments::Which which = ICGetElem_Arguments::Normal; + if (obj->isStrictArguments()) + which = ICGetElem_Arguments::Strict; + if (!ArgumentsGetElemStubExists(stub, which)) { + IonSpew(IonSpew_BaselineIC, " Generating GetElem(ArgsObj[Int32]) stub"); + ICGetElem_Arguments::Compiler compiler( + cx, stub->fallbackMonitorStub()->firstMonitorStub(), which); + ICStub *argsStub = compiler.getStub(compiler.getStubSpace(script)); + if (!argsStub) + return false; + + stub->addNewStub(argsStub); + return true; + } + } + if (obj->isNative()) { // Check for NativeObject[int] dense accesses. if (rhs.isInt32()) { @@ -3743,6 +3787,138 @@ ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm) return true; } +// +// GetEelem_Arguments +// +bool +ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler &masm) +{ + Label failure; + if (which_ == ICGetElem_Arguments::Magic) { + // Ensure that this is a magic arguments value. + masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure); + + // Ensure that frame has not loaded different arguments object since. + masm.branchTest32(Assembler::NonZero, + Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()), + Imm32(BaselineFrame::HAS_ARGS_OBJ), + &failure); + + // Ensure that index is an integer. + masm.branchTestInt32(Assembler::NotEqual, R1, &failure); + Register idx = masm.extractInt32(R1, ExtractTemp1); + + GeneralRegisterSet regs(availableGeneralRegs(2)); + Register scratch = regs.takeAny(); + + // Load num actual arguments + Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs()); + masm.loadPtr(actualArgs, scratch); + + // Ensure idx < argc + masm.branch32(Assembler::AboveOrEqual, idx, scratch, &failure); + + // Load argval + JS_ASSERT(sizeof(Value) == 8); + masm.movePtr(BaselineFrameReg, scratch); + masm.addPtr(Imm32(BaselineFrame::offsetOfArg(0)), scratch); + BaseIndex element(scratch, idx, TimesEight); + masm.loadValue(element, R0); + + // Enter type monitor IC to type-check result. + EmitEnterTypeMonitorIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; + } + + JS_ASSERT(which_ == ICGetElem_Arguments::Strict || + which_ == ICGetElem_Arguments::Normal); + + bool isStrict = which_ == ICGetElem_Arguments::Strict; + Class *clasp = isStrict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass; + + GeneralRegisterSet regs(availableGeneralRegs(2)); + Register scratchReg = regs.takeAny(); + + // Guard on input being an arguments object. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + Register objReg = masm.extractObject(R0, ExtractTemp0); + masm.branchTestObjClass(Assembler::NotEqual, objReg, scratchReg, clasp, &failure); + + // Guard on index being int32 + masm.branchTestInt32(Assembler::NotEqual, R1, &failure); + Register idxReg = masm.extractInt32(R1, ExtractTemp1); + + // Get initial ArgsObj length value. + masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg); + + // Test if length has been overridden. + masm.branchTest32(Assembler::NonZero, + scratchReg, + Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT), + &failure); + + // Length has not been overridden, ensure that R1 is an integer and is <= length. + masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg); + masm.branch32(Assembler::AboveOrEqual, idxReg, scratchReg, &failure); + + // Length check succeeded, now check the correct bit. We clobber potential type regs + // now. Inputs will have to be reconstructed if we fail after this point, but that's + // unlikely. + Label failureReconstructInputs; + regs = availableGeneralRegs(0); + if (regs.has(objReg)) + regs.take(objReg); + if (regs.has(idxReg)) + regs.take(idxReg); + if (regs.has(scratchReg)) + regs.take(scratchReg); + Register argData = regs.takeAny(); + Register tempReg = regs.takeAny(); + + // Load ArgumentsData + masm.loadPrivate(Address(objReg, ArgumentsObject::getDataSlotOffset()), argData); + + // Load deletedBits bitArray pointer into scratchReg + masm.loadPtr(Address(argData, offsetof(ArgumentsData, deletedBits)), scratchReg); + + // In tempReg, calculate index of word containing bit: (idx >> logBitsPerWord) + masm.movePtr(idxReg, tempReg); + masm.rshiftPtr(Imm32(JS_BITS_PER_WORD_LOG2), tempReg); + masm.loadPtr(BaseIndex(scratchReg, tempReg, ScaleFromElemWidth(sizeof(size_t))), scratchReg); + + // Don't bother testing specific bit, if any bit is set in the word, fail. + masm.branchPtr(Assembler::NotEqual, scratchReg, ImmWord((size_t)0), &failureReconstructInputs); + + // Load the value. use scratchReg and tempReg to form a ValueOperand to load into. + masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), argData); + regs.add(scratchReg); + regs.add(tempReg); + ValueOperand tempVal = regs.takeAnyValue(); + masm.loadValue(BaseIndex(argData, idxReg, ScaleFromElemWidth(sizeof(Value))), tempVal); + + // Makesure that this is not a FORWARD_TO_CALL_SLOT magic value. + masm.branchTestMagic(Assembler::Equal, tempVal, &failureReconstructInputs); + + // Everything checked out, return value. + masm.moveValue(tempVal, R0); + + // Type-check result + EmitEnterTypeMonitorIC(masm); + + // Failed, but inputs are deconstructed into object and int, and need to be + // reconstructed into values. + masm.bind(&failureReconstructInputs); + masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0); + masm.tagValue(JSVAL_TYPE_INT32, idxReg, R1); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + // // SetElem_Fallback // @@ -4849,6 +5025,18 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub return true; } + if (val.isMagic(JS_OPTIMIZED_ARGUMENTS) && res.isInt32()) { + IonSpew(IonSpew_BaselineIC, " Generating GetProp(MagicArgs.length) stub"); + ICGetProp_ArgumentsLength::Compiler compiler(cx, ICGetProp_ArgumentsLength::Magic); + ICStub *newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + *attached = true; + stub->addNewStub(newStub); + return true; + } + if (!val.isObject()) return true; @@ -4878,6 +5066,22 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub return true; } + if (obj->isArguments() && res.isInt32()) { + IonSpew(IonSpew_BaselineIC, " Generating GetProp(ArgsObj.length %s) stub", + obj->isStrictArguments() ? "Strict" : "Normal"); + ICGetProp_ArgumentsLength::Which which = ICGetProp_ArgumentsLength::Normal; + if (obj->isStrictArguments()) + which = ICGetProp_ArgumentsLength::Strict; + ICGetProp_ArgumentsLength::Compiler compiler(cx, which); + ICStub *newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + *attached = true; + stub->addNewStub(newStub); + return true; + } + return true; } @@ -5032,9 +5236,18 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub, if (op == JSOP_LENGTH && val.isMagic(JS_OPTIMIZED_ARGUMENTS)) { // Handle arguments.length access. if (IsOptimizedArguments(frame, val.address())) { - // TODO: attach optimized stub. res.setInt32(frame->numActualArgs()); + + // Monitor result types::TypeScript::Monitor(cx, script, pc, res); + if (!stub->addMonitorStubForValue(cx, script, res)) + return false; + + bool attached = false; + if (!TryAttachLengthStub(cx, script, stub, val, res, &attached)) + return false; + JS_ASSERT(attached); + return true; } } @@ -5567,6 +5780,62 @@ ICGetProp_CallListBaseNative::Compiler::generateStubCode(MacroAssembler &masm) return true; } +bool +ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler &masm) +{ + Label failure; + if (which_ == ICGetProp_ArgumentsLength::Magic) { + // Ensure that this is lazy arguments. + masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure); + + // Ensure that frame has not loaded different arguments object since. + masm.branchTest32(Assembler::NonZero, + Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()), + Imm32(BaselineFrame::HAS_ARGS_OBJ), + &failure); + + Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs()); + masm.loadPtr(actualArgs, R0.scratchReg()); + masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0); + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; + } + JS_ASSERT(which_ == ICGetProp_ArgumentsLength::Strict || + which_ == ICGetProp_ArgumentsLength::Normal); + + bool isStrict = which_ == ICGetProp_ArgumentsLength::Strict; + Class *clasp = isStrict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass; + + Register scratchReg = R1.scratchReg(); + + // Guard on input being an arguments object. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + Register objReg = masm.extractObject(R0, ExtractTemp0); + masm.branchTestObjClass(Assembler::NotEqual, objReg, scratchReg, clasp, &failure); + + // Get initial length value. + masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg); + + // Test if length has been overridden. + masm.branchTest32(Assembler::NonZero, + scratchReg, + Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT), + &failure); + + // Nope, shift out arguments length and return it. + // No need to type monitor because this stub always returns Int32. + masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg); + masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0); + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + // // SetProp_Fallback // diff --git a/js/src/ion/BaselineIC.h b/js/src/ion/BaselineIC.h index 461628faf47e..38b7b40a4808 100644 --- a/js/src/ion/BaselineIC.h +++ b/js/src/ion/BaselineIC.h @@ -331,6 +331,7 @@ class ICEntry _(GetElem_String) \ _(GetElem_Dense) \ _(GetElem_TypedArray) \ + _(GetElem_Arguments) \ \ _(SetElem_Fallback) \ _(SetElem_Dense) \ @@ -364,6 +365,7 @@ class ICEntry _(GetProp_CallScripted) \ _(GetProp_CallNative) \ _(GetProp_CallListBaseNative)\ + _(GetProp_ArgumentsLength) \ \ _(SetProp_Fallback) \ _(SetProp_Native) \ @@ -3034,6 +3036,56 @@ class ICGetElem_TypedArray : public ICStub }; }; +class ICGetElem_Arguments : public ICMonitoredStub +{ + friend class ICStubSpace; + public: + enum Which { Normal, Strict, Magic }; + + private: + ICGetElem_Arguments(IonCode *stubCode, ICStub *firstMonitorStub, Which which) + : ICMonitoredStub(ICStub::GetElem_Arguments, stubCode, firstMonitorStub) + { + extra_ = static_cast(which); + } + + public: + static inline ICGetElem_Arguments *New(ICStubSpace *space, IonCode *code, + ICStub *firstMonitorStub, Which which) + { + if (!code) + return NULL; + return space->allocate(code, firstMonitorStub, which); + } + + Which which() const { + return static_cast(extra_); + } + + class Compiler : public ICStubCompiler { + ICStub *firstMonitorStub_; + Which which_; + + protected: + bool generateStubCode(MacroAssembler &masm); + + virtual int32_t getKey() const { + return static_cast(kind) | (static_cast(which_) << 16); + } + + public: + Compiler(JSContext *cx, ICStub *firstMonitorStub, Which which) + : ICStubCompiler(cx, ICStub::GetElem_Arguments), + firstMonitorStub_(firstMonitorStub), + which_(which) + {} + + ICStub *getStub(ICStubSpace *space) { + return ICGetElem_Arguments::New(space, getStubCode(), firstMonitorStub_, which_); + } + }; +}; + // SetElem // JSOP_SETELEM // JSOP_INITELEM @@ -4219,6 +4271,47 @@ class ICGetProp_CallListBaseNative : public ICMonitoredStub }; }; +class ICGetProp_ArgumentsLength : public ICStub +{ + friend class ICStubSpace; + public: + enum Which { Normal, Strict, Magic }; + + protected: + ICGetProp_ArgumentsLength(IonCode *stubCode) + : ICStub(ICStub::GetProp_ArgumentsLength, stubCode) + { } + + public: + static inline ICGetProp_ArgumentsLength *New(ICStubSpace *space, IonCode *code) + { + if (!code) + return NULL; + return space->allocate(code); + } + + class Compiler : public ICStubCompiler { + protected: + Which which_; + + bool generateStubCode(MacroAssembler &masm); + + virtual int32_t getKey() const { + return static_cast(kind) | (static_cast(which_) << 16); + } + + public: + Compiler(JSContext *cx, Which which) + : ICStubCompiler(cx, ICStub::GetProp_ArgumentsLength), + which_(which) + {} + + ICStub *getStub(ICStubSpace *space) { + return ICGetProp_ArgumentsLength::New(space, getStubCode()); + } + }; +}; + // SetProp // JSOP_SETPROP // JSOP_SETNAME diff --git a/js/src/ion/arm/MacroAssembler-arm.h b/js/src/ion/arm/MacroAssembler-arm.h index 18b258b750cc..2f8551df5912 100644 --- a/js/src/ion/arm/MacroAssembler-arm.h +++ b/js/src/ion/arm/MacroAssembler-arm.h @@ -804,6 +804,17 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM cond = testMagic(cond, t); ma_b(label, cond); } + void branchTestMagicValue(Condition cond, const ValueOperand &val, JSWhyMagic why, + Label *label) { + JS_ASSERT(cond == Equal || cond == NotEqual); + // Test for magic + Label notmagic; + Condition testCond = testMagic(cond, val); + ma_b(¬magic, InvertCondition(testCond)); + // Test magic value + branch32(cond, val.payloadReg(), Imm32(static_cast(why)), label); + bind(¬magic); + } template void branchTestBooleanTruthy(bool b, const T & t, Label *label) { Condition c = testBooleanTruthy(b, t); diff --git a/js/src/ion/x64/MacroAssembler-x64.h b/js/src/ion/x64/MacroAssembler-x64.h index c7b8eecba0aa..112b3df66b1b 100644 --- a/js/src/ion/x64/MacroAssembler-x64.h +++ b/js/src/ion/x64/MacroAssembler-x64.h @@ -705,6 +705,19 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared cond = testMagic(cond, t); j(cond, label); } + void branchTestMagicValue(Condition cond, const ValueOperand &val, JSWhyMagic why, + Label *label) + { + JS_ASSERT(cond == Equal || cond == NotEqual); + // Test for magic + Label notmagic; + Condition testCond = testMagic(cond, val); + j(InvertCondition(testCond), ¬magic); + // Test magic value + unboxMagic(val, ScratchReg); + branch32(cond, ScratchReg, Imm32(static_cast(why)), label); + bind(¬magic); + } Condition testMagic(Condition cond, const ValueOperand &src) { splitTag(src, ScratchReg); return testMagic(cond, ScratchReg); @@ -768,6 +781,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared unboxBoolean(Operand(src), dest); } + void unboxMagic(const ValueOperand &src, const Register &dest) { + movl(Operand(src.valueReg()), dest); + } + void unboxDouble(const ValueOperand &src, const FloatRegister &dest) { movqsd(src.valueReg(), dest); } diff --git a/js/src/ion/x86/MacroAssembler-x86.h b/js/src/ion/x86/MacroAssembler-x86.h index 18f95d8bb89d..353ea072052d 100644 --- a/js/src/ion/x86/MacroAssembler-x86.h +++ b/js/src/ion/x86/MacroAssembler-x86.h @@ -607,6 +607,24 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared cond = testMagic(cond, t); j(cond, label); } + void branchTestMagicValue(Condition cond, const ValueOperand &val, JSWhyMagic why, + Label *label) + { + JS_ASSERT(cond == Equal || cond == NotEqual); + if (cond == Equal) { + // Test for magic + Label notmagic; + Condition testCond = testMagic(Equal, val); + j(InvertCondition(testCond), ¬magic); + // Test magic value + branch32(NotEqual, val.payloadReg(), Imm32(static_cast(why)), label); + bind(¬magic); + } else { + Condition testCond = testMagic(NotEqual, val); + j(testCond, label); + branch32(NotEqual, val.payloadReg(), Imm32(static_cast(why)), label); + } + } // Note: this function clobbers the source register. void boxDouble(const FloatRegister &src, const ValueOperand &dest) { diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index 7e8b90c64f2b..19dac2428b78 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -99,9 +99,11 @@ class ArgumentsObject : public JSObject static const uint32_t DATA_SLOT = 1; static const uint32_t MAYBE_CALL_SLOT = 2; + public: static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1; static const uint32_t PACKED_BITS_COUNT = 1; + protected: template static ArgumentsObject *create(JSContext *cx, HandleScript script, HandleFunction callee, unsigned numActuals, CopyArgs ©); @@ -204,6 +206,9 @@ class ArgumentsObject : public JSObject static size_t getDataSlotOffset() { return getFixedSlotOffset(DATA_SLOT); } + static size_t getInitialLengthSlotOffset() { + return getFixedSlotOffset(INITIAL_LENGTH_SLOT); + } static void MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, ArgumentsData *data); #if defined(JS_ION) From 58910d53cfd48b0e069559c7fd9f0fea8700f001 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Thu, 18 Apr 2013 15:34:48 -0700 Subject: [PATCH 10/90] Back out e1549b50183f (bug 860027), suspected of regressing Trace Malloc and/or Ts Paint --- js/xpconnect/src/XPCComponents.cpp | 144 +++++++++++++----- .../public/nsIInterfaceInfoManager.idl | 4 +- .../xptinfo/src/xptiInterfaceInfoManager.cpp | 22 +-- 3 files changed, 117 insertions(+), 53 deletions(-) diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 392f083a5f2d..07300eb0514a 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -34,7 +34,6 @@ #include "nsJSEnvironment.h" #include "nsXMLHttpRequest.h" #include "mozilla/Telemetry.h" -#include "mozilla/XPTInterfaceInfoManager.h" #include "nsDOMClassInfoID.h" using namespace mozilla; @@ -121,7 +120,7 @@ public: virtual ~nsXPCComponents_Interfaces(); private: - nsCOMArray mInterfaces; + nsCOMPtr mManager; }; /* void getInterfaces (out uint32_t count, [array, size_is (count), retval] @@ -216,9 +215,9 @@ nsXPCComponents_Interfaces::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) return NS_ERROR_NOT_AVAILABLE; } -nsXPCComponents_Interfaces::nsXPCComponents_Interfaces() +nsXPCComponents_Interfaces::nsXPCComponents_Interfaces() : + mManager(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)) { - XPTInterfaceInfoManager::GetSingleton()->GetScriptableInterfaces(mInterfaces); } nsXPCComponents_Interfaces::~nsXPCComponents_Interfaces() @@ -255,36 +254,64 @@ nsXPCComponents_Interfaces::NewEnumerate(nsIXPConnectWrappedNative *wrapper, uint32_t enum_op, jsval * statep, jsid * idp, bool *_retval) { + nsIEnumerator* e; + switch (enum_op) { case JSENUMERATE_INIT: case JSENUMERATE_INIT_ALL: { - *statep = JSVAL_ZERO; + if (!mManager || + NS_FAILED(mManager->EnumerateInterfaces(&e)) || !e || + NS_FAILED(e->First())) + + { + *statep = JSVAL_NULL; + return NS_ERROR_UNEXPECTED; + } + + *statep = PRIVATE_TO_JSVAL(e); if (idp) - *idp = INT_TO_JSID(mInterfaces.Length()); + *idp = INT_TO_JSID(0); // indicate that we don't know the count return NS_OK; } case JSENUMERATE_NEXT: { - uint32_t idx = JSVAL_TO_INT(*statep); - nsIInterfaceInfo* interface = mInterfaces.SafeElementAt(idx); - *statep = UINT_TO_JSVAL(idx + 1); + nsCOMPtr isup; - if (interface) { - JSString* idstr; - const char* name; + e = (nsIEnumerator*) JSVAL_TO_PRIVATE(*statep); - if (NS_SUCCEEDED(interface->GetNameShared(&name)) && name && - nullptr != (idstr = JS_NewStringCopyZ(cx, name)) && - JS_ValueToId(cx, STRING_TO_JSVAL(idstr), idp)) { - return NS_OK; + while (1) { + if (static_cast(NS_ENUMERATOR_FALSE) == e->IsDone() && + NS_SUCCEEDED(e->CurrentItem(getter_AddRefs(isup))) && isup) { + e->Next(); + nsCOMPtr iface(do_QueryInterface(isup)); + if (iface) { + JSString* idstr; + const char* name; + bool scriptable; + + if (NS_SUCCEEDED(iface->IsScriptable(&scriptable)) && + !scriptable) { + continue; + } + + if (NS_SUCCEEDED(iface->GetNameShared(&name)) && name && + nullptr != (idstr = JS_NewStringCopyZ(cx, name)) && + JS_ValueToId(cx, STRING_TO_JSVAL(idstr), idp)) { + return NS_OK; + } + } } + // else... + break; } - // fall through + // FALL THROUGH } case JSENUMERATE_DESTROY: default: + e = (nsIEnumerator*) JSVAL_TO_PRIVATE(*statep); + NS_IF_RELEASE(e); *statep = JSVAL_NULL; return NS_OK; } @@ -299,16 +326,17 @@ nsXPCComponents_Interfaces::NewResolve(nsIXPConnectWrappedNative *wrapper, { RootedObject obj(cx, objArg); RootedId id(cx, idArg); + + if (!mManager || !JSID_IS_STRING(id)) + return NS_OK; + JSAutoByteString name; RootedString str(cx, JSID_TO_STRING(id)); - if (JSID_IS_STRING(id) && name.encodeLatin1(cx, JSID_TO_STRING(id)) && - name.ptr()[0] != '{') - { - // we only allow interfaces by name here + // we only allow interfaces by name here + if (name.encodeLatin1(cx, JSID_TO_STRING(id)) && name.ptr()[0] != '{') { nsCOMPtr info; - XPTInterfaceInfoManager::GetSingleton()-> - GetInfoForName(name.ptr(), getter_AddRefs(info)); + mManager->GetInfoForName(name.ptr(), getter_AddRefs(info)); if (!info) return NS_OK; @@ -400,7 +428,7 @@ public: virtual ~nsXPCComponents_InterfacesByID(); private: - nsCOMArray mInterfaces; + nsCOMPtr mManager; }; /***************************************************************************/ @@ -496,9 +524,9 @@ nsXPCComponents_InterfacesByID::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) return NS_ERROR_NOT_AVAILABLE; } -nsXPCComponents_InterfacesByID::nsXPCComponents_InterfacesByID() +nsXPCComponents_InterfacesByID::nsXPCComponents_InterfacesByID() : + mManager(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)) { - XPTInterfaceInfoManager::GetSingleton()->GetScriptableInterfaces(mInterfaces); } nsXPCComponents_InterfacesByID::~nsXPCComponents_InterfacesByID() @@ -533,38 +561,68 @@ nsXPCComponents_InterfacesByID::NewEnumerate(nsIXPConnectWrappedNative *wrapper, uint32_t enum_op, jsval * statep, jsid * idp, bool *_retval) { + nsIEnumerator* e; + switch (enum_op) { case JSENUMERATE_INIT: case JSENUMERATE_INIT_ALL: { - *statep = JSVAL_ZERO; + if (!mManager || + NS_FAILED(mManager->EnumerateInterfaces(&e)) || !e || + NS_FAILED(e->First())) + + { + *statep = JSVAL_NULL; + return NS_ERROR_UNEXPECTED; + } + + *statep = PRIVATE_TO_JSVAL(e); if (idp) - *idp = INT_TO_JSID(mInterfaces.Length()); + *idp = INT_TO_JSID(0); // indicate that we don't know the count return NS_OK; } case JSENUMERATE_NEXT: { - uint32_t idx = JSVAL_TO_INT(*statep); - nsIInterfaceInfo* interface = mInterfaces.SafeElementAt(idx); - *statep = UINT_TO_JSVAL(idx + 1); - if (interface) { - nsIID const *iid; - char idstr[NSID_LENGTH]; - JSString* jsstr; + nsCOMPtr isup; - if (NS_SUCCEEDED(interface->GetIIDShared(&iid))) { - iid->ToProvidedString(idstr); - jsstr = JS_NewStringCopyZ(cx, idstr); - if (jsstr && JS_ValueToId(cx, STRING_TO_JSVAL(jsstr), idp)) { - return NS_OK; + e = (nsIEnumerator*) JSVAL_TO_PRIVATE(*statep); + + while (1) { + if (static_cast(NS_ENUMERATOR_FALSE) == e->IsDone() && + NS_SUCCEEDED(e->CurrentItem(getter_AddRefs(isup))) && isup) { + e->Next(); + nsCOMPtr iface(do_QueryInterface(isup)); + if (iface) { + nsIID const *iid; + char idstr[NSID_LENGTH]; + JSString* jsstr; + bool scriptable; + + if (NS_SUCCEEDED(iface->IsScriptable(&scriptable)) && + !scriptable) { + continue; + } + + if (NS_SUCCEEDED(iface->GetIIDShared(&iid))) { + iid->ToProvidedString(idstr); + jsstr = JS_NewStringCopyZ(cx, idstr); + if (jsstr && + JS_ValueToId(cx, STRING_TO_JSVAL(jsstr), idp)) { + return NS_OK; + } + } } } + // else... + break; } // FALL THROUGH } case JSENUMERATE_DESTROY: default: + e = (nsIEnumerator*) JSVAL_TO_PRIVATE(*statep); + NS_IF_RELEASE(e); *statep = JSVAL_NULL; return NS_OK; } @@ -581,6 +639,9 @@ nsXPCComponents_InterfacesByID::NewResolve(nsIXPConnectWrappedNative *wrapper, RootedId id(cx, idArg); RootedString str(cx, JSID_TO_STRING(id)); + if (!mManager || !JSID_IS_STRING(id)) + return NS_OK; + if (38 != JS_GetStringLength(str)) return NS_OK; @@ -590,8 +651,7 @@ nsXPCComponents_InterfacesByID::NewResolve(nsIXPConnectWrappedNative *wrapper, return NS_OK; nsCOMPtr info; - XPTInterfaceInfoManager::GetSingleton()-> - GetInfoForIID(&iid, getter_AddRefs(info)); + mManager->GetInfoForIID(&iid, getter_AddRefs(info)); if (!info) return NS_OK; diff --git a/xpcom/reflect/xptinfo/public/nsIInterfaceInfoManager.idl b/xpcom/reflect/xptinfo/public/nsIInterfaceInfoManager.idl index f0db98a2f6e1..72fb5f2f8a01 100644 --- a/xpcom/reflect/xptinfo/public/nsIInterfaceInfoManager.idl +++ b/xpcom/reflect/xptinfo/public/nsIInterfaceInfoManager.idl @@ -12,7 +12,7 @@ #include "nsISimpleEnumerator.idl" /* this is NOT intended to be scriptable */ -[uuid(1d53d8d9-1d92-428f-b5cc-198b55e897d7)] +[uuid(8B161900-BE2B-11d2-9831-006008962422)] interface nsIInterfaceInfoManager : nsISupports { nsIInterfaceInfo getInfoForIID(in nsIIDPtr iid); @@ -21,6 +21,8 @@ interface nsIInterfaceInfoManager : nsISupports nsIIDPtr getIIDForName(in string name); string getNameForIID(in nsIIDPtr iid); + nsIEnumerator enumerateInterfaces(); + void autoRegisterInterfaces(); nsIEnumerator enumerateInterfacesWhoseNamesStartWith(in string prefix); diff --git a/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp b/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp index 650c51dfb41e..e1b7c874b55c 100644 --- a/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp +++ b/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp @@ -268,30 +268,32 @@ XPTInterfaceInfoManager::GetNameForIID(const nsIID * iid, char **_retval) static PLDHashOperator xpti_ArrayAppender(const char* name, xptiInterfaceEntry* entry, void* arg) { - nsCOMArray* array = static_cast*>(arg); + nsISupportsArray* array = (nsISupportsArray*) arg; nsCOMPtr ii; - if (NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii)))) { - bool scriptable = false; - ii->IsScriptable(&scriptable); - if (scriptable) { + if (NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii)))) array->AppendElement(ii); - } - } return PL_DHASH_NEXT; } /* nsIEnumerator enumerateInterfaces (); */ -void -XPTInterfaceInfoManager::GetScriptableInterfaces(nsCOMArray& aInterfaces) +NS_IMETHODIMP +XPTInterfaceInfoManager::EnumerateInterfaces(nsIEnumerator **_retval) { // I didn't want to incur the size overhead of using nsHashtable just to // make building an enumerator easier. So, this code makes a snapshot of // the table using an nsISupportsArray and builds an enumerator for that. // We can afford this transient cost. + nsCOMPtr array; + NS_NewISupportsArray(getter_AddRefs(array)); + if (!array) + return NS_ERROR_UNEXPECTED; + ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); - mWorkingSet.mNameTable.EnumerateRead(xpti_ArrayAppender, &aInterfaces); + mWorkingSet.mNameTable.EnumerateRead(xpti_ArrayAppender, array); + + return array->Enumerate(_retval); } struct ArrayAndPrefix From 1640c1840c368b941d1853b08a4243daaf70c577 Mon Sep 17 00:00:00 2001 From: Tim Abraldes Date: Thu, 18 Apr 2013 15:52:38 -0700 Subject: [PATCH 11/90] bug 852805. Don't treat an empty search box as special; still perform the search. DONTBUILD. r=fryn --- browser/metro/base/content/helperui/FindHelperUI.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/browser/metro/base/content/helperui/FindHelperUI.js b/browser/metro/base/content/helperui/FindHelperUI.js index 3adfd4a7b5b5..4a21086ece2b 100644 --- a/browser/metro/base/content/helperui/FindHelperUI.js +++ b/browser/metro/base/content/helperui/FindHelperUI.js @@ -154,12 +154,6 @@ var FindHelperUI = { search: function findHelperSearch(aValue) { this.updateCommands(aValue); - // Don't bother searching if the value is empty - if (aValue == "") { - this.status = null; - return; - } - Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Find", { searchString: aValue }); }, From aef2ae28d55a7e3f9c27adbd5a71d5e2392e63cb Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Fri, 19 Apr 2013 02:13:35 +0300 Subject: [PATCH 12/90] Bug 856351 - Paris binding for DragEvent, r=peterv --HG-- extra : rebase_source : 695b5a3bf3641f0e39d33ca8718e1bc78695b09d --- content/events/src/nsDOMDragEvent.cpp | 17 +++++++++------ content/events/src/nsDOMDragEvent.h | 25 +++++++++++++++++++++ dom/bindings/Bindings.conf | 6 +++++- dom/webidl/DragEvent.webidl | 31 +++++++++++++++++++++++++++ dom/webidl/WebIDL.mk | 1 + 5 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 dom/webidl/DragEvent.webidl diff --git a/content/events/src/nsDOMDragEvent.cpp b/content/events/src/nsDOMDragEvent.cpp index 32c4a5dba9f3..68bfac9e0623 100644 --- a/content/events/src/nsDOMDragEvent.cpp +++ b/content/events/src/nsDOMDragEvent.cpp @@ -26,6 +26,7 @@ nsDOMDragEvent::nsDOMDragEvent(mozilla::dom::EventTarget* aOwner, mEvent->refPoint.x = mEvent->refPoint.y = 0; static_cast(mEvent)->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; } + SetIsDOMBinding(); } nsDOMDragEvent::~nsDOMDragEvent() @@ -74,27 +75,31 @@ nsDOMDragEvent::InitDragEvent(const nsAString & aType, NS_IMETHODIMP nsDOMDragEvent::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer) +{ + NS_IF_ADDREF(*aDataTransfer = GetDataTransfer()); + return NS_OK; +} + +nsIDOMDataTransfer* +nsDOMDragEvent::GetDataTransfer() { // the dataTransfer field of the event caches the DataTransfer associated // with the drag. It is initialized when an attempt is made to retrieve it // rather that when the event is created to avoid duplicating the data when // no listener ever uses it. - *aDataTransfer = nullptr; - if (!mEvent || mEvent->eventStructType != NS_DRAG_EVENT) { NS_WARNING("Tried to get dataTransfer from non-drag event!"); - return NS_OK; + return nullptr; } nsDragEvent* dragEvent = static_cast(mEvent); // for synthetic events, just use the supplied data transfer object even if null if (!mEventIsInternal) { nsresult rv = nsContentUtils::SetDataTransferInEvent(dragEvent); - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_SUCCESS(rv, nullptr); } - NS_IF_ADDREF(*aDataTransfer = dragEvent->dataTransfer); - return NS_OK; + return dragEvent->dataTransfer; } nsresult NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult, diff --git a/content/events/src/nsDOMDragEvent.h b/content/events/src/nsDOMDragEvent.h index 63803fe107e6..c93b1c12e2ae 100644 --- a/content/events/src/nsDOMDragEvent.h +++ b/content/events/src/nsDOMDragEvent.h @@ -8,6 +8,7 @@ #include "nsIDOMDragEvent.h" #include "nsDOMMouseEvent.h" +#include "mozilla/dom/DragEventBinding.h" class nsEvent; @@ -24,6 +25,30 @@ public: NS_DECL_NSIDOMDRAGEVENT NS_FORWARD_TO_NSDOMMOUSEEVENT + + virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope) + { + return mozilla::dom::DragEventBinding::Wrap(aCx, aScope, this); + } + + nsIDOMDataTransfer* GetDataTransfer(); + + void InitDragEvent(const nsAString& aType, + bool aCanBubble, bool aCancelable, + nsIDOMWindow* aView, int32_t aDetail, + int32_t aScreenX, int32_t aScreenY, + int32_t aClientX, int32_t aClientY, + bool aCtrlKey, bool aAltKey, bool aShiftKey, + bool aMetaKey, uint16_t aButton, + mozilla::dom::EventTarget* aRelatedTarget, + nsIDOMDataTransfer* aDataTransfer, + mozilla::ErrorResult& aRv) + { + aRv = InitDragEvent(aType, aCanBubble, aCancelable, + aView, aDetail, aScreenX, aScreenY, aClientX, aClientY, + aCtrlKey, aAltKey, aShiftKey, aMetaKey, aButton, + aRelatedTarget, aDataTransfer); + } }; nsresult NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult, diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 0fb4a3e8065f..6a2302652977 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -268,6 +268,10 @@ DOMInterfaces = { 'nativeType': 'nsDOMTokenList', }, +'DragEvent': { + 'nativeType': 'nsDOMDragEvent', +}, + 'DummyInterface': { 'skipGen': True, 'register': False, @@ -1528,4 +1532,4 @@ addExternalIface('XPathResult', nativeType='nsISupports') addExternalIface('XPathExpression') addExternalIface('XPathNSResolver') addExternalIface('XULCommandDispatcher') -addExternalIface('DataTransfer') +addExternalIface('DataTransfer', notflattened=True) diff --git a/dom/webidl/DragEvent.webidl b/dom/webidl/DragEvent.webidl new file mode 100644 index 000000000000..63846a45337f --- /dev/null +++ b/dom/webidl/DragEvent.webidl @@ -0,0 +1,31 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +interface WindowProxy; +interface DataTransfer; + +interface DragEvent : MouseEvent +{ + readonly attribute DataTransfer? dataTransfer; + + [Throws] + void initDragEvent(DOMString type, + boolean canBubble, + boolean cancelable, + WindowProxy? aView, + long aDetail, + long aScreenX, + long aScreenY, + long aClientX, + long aClientY, + boolean aCtrlKey, + boolean aAltKey, + boolean aShiftKey, + boolean aMetaKey, + unsigned short aButton, + EventTarget? aRelatedTarget, + DataTransfer? aDataTransfer); +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index e3be80f1894f..5a2be3b17138 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -58,6 +58,7 @@ webidl_files = \ DOMStringMap.webidl \ DOMTokenList.webidl \ DOMTransaction.webidl \ + DragEvent.webidl \ DummyBinding.webidl \ DynamicsCompressorNode.webidl \ Element.webidl \ From 5eab0658e4a15b653c72c148855180d6d267aba8 Mon Sep 17 00:00:00 2001 From: Steven Michaud Date: Thu, 18 Apr 2013 18:40:40 -0500 Subject: [PATCH 13/90] Bug 861317 - nsChildView::WillPaint terribly inefficient when drawing to titlebar. r=bgirard --- widget/cocoa/nsChildView.h | 17 ++++++++++ widget/cocoa/nsChildView.mm | 61 +++++++++++++++++++++++++---------- widget/cocoa/nsCocoaWindow.mm | 2 ++ 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index 0439b93e2afa..45ae0e2d1369 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -103,6 +103,19 @@ class GLManager; @end +@interface NSView (Undocumented) + +// Returns an NSRect that is the bounding box for all an NSView's dirty +// rectangles (ones that need to be redrawn). The full list of dirty +// rectangles can be obtained by calling -[NSView _dirtyRegion] and then +// calling -[NSRegion getRects:count:] on what it returns. Both these +// methods have been present in the same form since at least OS X 10.5. +// Unlike -[NSView getRectsBeingDrawn:count:], these methods can be called +// outside a call to -[NSView drawRect:]. +- (NSRect)_dirtyRect; + +@end + // Support for pixel scroll deltas, not part of NSEvent.h // See http://lists.apple.com/archives/cocoa-dev/2007/Feb/msg00050.html @interface NSEvent (DeviceDelta) @@ -298,6 +311,8 @@ typedef NSInteger NSEventGestureAxis; - (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent; +- (void)maybeDrawInTitlebar; + - (void)drawRect:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext; - (void)drawTitlebar:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext; @@ -532,6 +547,8 @@ public: NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent); + CGContextRef GetCGContextForTitlebarDrawing(NSSize aSize); + virtual void WillPaint() MOZ_OVERRIDE; mozilla::widget::TextInputHandler* GetTextInputHandler() diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 91ccf9566c80..55deefe8e112 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -691,29 +691,21 @@ nsChildView::ReparentNativeWidget(nsIWidget* aNewParent) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } -void -nsChildView::WillPaint() +CGContextRef +nsChildView::GetCGContextForTitlebarDrawing(NSSize aSize) { - if (!mView || ![mView isKindOfClass:[ChildView class]]) - return; - NSWindow* win = [mView window]; - if (!win || ![win isKindOfClass:[ToolbarWindow class]]) - return; - if (![(ToolbarWindow*)win drawsContentsIntoWindowFrame]) - return; - - NSRect titlebarRect = [(ToolbarWindow*)win titlebarRect]; - gfxSize titlebarSize(titlebarRect.size.width, titlebarRect.size.height); + gfxSize titlebarSize(aSize.width, aSize.height); if (!mTitlebarSurf || mTitlebarSize != titlebarSize) { mTitlebarSize = titlebarSize; mTitlebarSurf = new gfxQuartzSurface(titlebarSize, gfxASurface::ImageFormatARGB32); } - NSRect flippedTitlebarRect = { NSZeroPoint, titlebarRect.size }; - CGContextRef context = mTitlebarSurf->GetCGContext(); + return mTitlebarSurf->GetCGContext(); +} - CGContextSaveGState(context); - [(ChildView*)mView drawRect:flippedTitlebarRect inTitlebarContext:context]; - CGContextRestoreGState(context); +void +nsChildView::WillPaint() +{ + [mView maybeDrawInTitlebar]; } void @@ -2663,6 +2655,41 @@ NSEvent* gLastDragMouseDownEvent = nil; } } +- (void)maybeDrawInTitlebar +{ + if (!mGeckoChild) { + return; + } + ToolbarWindow* win = [self window]; + if (!win || ![win isKindOfClass:[ToolbarWindow class]]) { + return; + } + if (![win drawsContentsIntoWindowFrame]) { + return; + } + + // Check the parts of the frame view occupied by the titlebar to see if all + // or part of it is "dirty" (needs to be redrawn). This is effectively the + // top 22 pixels of the frame view, and is not the same thing as the + // "unified toolbar". Our ChildView ('self') is the same size as the frame + // view and covers it. Our ChildView has a flipped coordinate system, but + // the frame view doesn't. + NSRect titlebarRect = [win titlebarRect]; + NSView* frameView = [[win contentView] superview]; + NSRect dirtyRect = NSIntersectionRect([frameView _dirtyRect], titlebarRect); + // Flip dirtyRect's coordinate system. + dirtyRect.origin.y = [frameView bounds].size.height - + dirtyRect.origin.y - dirtyRect.size.height; + if (NSIsEmptyRect(dirtyRect)) { + return; + } + + CGContextRef context = mGeckoChild->GetCGContextForTitlebarDrawing(titlebarRect.size); + CGContextSaveGState(context); + [self drawRect:dirtyRect inTitlebarContext:context]; + CGContextRestoreGState(context); +} + - (void)drawTitlebar:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext { if (mGeckoChild) { diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 13ffabbff557..85929b465305 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -2901,6 +2901,8 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton"; } else { [borderView setNeedsDisplayInRect:rect]; } + + [[self mainChildView] maybeDrawInTitlebar]; } - (NSRect)titlebarRect From a93bc7345c49d7880ab2a38c296682375216373c Mon Sep 17 00:00:00 2001 From: John Schoenick Date: Mon, 18 Mar 2013 17:34:27 -0700 Subject: [PATCH 14/90] Bug 852315 - Use CheckPluginStopEvent for plugins when documents go inactive. r=bsmedberg --- content/base/src/nsObjectLoadingContent.cpp | 48 +++++++++------------ content/base/src/nsObjectLoadingContent.h | 5 +++ 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index 08236ac532de..ea9e88d5dc77 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -556,6 +556,19 @@ IsPluginEnabledForType(const nsCString& aMIMEType) /// Member Functions /// +// Helper to queue a CheckPluginStopEvent for a OBJLC object +void +nsObjectLoadingContent::QueueCheckPluginStopEvent() +{ + nsCOMPtr event = new CheckPluginStopEvent(this); + mPendingCheckPluginStopEvent = event; + + nsCOMPtr appShell = do_GetService(kAppShellCID); + if (appShell) { + appShell->RunInStableState(event); + } +} + // Tedious syntax to create a plugin stream listener with checks and put it in // mFinalListener bool @@ -659,13 +672,7 @@ nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent) // the event loop. If we get back to the event loop and the node // has still not been added back to the document then we tear down the // plugin - nsCOMPtr event = new CheckPluginStopEvent(this); - mPendingCheckPluginStopEvent = event; - - nsCOMPtr appShell = do_GetService(kAppShellCID); - if (appShell) { - appShell->RunInStableState(event); - } + QueueCheckPluginStopEvent(); } else if (mType != eType_Image) { // nsImageLoadingContent handles the image case. // Reset state and clear pending events @@ -690,8 +697,8 @@ nsObjectLoadingContent::nsObjectLoadingContent() nsObjectLoadingContent::~nsObjectLoadingContent() { - // Should have been unbound from the tree at this point, and CheckPluginStopEvent - // keeps us alive + // Should have been unbound from the tree at this point, and + // CheckPluginStopEvent keeps us alive if (mFrameLoader) { NS_NOTREACHED("Should not be tearing down frame loaders at this point"); mFrameLoader->Destroy(); @@ -820,16 +827,11 @@ nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading) void nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged() { - if (!mInstanceOwner) { - return; - } - nsCOMPtr thisContent = - do_QueryInterface(static_cast(this)); - nsIDocument* ownerDoc = thisContent->OwnerDoc(); - if (!ownerDoc->IsActive()) { - StopPluginInstance(); - } + // If we have a plugin we want to queue an event to stop it unless we are + // moved into an active document before returning to the event loop. + if (mInstanceOwner) + QueueCheckPluginStopEvent(); } // nsIRequestObserver @@ -995,15 +997,7 @@ nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame) // CheckPluginStopEvent if (mInstanceOwner) { mInstanceOwner->SetFrame(nullptr); - - nsCOMPtr event = new CheckPluginStopEvent(this); - mPendingCheckPluginStopEvent = event; - nsCOMPtr appShell = do_GetService(kAppShellCID); - if (appShell) { - appShell->RunInStableState(event); - } else { - NS_NOTREACHED("No app shell?"); - } + QueueCheckPluginStopEvent(); } return NS_OK; } diff --git a/content/base/src/nsObjectLoadingContent.h b/content/base/src/nsObjectLoadingContent.h index 5dfb17dbba32..f37b87850e7d 100644 --- a/content/base/src/nsObjectLoadingContent.h +++ b/content/base/src/nsObjectLoadingContent.h @@ -344,6 +344,11 @@ class nsObjectLoadingContent : public nsImageLoadingContent */ ParameterUpdateFlags UpdateObjectParameters(); + /** + * Queue a CheckPluginStopEvent and track it in mPendingCheckPluginStopEvent + */ + void QueueCheckPluginStopEvent(); + void NotifyContentObjectWrapper(); /** From 249e84efe1b1040dfda201faccbd50c2f92b5bf8 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Thu, 14 Mar 2013 10:26:06 -0700 Subject: [PATCH 15/90] Bug 706885 - Implement generational GC for the SpiderMonkey interpreter; r=billm --- js/public/GCAPI.h | 25 ++ js/public/HashTable.h | 3 + js/src/Makefile.in | 1 + js/src/gc/Marking.cpp | 78 ++++-- js/src/gc/Marking.h | 12 +- js/src/gc/Nursery-inl.h | 83 ++++++ js/src/gc/Nursery.cpp | 516 +++++++++++++++++++++++++++++++++++ js/src/gc/Nursery.h | 185 +++++++++++++ js/src/gc/StoreBuffer.cpp | 116 +++++++- js/src/gc/StoreBuffer.h | 85 ++++-- js/src/gc/Verifier.cpp | 13 +- js/src/gc/Zone.h | 2 +- js/src/jsapi.cpp | 8 +- js/src/jscntxt.h | 19 +- js/src/jscompartment.cpp | 21 ++ js/src/jscompartment.h | 2 + js/src/jsfriendapi.cpp | 1 + js/src/jsgc.cpp | 62 ++++- js/src/jsgc.h | 20 +- js/src/jsgcinlines.h | 39 ++- js/src/jsobj.cpp | 101 +++++-- js/src/jsobj.h | 18 +- js/src/jsobjinlines.h | 9 +- js/src/jstypedarray.cpp | 36 ++- js/src/jstypedarrayinlines.h | 27 +- js/src/shell/js.cpp | 4 +- js/src/vm/ObjectImpl.h | 6 + js/src/vm/Shape.cpp | 24 ++ js/src/vm/Shape.h | 2 + 29 files changed, 1378 insertions(+), 140 deletions(-) create mode 100644 js/src/gc/Nursery-inl.h create mode 100644 js/src/gc/Nursery.cpp create mode 100644 js/src/gc/Nursery.h diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 80d8f8a426fb..a69b567f65e2 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -24,6 +24,31 @@ namespace JS { D(DEBUG_MODE_GC) \ D(TRANSPLANT) \ D(RESET) \ + D(OUT_OF_NURSERY) \ + D(EVICT_NURSERY) \ + D(FULL_STORE_BUFFER) \ + \ + /* These are reserved for future use. */ \ + D(RESERVED0) \ + D(RESERVED1) \ + D(RESERVED2) \ + D(RESERVED3) \ + D(RESERVED4) \ + D(RESERVED5) \ + D(RESERVED6) \ + D(RESERVED7) \ + D(RESERVED8) \ + D(RESERVED9) \ + D(RESERVED10) \ + D(RESERVED11) \ + D(RESERVED12) \ + D(RESERVED13) \ + D(RESERVED14) \ + D(RESERVED15) \ + D(RESERVED16) \ + D(RESERVED17) \ + D(RESERVED18) \ + D(RESERVED19) \ \ /* Reasons from Firefox */ \ D(DOM_WINDOW_UTILS) \ diff --git a/js/public/HashTable.h b/js/public/HashTable.h index acd4e310ac79..3402bfbff446 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -580,6 +580,9 @@ class HashMapEntry HashMapEntry(MoveRef rhs) : key(Move(rhs->key)), value(Move(rhs->value)) { } + typedef Key KeyType; + typedef Value ValueType; + const Key key; Value value; }; diff --git a/js/src/Makefile.in b/js/src/Makefile.in index f8f00c3557a0..8edf266813e9 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -135,6 +135,7 @@ CPPSRCS = \ Memory.cpp \ Statistics.cpp \ StoreBuffer.cpp \ + Nursery.cpp \ Iteration.cpp \ Zone.cpp \ Verifier.cpp \ diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index f6b18144a985..383517e2f4c0 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -10,6 +10,7 @@ #include "jsstr.h" #include "gc/Marking.h" +#include "gc/Nursery-inl.h" #include "methodjit/MethodJIT.h" #include "vm/Shape.h" @@ -105,10 +106,22 @@ IsThingPoisoned(T *thing) } #endif +static GCMarker * +AsGCMarker(JSTracer *trc) +{ + JS_ASSERT(IS_GC_MARKING_TRACER(trc)); + return static_cast(trc); +} + template static inline void CheckMarkedThing(JSTracer *trc, T *thing) { +#ifdef DEBUG + /* This function uses data that's not available in the nursery. */ + if (IsInsideNursery(trc->runtime, thing)) + return; + JS_ASSERT(trc); JS_ASSERT(thing); JS_ASSERT(thing->zone()); @@ -120,21 +133,20 @@ CheckMarkedThing(JSTracer *trc, T *thing) JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && rt->gcManipulatingDeadZones, !thing->zone()->scheduledForDestruction); -#ifdef DEBUG rt->assertValidThread(); -#endif - JS_ASSERT_IF(thing->zone()->requireGCTracer(), IS_GC_MARKING_TRACER(trc)); + JS_ASSERT_IF(thing->zone()->requireGCTracer(), + IS_GC_MARKING_TRACER(trc)); JS_ASSERT(thing->isAligned()); - JS_ASSERT_IF(thing->isTenured(), MapTypeToTraceKind::kind == GetGCThingTraceKind(thing)); + JS_ASSERT(MapTypeToTraceKind::kind == GetGCThingTraceKind(thing)); JS_ASSERT_IF(rt->gcStrictCompartmentChecking, thing->zone()->isCollecting() || thing->zone() == rt->atomsCompartment->zone()); - JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && ((GCMarker *)trc)->getMarkColor() == GRAY, + JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && AsGCMarker(trc)->getMarkColor() == GRAY, thing->zone()->isGCMarkingGray() || thing->zone() == rt->atomsCompartment->zone()); @@ -146,13 +158,7 @@ CheckMarkedThing(JSTracer *trc, T *thing) */ JS_ASSERT_IF(IsThingPoisoned(thing) && rt->isHeapBusy(), !InFreeList(thing->arenaHeader(), thing)); -} - -static GCMarker * -AsGCMarker(JSTracer *trc) -{ - JS_ASSERT(IS_GC_MARKING_TRACER(trc)); - return static_cast(trc); +#endif } template @@ -164,15 +170,25 @@ MarkInternal(JSTracer *trc, T **thingp) CheckMarkedThing(trc, thing); - /* - * Don't mark things outside a compartment if we are in a per-compartment - * GC. - */ if (!trc->callback) { - if (thing->zone()->isGCMarking()) { - PushMarkStack(AsGCMarker(trc), thing); - thing->zone()->maybeAlive = true; - } + /* + * We may mark a Nursery thing outside the context of the + * MinorCollectionTracer because of a pre-barrier. The pre-barrier is + * not needed in this case because we perform a minor collection before + * each incremental slice. + */ + if (IsInsideNursery(trc->runtime, thing)) + return; + + /* + * Don't mark things outside a compartment if we are in a + * per-compartment GC. + */ + if (!thing->zone()->isGCMarking()) + return; + + PushMarkStack(AsGCMarker(trc), thing); + thing->zone()->maybeAlive = true; } else { trc->callback(trc, (void **)thingp, MapTypeToTraceKind::kind); JS_UNSET_TRACING_LOCATION(trc); @@ -252,6 +268,11 @@ IsMarked(T **thingp) { JS_ASSERT(thingp); JS_ASSERT(*thingp); +#ifdef JSGC_GENERATIONAL + Nursery &nursery = (*thingp)->runtime()->gcNursery; + if (nursery.isInside(*thingp)) + return nursery.getForwardedPointer(thingp); +#endif Zone *zone = (*thingp)->tenuredZone(); if (!zone->isCollecting() || zone->isGCFinished()) return true; @@ -264,6 +285,11 @@ IsAboutToBeFinalized(T **thingp) { JS_ASSERT(thingp); JS_ASSERT(*thingp); +#ifdef JSGC_GENERATIONAL + Nursery &nursery = (*thingp)->runtime()->gcNursery; + if (nursery.isInside(*thingp)) + return !nursery.getForwardedPointer(thingp); +#endif if (!(*thingp)->tenuredZone()->isGCSweeping()) return false; return !(*thingp)->isMarked(); @@ -328,12 +354,12 @@ DeclMarkerImpl(BaseShape, BaseShape) DeclMarkerImpl(BaseShape, UnownedBaseShape) DeclMarkerImpl(IonCode, ion::IonCode) DeclMarkerImpl(Object, ArgumentsObject) +DeclMarkerImpl(Object, ArrayBufferObject) DeclMarkerImpl(Object, DebugScopeObject) DeclMarkerImpl(Object, GlobalObject) DeclMarkerImpl(Object, JSObject) DeclMarkerImpl(Object, JSFunction) DeclMarkerImpl(Object, ScopeObject) -DeclMarkerImpl(Object, ArrayBufferObject) DeclMarkerImpl(Script, JSScript) DeclMarkerImpl(Shape, Shape) DeclMarkerImpl(String, JSAtom) @@ -353,7 +379,8 @@ gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind) { JS_ASSERT(thingp); JS_ASSERT(*thingp); - JS_ASSERT(kind == GetGCThingTraceKind(*thingp)); + DebugOnly cell = static_cast(*thingp); + JS_ASSERT_IF(cell->isTenured(), kind == MapAllocToTraceKind(cell->tenuredGetAllocKind())); switch (kind) { case JSTRACE_OBJECT: MarkInternal(trc, reinterpret_cast(thingp)); @@ -725,6 +752,7 @@ static void PushMarkStack(GCMarker *gcmarker, JSObject *thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); + JS_ASSERT(!IsInsideNursery(thing->runtime(), thing)); if (thing->markIfUnmarked(gcmarker->getMarkColor())) gcmarker->pushObject(thing); @@ -734,6 +762,7 @@ static void PushMarkStack(GCMarker *gcmarker, JSFunction *thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); + JS_ASSERT(!IsInsideNursery(thing->runtime(), thing)); if (thing->markIfUnmarked(gcmarker->getMarkColor())) gcmarker->pushObject(thing); @@ -743,6 +772,7 @@ static void PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); + JS_ASSERT(!IsInsideNursery(thing->runtime(), thing)); if (thing->markIfUnmarked(gcmarker->getMarkColor())) gcmarker->pushType(thing); @@ -752,6 +782,7 @@ static void PushMarkStack(GCMarker *gcmarker, RawScript thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); + JS_ASSERT(!IsInsideNursery(thing->runtime(), thing)); /* * We mark scripts directly rather than pushing on the stack as they can @@ -769,6 +800,7 @@ static void PushMarkStack(GCMarker *gcmarker, RawShape thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); + JS_ASSERT(!IsInsideNursery(thing->runtime(), thing)); /* We mark shapes directly rather than pushing on the stack. */ if (thing->markIfUnmarked(gcmarker->getMarkColor())) @@ -779,6 +811,7 @@ static void PushMarkStack(GCMarker *gcmarker, ion::IonCode *thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); + JS_ASSERT(!IsInsideNursery(thing->runtime(), thing)); if (thing->markIfUnmarked(gcmarker->getMarkColor())) gcmarker->pushIonCode(thing); @@ -791,6 +824,7 @@ static void PushMarkStack(GCMarker *gcmarker, RawBaseShape thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); + JS_ASSERT(!IsInsideNursery(thing->runtime(), thing)); /* We mark base shapes directly rather than pushing on the stack. */ if (thing->markIfUnmarked(gcmarker->getMarkColor())) diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index 3d89bd3e607b..a5c9bba26ef6 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -12,6 +12,7 @@ #include "jslock.h" #include "gc/Barrier.h" +#include "gc/Nursery.h" #include "js/TemplateLib.h" #include "ion/IonCode.h" @@ -28,6 +29,7 @@ class JSLinearString; namespace js { class ArgumentsObject; +class ArrayBufferObject; class BaseShape; class GlobalObject; class UnownedBaseShape; @@ -92,12 +94,12 @@ DeclMarker(BaseShape, BaseShape) DeclMarker(BaseShape, UnownedBaseShape) DeclMarker(IonCode, ion::IonCode) DeclMarker(Object, ArgumentsObject) +DeclMarker(Object, ArrayBufferObject) DeclMarker(Object, DebugScopeObject) DeclMarker(Object, GlobalObject) DeclMarker(Object, JSObject) DeclMarker(Object, JSFunction) DeclMarker(Object, ScopeObject) -DeclMarker(Object, ArrayBufferObject) DeclMarker(Script, JSScript) DeclMarker(Shape, Shape) DeclMarker(String, JSAtom) @@ -279,12 +281,20 @@ Mark(JSTracer *trc, HeapPtr *code, const char *name) MarkIonCode(trc, code, name); } +/* For use by WeakMap's HashKeyRef instantiation. */ inline void Mark(JSTracer *trc, JSObject **objp, const char *name) { MarkObjectUnbarriered(trc, objp, name); } +/* For use by Debugger::WeakMap's proxiedScopes HashKeyRef instantiation. */ +inline void +Mark(JSTracer *trc, ScopeObject **obj, const char *name) +{ + MarkObjectUnbarriered(trc, obj, name); +} + bool IsCellMarked(Cell **thingp); diff --git a/js/src/gc/Nursery-inl.h b/js/src/gc/Nursery-inl.h new file mode 100644 index 000000000000..c2b2e789a914 --- /dev/null +++ b/js/src/gc/Nursery-inl.h @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=79 ft=cpp: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef JSGC_GENERATIONAL +#ifndef gc_Nursery_inl_h__ +#define gc_Nursery_inl_h__ + +#include "gc/Heap.h" +#include "gc/Nursery.h" + +namespace js { +namespace gc { + +/* + * This structure overlays a Cell in the Nursery and re-purposes its memory + * for managing the Nursery collection process. + */ +class RelocationOverlay +{ + friend struct MinorCollectionTracer; + + /* The low bit is set so this should never equal a normal pointer. */ + const static uintptr_t Relocated = uintptr_t(0xbad0bad1); + + /* Set to Relocated when moved. */ + uintptr_t magic_; + + /* The location |this| was moved to. */ + Cell *newLocation_; + + /* A list entry to track all relocated things. */ + RelocationOverlay *next_; + + public: + static RelocationOverlay *fromCell(Cell *cell) { + JS_ASSERT(!cell->isTenured()); + return reinterpret_cast(cell); + } + + bool isForwarded() const { + return magic_ == Relocated; + } + + Cell *forwardingAddress() const { + JS_ASSERT(isForwarded()); + return newLocation_; + } + + void forwardTo(Cell *cell) { + JS_ASSERT(!isForwarded()); + magic_ = Relocated; + newLocation_ = cell; + next_ = NULL; + } + + RelocationOverlay *next() const { + return next_; + } +}; + +} /* namespace gc */ +} /* namespace js */ + +template +JS_ALWAYS_INLINE bool +js::Nursery::getForwardedPointer(T **ref) +{ + JS_ASSERT(ref); + JS_ASSERT(isInside(*ref)); + const gc::RelocationOverlay *overlay = reinterpret_cast(*ref); + if (!overlay->isForwarded()) + return false; + /* This static cast from Cell* restricts T to valid (GC thing) types. */ + *ref = static_cast(overlay->forwardingAddress()); + return true; +} + +#endif /* gc_Nursery_inl_h__ */ +#endif /* JSGC_GENERATIONAL */ diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp new file mode 100644 index 000000000000..501a4929ef42 --- /dev/null +++ b/js/src/gc/Nursery.cpp @@ -0,0 +1,516 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef JSGC_GENERATIONAL + +#include "jscompartment.h" +#include "jsgc.h" + +#include "gc/GCInternals.h" +#include "gc/Memory.h" +#include "vm/Debugger.h" + +#include "gc/Barrier-inl.h" +#include "gc/Nursery-inl.h" + +using namespace js; +using namespace gc; +using namespace mozilla; + +bool +js::Nursery::enable() +{ + if (isEnabled()) + return true; + + if (!hugeSlots.init()) + return false; + + fallbackBitmap.clear(false); + + void *heap = MapAlignedPages(NurserySize, Alignment); + if (!heap) + return false; + + JSRuntime *rt = runtime(); + rt->gcNurseryStart_ = position_ = uintptr_t(heap); + rt->gcNurseryEnd_ = start() + NurseryUsableSize; + asLayout().runtime = rt; + JS_POISON(asLayout().data, FreshNursery, sizeof(asLayout().data)); + + JS_ASSERT(isEnabled()); + return true; +} + +void +js::Nursery::disable() +{ + if (!isEnabled()) + return; + + hugeSlots.finish(); + JS_ASSERT(start()); + UnmapPages((void *)start(), NurserySize); + runtime()->gcNurseryStart_ = runtime()->gcNurseryEnd_ = position_ = 0; +} + +js::Nursery::~Nursery() +{ + disable(); +} + +void * +js::Nursery::allocate(size_t size) +{ + JS_ASSERT(size % ThingAlignment == 0); + JS_ASSERT(position() % ThingAlignment == 0); + + if (position() + size > end()) + return NULL; + + void *thing = (void *)position(); + position_ = position() + size; + + JS_POISON(thing, AllocatedThing, size); + return thing; +} + +/* Internally, this function is used to allocate elements as well as slots. */ +HeapSlot * +js::Nursery::allocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots) +{ + JS_ASSERT(obj); + JS_ASSERT(nslots > 0); + + if (!isInside(obj)) + return cx->pod_malloc(nslots); + + if (nslots > MaxNurserySlots) + return allocateHugeSlots(cx, nslots); + + size_t size = sizeof(HeapSlot) * nslots; + HeapSlot *slots = static_cast(allocate(size)); + if (slots) + return slots; + + return allocateHugeSlots(cx, nslots); +} + +ObjectElements * +js::Nursery::allocateElements(JSContext *cx, JSObject *obj, uint32_t nelems) +{ + return reinterpret_cast(allocateSlots(cx, obj, nelems)); +} + +HeapSlot * +js::Nursery::reallocateSlots(JSContext *cx, JSObject *obj, HeapSlot *oldSlots, + uint32_t oldCount, uint32_t newCount) +{ + size_t oldSize = oldCount * sizeof(HeapSlot); + size_t newSize = newCount * sizeof(HeapSlot); + + if (!isInside(obj)) + return static_cast(cx->realloc_(oldSlots, oldSize, newSize)); + + if (!isInside(oldSlots)) { + HeapSlot *newSlots = static_cast(cx->realloc_(oldSlots, oldSize, newSize)); + if (oldSlots != newSlots) { + hugeSlots.remove(oldSlots); + /* If this put fails, we will only leak the slots. */ + (void)hugeSlots.put(newSlots); + } + return newSlots; + } + + /* The nursery cannot make use of the returned slots data. */ + if (newCount < oldCount) + return oldSlots; + + HeapSlot *newSlots = allocateSlots(cx, obj, newCount); + PodCopy(newSlots, oldSlots, oldCount); + return newSlots; +} + +ObjectElements * +js::Nursery::reallocateElements(JSContext *cx, JSObject *obj, ObjectElements *oldHeader, + uint32_t oldCount, uint32_t newCount) +{ + HeapSlot *slots = reallocateSlots(cx, obj, reinterpret_cast(oldHeader), + oldCount, newCount); + return reinterpret_cast(slots); +} + +HeapSlot * +js::Nursery::allocateHugeSlots(JSContext *cx, size_t nslots) +{ + HeapSlot *slots = cx->pod_malloc(nslots); + /* If this put fails, we will only leak the slots. */ + (void)hugeSlots.put(slots); + return slots; +} + +void +js::Nursery::notifyInitialSlots(Cell *cell, HeapSlot *slots) +{ + if (isInside(cell) && !isInside(slots)) { + /* If this put fails, we will only leak the slots. */ + (void)hugeSlots.put(slots); + } +} + +namespace js { +namespace gc { + +class MinorCollectionTracer : public JSTracer +{ + public: + Nursery *nursery; + JSRuntime *runtime; + AutoTraceSession session; + + /* + * This list is threaded through the Nursery using the space from already + * moved things. The list is used to fix up the moved things and to find + * things held live by intra-Nursery pointers. + */ + RelocationOverlay *head; + RelocationOverlay **tail; + + /* Save and restore all of the runtime state we use during MinorGC. */ + bool priorNeedsBarrier; + + /* Insert the given relocation entry into the list of things to visit. */ + JS_ALWAYS_INLINE void insertIntoFixupList(RelocationOverlay *entry) { + *tail = entry; + tail = &entry->next_; + *tail = NULL; + } + + MinorCollectionTracer(JSRuntime *rt, Nursery *nursery) + : JSTracer(), + nursery(nursery), + runtime(rt), + session(runtime, MinorCollecting), + head(NULL), + tail(&head), + priorNeedsBarrier(runtime->needsBarrier()) + { + JS_TracerInit(this, runtime, Nursery::MinorGCCallback); + eagerlyTraceWeakMaps = TraceWeakMapKeysValues; + + runtime->gcNumber++; + runtime->setNeedsBarrier(false); + ++runtime->gcDisableStrictProxyCheckingCount; + } + + ~MinorCollectionTracer() { + --runtime->gcDisableStrictProxyCheckingCount; + runtime->setNeedsBarrier(priorNeedsBarrier); + } +}; + +} /* namespace gc */ +} /* namespace js */ + +static AllocKind +GetObjectAllocKindForCopy(JSObject *obj) +{ + if (obj->isArray()) { + JS_ASSERT(obj->numFixedSlots() == 0); + size_t nelements = obj->getDenseInitializedLength(); + return GetBackgroundAllocKind(GetGCArrayKind(nelements)); + } + + if (obj->isFunction()) + return obj->toFunction()->getAllocKind(); + + AllocKind kind = GetGCObjectFixedSlotsKind(obj->numFixedSlots()); + if (CanBeFinalizedInBackground(kind, obj->getClass())) + kind = GetBackgroundAllocKind(kind); + return kind; +} + +void * +js::Nursery::allocateFromTenured(Zone *zone, AllocKind thingKind) +{ + void *t = zone->allocator.arenas.allocateFromFreeList(thingKind, Arena::thingSize(thingKind)); + if (!t) { + zone->allocator.arenas.checkEmptyFreeList(thingKind); + t = zone->allocator.arenas.allocateFromArena(zone, thingKind); + } + return t; +} + +void * +js::Nursery::moveToTenured(MinorCollectionTracer *trc, JSObject *src) +{ + Zone *zone = src->zone(); + AllocKind dstKind = GetObjectAllocKindForCopy(src); + JSObject *dst = static_cast(allocateFromTenured(zone, dstKind)); + if (!dst) + MOZ_CRASH(); + + moveObjectToTenured(dst, src, dstKind); + + RelocationOverlay *overlay = reinterpret_cast(src); + overlay->forwardTo(dst); + trc->insertIntoFixupList(overlay); + + return static_cast(dst); +} + +void +js::Nursery::moveObjectToTenured(JSObject *dst, JSObject *src, AllocKind dstKind) +{ + size_t srcSize = Arena::thingSize(dstKind); + + /* + * Arrays do not necessarily have the same AllocKind between src and dst. + * We deal with this by copying elements manually, possibly re-inlining + * them if there is adequate room inline in dst. + */ + if (src->isArray()) + srcSize = sizeof(ObjectImpl); + + js_memcpy(dst, src, srcSize); + moveSlotsToTenured(dst, src, dstKind); + moveElementsToTenured(dst, src, dstKind); + + /* The shape's list head may point into the old object. */ + if (&src->shape_ == dst->shape_->listp) + dst->shape_->listp = &dst->shape_; +} + +void +js::Nursery::moveSlotsToTenured(JSObject *dst, JSObject *src, AllocKind dstKind) +{ + /* Fixed slots have already been copied over. */ + if (!src->hasDynamicSlots()) + return; + + if (!isInside(src->slots)) { + hugeSlots.remove(src->slots); + return; + } + + Allocator *alloc = &src->zone()->allocator; + size_t count = src->numDynamicSlots(); + dst->slots = alloc->pod_malloc(count); + PodCopy(dst->slots, src->slots, count); +} + +void +js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKind) +{ + if (src->hasEmptyElements()) + return; + + Allocator *alloc = &src->zone()->allocator; + ObjectElements *srcHeader = src->getElementsHeader(); + ObjectElements *dstHeader; + + if (!isInside(srcHeader)) { + JS_ASSERT(src->elements == dst->elements); + hugeSlots.remove(reinterpret_cast(srcHeader)); + return; + } + + /* ArrayBuffer stores byte-length, not Value count. */ + if (src->isArrayBuffer()) { + size_t nbytes = sizeof(ObjectElements) + srcHeader->initializedLength; + if (src->hasDynamicElements()) { + dstHeader = static_cast(alloc->malloc_(nbytes)); + if (!dstHeader) + MOZ_CRASH(); + } else { + dst->setFixedElements(); + dstHeader = dst->getElementsHeader(); + } + js_memcpy(dstHeader, srcHeader, nbytes); + dst->elements = dstHeader->elements(); + return; + } + + size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->initializedLength; + + /* Unlike other objects, Arrays can have fixed elements. */ + if (src->isArray() && nslots <= GetGCKindSlots(dstKind)) { + dst->setFixedElements(); + dstHeader = dst->getElementsHeader(); + js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot)); + dstHeader->capacity = GetGCKindSlots(dstKind) - ObjectElements::VALUES_PER_HEADER; + return; + } + + size_t nbytes = nslots * sizeof(HeapValue); + dstHeader = static_cast(alloc->malloc_(nbytes)); + if (!dstHeader) + MOZ_CRASH(); + js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot)); + dstHeader->capacity = srcHeader->initializedLength; + dst->elements = dstHeader->elements(); +} + +static bool +ShouldMoveToTenured(MinorCollectionTracer *trc, void **thingp) +{ + Cell *cell = static_cast(*thingp); + Nursery &nursery = *trc->nursery; + return !nursery.isInside(thingp) && nursery.isInside(cell) && + !nursery.getForwardedPointer(thingp); +} + +/* static */ void +js::Nursery::MinorGCCallback(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) +{ + MinorCollectionTracer *trc = static_cast(jstrc); + if (ShouldMoveToTenured(trc, thingp)) + *thingp = trc->nursery->moveToTenured(trc, static_cast(*thingp)); +} + +void +js::Nursery::markFallback(Cell *cell) +{ + JS_ASSERT(uintptr_t(cell) >= start()); + size_t offset = uintptr_t(cell) - start(); + JS_ASSERT(offset < end() - start()); + JS_ASSERT(offset % ThingAlignment == 0); + fallbackBitmap.set(offset / ThingAlignment); +} + +void +js::Nursery::moveFallbackToTenured(gc::MinorCollectionTracer *trc) +{ + for (size_t i = 0; i < FallbackBitmapBits; ++i) { + if (fallbackBitmap.get(i)) { + JSObject *src = reinterpret_cast(start() + i * ThingAlignment); + moveToTenured(trc, src); + } + } + fallbackBitmap.clear(false); +} + +/* static */ void +js::Nursery::MinorFallbackMarkingCallback(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) +{ + MinorCollectionTracer *trc = static_cast(jstrc); + if (ShouldMoveToTenured(trc, thingp)) + trc->nursery->markFallback(static_cast(*thingp)); +} + +/* static */ void +js::Nursery::MinorFallbackFixupCallback(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) +{ + MinorCollectionTracer *trc = static_cast(jstrc); + if (trc->nursery->isInside(*thingp)) + trc->nursery->getForwardedPointer(thingp); +} + +static void +TraceHeapWithCallback(JSTracer *trc, JSTraceCallback callback) +{ + JSTraceCallback prior = trc->callback; + + AutoCopyFreeListToArenas copy(trc->runtime); + trc->callback = callback; + for (ZonesIter zone(trc->runtime); !zone.done(); zone.next()) { + for (size_t i = 0; i < FINALIZE_LIMIT; ++i) { + AllocKind kind = AllocKind(i); + for (CellIterUnderGC cells(zone, kind); !cells.done(); cells.next()) + JS_TraceChildren(trc, cells.getCell(), MapAllocToTraceKind(kind)); + } + } + + trc->callback = prior; +} + +void +js::Nursery::markStoreBuffer(MinorCollectionTracer *trc) +{ + JSRuntime *rt = trc->runtime; + if (!rt->gcStoreBuffer.hasOverflowed()) { + rt->gcStoreBuffer.mark(trc); + return; + } + + /* + * If the store buffer has overflowed, we need to walk the full heap to + * discover cross-generation edges. Since we cannot easily walk the heap + * while simultaneously allocating, we use a three pass algorithm: + * 1) Walk the major heap and mark live things in the nursery in a + * pre-allocated bitmap. + * 2) Use the bitmap to move all live nursery things to the tenured + * heap. + * 3) Walk the heap a second time to find and update all of the moved + * references in the tenured heap. + */ + TraceHeapWithCallback(trc, MinorFallbackMarkingCallback); + moveFallbackToTenured(trc); + TraceHeapWithCallback(trc, MinorFallbackFixupCallback); +} + +void +js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason) +{ + JS_AbortIfWrongThread(rt); + + if (!isEnabled()) + return; + + if (position() == start()) + return; + + rt->gcHelperThread.waitBackgroundSweepEnd(); + + /* Move objects pointed to by roots from the nursery to the major heap. */ + MinorCollectionTracer trc(rt, this); + MarkRuntime(&trc); + Debugger::markAll(&trc); + for (CompartmentsIter comp(rt); !comp.done(); comp.next()) { + comp->markAllCrossCompartmentWrappers(&trc); + comp->markAllInitialShapeTableEntries(&trc); + } + markStoreBuffer(&trc); + + /* + * Most of the work is done here. This loop iterates over objects that have + * been moved to the major heap. If these objects have any outgoing pointers + * to the nursery, then those nursery objects get moved as well, until no + * objects are left to move. That is, we iterate to a fixed point. + */ + for (RelocationOverlay *p = trc.head; p; p = p->next()) { + JSObject *obj = static_cast(p->forwardingAddress()); + JS_TraceChildren(&trc, obj, MapAllocToTraceKind(obj->tenuredGetAllocKind())); + } + + /* Sweep. */ + sweep(rt->defaultFreeOp()); + rt->gcStoreBuffer.clear(); + + /* + * We ignore gcMaxBytes when allocating for minor collection. However, if we + * overflowed, we disable the nursery. The next time we allocate, we'll fail + * because gcBytes >= gcMaxBytes. + */ + if (rt->gcBytes >= rt->gcMaxBytes) + disable(); +} + + +void +js::Nursery::sweep(FreeOp *fop) +{ + for (HugeSlotsSet::Range r = hugeSlots.all(); !r.empty(); r.popFront()) + fop->free_(r.front()); + hugeSlots.clear(); + + JS_POISON((void *)start(), SweptNursery, NurserySize - sizeof(JSRuntime *)); + + position_ = start(); +} + +#endif /* JSGC_GENERATIONAL */ diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h new file mode 100644 index 000000000000..c720bfb5947b --- /dev/null +++ b/js/src/gc/Nursery.h @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jsgc_nursery_h___ +#define jsgc_nursery_h___ + +#ifdef JSGC_GENERATIONAL + +#include "ds/BitArray.h" +#include "js/HashTable.h" + +#include "jsgc.h" +#include "jspubtd.h" + +namespace js { + +class ObjectElements; + +namespace gc { +class MinorCollectionTracer; +} /* namespace gc */ + +class Nursery +{ + public: + const static size_t Alignment = gc::ChunkSize; + const static size_t NurserySize = gc::ChunkSize; + const static size_t NurseryMask = NurserySize - 1; + + explicit Nursery(JSRuntime *rt) + : runtime_(rt), + position_(0) + {} + ~Nursery(); + + bool enable(); + void disable(); + bool isEnabled() const { return bool(start()); } + + template + JS_ALWAYS_INLINE bool isInside(const T *p) const { + return uintptr_t(p) >= start() && uintptr_t(p) < end(); + } + + /* + * Allocate and return a pointer to a new GC thing. Returns NULL if the + * Nursery is full. + */ + void *allocate(size_t size); + + /* Allocate a slots array for the given object. */ + HeapSlot *allocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots); + + /* Allocate an elements vector for the given object. */ + ObjectElements *allocateElements(JSContext *cx, JSObject *obj, uint32_t nelems); + + /* Resize an existing slots array. */ + HeapSlot *reallocateSlots(JSContext *cx, JSObject *obj, HeapSlot *oldSlots, + uint32_t oldCount, uint32_t newCount); + + /* Resize an existing elements vector. */ + ObjectElements *reallocateElements(JSContext *cx, JSObject *obj, ObjectElements *oldHeader, + uint32_t oldCount, uint32_t newCount); + + /* Add a slots to our tracking list if it is out-of-line. */ + void notifyInitialSlots(gc::Cell *cell, HeapSlot *slots); + + /* Do a minor collection. */ + void collect(JSRuntime *rt, JS::gcreason::Reason reason); + + /* + * Check if the thing at |*ref| in the Nursery has been forwarded. If so, + * sets |*ref| to the new location of the object and returns true. Otherwise + * returns false and leaves |*ref| unset. + */ + template + JS_ALWAYS_INLINE bool getForwardedPointer(T **ref); + + private: + /* + * The start and end pointers are stored under the runtime so that we can + * inline the isInsideNursery check into embedder code. Use the start() + * and end() functions to access these values. + */ + JSRuntime *runtime_; + + /* Pointer to the first unallocated byte in the nursery. */ + uintptr_t position_; + + /* + * The set of externally malloced slots potentially kept live by objects + * stored in the nursery. Any external slots that do not belong to a + * tenured thing at the end of a minor GC must be freed. + */ + typedef HashSet, SystemAllocPolicy> HugeSlotsSet; + HugeSlotsSet hugeSlots; + + /* The marking bitmap for the fallback marker. */ + const static size_t ThingAlignment = sizeof(Value); + const static size_t FallbackBitmapBits = NurserySize / ThingAlignment; + BitArray fallbackBitmap; + +#ifdef DEBUG + /* + * In DEBUG builds, these bytes indicate the state of an unused segment of + * nursery-allocated memory. + */ + const static uint8_t FreshNursery = 0x2a; + const static uint8_t SweptNursery = 0x2b; + const static uint8_t AllocatedThing = 0x2c; +#endif + + /* The maximum number of slots allowed to reside inline in the nursery. */ + const static size_t MaxNurserySlots = 100; + + /* The amount of space in the mapped nursery available to allocations. */ + const static size_t NurseryUsableSize = NurserySize - sizeof(JSRuntime *); + + struct Layout { + char data[NurseryUsableSize]; + JSRuntime *runtime; + }; + Layout &asLayout() { + JS_STATIC_ASSERT(sizeof(Layout) == NurserySize); + JS_ASSERT(start()); + return *reinterpret_cast(start()); + } + + JS_ALWAYS_INLINE uintptr_t start() const { + JS_ASSERT(runtime_); + return ((JS::shadow::Runtime *)runtime_)->gcNurseryStart_; + } + + JS_ALWAYS_INLINE uintptr_t end() const { + JS_ASSERT(runtime_); + return ((JS::shadow::Runtime *)runtime_)->gcNurseryEnd_; + } + + uintptr_t position() const { return position_; } + + JSRuntime *runtime() const { return runtime_; } + + /* Allocates and registers external slots with the nursery. */ + HeapSlot *allocateHugeSlots(JSContext *cx, size_t nslots); + + /* Allocates a new GC thing from the tenured generation during minor GC. */ + void *allocateFromTenured(Zone *zone, gc::AllocKind thingKind); + + /* + * Move the object at |src| in the Nursery to an already-allocated cell + * |dst| in Tenured. + */ + void *moveToTenured(gc::MinorCollectionTracer *trc, JSObject *src); + void moveObjectToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind); + void moveElementsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind); + void moveSlotsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind); + + /* Handle fallback marking. See the comment in MarkStoreBuffer. */ + void markFallback(gc::Cell *cell); + void moveFallbackToTenured(gc::MinorCollectionTracer *trc); + + void markStoreBuffer(gc::MinorCollectionTracer *trc); + + /* + * Frees all non-live nursery-allocated things at the end of a minor + * collection. This operation takes time proportional to the number of + * dead things. + */ + void sweep(FreeOp *fop); + + static void MinorGCCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind); + static void MinorFallbackMarkingCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind); + static void MinorFallbackFixupCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind); + + friend class gc::MinorCollectionTracer; +}; + +} /* namespace js */ + +#endif /* JSGC_GENERATIONAL */ +#endif /* jsgc_nursery_h___ */ diff --git a/js/src/gc/StoreBuffer.cpp b/js/src/gc/StoreBuffer.cpp index 236fcbd1cf6e..822e3a455883 100644 --- a/js/src/gc/StoreBuffer.cpp +++ b/js/src/gc/StoreBuffer.cpp @@ -75,6 +75,13 @@ StoreBuffer::MonoTypeBuffer::disable() base = pos = top = NULL; } +template +void +StoreBuffer::MonoTypeBuffer::clear() +{ + pos = base; +} + template template void @@ -95,7 +102,9 @@ StoreBuffer::MonoTypeBuffer::compact() #ifdef JS_GC_ZEAL if (owner->runtime->gcVerifyPostData) compactNotInSet(&owner->runtime->gcVerifierNursery); + else #endif + compactNotInSet(&owner->runtime->gcNursery); } template @@ -113,8 +122,27 @@ StoreBuffer::MonoTypeBuffer::put(const T &v) */ *pos++ = v; if (isFull()) { - owner->setOverflowed(); - pos = base; + compact(); + if (isFull()) { + owner->setOverflowed(); + pos = base; + } + } +} + +template +void +StoreBuffer::MonoTypeBuffer::mark(JSTracer *trc) +{ + compact(); + T *cursor = base; + while (cursor != pos) { + T edge = *cursor++; + + if (edge.isNullEdge()) + continue; + + edge.mark(trc); } } @@ -196,6 +224,27 @@ StoreBuffer::GenericBuffer::disable() base = pos = top = NULL; } +void +StoreBuffer::GenericBuffer::clear() +{ + pos = base; +} + +void +StoreBuffer::GenericBuffer::mark(JSTracer *trc) +{ + uint8_t *p = base; + while (p < pos) { + unsigned size = *((unsigned *)p); + p += sizeof(unsigned); + + BufferableRef *edge = reinterpret_cast(p); + edge->mark(trc); + + p += size; + } +} + bool StoreBuffer::GenericBuffer::containsEdge(void *location) const { @@ -212,11 +261,37 @@ StoreBuffer::GenericBuffer::containsEdge(void *location) const return false; } +/*** Edges ***/ + +void +StoreBuffer::CellPtrEdge::mark(JSTracer *trc) +{ + MarkObjectRoot(trc, reinterpret_cast(edge), "store buffer edge"); +} + +void +StoreBuffer::ValueEdge::mark(JSTracer *trc) +{ + MarkValueRoot(trc, edge, "store buffer edge"); +} + +void +StoreBuffer::SlotEdge::mark(JSTracer *trc) +{ + if (kind == HeapSlot::Element) + MarkSlot(trc, (HeapSlot*)&object->getDenseElement(offset), "store buffer edge"); + else + MarkSlot(trc, &object->getSlotRef(offset), "store buffer edge"); +} + /*** StoreBuffer ***/ bool StoreBuffer::enable() { + if (enabled) + return true; + buffer = js_malloc(TotalSize); if (!buffer) return false; @@ -273,6 +348,43 @@ StoreBuffer::disable() overflowed = false; } +bool +StoreBuffer::clear() +{ + if (!enabled) + return true; + + bufferVal.clear(); + bufferCell.clear(); + bufferSlot.clear(); + bufferRelocVal.clear(); + bufferRelocCell.clear(); + bufferGeneric.clear(); + + return true; +} + +void +StoreBuffer::mark(JSTracer *trc) +{ + JS_ASSERT(isEnabled()); + JS_ASSERT(!overflowed); + + bufferVal.mark(trc); + bufferCell.mark(trc); + bufferSlot.mark(trc); + bufferRelocVal.mark(trc); + bufferRelocCell.mark(trc); + bufferGeneric.mark(trc); +} + +void +StoreBuffer::setOverflowed() +{ + JS_ASSERT(enabled); + overflowed = true; +} + bool StoreBuffer::coalesceForVerification() { diff --git a/js/src/gc/StoreBuffer.h b/js/src/gc/StoreBuffer.h index d927d098f695..6a15cbf02793 100644 --- a/js/src/gc/StoreBuffer.h +++ b/js/src/gc/StoreBuffer.h @@ -29,7 +29,7 @@ class VerifierNursery HashSet, SystemAllocPolicy> nursery; public: - VerifierNursery() : nursery() {} + explicit VerifierNursery() : nursery() {} bool enable() { if (!nursery.initialized()) @@ -53,7 +53,6 @@ class VerifierNursery } bool isInside(const void *cell) const { - JS_ASSERT((uintptr_t(cell) & 0x3) == 0); return nursery.initialized() && nursery.has(cell); } @@ -87,6 +86,7 @@ class HashKeyRef : public BufferableRef Map *map; Key key; + typedef typename Map::Entry::ValueType ValueType; typedef typename Map::Ptr Ptr; public: @@ -99,7 +99,18 @@ class HashKeyRef : public BufferableRef return &p->key == location; } - void mark(JSTracer *trc) {} + void mark(JSTracer *trc) { + Key prior = key; + typename Map::Ptr p = map->lookup(key); + if (!p) + return; + ValueType value = p->value; + Mark(trc, &key, "HashKeyRef"); + if (prior != key) { + map->remove(prior); + map->put(key, value); + } + } }; /* @@ -108,27 +119,6 @@ class HashKeyRef : public BufferableRef */ class StoreBuffer { -#ifdef JS_GC_ZEAL - /* For verification, we approximate an infinitly large buffer. */ - static const size_t ValueBufferSize = 1024 * 1024 * sizeof(Value *); - static const size_t CellBufferSize = 1024 * 1024 * sizeof(Cell **); - static const size_t SlotBufferSize = 1024 * 1024 * (sizeof(JSObject *) + 2 * sizeof(uint32_t)); - static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(Value *); - static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(Cell **); - static const size_t GenericBufferSize = 1024 * 1024 * sizeof(int); -#else - /* TODO: profile to find the ideal size for these. */ - static const size_t ValueBufferSize = 1 * 1024 * sizeof(Value *); - static const size_t CellBufferSize = 2 * 1024 * sizeof(Cell **); - static const size_t SlotBufferSize = 2 * 1024 * (sizeof(JSObject *) + sizeof(uint32_t)); - static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(Value *); - static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(Cell **); - static const size_t GenericBufferSize = 1 * 1024 * sizeof(int); -#endif - static const size_t TotalSize = ValueBufferSize + CellBufferSize + - SlotBufferSize + RelocValueBufferSize + RelocCellBufferSize + - GenericBufferSize; - typedef HashSet, SystemAllocPolicy> EdgeSet; /* @@ -155,6 +145,7 @@ class StoreBuffer bool enable(uint8_t *region, size_t len); void disable(); + void clear(); bool isEmpty() const { return pos == base; } bool isFull() const { JS_ASSERT(pos <= top); return pos == top; } @@ -172,6 +163,9 @@ class StoreBuffer /* Add one item to the buffer. */ void put(const T &v); + /* Mark the source of all edges in the store buffer. */ + void mark(JSTracer *trc); + /* For verification. */ bool accumulateEdges(EdgeSet &edges); }; @@ -215,6 +209,10 @@ class StoreBuffer bool enable(uint8_t *region, size_t len); void disable(); + void clear(); + + /* Mark all generic edges. */ + void mark(JSTracer *trc); /* Check if a pointer is present in the buffer. */ bool containsEdge(void *location) const; @@ -263,6 +261,8 @@ class StoreBuffer return !*edge; } + void mark(JSTracer *trc); + CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); } CellPtrEdge untagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) & ~1)); } bool isTagged() const { return bool(uintptr_t(edge) & 1); } @@ -292,6 +292,8 @@ class StoreBuffer return !deref(); } + void mark(JSTracer *trc); + ValueEdge tagged() const { return ValueEdge((Value *)(uintptr_t(edge) | 1)); } ValueEdge untagged() const { return ValueEdge((Value *)(uintptr_t(edge) & ~1)); } bool isTagged() const { return bool(uintptr_t(edge) & 1); } @@ -328,6 +330,8 @@ class StoreBuffer JS_ALWAYS_INLINE bool inRememberedSet(NurseryType *nursery) const; JS_ALWAYS_INLINE bool isNullEdge() const; + + void mark(JSTracer *trc); }; MonoTypeBuffer bufferVal; @@ -347,11 +351,32 @@ class StoreBuffer /* For the verifier. */ EdgeSet edgeSet; +#ifdef JS_GC_ZEAL + /* For verification, we approximate an infinitly large buffer. */ + static const size_t ValueBufferSize = 1024 * 1024 * sizeof(ValueEdge); + static const size_t CellBufferSize = 1024 * 1024 * sizeof(CellPtrEdge); + static const size_t SlotBufferSize = 1024 * 1024 * sizeof(SlotEdge); + static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(ValueEdge); + static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(CellPtrEdge); + static const size_t GenericBufferSize = 1024 * 1024 * sizeof(int); +#else + /* TODO: profile to find the ideal size for these. */ + static const size_t ValueBufferSize = 1 * 1024 * sizeof(ValueEdge); + static const size_t CellBufferSize = 2 * 1024 * sizeof(CellPtrEdge); + static const size_t SlotBufferSize = 2 * 1024 * sizeof(SlotEdge); + static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(ValueEdge); + static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(CellPtrEdge); + static const size_t GenericBufferSize = 1 * 1024 * sizeof(int); +#endif + static const size_t TotalSize = ValueBufferSize + CellBufferSize + + SlotBufferSize + RelocValueBufferSize + RelocCellBufferSize + + GenericBufferSize; + /* For use by our owned buffers. */ - void setOverflowed() { overflowed = true; } + void setOverflowed(); public: - StoreBuffer(JSRuntime *rt) + explicit StoreBuffer(JSRuntime *rt) : bufferVal(this), bufferCell(this), bufferSlot(this), bufferRelocVal(this), bufferRelocCell(this), bufferGeneric(this), runtime(rt), buffer(NULL), overflowed(false), enabled(false) @@ -361,10 +386,7 @@ class StoreBuffer void disable(); bool isEnabled() { return enabled; } - bool clear() { - disable(); - return enable(); - } + bool clear(); /* Get the overflowed status. */ bool hasOverflowed() const { return overflowed; } @@ -400,6 +422,9 @@ class StoreBuffer bufferGeneric.put(t); } + /* Mark the source of all edges in the store buffer. */ + void mark(JSTracer *trc); + /* For the verifier. */ bool coalesceForVerification(); void releaseVerificationData(); diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index 9cdebbd5afe2..cf3f0f1838b9 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -326,6 +326,11 @@ DisableGGCForVerification(JSRuntime *rt) if (rt->gcVerifyPreData || rt->gcVerifyPostData) return; + if (rt->gcNursery.isEnabled()) { + MinorGC(rt, JS::gcreason::API); + rt->gcNursery.disable(); + } + if (rt->gcStoreBuffer.isEnabled()) rt->gcStoreBuffer.disable(); #endif @@ -338,8 +343,10 @@ EnableGGCAfterVerification(JSRuntime *rt) if (rt->gcVerifyPreData || rt->gcVerifyPostData) return; - if (rt->gcGenerationalEnabled) + if (rt->gcGenerationalEnabled) { + rt->gcNursery.enable(); rt->gcStoreBuffer.enable(); + } #endif } @@ -764,10 +771,6 @@ js::gc::EndVerifyPostBarriers(JSRuntime *rt) goto oom; /* Walk the heap. */ - for (CompartmentsIter comp(rt); !comp.done(); comp.next()) { - if (comp->watchpointMap) - comp->watchpointMap->markAll(trc); - } for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { for (size_t kind = 0; kind < FINALIZE_LIMIT; ++kind) { for (CellIterUnderGC cells(zone, AllocKind(kind)); !cells.done(); cells.next()) { diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 4fb93bf50250..c90dec185edd 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -181,7 +181,7 @@ struct Zone : private JS::shadow::Zone, public js::gc::GraphNodeBase * tracer. */ bool requireGCTracer() const { - return rt->isHeapCollecting() && gcState != NoGC; + return rt->isHeapMajorCollecting() && gcState != NoGC; } void setGCState(CompartmentGCState state) { diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ec8768853859..14f0b2a55f71 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -241,7 +241,7 @@ AssertHeapIsIdle(JSContext *cx) static void AssertHeapIsIdleOrIterating(JSRuntime *rt) { - JS_ASSERT(rt->heapState != js::Collecting); + JS_ASSERT(!rt->isHeapCollecting()); } static void @@ -843,6 +843,7 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) # ifdef JS_GC_ZEAL gcVerifierNursery(), # endif + gcNursery(thisFromCtor()), gcStoreBuffer(thisFromCtor()), #endif #ifdef JS_GC_ZEAL @@ -1074,6 +1075,11 @@ JSRuntime::~JSRuntime() if (ionPcScriptCache) js_delete(ionPcScriptCache); + +#ifdef JSGC_GENERATIONAL + gcStoreBuffer.disable(); + gcNursery.disable(); +#endif } #ifdef JS_THREADSAFE diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index e1c73498bb41..763aff3cb41b 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -29,6 +29,7 @@ #include "prmjtime.h" #include "ds/LifoAlloc.h" +#include "gc/Nursery.h" #include "gc/Statistics.h" #include "gc/StoreBuffer.h" #include "js/HashTable.h" @@ -603,8 +604,12 @@ struct MallocProvider void *realloc_(void *p, size_t oldBytes, size_t newBytes) { Client *client = static_cast(this); - JS_ASSERT(oldBytes < newBytes); - client->updateMallocCounter(newBytes - oldBytes); + /* + * For compatibility we do not account for realloc that decreases + * previously allocated memory. + */ + if (newBytes > oldBytes) + client->updateMallocCounter(newBytes - oldBytes); void *p2 = js_realloc(p, newBytes); return JS_LIKELY(!!p2) ? p2 : client->onOutOfMemory(p, newBytes); } @@ -664,7 +669,7 @@ typedef Vector ZoneVector; } // namespace js -struct JSRuntime : private JS::shadow::Runtime, +struct JSRuntime : public JS::shadow::Runtime, public js::MallocProvider { /* @@ -937,7 +942,7 @@ struct JSRuntime : private JS::shadow::Runtime, uint64_t gcStartNumber; /* Whether the currently running GC can finish in multiple slices. */ - int gcIsIncremental; + bool gcIsIncremental; /* Whether all compartments are being collected in first GC slice. */ bool gcIsFull; @@ -1042,13 +1047,15 @@ struct JSRuntime : private JS::shadow::Runtime, volatile js::HeapState heapState; bool isHeapBusy() { return heapState != js::Idle; } - - bool isHeapCollecting() { return heapState == js::Collecting; } + bool isHeapMajorCollecting() { return heapState == js::MajorCollecting; } + bool isHeapMinorCollecting() { return heapState == js::MinorCollecting; } + bool isHeapCollecting() { return isHeapMajorCollecting() || isHeapMinorCollecting(); } #ifdef JSGC_GENERATIONAL # ifdef JS_GC_ZEAL js::gc::VerifierNursery gcVerifierNursery; # endif + js::Nursery gcNursery; js::gc::StoreBuffer gcStoreBuffer; #endif diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 2a6dc4dc4edf..15d0a4890e73 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -488,6 +488,26 @@ JSCompartment::markCrossCompartmentWrappers(JSTracer *trc) } } +/* + * This method marks and keeps live all pointers in the cross compartment + * wrapper map. It should be called only for minor GCs, since minor GCs cannot, + * by their nature, apply the weak constraint to safely remove items from the + * wrapper map. + */ +void +JSCompartment::markAllCrossCompartmentWrappers(JSTracer *trc) +{ + for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { + CrossCompartmentKey key = e.front().key; + MarkGCThingRoot(trc, (void **)&key.wrapped, "CrossCompartmentKey::wrapped"); + if (key.debugger) + MarkObjectRoot(trc, &key.debugger, "CrossCompartmentKey::debugger"); + MarkValueRoot(trc, e.front().value.unsafeGet(), "CrossCompartmentWrapper"); + if (key.wrapped != e.front().key.wrapped || key.debugger != e.front().key.debugger) + e.rekeyFront(key); + } +} + void JSCompartment::mark(JSTracer *trc) { @@ -760,6 +780,7 @@ JSCompartment::clearBreakpointsIn(FreeOp *fop, js::Debugger *dbg, JSObject *hand void JSCompartment::clearTraps(FreeOp *fop) { + MinorGC(rt, JS::gcreason::EVICT_NURSERY); for (gc::CellIter i(zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get(); if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode()) diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 454f13137d3a..47674085bc55 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -212,6 +212,7 @@ struct JSCompartment /* Set of initial shapes in the compartment. */ js::InitialShapeSet initialShapes; void sweepInitialShapeTable(); + void markAllInitialShapeTableEntries(JSTracer *trc); /* Set of default 'new' or lazy types in the compartment. */ js::types::TypeObjectSet newTypeObjects; @@ -262,6 +263,7 @@ struct JSCompartment /* Mark cross-compartment wrappers. */ void markCrossCompartmentWrappers(JSTracer *trc); + void markAllCrossCompartmentWrappers(JSTracer *trc); bool wrap(JSContext *cx, JS::MutableHandleValue vp, JS::HandleObject existing = js::NullPtr()); bool wrap(JSContext *cx, JSString **strp); diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index cb69671550fe..49fce1271fd4 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -869,6 +869,7 @@ JS::DisableGenerationalGC(JSRuntime *rt) { rt->gcGenerationalEnabled = false; #ifdef JSGC_GENERATIONAL + rt->gcNursery.disable(); rt->gcStoreBuffer.disable(); #endif } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 7e5e0fda211c..203502b82a4f 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -92,6 +92,7 @@ #include "jsobjinlines.h" #include "gc/FindSCCs-inl.h" +#include "gc/Nursery-inl.h" #include "vm/ScopeObject-inl.h" #include "vm/String-inl.h" @@ -777,8 +778,7 @@ Chunk::allocateArena(Zone *zone, AllocKind thingKind) JS_ASSERT(hasAvailableArenas()); JSRuntime *rt = zone->rt; - JS_ASSERT(rt->gcBytes <= rt->gcMaxBytes); - if (rt->gcMaxBytes - rt->gcBytes < ArenaSize) + if (!rt->isHeapMinorCollecting() && rt->gcBytes >= rt->gcMaxBytes) return NULL; ArenaHeader *aheader = JS_LIKELY(info.numArenasFreeCommitted > 0) @@ -974,6 +974,9 @@ js_InitGC(JSRuntime *rt, uint32_t maxbytes) #endif #ifdef JSGC_GENERATIONAL + if (!rt->gcNursery.enable()) + return false; + if (!rt->gcStoreBuffer.enable()) return false; #endif @@ -1202,11 +1205,11 @@ ArenaLists::parallelAllocate(Zone *zone, AllocKind thingKind, size_t thingSize) if (t) return t; - return allocateFromArena(zone, thingKind); + return allocateFromArenaInline(zone, thingKind); } inline void * -ArenaLists::allocateFromArena(Zone *zone, AllocKind thingKind) +ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind) { /* * Parallel JS Note: @@ -1324,6 +1327,12 @@ ArenaLists::allocateFromArena(Zone *zone, AllocKind thingKind) Arena::thingSize(thingKind)); } +void * +ArenaLists::allocateFromArena(JS::Zone *zone, AllocKind thingKind) +{ + return allocateFromArenaInline(zone, thingKind); +} + void ArenaLists::finalizeNow(FreeOp *fop, AllocKind thingKind) { @@ -1537,7 +1546,7 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) * always try to allocate twice. */ for (bool secondAttempt = false; ; secondAttempt = true) { - void *thing = zone->allocator.arenas.allocateFromArena(zone, thingKind); + void *thing = zone->allocator.arenas.allocateFromArenaInline(zone, thingKind); if (JS_LIKELY(!!thing)) return thing; if (secondAttempt) @@ -4014,7 +4023,7 @@ AutoTraceSession::AutoTraceSession(JSRuntime *rt, js::HeapState heapState) { JS_ASSERT(!rt->noGCOrAllocationCheck); JS_ASSERT(!rt->isHeapBusy()); - JS_ASSERT(heapState == Collecting || heapState == Tracing); + JS_ASSERT(heapState != Idle); rt->heapState = heapState; } @@ -4025,7 +4034,7 @@ AutoTraceSession::~AutoTraceSession() } AutoGCSession::AutoGCSession(JSRuntime *rt) - : AutoTraceSession(rt, Collecting) + : AutoTraceSession(rt, MajorCollecting) { runtime->gcIsNeeded = false; runtime->gcInterFrameGC = true; @@ -4472,6 +4481,29 @@ ShouldCleanUpEverything(JSRuntime *rt, JS::gcreason::Reason reason, JSGCInvocati gckind == GC_SHRINK; } +#ifdef JSGC_GENERATIONAL +class AutoDisableStoreBuffer +{ + JSRuntime *runtime; + bool prior; + + public: + AutoDisableStoreBuffer(JSRuntime *rt) : runtime(rt) { + prior = rt->gcStoreBuffer.isEnabled(); + rt->gcStoreBuffer.disable(); + } + ~AutoDisableStoreBuffer() { + if (prior) + runtime->gcStoreBuffer.enable(); + } +}; +#else +struct AutoDisableStoreBuffer +{ + AutoDisableStoreBuffer(JSRuntime *) {} +}; +#endif + static void Collect(JSRuntime *rt, bool incremental, int64_t budget, JSGCInvocationKind gckind, JS::gcreason::Reason reason) @@ -4526,6 +4558,14 @@ Collect(JSRuntime *rt, bool incremental, int64_t budget, } av(rt, isShutdown); #endif + MinorGC(rt, reason); + + /* + * Marking can trigger many incidental post barriers, some of them for + * objects which are not going to be live after the GC. + */ + AutoDisableStoreBuffer adsb(rt); + RecordNativeStackTopForGC(rt); int zoneCount = 0; @@ -4651,6 +4691,14 @@ JS::ShrinkGCBuffers(JSRuntime *rt) rt->gcHelperThread.startBackgroundShrink(); } +void +js::MinorGC(JSRuntime *rt, JS::gcreason::Reason reason) +{ +#ifdef JSGC_GENERATIONAL + rt->gcNursery.collect(rt, reason); +#endif +} + void js::gc::FinishBackgroundFinalize(JSRuntime *rt) { diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 22894caf3bdf..2ee1d5830579 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -43,6 +43,7 @@ class BaseShape; class DebugScopeObject; class GCHelperThread; class GlobalObject; +class Nursery; class PropertyName; class ScopeObject; class Shape; @@ -50,9 +51,10 @@ class UnownedBaseShape; struct SliceBudget; enum HeapState { - Idle, // doing nothing with the GC heap - Tracing, // tracing the GC heap without collecting, e.g. IterateCompartments() - Collecting // doing a GC of the heap + Idle, // doing nothing with the GC heap + Tracing, // tracing the GC heap without collecting, e.g. IterateCompartments() + MajorCollecting, // doing a GC of the major heap + MinorCollecting // doing a GC of the minor heap (nursery) }; namespace ion { @@ -241,8 +243,8 @@ struct ArenaList { void insert(ArenaHeader *arena); }; -struct ArenaLists { - +struct ArenaLists +{ private: /* * For each arena kind its free list is represented as the first span with @@ -497,7 +499,10 @@ struct ArenaLists { inline void queueForForegroundSweep(FreeOp *fop, AllocKind thingKind); inline void queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind); - inline void *allocateFromArena(JS::Zone *zone, AllocKind thingKind); + void *allocateFromArena(JS::Zone *zone, AllocKind thingKind); + inline void *allocateFromArenaInline(JS::Zone *zone, AllocKind thingKind); + + friend class js::Nursery; }; /* @@ -602,6 +607,9 @@ GCDebugSlice(JSRuntime *rt, bool limit, int64_t objCount); extern void PrepareForDebugGC(JSRuntime *rt); +extern void +MinorGC(JSRuntime *rt, JS::gcreason::Reason reason); + #ifdef JS_GC_ZEAL extern void SetGCZeal(JSRuntime *rt, uint8_t zeal, uint32_t frequency); diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 9fc17ff96585..53f78be7675d 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -190,6 +190,7 @@ IsInsideNursery(JSRuntime *rt, const void *thing) if (rt->gcVerifyPostData) return rt->gcVerifierNursery.isInside(thing); #endif + return rt->gcNursery.isInside(thing); #endif return false; } @@ -463,13 +464,41 @@ class GCZoneGroupIter { typedef CompartmentsIterT GCCompartmentGroupIter; +#ifdef JSGC_GENERATIONAL +/* + * Attempt to allocate a new GC thing out of the nursery. If there is not enough + * room in the nursery or there is an OOM, this method will return NULL. + */ +template +inline T * +TryNewNurseryGCThing(JSContext *cx, size_t thingSize) +{ + JS_ASSERT(!IsAtomsCompartment(cx->compartment)); + JSRuntime *rt = cx->runtime; + Nursery &nursery = rt->gcNursery; + T *t = static_cast(nursery.allocate(thingSize)); + if (t) + return t; + if (allowGC && !rt->mainThread.suppressGC) { + MinorGC(rt, JS::gcreason::OUT_OF_NURSERY); + + /* Exceeding gcMaxBytes while tenuring can disable the Nursery. */ + if (nursery.isEnabled()) { + t = static_cast(nursery.allocate(thingSize)); + JS_ASSERT(t); + return t; + } + } + return NULL; +} +#endif /* JSGC_GENERATIONAL */ + /* * Allocates a new GC thing. After a successful allocation the caller must * fully initialize the thing before calling any function that can potentially * trigger GC. This will ensure that GC tracing never sees junk values stored * in the partially initialized thing. */ - template inline T * NewGCThing(JSContext *cx, AllocKind kind, size_t thingSize, InitialHeap heap) @@ -493,6 +522,14 @@ NewGCThing(JSContext *cx, AllocKind kind, size_t thingSize, InitialHeap heap) if (allowGC) MaybeCheckStackRoots(cx); +#if defined(JSGC_GENERATIONAL) + if (ShouldNurseryAllocate(cx->runtime->gcNursery, kind, heap)) { + T *t = TryNewNurseryGCThing(cx, thingSize); + if (t) + return t; + } +#endif + JS::Zone *zone = cx->zone(); T *t = static_cast(zone->allocator.arenas.allocateFromFreeList(kind, thingSize)); if (!t) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 122aa48b0576..5ad122334407 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2365,6 +2365,28 @@ JSObject::setSlotSpan(JSContext *cx, HandleObject obj, uint32_t span) return true; } +static HeapSlot * +AllocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots) +{ +#ifdef JSGC_GENERATIONAL + return cx->runtime->gcNursery.allocateSlots(cx, obj, nslots); +#else + return cx->pod_malloc(nslots); +#endif +} + +static HeapSlot * +ReallocateSlots(JSContext *cx, JSObject *obj, HeapSlot *oldSlots, + uint32_t oldCount, uint32_t newCount) +{ +#ifdef JSGC_GENERATIONAL + return cx->runtime->gcNursery.reallocateSlots(cx, obj, oldSlots, oldCount, newCount); +#else + return (HeapSlot *)cx->realloc_(oldSlots, oldCount * sizeof(HeapSlot), + newCount * sizeof(HeapSlot)); +#endif +} + /* static */ bool JSObject::growSlots(JSContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount) { @@ -2403,15 +2425,14 @@ JSObject::growSlots(JSContext *cx, HandleObject obj, uint32_t oldCount, uint32_t } if (!oldCount) { - obj->slots = cx->pod_malloc(newCount); + obj->slots = AllocateSlots(cx, obj, newCount); if (!obj->slots) return false; Debug_SetSlotRangeToCrashOnTouch(obj->slots, newCount); return true; } - HeapSlot *newslots = (HeapSlot*) cx->realloc_(obj->slots, oldCount * sizeof(HeapSlot), - newCount * sizeof(HeapSlot)); + HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount); if (!newslots) return false; /* Leave slots at its old size. */ @@ -2427,6 +2448,15 @@ JSObject::growSlots(JSContext *cx, HandleObject obj, uint32_t oldCount, uint32_t return true; } +static void +FreeSlots(JSContext *cx, HeapSlot *slots) +{ +#ifdef JSGC_GENERATIONAL + if (!cx->runtime->gcNursery.isInside(slots)) +#endif + js_free(slots); +} + /* static */ void JSObject::shrinkSlots(JSContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount) { @@ -2442,14 +2472,14 @@ JSObject::shrinkSlots(JSContext *cx, HandleObject obj, uint32_t oldCount, uint32 return; if (newCount == 0) { - js_free(obj->slots); + FreeSlots(cx, obj->slots); obj->slots = NULL; return; } JS_ASSERT(newCount >= SLOT_CAPACITY_MIN); - HeapSlot *newslots = (HeapSlot *) cx->realloc_(obj->slots, newCount * sizeof(HeapSlot)); + HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount); if (!newslots) return; /* Leave slots at its old size. */ @@ -2652,27 +2682,43 @@ JSObject::maybeDensifySparseElements(JSContext *cx, HandleObject obj) return ED_OK; } -bool -JSObject::growElements(JSContext *cx, unsigned newcap) +ObjectElements * +AllocateElements(JSObject::MaybeContext maybecx, JSObject *obj, uint32_t nelems) { - if (!growElements(&cx->zone()->allocator, newcap)) { - JS_ReportOutOfMemory(cx); - return false; + if (JSContext *cx = maybecx.context) { +#ifdef JSGC_GENERATIONAL + return cx->runtime->gcNursery.allocateElements(cx, obj, nelems); +#else + return static_cast(cx->malloc_(nelems * sizeof(HeapValue))); +#endif } - return true; + Allocator *alloc = maybecx.allocator; + return static_cast(alloc->malloc_(nelems * sizeof(HeapValue))); +} + +ObjectElements * +ReallocateElements(JSObject::MaybeContext maybecx, JSObject *obj, ObjectElements *oldHeader, + uint32_t oldCount, uint32_t newCount) +{ + if (JSContext *cx = maybecx.context) { +#ifdef JSGC_GENERATIONAL + return cx->runtime->gcNursery.reallocateElements(cx, obj, oldHeader, oldCount, newCount); +#else + return static_cast(cx->realloc_(oldHeader, + oldCount * sizeof(HeapValue), + newCount * sizeof(HeapSlot))); +#endif + } + + Allocator *alloc = maybecx.allocator; + return static_cast(alloc->realloc_(oldHeader, oldCount * sizeof(HeapSlot), + newCount * sizeof(HeapSlot))); } bool -JSObject::growElements(js::Allocator *alloc, unsigned newcap) +JSObject::growElements(MaybeContext cx, unsigned newcap) { - /* - * This version of |growElements()|, which takes a - * |js::Allocator*| as opposed to a |JSContext*|, is intended to - * run either during sequential or parallel execution. As per - * convention, since it does not take a JSContext*, it does not - * report an error on out of memory but simply returns false. - */ JS_ASSERT(isExtensible()); /* @@ -2704,20 +2750,18 @@ JSObject::growElements(js::Allocator *alloc, unsigned newcap) } uint32_t initlen = getDenseInitializedLength(); + uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER; uint32_t newAllocated = actualCapacity + ObjectElements::VALUES_PER_HEADER; ObjectElements *newheader; if (hasDynamicElements()) { - uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER; - newheader = (ObjectElements *) - alloc->realloc_(getElementsHeader(), oldAllocated * sizeof(Value), - newAllocated * sizeof(Value)); + newheader = ReallocateElements(cx, this, getElementsHeader(), oldAllocated, newAllocated); if (!newheader) - return false; /* Leave elements as its old size. */ + return false; /* Leave elements as its old size. */ } else { - newheader = (ObjectElements *) alloc->malloc_(newAllocated * sizeof(Value)); + newheader = AllocateElements(cx, this, newAllocated); if (!newheader) - return false; /* Ditto. */ + return false; /* Leave elements as its old size. */ js_memcpy(newheader, getElementsHeader(), (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value)); } @@ -2742,10 +2786,11 @@ JSObject::shrinkElements(JSContext *cx, unsigned newcap) newcap = Max(newcap, SLOT_CAPACITY_MIN); + uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER; uint32_t newAllocated = newcap + ObjectElements::VALUES_PER_HEADER; - ObjectElements *newheader = (ObjectElements *) - cx->realloc_(getElementsHeader(), newAllocated * sizeof(Value)); + ObjectElements *newheader = ReallocateElements(cx, this, getElementsHeader(), + oldAllocated, newAllocated); if (!newheader) return; /* Leave elements at its old size. */ diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 8579222b1c11..ca019d89c347 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -39,6 +39,7 @@ class BaseProxyHandler; class CallObject; struct GCMarker; struct NativeIterator; +class Nursery; ForwardDeclare(Shape); struct StackShape; @@ -281,6 +282,7 @@ class JSObject : public js::ObjectImpl friend class js::Shape; friend struct js::GCMarker; friend class js::NewObjectCache; + friend class js::Nursery; /* Make the type object to use for LAZY_TYPE objects. */ static js::types::TypeObject *makeLazyType(JSContext *cx, js::HandleObject obj); @@ -561,9 +563,16 @@ class JSObject : public js::ObjectImpl /* Accessors for elements. */ + struct MaybeContext { + js::Allocator *allocator; + JSContext *context; + + MaybeContext(JSContext *cx) : allocator(NULL), context(cx) {} + MaybeContext(js::Allocator *alloc) : allocator(alloc), context(NULL) {} + }; + inline bool ensureElements(JSContext *cx, unsigned cap); - bool growElements(JSContext *cx, unsigned cap); - bool growElements(js::Allocator *alloc, unsigned cap); + bool growElements(MaybeContext cx, unsigned newcap); void shrinkElements(JSContext *cx, unsigned cap); inline void setDynamicElements(js::ObjectElements *header); @@ -601,8 +610,9 @@ class JSObject : public js::ObjectImpl inline EnsureDenseResult ensureDenseElements(JSContext *cx, unsigned index, unsigned extra); inline EnsureDenseResult parExtendDenseElements(js::Allocator *alloc, js::Value *v, uint32_t extra); - template - inline EnsureDenseResult extendDenseElements(CONTEXT *cx, unsigned requiredCapacity, unsigned extra); + template + inline EnsureDenseResult extendDenseElements(MallocProviderType *cx, + unsigned requiredCapacity, unsigned extra); /* Convert a single dense element to a sparse property. */ static bool sparsifyDenseElement(JSContext *cx, js::HandleObject obj, unsigned index); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index e00de1cb4160..4c34cf94f26c 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -594,9 +594,10 @@ JSObject::ensureDenseInitializedLength(JSContext *cx, uint32_t index, uint32_t e } } -template +template JSObject::EnsureDenseResult -JSObject::extendDenseElements(CONTEXT *cx, unsigned requiredCapacity, unsigned extra) +JSObject::extendDenseElements(MallocProviderType *cx, + unsigned requiredCapacity, unsigned extra) { /* * Don't grow elements for non-extensible objects or watched objects. Dense @@ -949,6 +950,10 @@ JSObject::create(JSContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap return NULL; } +#ifdef JSGC_GENERATIONAL + cx->runtime->gcNursery.notifyInitialSlots(obj, slots); +#endif + obj->shape_.init(shape); obj->type_.init(type); obj->slots = slots; diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 402cf9c35998..08d73fd5f1a4 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -259,7 +259,7 @@ NextView(JSObject *obj) return static_cast(obj->getFixedSlot(BufferView::NEXT_VIEW_SLOT).toPrivate()); } -static JSObject ** +static HeapPtrObject * GetViewList(ArrayBufferObject *obj) { #if USE_NEW_OBJECT_REPRESENTATION @@ -275,7 +275,7 @@ GetViewList(ArrayBufferObject *obj) struct OldObjectRepresentationHack { uint32_t capacity; uint32_t initializedLength; - JSObject *views; + HeapPtrObject views; }; return &reinterpret_cast(obj->getElementsHeader())->views; #endif @@ -468,8 +468,10 @@ class WeakObjectSlotRef : public js::gc::BufferableRef } virtual void mark(JSTracer *trc) { + MarkObjectUnbarriered(trc, &owner, "weak TypeArrayView ref"); JSObject *obj = static_cast(owner->getFixedSlot(slot).toPrivate()); - MarkObjectUnbarriered(trc, &obj, desc); + if (obj && obj != UNSET_BUFFER_LINK) + MarkObjectUnbarriered(trc, &obj, desc); owner->setFixedSlot(slot, PrivateValue(obj)); } }; @@ -508,7 +510,7 @@ ArrayBufferObject::addView(RawObject view) // list was nonempty and will be made weak during this call (and weak // pointers cannot violate the snapshot-at-the-beginning invariant.) - JSObject **views = GetViewList(this); + HeapPtrObject *views = GetViewList(this); if (*views == NULL) { // This ArrayBuffer will have a single view at this point, so it is a // strong pointer (it will be marked during tracing.) @@ -638,7 +640,7 @@ ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents, // Neuter the donor ArrayBuffer and all views of it ArrayBufferObject::setElementsHeader(header, 0); - *GetViewList(&buffer) = views; + GetViewList(&buffer)->init(views); for (JSObject *view = views; view; view = NextView(view)) TypedArray::neuter(view); @@ -671,20 +673,32 @@ ArrayBufferObject::obj_trace(JSTracer *trc, RawObject obj) // linked list during collection, and then swept to prune out their dead // views. - JSObject **views = GetViewList(&obj->asArrayBuffer()); + HeapPtrObject *views = GetViewList(&obj->asArrayBuffer()); if (!*views) return; + // During minor collections, these edges are normally kept alive by the + // store buffer. If the store buffer overflows, fallback marking should + // just treat these as strong references for simplicity. + if (trc->runtime->isHeapMinorCollecting()) { + MarkObject(trc, views, "arraybuffer.viewlist"); + JSObject *prior = views->get(); + for (JSObject *view = NextView(prior); view; prior = view, view = NextView(view)) { + MarkObjectUnbarriered(trc, &view, "arraybuffer.views"); + prior->setFixedSlot(BufferView::NEXT_VIEW_SLOT, PrivateValue(view)); + } + return; + } + JSObject *firstView = *views; if (NextView(firstView) == NULL) { // Single view: mark it, but only if we're actually doing a GC pass // right now. Otherwise, the tracing pass for barrier verification will // fail if we add another view and the pointer becomes weak. if (IS_GC_MARKING_TRACER(trc)) - MarkObjectUnbarriered(trc, views, "arraybuffer.singleview"); + MarkObject(trc, views, "arraybuffer.singleview"); } else { // Multiple views: do not mark, but append buffer to list. - if (IS_GC_MARKING_TRACER(trc)) { // obj_trace may be called multiple times before sweep(), so avoid // adding this buffer to the list multiple times. @@ -715,7 +729,7 @@ ArrayBufferObject::sweep(JSCompartment *compartment) compartment->gcLiveArrayBuffers = NULL; while (buffer) { - JSObject **views = GetViewList(&buffer->asArrayBuffer()); + HeapPtrObject *views = GetViewList(&buffer->asArrayBuffer()); JS_ASSERT(*views); JSObject *nextBuffer = BufferLink(*views); @@ -735,7 +749,7 @@ ArrayBufferObject::sweep(JSCompartment *compartment) } view = nextView; } - *views = prevLiveView; + *(views->unsafeGet()) = prevLiveView; buffer = nextBuffer; } @@ -1741,7 +1755,7 @@ class TypedArrayTemplate JS_ASSERT(bufobj->isArrayBuffer()); Rooted buffer(cx, &bufobj->asArrayBuffer()); - InitTypedArrayDataPointer(obj, buffer, byteOffset); + InitArrayBufferViewDataPointer(obj, buffer, byteOffset); obj->setSlot(LENGTH_SLOT, Int32Value(len)); obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset)); obj->setSlot(BYTELENGTH_SLOT, Int32Value(len * sizeof(NativeType))); diff --git a/js/src/jstypedarrayinlines.h b/js/src/jstypedarrayinlines.h index 952ecb100505..9b1f9cd6dfab 100644 --- a/js/src/jstypedarrayinlines.h +++ b/js/src/jstypedarrayinlines.h @@ -180,27 +180,32 @@ DataViewObject::is(const Value &v) } #ifdef JSGC_GENERATIONAL -class TypedArrayPrivateRef : public gc::BufferableRef +class ArrayBufferViewByteOffsetRef : public gc::BufferableRef { JSObject *obj; - ArrayBufferObject *buffer; - size_t byteOffset; public: - TypedArrayPrivateRef(JSObject *obj, ArrayBufferObject *buffer, size_t byteOffset) - : obj(obj), buffer(buffer), byteOffset(byteOffset) {} + explicit ArrayBufferViewByteOffsetRef(JSObject *obj) : obj(obj) {} bool match(void *location) { - // The private field of obj is not traced, but needs to be updated by mark. + /* The private field of obj is not traced, but needs to be updated by mark. */ return false; } - void mark(JSTracer *trc) {} + void mark(JSTracer *trc) { + /* Update obj's private to point to the moved buffer's array data. */ + MarkObjectUnbarriered(trc, &obj, "TypedArray"); + HeapSlot &bufSlot = obj->getReservedSlotRef(BufferView::BUFFER_SLOT); + gc::MarkSlot(trc, &bufSlot, "TypedArray::BUFFER_SLOT"); + ArrayBufferObject &buf = bufSlot.toObject().asArrayBuffer(); + int32_t offset = obj->getReservedSlot(BufferView::BYTEOFFSET_SLOT).toInt32(); + obj->initPrivate(buf.dataPointer() + offset); + } }; #endif static inline void -InitTypedArrayDataPointer(JSObject *obj, ArrayBufferObject *buffer, size_t byteOffset) +InitArrayBufferViewDataPointer(JSObject *obj, ArrayBufferObject *buffer, size_t byteOffset) { /* * N.B. The base of the array's data is stored in the object's @@ -209,8 +214,8 @@ InitTypedArrayDataPointer(JSObject *obj, ArrayBufferObject *buffer, size_t byteO */ obj->initPrivate(buffer->dataPointer() + byteOffset); #ifdef JSGC_GENERATIONAL - if (IsInsideNursery(obj->runtime(), buffer)) - obj->runtime()->gcStoreBuffer.putGeneric(TypedArrayPrivateRef(obj, buffer, byteOffset)); + if (IsInsideNursery(obj->runtime(), buffer) && buffer->hasFixedElements()) + obj->runtime()->gcStoreBuffer.putGeneric(ArrayBufferViewByteOffsetRef(obj)); #endif } @@ -267,7 +272,7 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, dvobj.setFixedSlot(BUFFER_SLOT, ObjectValue(*arrayBuffer)); dvobj.setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(NULL)); dvobj.setFixedSlot(NEXT_BUFFER_SLOT, PrivateValue(UNSET_BUFFER_LINK)); - InitTypedArrayDataPointer(obj, arrayBuffer, byteOffset); + InitArrayBufferViewDataPointer(obj, arrayBuffer, byteOffset); JS_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength()); // Verify that the private slot is at the expected place diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 5d08e05c3eed..3fa42b504c28 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5305,7 +5305,7 @@ main(int argc, char **argv, char **envp) || !op.addBoolOption('\0', "no-fpu", "Pretend CPU does not support floating-point operations " "to test JIT codegen (no-op on platforms other than x86).") #ifdef JSGC_GENERATIONAL - || !op.addBoolOption('\0', "ggc", "Enable Generational GC") + || !op.addBoolOption('\0', "no-ggc", "Disable Generational GC") #endif ) { @@ -5355,7 +5355,7 @@ main(int argc, char **argv, char **envp) JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff); #ifdef JSGC_GENERATIONAL - if (!op.getBoolOption("ggc")) + if (op.getBoolOption("no-ggc")) JS::DisableGenerationalGC(rt); #endif diff --git a/js/src/vm/ObjectImpl.h b/js/src/vm/ObjectImpl.h index 41237dc2c8e7..6e64770c1c22 100644 --- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -24,6 +24,7 @@ namespace js { class Debugger; class ObjectImpl; +class Nursery; ForwardDeclare(Shape); class AutoPropDescArrayRooter; @@ -930,6 +931,7 @@ class ObjectElements friend class ::JSObject; friend class ObjectImpl; friend class ArrayBufferObject; + friend class Nursery; /* See Flags enum above. */ uint32_t flags; @@ -1413,6 +1415,10 @@ class ObjectImpl : public gc::Cell return elements != emptyObjectElements && elements != fixedElements(); } + inline bool hasFixedElements() const { + return elements == fixedElements(); + } + inline bool hasEmptyElements() const { return elements == emptyObjectElements; } diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index 674e67958f74..9dabec364ac4 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1341,6 +1341,30 @@ EmptyShape::insertInitialShape(JSContext *cx, HandleShape shape, HandleObject pr cx->runtime->newObjectCache.invalidateEntriesForShape(cx, shape, proto); } +/* + * This is called by the minor GC to ensure that any relocated proto objects + * get updated in the shape table. + */ +void +JSCompartment::markAllInitialShapeTableEntries(JSTracer *trc) +{ + if (!initialShapes.initialized()) + return; + + for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { + if (!e.front().proto.isObject()) + continue; + JSObject *proto = e.front().proto.toObject(); + JS_SET_TRACING_LOCATION(trc, (void*)&e.front().proto); + MarkObjectRoot(trc, &proto, "InitialShapeSet proto"); + if (proto != e.front().proto.toObject()) { + InitialShapeEntry moved = e.front(); + moved.proto = TaggedProto(proto); + e.rekeyFront(e.front().getLookup(), moved); + } + } +} + void JSCompartment::sweepInitialShapeTable() { diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index dff5a03c63b8..2c942608dcbb 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -95,6 +95,7 @@ class JSObject; namespace js { class Bindings; +class Nursery; /* Limit on the number of slotful properties in an object. */ static const uint32_t SHAPE_INVALID_SLOT = JS_BIT(24) - 1; @@ -461,6 +462,7 @@ class Shape : public js::gc::Cell friend class ::JSObject; friend class ::JSFunction; friend class js::Bindings; + friend class js::Nursery; friend class js::ObjectImpl; friend class js::PropertyTree; friend class js::StaticBlockObject; From 7593fb11f1acb5433d6d78a26d92207946785c35 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Thu, 18 Apr 2013 17:19:10 -0700 Subject: [PATCH 16/90] Bug 859391 - Disable session restore in webapps. r=bnicholson --- mobile/android/base/GeckoApp.java | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 73be8862be55..f4329f6cbf69 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -1405,9 +1405,8 @@ abstract public class GeckoApp // check if the last run was exited due to a normal kill while // we were in the background, or a more harsh kill while we were // active - if (savedInstanceState != null) { - mRestoreMode = RESTORE_OOM; - + mRestoreMode = getSessionRestoreState(savedInstanceState); + if (mRestoreMode == RESTORE_OOM) { boolean wasInBackground = savedInstanceState.getBoolean(SAVED_STATE_IN_BACKGROUND, false); @@ -1477,10 +1476,6 @@ abstract public class GeckoApp passedUri = uri; } - if (mRestoreMode == RESTORE_NONE && shouldRestoreSession()) { - mRestoreMode = RESTORE_CRASH; - } - final boolean isExternalURL = passedUri != null && !passedUri.equals("about:home"); StartupAction startupAction; if (isExternalURL) { @@ -1714,20 +1709,32 @@ abstract public class GeckoApp return mProfile; } - protected boolean shouldRestoreSession() { - SharedPreferences prefs = GeckoApp.mAppContext.getSharedPreferences(PREFS_NAME, 0); + protected int getSessionRestoreState(Bundle savedInstanceState) { + if (savedInstanceState != null) { + return RESTORE_OOM; + } + + final SharedPreferences prefs = GeckoApp.mAppContext.getSharedPreferences(PREFS_NAME, 0); // We record crashes in the crash reporter. If sessionstore.js // exists, but we didn't flag a crash in the crash reporter, we // were probably just force killed by the user, so we shouldn't do // a restore. if (prefs.getBoolean(PREFS_CRASHED, false)) { - prefs.edit().putBoolean(GeckoApp.PREFS_CRASHED, false).commit(); + ThreadUtils.postToBackgroundThread(new Runnable() { + @Override + public void run() { + prefs.edit() + .putBoolean(GeckoApp.PREFS_CRASHED, false) + .commit(); + } + }); + if (getProfile().shouldRestoreSession()) { - return true; + return RESTORE_CRASH; } } - return false; + return RESTORE_NONE; } /** From bad51eb8537b8f6b8a0bd6bb45a5cc6998185e7b Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 18 Apr 2013 17:29:08 -0700 Subject: [PATCH 17/90] Bug 847354 - Avoid the xbl bindings for inputs/textareas on b2g r=fabrice --- b2g/chrome/content/content.css | 8 ++++++++ b2g/chrome/jar.mn | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/b2g/chrome/content/content.css b/b2g/chrome/content/content.css index 22ae909eefa0..82eb4378f139 100644 --- a/b2g/chrome/content/content.css +++ b/b2g/chrome/content/content.css @@ -282,3 +282,11 @@ label:active, textarea:active { background-color: rgba(141, 184, 216, 0.5); } + +#ifdef MOZ_WIDGET_GONK +/* This binding only provide key shortcuts that we can't use on devices */ +input, +textarea { +-moz-binding: none !important; +} +#endif diff --git a/b2g/chrome/jar.mn b/b2g/chrome/jar.mn index f850eaecb2d2..886e9e173274 100644 --- a/b2g/chrome/jar.mn +++ b/b2g/chrome/jar.mn @@ -19,7 +19,7 @@ chrome.jar: content/screen.js (content/screen.js) content/runapp.js (content/runapp.js) #endif - content/content.css (content/content.css) +* content/content.css (content/content.css) content/touchcontrols.css (content/touchcontrols.css) content/payment.js (content/payment.js) From fbb393d573a25027b874c3e6c9140de3cfa67da5 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 18 Apr 2013 17:41:43 -0700 Subject: [PATCH 18/90] Bug 844887 - Inline IsCallable and ToObject intrinsics in Ion. (r=sstangl) --- js/src/ion/CodeGenerator.cpp | 22 +++++++++ js/src/ion/CodeGenerator.h | 1 + js/src/ion/IonBuilder.h | 2 + js/src/ion/LIR-Common.h | 16 +++++++ js/src/ion/LOpcodes.h | 1 + js/src/ion/Lowering.cpp | 8 ++++ js/src/ion/Lowering.h | 1 + js/src/ion/MCallOptimize.cpp | 69 +++++++++++++++++++++------- js/src/ion/MIR.h | 23 ++++++++++ js/src/ion/MOpcodes.h | 1 + js/src/ion/ParallelArrayAnalysis.cpp | 1 + js/src/jscntxt.h | 2 + js/src/vm/SelfHosting.cpp | 8 ++-- 13 files changed, 134 insertions(+), 21 deletions(-) diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index 92bea9259955..f208b20c5896 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -6102,6 +6102,28 @@ CodeGenerator::visitOutOfLineParallelAbort(OutOfLineParallelAbort *ool) return true; } +bool +CodeGenerator::visitIsCallable(LIsCallable *ins) +{ + Register object = ToRegister(ins->object()); + Register output = ToRegister(ins->output()); + + masm.loadObjClass(object, output); + + // An object is callable iff (isFunction() || getClass()->call). + Label notFunction, done; + masm.branchPtr(Assembler::NotEqual, output, ImmWord(&js::FunctionClass), ¬Function); + masm.move32(Imm32(1), output); + masm.jump(&done); + + masm.bind(¬Function); + masm.cmpPtr(Address(output, offsetof(js::Class, call)), ImmWord((void *)NULL)); + masm.emitSet(Assembler::NonZero, output); + masm.bind(&done); + + return true; +} + bool CodeGenerator::visitAsmJSCall(LAsmJSCall *ins) { diff --git a/js/src/ion/CodeGenerator.h b/js/src/ion/CodeGenerator.h index 302720394c3b..2e8713169742 100644 --- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -217,6 +217,7 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitSetDOMProperty(LSetDOMProperty *lir); bool visitCallDOMNative(LCallDOMNative *lir); bool visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir); + bool visitIsCallable(LIsCallable *lir); bool visitAsmJSCall(LAsmJSCall *lir); bool visitAsmJSParameter(LAsmJSParameter *lir); bool visitAsmJSReturn(LAsmJSReturn *ret); diff --git a/js/src/ion/IonBuilder.h b/js/src/ion/IonBuilder.h index 0bc2664583b8..1857d50697bf 100644 --- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -460,6 +460,8 @@ class IonBuilder : public MIRGenerator uint32_t discards); InliningStatus inlineThrowError(CallInfo &callInfo); + InliningStatus inlineIsCallable(CallInfo &callInfo); + InliningStatus inlineToObject(CallInfo &callInfo); InliningStatus inlineDump(CallInfo &callInfo); // Main inlining functions diff --git a/js/src/ion/LIR-Common.h b/js/src/ion/LIR-Common.h index 542403b2586b..8e805cc3e84d 100644 --- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -4162,6 +4162,22 @@ class LFunctionBoundary : public LInstructionHelper<0, 0, 1> } }; +class LIsCallable : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(IsCallable); + LIsCallable(const LAllocation &object) { + setOperand(0, object); + } + + const LAllocation *object() { + return getOperand(0); + } + MIsCallable *mir() const { + return mir_->toIsCallable(); + } +}; + class LAsmJSLoadHeap : public LInstructionHelper<1, 1, 0> { public: diff --git a/js/src/ion/LOpcodes.h b/js/src/ion/LOpcodes.h index 02d63fde4de6..8076fafdde2b 100644 --- a/js/src/ion/LOpcodes.h +++ b/js/src/ion/LOpcodes.h @@ -210,6 +210,7 @@ _(GetDOMProperty) \ _(SetDOMProperty) \ _(CallDOMNative) \ + _(IsCallable) \ _(AsmJSLoadHeap) \ _(AsmJSStoreHeap) \ _(AsmJSLoadGlobalVar) \ diff --git a/js/src/ion/Lowering.cpp b/js/src/ion/Lowering.cpp index b29b50e27ca0..d6dca2918432 100644 --- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -2461,6 +2461,14 @@ LIRGenerator::visitFunctionBoundary(MFunctionBoundary *ins) assignSafepoint(lir, ins); } +bool +LIRGenerator::visitIsCallable(MIsCallable *ins) +{ + JS_ASSERT(ins->object()->type() == MIRType_Object); + JS_ASSERT(ins->type() == MIRType_Boolean); + return define(new LIsCallable(useRegister(ins->object())), ins); +} + bool LIRGenerator::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) { diff --git a/js/src/ion/Lowering.h b/js/src/ion/Lowering.h index d2733b9a4242..d3be75f60572 100644 --- a/js/src/ion/Lowering.h +++ b/js/src/ion/Lowering.h @@ -220,6 +220,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitInstanceOf(MInstanceOf *ins); bool visitCallInstanceOf(MCallInstanceOf *ins); bool visitFunctionBoundary(MFunctionBoundary *ins); + bool visitIsCallable(MIsCallable *ins); bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins); bool visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins); bool visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins); diff --git a/js/src/ion/MCallOptimize.cpp b/js/src/ion/MCallOptimize.cpp index 98e986259127..0783a15d017d 100644 --- a/js/src/ion/MCallOptimize.cpp +++ b/js/src/ion/MCallOptimize.cpp @@ -85,25 +85,25 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) if (native == regexp_test) return inlineRegExpTest(callInfo); - // Parallel Array + // Array intrinsics. if (native == intrinsic_UnsafeSetElement) return inlineUnsafeSetElement(callInfo); - if (native == testingFunc_inParallelSection) - return inlineShouldForceSequentialOrInParallelSection(callInfo); if (native == intrinsic_NewDenseArray) return inlineNewDenseArray(callInfo); - // Self-hosting + // Utility intrinsics. if (native == intrinsic_ThrowError) return inlineThrowError(callInfo); + if (native == intrinsic_IsCallable) + return inlineIsCallable(callInfo); + if (native == intrinsic_ToObject) + return inlineToObject(callInfo); #ifdef DEBUG if (native == intrinsic_Dump) return inlineDump(callInfo); #endif - // Parallel Array - if (native == intrinsic_UnsafeSetElement) - return inlineUnsafeSetElement(callInfo); + // Parallel intrinsics. if (native == intrinsic_ShouldForceSequential) return inlineShouldForceSequentialOrInParallelSection(callInfo); if (native == testingFunc_inParallelSection) @@ -112,16 +112,6 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) return inlineNewParallelArray(callInfo); if (native == ParallelArrayObject::construct) return inlineParallelArray(callInfo); - if (native == intrinsic_NewDenseArray) - return inlineNewDenseArray(callInfo); - - // Self-hosting - if (native == intrinsic_ThrowError) - return inlineThrowError(callInfo); -#ifdef DEBUG - if (native == intrinsic_Dump) - return inlineDump(callInfo); -#endif return InliningStatus_NotInlined; } @@ -1317,6 +1307,51 @@ IonBuilder::inlineThrowError(CallInfo &callInfo) return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineIsCallable(CallInfo &callInfo) +{ + if (callInfo.argc() != 1 || callInfo.constructing()) + return InliningStatus_NotInlined; + + if (getInlineReturnType() != MIRType_Boolean) + return InliningStatus_NotInlined; + if (getInlineArgType(callInfo, 0) != MIRType_Object) + return InliningStatus_NotInlined; + + callInfo.unwrapArgs(); + + MIsCallable *isCallable = MIsCallable::New(callInfo.getArg(0)); + current->add(isCallable); + current->push(isCallable); + + return InliningStatus_Inlined; +} + +IonBuilder::InliningStatus +IonBuilder::inlineToObject(CallInfo &callInfo) +{ + if (callInfo.argc() != 1 || callInfo.constructing()) + return InliningStatus_NotInlined; + + // If we know the input type is an object, nop ToObject. + if (getInlineReturnType() != MIRType_Object) + return InliningStatus_NotInlined; + if (getInlineArgType(callInfo, 0) != MIRType_Object) + return InliningStatus_NotInlined; + + callInfo.unwrapArgs(); + MDefinition *object = callInfo.getArg(0); + + // TI still expects the barrier to be checked, since this was a native + // call. We manually make a MTypeBarrier here to avoid pointless + // boxing-unbox sequences. + MTypeBarrier *barrier = MTypeBarrier::New(object, cloneTypeSet(callInfo.barrier())); + current->add(barrier); + + current->push(object); + return InliningStatus_Inlined; +} + IonBuilder::InliningStatus IonBuilder::inlineDump(CallInfo &callInfo) { diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 7550f543e9f7..cbdc2b736ae5 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -7172,6 +7172,29 @@ class FlattenedMResumePointIter } }; +class MIsCallable + : public MUnaryInstruction, + public SingleObjectPolicy +{ + MIsCallable(MDefinition *object) + : MUnaryInstruction(object) + { + setResultType(MIRType_Boolean); + setMovable(); + } + + public: + INSTRUCTION_HEADER(IsCallable); + + static MIsCallable *New(MDefinition *obj) { + return new MIsCallable(obj); + } + + MDefinition *object() const { + return getOperand(0); + } +}; + class MAsmJSNeg : public MUnaryInstruction { MAsmJSNeg(MDefinition *op, MIRType type) diff --git a/js/src/ion/MOpcodes.h b/js/src/ion/MOpcodes.h index c73995d35e9b..4f6bf009f4ea 100644 --- a/js/src/ion/MOpcodes.h +++ b/js/src/ion/MOpcodes.h @@ -156,6 +156,7 @@ namespace ion { _(FunctionBoundary) \ _(GetDOMProperty) \ _(SetDOMProperty) \ + _(IsCallable) \ _(AsmJSNeg) \ _(AsmJSUDiv) \ _(AsmJSUMod) \ diff --git a/js/src/ion/ParallelArrayAnalysis.cpp b/js/src/ion/ParallelArrayAnalysis.cpp index 0d86fe700b90..1866153297d3 100644 --- a/js/src/ion/ParallelArrayAnalysis.cpp +++ b/js/src/ion/ParallelArrayAnalysis.cpp @@ -264,6 +264,7 @@ class ParallelArrayVisitor : public MInstructionVisitor SAFE_OP(PolyInlineDispatch) SAFE_OP(FunctionDispatch) SAFE_OP(TypeObjectDispatch) + SAFE_OP(IsCallable) UNSAFE_OP(EffectiveAddress) UNSAFE_OP(AsmJSUnsignedToDouble) UNSAFE_OP(AsmJSNeg) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 763aff3cb41b..53a5256e1071 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -2393,6 +2393,8 @@ class ContextAllocPolicy void reportAllocOverflow() const { js_ReportAllocationOverflow(cx_); } }; +JSBool intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp); +JSBool intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_UnsafeSetElement(JSContext *cx, unsigned argc, Value *vp); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 7f6ba8028160..47e850f53af2 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -43,8 +43,8 @@ static JSClass self_hosting_global_class = { JS_ConvertStub, NULL }; -static JSBool -intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp) +JSBool +js::intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedValue val(cx, args[0]); @@ -66,8 +66,8 @@ intrinsic_ToInteger(JSContext *cx, unsigned argc, Value *vp) return true; } -static JSBool -intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp) +JSBool +js::intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); Value val = args[0]; From 2d54e77b8da28baf6a57d8d725acb74dbaadde62 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 18 Apr 2013 17:42:01 -0700 Subject: [PATCH 19/90] Bug 859255 - Fix stitching up of parallel bailout basic blocks. (r=dvander) --- js/src/ion/MIRGraph.cpp | 16 ++++++++++------ js/src/ion/MIRGraph.h | 3 ++- js/src/ion/ParallelArrayAnalysis.cpp | 8 ++------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/js/src/ion/MIRGraph.cpp b/js/src/ion/MIRGraph.cpp index 5e797430c8ed..62ef06044b98 100644 --- a/js/src/ion/MIRGraph.cpp +++ b/js/src/ion/MIRGraph.cpp @@ -161,17 +161,21 @@ MBasicBlock::NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred) MBasicBlock * MBasicBlock::NewParBailout(MIRGraph &graph, CompileInfo &info, - MBasicBlock *pred, jsbytecode *entryPc) + MBasicBlock *pred, jsbytecode *entryPc, + MResumePoint *resumePoint) { - MBasicBlock *block = MBasicBlock::New(graph, info, pred, entryPc, NORMAL); - if (!block) + MBasicBlock *block = new MBasicBlock(graph, info, entryPc, NORMAL); + + resumePoint->block_ = block; + block->entryResumePoint_ = resumePoint; + + if (!block->init()) return NULL; - MParBailout *bailout = new MParBailout(); - if (!bailout) + if (!block->addPredecessorWithoutPhis(pred)) return NULL; - block->end(bailout); + block->end(new MParBailout()); return block; } diff --git a/js/src/ion/MIRGraph.h b/js/src/ion/MIRGraph.h index e1bbb3e50248..cdbc5117301e 100644 --- a/js/src/ion/MIRGraph.h +++ b/js/src/ion/MIRGraph.h @@ -75,7 +75,8 @@ class MBasicBlock : public TempObject, public InlineListNode MBasicBlock *pred, jsbytecode *entryPc); static MBasicBlock *NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred); static MBasicBlock *NewParBailout(MIRGraph &graph, CompileInfo &info, - MBasicBlock *pred, jsbytecode *entryPc); + MBasicBlock *pred, jsbytecode *entryPc, + MResumePoint *resumePoint); bool dominates(MBasicBlock *other); diff --git a/js/src/ion/ParallelArrayAnalysis.cpp b/js/src/ion/ParallelArrayAnalysis.cpp index 1866153297d3..3fc4ca223ab2 100644 --- a/js/src/ion/ParallelArrayAnalysis.cpp +++ b/js/src/ion/ParallelArrayAnalysis.cpp @@ -508,11 +508,6 @@ ParallelArrayVisitor::convertToBailout(MBasicBlock *block, MInstruction *ins) // This block is no longer reachable. block->unmark(); - // Determine the best PC to use for the bailouts we'll be creating. - jsbytecode *pc = block->pc(); - if (!pc) - pc = block->pc(); - // Create a bailout block for each predecessor. In principle, we // only need one bailout block--in fact, only one per graph! But I // found this approach easier to implement given the design of the @@ -528,7 +523,8 @@ ParallelArrayVisitor::convertToBailout(MBasicBlock *block, MInstruction *ins) continue; // create bailout block to insert on this edge - MBasicBlock *bailBlock = MBasicBlock::NewParBailout(graph_, pred->info(), pred, pc); + MBasicBlock *bailBlock = MBasicBlock::NewParBailout(graph_, block->info(), pred, + block->pc(), block->entryResumePoint()); if (!bailBlock) return false; From 57c72db790bddd8b95a74eb695fefa13541db2d9 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 18 Apr 2013 17:43:26 -0700 Subject: [PATCH 20/90] Bug 860531 - Change AliasSet of MCallGetIntrinsicValue to None. (r=dvander) --- js/src/ion/IonBuilder.cpp | 1 - js/src/ion/MIR.h | 3 +++ js/src/ion/VMFunctions.cpp | 12 +++++++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index ca6fca65af28..6cb51db760b6 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -5737,7 +5737,6 @@ IonBuilder::jsop_intrinsic(HandlePropertyName name) RootedScript scriptRoot(cx, script()); types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc); - monitorResult(ins, barrier, types); return pushTypeBarrier(ins, types, barrier); } diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index cbdc2b736ae5..b24dde68e04e 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -5908,6 +5908,9 @@ class MCallGetIntrinsicValue : public MNullaryInstruction PropertyName *name() const { return name_; } + AliasSet getAliasSet() const { + return AliasSet::None(); + } }; class MCallsiteCloneCache diff --git a/js/src/ion/VMFunctions.cpp b/js/src/ion/VMFunctions.cpp index b971fb606f04..329a997e0862 100644 --- a/js/src/ion/VMFunctions.cpp +++ b/js/src/ion/VMFunctions.cpp @@ -517,7 +517,17 @@ OperatorInI(JSContext *cx, uint32_t index, HandleObject obj, JSBool *out) bool GetIntrinsicValue(JSContext *cx, HandlePropertyName name, MutableHandleValue rval) { - return cx->global()->getIntrinsicValue(cx, name, rval); + if (!cx->global()->getIntrinsicValue(cx, name, rval)) + return false; + + // This function is called when we try to compile a cold getintrinsic + // op. MCallGetIntrinsicValue has an AliasSet of None for optimization + // purposes, as its side effect is not observable from JS. We are + // guaranteed to bail out after this function, but because of its AliasSet, + // type info will not be reflowed. Manually monitor here. + types::TypeScript::Monitor(cx, rval); + + return true; } bool From 985b968a71dfa813c2a708a4d1b3baf31a61d006 Mon Sep 17 00:00:00 2001 From: Neil Deakin Date: Fri, 19 Apr 2013 10:47:27 +1000 Subject: [PATCH 21/90] Bug 824963 part 1 - Rework arrow positioning and alignment code for arrow panels to be simpler, using the actual anchor/alignment position rather than guessing this from the coordinates, fix centred positioning in rtl, r=neil --- layout/xul/base/public/nsIPopupBoxObject.idl | 7 +- layout/xul/base/src/nsMenuPopupFrame.cpp | 58 ++++++ layout/xul/base/src/nsMenuPopupFrame.h | 22 +++ layout/xul/base/src/nsPopupBoxObject.cpp | 50 +++++ .../content/tests/chrome/test_arrowpanel.xul | 182 ++++++++++-------- toolkit/content/widgets/popup.xml | 145 ++++---------- 6 files changed, 277 insertions(+), 187 deletions(-) diff --git a/layout/xul/base/public/nsIPopupBoxObject.idl b/layout/xul/base/public/nsIPopupBoxObject.idl index f4548629963c..6bff30c8a82a 100644 --- a/layout/xul/base/public/nsIPopupBoxObject.idl +++ b/layout/xul/base/public/nsIPopupBoxObject.idl @@ -10,7 +10,7 @@ interface nsIDOMNode; interface nsIDOMEvent; interface nsIDOMClientRect; -[scriptable, uuid(ACCEA57B-C3D8-4B6E-9101-90F04EE9DEA0)] +[scriptable, uuid(DF60BA02-005B-4F61-AB1C-5632D0779DB4)] interface nsIPopupBoxObject : nsISupports { /** @@ -166,6 +166,11 @@ interface nsIPopupBoxObject : nsISupports in AString position, in long x, in long y, in boolean attributesOverride); + + /** Returns the alignment position where the popup has appeared relative to its + * anchor node or point, accounting for any flipping that occurred. + */ + readonly attribute AString alignmentPosition; }; %{C++ diff --git a/layout/xul/base/src/nsMenuPopupFrame.cpp b/layout/xul/base/src/nsMenuPopupFrame.cpp index b8894a657ceb..410fa8761907 100644 --- a/layout/xul/base/src/nsMenuPopupFrame.cpp +++ b/layout/xul/base/src/nsMenuPopupFrame.cpp @@ -83,6 +83,7 @@ nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContex mPopupState(ePopupClosed), mPopupAlignment(POPUPALIGNMENT_NONE), mPopupAnchor(POPUPALIGNMENT_NONE), + mPosition(POPUPPOSITION_UNKNOWN), mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT), mFlipBoth(false), mIsOpenChanged(false), @@ -531,6 +532,8 @@ nsMenuPopupFrame::InitPositionFromAnchorAlign(const nsAString& aAnchor, mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT; else mPopupAlignment = POPUPALIGNMENT_NONE; + + mPosition = POPUPPOSITION_UNKNOWN; } void @@ -583,42 +586,52 @@ nsMenuPopupFrame::InitializePopup(nsIContent* aAnchorContent, else if (position.EqualsLiteral("before_start")) { mPopupAnchor = POPUPALIGNMENT_TOPLEFT; mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT; + mPosition = POPUPPOSITION_BEFORESTART; } else if (position.EqualsLiteral("before_end")) { mPopupAnchor = POPUPALIGNMENT_TOPRIGHT; mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT; + mPosition = POPUPPOSITION_BEFOREEND; } else if (position.EqualsLiteral("after_start")) { mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT; mPopupAlignment = POPUPALIGNMENT_TOPLEFT; + mPosition = POPUPPOSITION_AFTERSTART; } else if (position.EqualsLiteral("after_end")) { mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT; mPopupAlignment = POPUPALIGNMENT_TOPRIGHT; + mPosition = POPUPPOSITION_AFTEREND; } else if (position.EqualsLiteral("start_before")) { mPopupAnchor = POPUPALIGNMENT_TOPLEFT; mPopupAlignment = POPUPALIGNMENT_TOPRIGHT; + mPosition = POPUPPOSITION_STARTBEFORE; } else if (position.EqualsLiteral("start_after")) { mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT; mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT; + mPosition = POPUPPOSITION_STARTAFTER; } else if (position.EqualsLiteral("end_before")) { mPopupAnchor = POPUPALIGNMENT_TOPRIGHT; mPopupAlignment = POPUPALIGNMENT_TOPLEFT; + mPosition = POPUPPOSITION_ENDBEFORE; } else if (position.EqualsLiteral("end_after")) { mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT; mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT; + mPosition = POPUPPOSITION_ENDAFTER; } else if (position.EqualsLiteral("overlap")) { mPopupAnchor = POPUPALIGNMENT_TOPLEFT; mPopupAlignment = POPUPALIGNMENT_TOPLEFT; + mPosition = POPUPPOSITION_OVERLAP; } else if (position.EqualsLiteral("after_pointer")) { mPopupAnchor = POPUPALIGNMENT_TOPLEFT; mPopupAlignment = POPUPALIGNMENT_TOPLEFT; + mPosition = POPUPPOSITION_AFTERPOINTER; // XXXndeakin this is supposed to anchor vertically after, but with the // horizontal position as the mouse pointer. mYPos += 21; @@ -1907,6 +1920,51 @@ nsMenuPopupFrame::SetConsumeRollupEvent(uint32_t aConsumeMode) mConsumeRollupEvent = aConsumeMode; } +int8_t +nsMenuPopupFrame::GetAlignmentPosition() const +{ + // The code below handles most cases of alignment, anchor and position values. Those that are + // not handled just return POPUPPOSITION_UNKNOWN. + + if (mPosition == POPUPPOSITION_OVERLAP || mPosition == POPUPPOSITION_AFTERPOINTER) + return mPosition; + + int8_t position = mPosition; + + if (position == POPUPPOSITION_UNKNOWN) { + switch (mPopupAnchor) { + case POPUPALIGNMENT_BOTTOMCENTER: + position = mPopupAlignment == POPUPALIGNMENT_TOPRIGHT ? + POPUPPOSITION_AFTEREND : POPUPPOSITION_AFTERSTART; + break; + case POPUPALIGNMENT_TOPCENTER: + position = mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT ? + POPUPPOSITION_BEFOREEND : POPUPPOSITION_BEFORESTART; + break; + case POPUPALIGNMENT_LEFTCENTER: + position = mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT ? + POPUPPOSITION_STARTAFTER : POPUPPOSITION_STARTBEFORE; + break; + case POPUPALIGNMENT_RIGHTCENTER: + position = mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT ? + POPUPPOSITION_ENDAFTER : POPUPPOSITION_ENDBEFORE; + break; + default: + break; + } + } + + if (mHFlip) { + position = POPUPPOSITION_HFLIP(position); + } + + if (mVFlip) { + position = POPUPPOSITION_VFLIP(position); + } + + return position; +} + /** * KEEP THIS IN SYNC WITH nsContainerFrame::CreateViewForFrame * as much as possible. Until we get rid of views finally... diff --git a/layout/xul/base/src/nsMenuPopupFrame.h b/layout/xul/base/src/nsMenuPopupFrame.h index 4d3d21d419bb..2ed06ce33757 100644 --- a/layout/xul/base/src/nsMenuPopupFrame.h +++ b/layout/xul/base/src/nsMenuPopupFrame.h @@ -79,6 +79,23 @@ enum FlipStyle { #define POPUPALIGNMENT_TOPCENTER 17 #define POPUPALIGNMENT_BOTTOMCENTER 18 +// The constants here are selected so that horizontally and vertically flipping +// can be easily handled using the two flip macros below. +#define POPUPPOSITION_UNKNOWN -1 +#define POPUPPOSITION_BEFORESTART 0 +#define POPUPPOSITION_BEFOREEND 1 +#define POPUPPOSITION_AFTERSTART 2 +#define POPUPPOSITION_AFTEREND 3 +#define POPUPPOSITION_STARTBEFORE 4 +#define POPUPPOSITION_ENDBEFORE 5 +#define POPUPPOSITION_STARTAFTER 6 +#define POPUPPOSITION_ENDAFTER 7 +#define POPUPPOSITION_OVERLAP 8 +#define POPUPPOSITION_AFTERPOINTER 9 + +#define POPUPPOSITION_HFLIP(v) (v ^ 1) +#define POPUPPOSITION_VFLIP(v) (v ^ 2) + #define INC_TYP_INTERVAL 1000 // 1s. If the interval between two keypresses is shorter than this, // treat as a continue typing // XXX, kyle.yuan@sun.com, there are 4 definitions for the same purpose: @@ -315,6 +332,9 @@ public: nsIntPoint GetLastClientOffset() const { return mLastClientOffset; } + // Return the alignment of the popup + int8_t GetAlignmentPosition() const; + protected: // returns the popup's level. @@ -410,6 +430,8 @@ protected: // popup alignment relative to the anchor node int8_t mPopupAlignment; int8_t mPopupAnchor; + int8_t mPosition; + // One of nsIPopupBoxObject::ROLLUP_DEFAULT/ROLLUP_CONSUME/ROLLUP_NO_CONSUME int8_t mConsumeRollupEvent; bool mFlipBoth; // flip in both directions diff --git a/layout/xul/base/src/nsPopupBoxObject.cpp b/layout/xul/base/src/nsPopupBoxObject.cpp index 8fb49bc5a99c..6f8a33b0478b 100644 --- a/layout/xul/base/src/nsPopupBoxObject.cpp +++ b/layout/xul/base/src/nsPopupBoxObject.cpp @@ -305,6 +305,56 @@ nsPopupBoxObject::GetOuterScreenRect(nsIDOMClientRect** aRect) return NS_OK; } +NS_IMETHODIMP +nsPopupBoxObject::GetAlignmentPosition(nsAString& positionStr) +{ + positionStr.Truncate(); + + // This needs to flush layout. + nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(true)); + if (!menuPopupFrame) + return NS_OK; + + int8_t position = menuPopupFrame->GetAlignmentPosition(); + switch (position) { + case POPUPPOSITION_AFTERSTART: + positionStr.AssignLiteral("after_start"); + break; + case POPUPPOSITION_AFTEREND: + positionStr.AssignLiteral("after_end"); + break; + case POPUPPOSITION_BEFORESTART: + positionStr.AssignLiteral("before_start"); + break; + case POPUPPOSITION_BEFOREEND: + positionStr.AssignLiteral("before_end"); + break; + case POPUPPOSITION_STARTBEFORE: + positionStr.AssignLiteral("start_before"); + break; + case POPUPPOSITION_ENDBEFORE: + positionStr.AssignLiteral("end_before"); + break; + case POPUPPOSITION_STARTAFTER: + positionStr.AssignLiteral("start_after"); + break; + case POPUPPOSITION_ENDAFTER: + positionStr.AssignLiteral("end_after"); + break; + case POPUPPOSITION_OVERLAP: + positionStr.AssignLiteral("overlap"); + break; + case POPUPPOSITION_AFTERPOINTER: + positionStr.AssignLiteral("after_pointer"); + break; + default: + // Leave as an empty string. + break; + } + + return NS_OK; +} + // Creation Routine /////////////////////////////////////////////////////////////////////// nsresult diff --git a/toolkit/content/tests/chrome/test_arrowpanel.xul b/toolkit/content/tests/chrome/test_arrowpanel.xul index 43480bef3c43..53b35ce4367b 100644 --- a/toolkit/content/tests/chrome/test_arrowpanel.xul +++ b/toolkit/content/tests/chrome/test_arrowpanel.xul @@ -25,7 +25,7 @@