diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 463809f9d..7d5157b5c 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -85,8 +85,10 @@ nsContextMenu.prototype = { // Initialize (disable/remove) menu items. this.initItems(); +/* // Register this opening of the menu with telemetry: this._checkTelemetryForMenu(aXulMenu); +*/ }, hiding: function CM_hiding() { diff --git a/dom/base/URL.h b/dom/base/URL.h index 8b10df9df..45c2a735a 100644 --- a/dom/base/URL.h +++ b/dom/base/URL.h @@ -137,6 +137,12 @@ public: GetHref(aRetval); } + void + ToJSON(nsAString& aResult, ErrorResult& aRv) const + { + GetHref(aResult); // XXX: aRv not currently used + } + // URLSearchParamsObserver void URLSearchParamsUpdated(URLSearchParams* aSearchParams) override; diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 99ae6209c..6a7688720 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -5973,6 +5973,26 @@ nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsAString& aOrigin) NS_ENSURE_SUCCESS(rv, rv); if (uri && uri != aURI) { + return GetUTFOrigin(uri, aOrigin); + } + } else { + // We are probably dealing with an unknown blob. + bool isBlob = false; + nsresult rv = aURI->SchemeIs(BLOBURI_SCHEME, &isBlob); + NS_ENSURE_SUCCESS(rv, rv); + + if (isBlob) { + nsAutoCString path; + rv = aURI->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), path); + if (NS_FAILED(rv)) { + aOrigin.AssignLiteral("null"); + return NS_OK; + } + return GetUTFOrigin(uri, aOrigin); } } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index b1a576d0a..66f4a8aca 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -9,7 +9,7 @@ */ #include "nsDocument.h" - +#include "nsIDocumentInlines.h" #include "mozilla/ArrayUtils.h" #include "mozilla/AutoRestore.h" #include "mozilla/BinarySearch.h" @@ -10726,6 +10726,54 @@ nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos) return NS_OK; } +static bool +IsPotentiallyScrollable(HTMLBodyElement* aBody) +{ + // An element is potentially scrollable if all of the following conditions are + // true: + + // The element has an associated CSS layout box. + nsIFrame* bodyFrame = aBody->GetPrimaryFrame(); + if (!bodyFrame) { + return false; + } + + // The element is not the HTML body element, or it is and the root element's + // used value of the overflow-x or overflow-y properties is not visible. + MOZ_ASSERT(aBody->GetParent() == aBody->OwnerDoc()->GetRootElement()); + nsIFrame* parentFrame = aBody->GetParent()->GetPrimaryFrame(); + if (parentFrame && + parentFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE && + parentFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) { + return false; + } + + // The element's used value of the overflow-x or overflow-y properties is not + // visible. + if (bodyFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE && + bodyFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) { + return false; + } + + return true; +} + +Element* +nsIDocument::GetScrollingElement() +{ + if (GetCompatibilityMode() == eCompatibility_NavQuirks) { + FlushPendingNotifications(Flush_Layout); + HTMLBodyElement* body = GetBodyElement(); + if (body && !IsPotentiallyScrollable(body)) { + return body; + } + + return nullptr; + } + + return GetRootElement(); +} + void nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv) { diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index dd349274f..e80ba6ab9 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -810,9 +810,6 @@ nsOuterWindowProxy::getOwnPropertyDescriptor(JSContext* cx, } // else fall through to js::Wrapper - // When we change this to always claim the property is configurable (bug - // 1178639), update the comments in nsOuterWindowProxy::defineProperty - // accordingly. return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc); } @@ -831,29 +828,6 @@ nsOuterWindowProxy::defineProperty(JSContext* cx, return result.failCantDefineWindowElement(); } -#ifndef RELEASE_BUILD // To be turned on in bug 1178638. - // For now, allow chrome code to define non-configurable properties - // on windows, until we sort out what exactly the addon SDK is - // doing. In the meantime, this still allows us to test web compat - // behavior. - if (desc.hasConfigurable() && !desc.configurable() && - !nsContentUtils::IsCallerChrome()) { - return ThrowErrorMessage(cx, MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW); - } - - // Note that if hasConfigurable() is false we do NOT want to - // setConfigurable(true). That would make this code: - // - // var x; - // window.x = 5; - // - // fail, because the JS engine ends up converting the assignment into a define - // with !hasConfigurable(), but the var actually declared a non-configurable - // property on our underlying Window object, so the set would fail if we - // forced setConfigurable(true) here. What we want to do instead is change - // getOwnPropertyDescriptor to always claim configurable. See bug 1178639. -#endif - return js::Wrapper::defineProperty(cx, proxy, id, desc, result); } diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index ecab86e3c..c74f96dbd 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -2529,6 +2529,8 @@ public: already_AddRefed CaretPositionFromPoint(float aX, float aY); + Element* GetScrollingElement(); + // QuerySelector and QuerySelectorAll already defined on nsINode nsINodeList* GetAnonymousNodes(Element& aElement); Element* GetAnonymousElementByAttribute(Element& aElement, diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index eeebbff4f..205bd1450 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -968,6 +968,12 @@ nsINode::CompareDocumentPosition(nsINode& aOtherNode) const nsIDOMNode::DOCUMENT_POSITION_CONTAINED_BY); } +bool +nsINode::IsSameNode(nsINode *other) +{ + return other == this; +} + bool nsINode::IsEqualNode(nsINode* aOther) { diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index fedfac9e5..91bbfdd0b 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -1764,6 +1764,7 @@ public: } nsINode* RemoveChild(nsINode& aChild, mozilla::ErrorResult& aError); already_AddRefed CloneNode(bool aDeep, mozilla::ErrorResult& aError); + bool IsSameNode(nsINode* aNode); bool IsEqualNode(nsINode* aNode); void GetNamespaceURI(nsAString& aNamespaceURI) const { diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index a89ce9e16..432b4fc33 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -1552,7 +1552,9 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, } nsAutoCString sourceMapURL; - rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), sourceMapURL); + rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("SourceMap"), sourceMapURL); + if (NS_FAILED(rv)) + rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), sourceMapURL); if (NS_SUCCEEDED(rv)) { aRequest->mHasSourceMapURL = true; aRequest->mSourceMapURL = NS_ConvertUTF8toUTF16(sourceMapURL); diff --git a/dom/html/HTMLSelectElement.cpp b/dom/html/HTMLSelectElement.cpp index 7243af841..824084fd3 100644 --- a/dom/html/HTMLSelectElement.cpp +++ b/dom/html/HTMLSelectElement.cpp @@ -1149,9 +1149,11 @@ HTMLSelectElement::SetValue(const nsAString& aValue) option->GetValue(optionVal); if (optionVal.Equals(aValue)) { SetSelectedIndexInternal(int32_t(i), true); - break; + return NS_OK; } } + // No matching option was found. + SetSelectedIndexInternal(-1, true); return NS_OK; } diff --git a/dom/html/nsTextEditorState.cpp b/dom/html/nsTextEditorState.cpp index c58fd2767..666c8e8c6 100644 --- a/dom/html/nsTextEditorState.cpp +++ b/dom/html/nsTextEditorState.cpp @@ -1384,19 +1384,16 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue) } } - if (shouldInitializeEditor) { - // Initialize the plaintext editor - nsCOMPtr textEditor(do_QueryInterface(newEditor)); - if (textEditor) { + // Initialize the plaintext editor + nsCOMPtr textEditor = do_QueryInterface(newEditor); + if (textEditor) { + if (shouldInitializeEditor) { // Set up wrapping textEditor->SetWrapColumn(GetWrapCols()); - - // Set max text field length - int32_t maxLength; - if (GetMaxLength(&maxLength)) { - textEditor->SetMaxTextLength(maxLength); - } } + + // Set max text field length + textEditor->SetMaxTextLength(GetMaxLength()); } nsCOMPtr content = do_QueryInterface(mTextCtrlElement); @@ -1840,22 +1837,22 @@ be called if @placeholder is the empty string when trimmed from line breaks"); return NS_OK; } -bool -nsTextEditorState::GetMaxLength(int32_t* aMaxLength) +int32_t +nsTextEditorState::GetMaxLength() { nsCOMPtr content = do_QueryInterface(mTextCtrlElement); nsGenericHTMLElement* element = nsGenericHTMLElement::FromContentOrNull(content); - NS_ENSURE_TRUE(element, false); + if (MOZ_UNLIKELY(!element)) { + return -1; + } const nsAttrValue* attr = element->GetParsedAttr(nsGkAtoms::maxlength); if (attr && attr->Type() == nsAttrValue::eInteger) { - *aMaxLength = attr->GetIntegerValue(); - - return true; + return attr->GetIntegerValue(); } - return false; + return -1; } void diff --git a/dom/html/nsTextEditorState.h b/dom/html/nsTextEditorState.h index 678557607..d7b7ea903 100644 --- a/dom/html/nsTextEditorState.h +++ b/dom/html/nsTextEditorState.h @@ -211,7 +211,7 @@ public: * @param aMaxLength the value of the max length attr * @returns false if attr not defined */ - bool GetMaxLength(int32_t* aMaxLength); + int32_t GetMaxLength(); void ClearValueCache() { mCachedValue.Truncate(); } diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index 5c216e977..f78005f3e 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -280,6 +280,8 @@ partial interface Document { Element? elementFromPoint (float x, float y); CaretPosition? caretPositionFromPoint (float x, float y); + + readonly attribute Element? scrollingElement; }; // http://dvcs.w3.org/hg/undomanager/raw-file/tip/undomanager.html diff --git a/dom/webidl/Node.webidl b/dom/webidl/Node.webidl index d85f2a6a5..5400fff21 100644 --- a/dom/webidl/Node.webidl +++ b/dom/webidl/Node.webidl @@ -70,6 +70,8 @@ interface Node : EventTarget { [Throws] Node cloneNode(optional boolean deep = false); [Pure] + boolean isSameNode(Node? node); + [Pure] boolean isEqualNode(Node? node); const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01; diff --git a/dom/webidl/URL.webidl b/dom/webidl/URL.webidl index fd577885d..5b7fa08f0 100644 --- a/dom/webidl/URL.webidl +++ b/dom/webidl/URL.webidl @@ -47,6 +47,8 @@ interface URL { readonly attribute URLSearchParams searchParams; [Throws=Workers] attribute USVString hash; + [Throws] + USVString toJSON(); }; partial interface URL { diff --git a/dom/workers/URL.h b/dom/workers/URL.h index fe7acc539..bbb11321e 100644 --- a/dom/workers/URL.h +++ b/dom/workers/URL.h @@ -119,6 +119,11 @@ public: GetHref(aRetval, aRv); } + void ToJSON(nsAString& aResult, ErrorResult& aRv) const + { + GetHref(aResult, aRv); + } + // IURLSearchParamsObserver void URLSearchParamsUpdated(URLSearchParams* aSearchParams) override; diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index cdbd9f76b..11a0832dc 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -680,7 +680,109 @@ js::obj_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, Value* vp) FromPropertyDescriptor(cx, desc, args.rval()); } -// ES6 draft rev27 (2014/08/24) 19.1.2.14 Object.keys(O) +enum EnumerableOwnPropertiesKind { + Keys, + Values, + KeysAndValues +}; + +// ES7 proposal 2015-12-14 +// http://tc39.github.io/proposal-object-values-entries/#EnumerableOwnProperties +static bool +EnumerableOwnProperties(JSContext* cx, const JS::CallArgs& args, EnumerableOwnPropertiesKind kind) +{ + // Step 1. (Step 1 of Object.{keys,values,entries}, really.) + RootedObject obj(cx, ToObject(cx, args.get(0))); + if (!obj) + return false; + + // Step 2. + AutoIdVector ids(cx); + if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &ids)) + return false; + + // Step 3. + AutoValueVector properties(cx); + size_t len = ids.length(); + if (!properties.resize(len)) + return false; + + RootedId id(cx); + RootedValue key(cx); + RootedValue value(cx); + RootedNativeObject nobj(cx); + if (obj->is()) + nobj = &obj->as(); + RootedShape shape(cx); + Rooted desc(cx); + + // Step 4. + size_t out = 0; + for (size_t i = 0; i < len; i++) { + id = ids[i]; + + // Step 4.a. (Symbols were filtered out in step 2.) + MOZ_ASSERT(!JSID_IS_SYMBOL(id)); + + if (kind != Values) { + if (!IdToStringOrSymbol(cx, id, &key)) + return false; + } + + // Step 4.a.i. + if (nobj) { + if (JSID_IS_INT(id) && nobj->containsDenseElement(JSID_TO_INT(id))) { + value = nobj->getDenseOrTypedArrayElement(JSID_TO_INT(id)); + } else { + shape = nobj->lookup(cx, id); + if (!shape || !(GetShapeAttributes(nobj, shape) & JSPROP_ENUMERATE)) + continue; + if (!shape->isAccessorShape()) { + if (!NativeGetExistingProperty(cx, nobj, nobj, shape, &value)) + return false; + } else if (!GetProperty(cx, obj, obj, id, &value)) { + return false; + } + } + } else { + if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) + return false; + + // Step 4.a.ii. (inverted.) + if (!desc.object() || !desc.enumerable()) + continue; + + // Step 4.a.ii.1. + // (Omitted because Object.keys doesn't use this implementation.) + + // Step 4.a.ii.2.a. + if (obj->isNative() && desc.hasValue()) + value = desc.value(); + else if (!GetProperty(cx, obj, obj, id, &value)) + return false; + } + + // Steps 4.a.ii.2.b-c. + if (kind == Values) + properties[out++].set(value); + else if (!NewValuePair(cx, key, value, properties[out++])) + return false; + } + + // Step 5. + // (Implemented in step 2.) + + // Step 3 of Object.{keys,values,entries} + JSObject* aobj = NewDenseCopiedArray(cx, out, properties.begin()); + if (!aobj) + return false; + + args.rval().setObject(*aobj); + return true; +} + +// ES7 proposal 2015-12-14 +// http://tc39.github.io/proposal-object-values-entries/#Object.keys static bool obj_keys(JSContext* cx, unsigned argc, Value* vp) { @@ -688,6 +790,24 @@ obj_keys(JSContext* cx, unsigned argc, Value* vp) return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY); } +// ES7 proposal 2015-12-14 +// http://tc39.github.io/proposal-object-values-entries/#Object.values +static bool +obj_values(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return EnumerableOwnProperties(cx, args, Values); +} + +// ES7 proposal 2015-12-14 +// http://tc39.github.io/proposal-object-values-entries/#Object.entries +static bool +obj_entries(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return EnumerableOwnProperties(cx, args, KeysAndValues); +} + /* ES6 draft 15.2.3.16 */ static bool obj_is(JSContext* cx, unsigned argc, Value* vp) @@ -1009,10 +1129,8 @@ static const JSFunctionSpec object_static_methods[] = { JS_FN("setPrototypeOf", obj_setPrototypeOf, 2, 0), JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2, 0), JS_FN("keys", obj_keys, 1, 0), -#ifndef RELEASE_BUILD - JS_SELF_HOSTED_FN("values", "ObjectValues", 1, JSPROP_DEFINE_LATE), - JS_SELF_HOSTED_FN("entries", "ObjectEntries", 1, JSPROP_DEFINE_LATE), -#endif + JS_FN("values", obj_values, 1, 0), + JS_FN("entries", obj_entries, 1, 0), JS_FN("is", obj_is, 2, 0), JS_FN("defineProperty", obj_defineProperty, 3, 0), JS_FN("defineProperties", obj_defineProperties, 2, 0), diff --git a/js/src/builtin/Object.js b/js/src/builtin/Object.js index db3e4aa67..150f1397a 100644 --- a/js/src/builtin/Object.js +++ b/js/src/builtin/Object.js @@ -133,49 +133,3 @@ function ObjectLookupGetter(name) { object = std_Reflect_getPrototypeOf(object); } while (object !== null); } - -// Draft proposal http://tc39.github.io/proposal-object-values-entries/#Object.values -function ObjectValues(O) { - // Steps 1-2. - var object = ToObject(O); - - // Steps 3-4. - // EnumerableOwnProperties is inlined here. - var keys = OwnPropertyKeys(object, JSITER_OWNONLY | JSITER_HIDDEN); - var values = []; - var valuesCount = 0; - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (!callFunction(std_Object_propertyIsEnumerable, object, key)) - continue; - - var value = object[key]; - _DefineDataProperty(values, valuesCount++, value); - } - - // Step 5. - return values; -} - -// Draft proposal http://tc39.github.io/proposal-object-values-entries/#Object.entries -function ObjectEntries(O) { - // Steps 1-2. - var object = ToObject(O); - - // Steps 3-4. - // EnumerableOwnProperties is inlined here. - var keys = OwnPropertyKeys(object, JSITER_OWNONLY | JSITER_HIDDEN); - var entries = []; - var entriesCount = 0; - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (!callFunction(std_Object_propertyIsEnumerable, object, key)) - continue; - - var value = object[key]; - _DefineDataProperty(entries, entriesCount++, [key, value]); - } - - // Step 5. - return entries; -} diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 23e2110db..4670b64af 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -609,6 +609,11 @@ class FullParseHandler return pn; } + ParseNode* newComprehensionBinding(ParseNode* kid) { + MOZ_ASSERT(kid->isKind(PNK_NAME)); + return new_(PNK_LET, JSOP_NOP, kid); + } + ParseNode* newForHead(ParseNodeKind kind, ParseNode* pn1, ParseNode* pn2, ParseNode* pn3, const TokenPos& pos) { @@ -616,6 +621,14 @@ class FullParseHandler return new_(kind, JSOP_NOP, pn1, pn2, pn3, pos); } + void initForLetBlock(ParseNode* forLetImpliedBlock, ParseNode* nestedForLoop) { + MOZ_ASSERT(forLetImpliedBlock->isKind(PNK_LEXICALSCOPE)); + MOZ_ASSERT(nestedForLoop->isKind(PNK_FOR)); + + forLetImpliedBlock->pn_expr = nestedForLoop; + forLetImpliedBlock->pn_pos = nestedForLoop->pn_pos; + } + ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* caseList) { TokenPos pos(begin, caseList->pn_pos.end); return new_(PNK_SWITCH, JSOP_NOP, pos, discriminant, caseList); @@ -798,28 +811,60 @@ class FullParseHandler return pn->pn_pos; } + bool isDeclarationKind(ParseNodeKind kind) { + return kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST; + } + ParseNode* newList(ParseNodeKind kind, JSOp op = JSOP_NOP) { - MOZ_ASSERT(kind != PNK_VAR); - return new_(kind, op, pos()); - } - ParseNode* newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) { - return new_(kind, op, TokenPos(begin, begin + 1)); - } - ParseNode* newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) { - MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET); + MOZ_ASSERT(!isDeclarationKind(kind)); return new_(kind, op, pos()); } - /* New list with one initial child node. kid must be non-null. */ + ParseNode* newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) { + MOZ_ASSERT(!isDeclarationKind(kind)); + return new_(kind, op, TokenPos(begin, begin + 1)); + } + ParseNode* newList(ParseNodeKind kind, ParseNode* kid, JSOp op = JSOP_NOP) { - MOZ_ASSERT(kind != PNK_VAR); + MOZ_ASSERT(!isDeclarationKind(kind)); return new_(kind, op, kid); } + + ParseNode* newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) { + MOZ_ASSERT(isDeclarationKind(kind)); + return new_(kind, op, pos()); + } + ParseNode* newDeclarationList(ParseNodeKind kind, ParseNode* kid, JSOp op = JSOP_NOP) { - MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET); + MOZ_ASSERT(isDeclarationKind(kind)); return new_(kind, op, kid); } + bool isDeclarationList(ParseNode* node) { + return isDeclarationKind(node->getKind()); + } + + bool declarationIsVar(ParseNode* node) { + MOZ_ASSERT(isDeclarationList(node)); + return node->isKind(PNK_VAR); + } + + bool declarationIsLet(ParseNode* node) { + MOZ_ASSERT(isDeclarationList(node)); + return node->isKind(PNK_LET); + } + + bool declarationIsConst(ParseNode* node) { + MOZ_ASSERT(isDeclarationList(node)); + return node->isKind(PNK_CONST); + } + + ParseNode* singleBindingFromDeclaration(ParseNode* decl) { + MOZ_ASSERT(isDeclarationList(decl)); + MOZ_ASSERT(decl->pn_count == 1); + return decl->pn_head; + } + ParseNode* newCatchList() { return new_(PNK_CATCHLIST, JSOP_NOP, pos()); } @@ -860,22 +905,30 @@ class FullParseHandler return pn->isConstant(); } - PropertyName* maybeUnparenthesizedName(ParseNode* pn) { - if (!pn->isInParens() && pn->isKind(PNK_NAME)) - return pn->pn_atom->asPropertyName(); - return nullptr; + bool isUnparenthesizedName(ParseNode* node) { + return node->isKind(PNK_NAME) && !node->isInParens(); } - PropertyName* maybeParenthesizedName(ParseNode* pn) { - if (pn->isInParens() && pn->isKind(PNK_NAME)) - return pn->pn_atom->asPropertyName(); - return nullptr; + bool isNameAnyParentheses(ParseNode* node) { + return node->isKind(PNK_NAME); } - PropertyName* maybeNameAnyParentheses(ParseNode* node) { - if (PropertyName* name = maybeUnparenthesizedName(node)) - return name; - return maybeParenthesizedName(node); + bool nameIsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this function on known names"); + + return node->pn_atom == cx->names().eval; + } + + const char* nameIsArgumentsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this function on known names"); + + if (nameIsEvalAnyParentheses(node, cx)) + return js_eval_str; + if (node->pn_atom == cx->names().arguments) + return js_arguments_str; + return nullptr; } bool isCall(ParseNode* pn) { diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 0dfeb0cc4..bd8b262aa 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -775,10 +775,6 @@ Parser::cloneParseTree(ParseNode* opn) return pn; } -template <> -ParseNode* -Parser::cloneLeftHandSide(ParseNode* opn); - /* * Used by Parser::cloneLeftHandSide to clone a default expression * in the form of @@ -891,6 +887,16 @@ Parser::cloneLeftHandSide(ParseNode* opn) return pn; } +template <> +SyntaxParseHandler::Node +Parser::cloneLeftHandSide(Node node) +{ + // See the comment in SyntaxParseHandler::singleBindingFromDeclaration for + // why this is okay. + MOZ_ASSERT(node == SyntaxParseHandler::NodeUnparenthesizedName); + return SyntaxParseHandler::NodeGeneric; +} + } /* namespace frontend */ } /* namespace js */ diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 764bf48b2..a612975e0 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -717,7 +717,6 @@ template Parser::~Parser() { MOZ_ASSERT(checkOptionsCalled); - alloc.release(tempPoolMark); /* @@ -1543,6 +1542,15 @@ struct BindData return isAnnexB_; } + // The BoundNames of LexicalDeclaration and ForDeclaration must not contain + // 'let'. (CatchParameter is the only lexical binding form without this + // restriction.) + bool mustNotBindLet() { + MOZ_ASSERT(isInitialized()); + return isConst_ || + (kind_ == LexicalBinding && letData_.overflow != JSMSG_TOO_MANY_CATCH_VARS); + } + const LetData& letData() { MOZ_ASSERT(kind_ == LexicalBinding); return letData_; @@ -3595,6 +3603,13 @@ Parser::bindLexical(BindData* data, return false; ExclusiveContext* cx = parser->context; + + // Most lexical declaration patterns can't bind the name 'let'. + if (data->mustNotBindLet() && name == cx->names().let) { + parser->report(ParseError, false, pn, JSMSG_LEXICAL_DECL_DEFINES_LET); + return false; + } + Rooted blockObj(cx, data->letData().blockObj); uint32_t index = StaticBlockObject::LOCAL_INDEX_LIMIT; @@ -3849,6 +3864,70 @@ Parser::AutoPushStmtInfoPC::makeInnermostLexicalScope(StaticBlockO return generateBlockId(); } +template +Parser::PossibleError::PossibleError(Parser& parser) + : parser_(parser) +{ + state_ = ErrorState::None; +} + +template +void +Parser::PossibleError::setPending(ParseReportKind kind, unsigned errorNumber, + bool strict) +{ + // If we report an error later, we'll do it from the position where we set + // the state to pending. + offset_ = parser_.pos().begin; + reportKind_ = kind; + strict_ = strict; + errorNumber_ = errorNumber; + state_ = ErrorState::Pending; +} + +template +void +Parser::PossibleError::setResolved() +{ + state_ = ErrorState::None; +} + +template +bool +Parser::PossibleError::hasError() +{ + return state_ == ErrorState::Pending; +} + +template +bool +Parser::PossibleError::checkForExprErrors() +{ + bool err = hasError(); + if (err) { + parser_.reportWithOffset(reportKind_, strict_, offset_, errorNumber_); + } + return !err; +} + +template +void +Parser::PossibleError::transferErrorTo(PossibleError* other) +{ + if (other) { + MOZ_ASSERT(this != other); + MOZ_ASSERT(!other->hasError()); + // We should never allow fields to be copied between instances + // that point to different underlying parsers. + MOZ_ASSERT(&parser_ == &other->parser_); + other->offset_ = offset_; + other->reportKind_ = reportKind_; + other->errorNumber_ = errorNumber_; + other->strict_ = strict_; + other->state_ = state_; + } +} + template static inline bool OuterLet(ParseContext* pc, StmtInfoPC* stmt, HandleAtom atom) @@ -4101,7 +4180,7 @@ Parser::checkDestructuringName(BindData* dat if (data) { // Destructuring patterns in declarations must only contain // unparenthesized names. - if (!handler.maybeUnparenthesizedName(expr)) { + if (!handler.isUnparenthesizedName(expr)) { report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME); return false; } @@ -4117,7 +4196,7 @@ Parser::checkDestructuringName(BindData* dat "function calls shouldn't be considered valid targets in " "destructuring patterns"); - if (handler.maybeNameAnyParentheses(expr)) { + if (handler.isNameAnyParentheses(expr)) { // The arguments/eval identifiers are simple in non-strict mode code. // Warn to discourage their use nonetheless. if (!reportIfArgumentsEvalTarget(expr)) @@ -4285,7 +4364,8 @@ Parser::destructuringExpr(YieldHandling yieldHandling, BindDatainDeclDestructuring = true; - Node pn = primaryExpr(yieldHandling, TripledotProhibited, tt); + Node pn = primaryExpr(yieldHandling, TripledotProhibited, + nullptr /* possibleError */, tt); pc->inDeclDestructuring = false; if (!pn) return null(); @@ -4432,6 +4512,296 @@ Parser::newBindingNode(PropertyName* name, bool functionScope, Var return newName(name); } +template +typename ParseHandler::Node +Parser::expressionAfterForInOrOf(ParseNodeKind forHeadKind, + YieldHandling yieldHandling) +{ + MOZ_ASSERT(forHeadKind == PNK_FORIN || forHeadKind == PNK_FOROF); + Node pn = forHeadKind == PNK_FOROF + ? assignExpr(InAllowed, yieldHandling, TripledotProhibited) + : expr(InAllowed, yieldHandling, TripledotProhibited); + return pn; +} + +template +typename ParseHandler::Node +Parser::declarationPattern(Node decl, TokenKind tt, BindData* data, + bool initialDeclaration, YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, + Node* forInOrOfExpression) +{ + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB) || + tokenStream.isCurrentTokenType(TOK_LC)); + + Node pattern; + { + pc->inDeclDestructuring = true; + + // No possible error is required because we already know we're + // destructuring. + pattern = primaryExpr(yieldHandling, TripledotProhibited, + nullptr /* possibleError */ , tt); + pc->inDeclDestructuring = false; + } + if (!pattern) + return null(); + + if (initialDeclaration && forHeadKind) { + bool isForIn, isForOf; + if (!matchInOrOf(&isForIn, &isForOf)) + return null(); + + if (isForIn) + *forHeadKind = PNK_FORIN; + else if (isForOf) + *forHeadKind = PNK_FOROF; + else + *forHeadKind = PNK_FORHEAD; + + if (*forHeadKind != PNK_FORHEAD) { + // |for (const ... in ...);| and |for (const ... of ...);| are + // syntax errors for now. We'll fix this in bug 449811. + if (handler.declarationIsConst(decl)) { + report(ParseError, false, pattern, JSMSG_BAD_CONST_DECL); + return null(); + } + + if (!checkDestructuringPattern(data, pattern)) + return null(); + + *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling); + if (!*forInOrOfExpression) + return null(); + + return pattern; + } + } + + // See comment below for bindBeforeInitializer in the code that + // handles the non-destructuring case. + bool bindBeforeInitializer = handler.declarationIsVar(decl); + if (bindBeforeInitializer) { + if (!checkDestructuringPattern(data, pattern)) + return null(); + } + + TokenKind token; + if (!tokenStream.getToken(&token, TokenStream::None)) + return null(); + + if (token != TOK_ASSIGN) { + report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_DECL); + return null(); + } + + Node init = assignExpr(forHeadKind ? InProhibited : InAllowed, + yieldHandling, TripledotProhibited); + if (!init) + return null(); + + if (forHeadKind) { + // For for(;;) declarations, consistency with |for (;| parsing requires + // that the ';' first be examined as Operand, even though absence of a + // binary operator (examined with modifier None) terminated |init|. + // For all other declarations, through ASI's infinite majesty, a next + // token on a new line would begin an expression. + tokenStream.addModifierException(TokenStream::OperandIsNone); + } + + if (!bindBeforeInitializer) { + if (!checkDestructuringPattern(data, pattern)) + return null(); + } + + return handler.newBinary(PNK_ASSIGN, pattern, init); +} + +template +bool +Parser::initializerInNameDeclaration(Node decl, Node binding, + Handle name, + BindData* data, + bool initialDeclaration, + YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, + Node* forInOrOfExpression) +{ + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN)); + + // Lexical bindings can't be accessed until initialized. A declaration + // of the form |let x = x| raises a ReferenceError, as the 'x' on the + // RHS accesses the binding before it's initialized. + // + // If we're not parsing a lexical declaration, bind the name now. + // Otherwise we must wait until after parsing the initializing + // assignment. + bool bindBeforeInitializer = handler.declarationIsVar(decl); + if (bindBeforeInitializer) { + if (!data->bind(name, this)) + return false; + } + + Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed, + yieldHandling, TripledotProhibited); + if (!initializer) + return false; + + bool performAssignment = true; + if (forHeadKind) { + if (initialDeclaration) { + bool isForIn, isForOf; + if (!matchInOrOf(&isForIn, &isForOf)) + return false; + + // An initialized declaration can't appear in a for-of: + // + // for (var/let/const x = ... of ...); // BAD + if (isForOf) { + report(ParseError, false, binding, JSMSG_BAD_FOR_LEFTSIDE); + return false; + } + + if (isForIn) { + // Lexical declarations in for-in loops can't be initialized: + // + // for (let/const x = ... in ...); // BAD + if (!handler.declarationIsVar(decl)) { + report(ParseError, false, binding, JSMSG_BAD_FOR_LEFTSIDE); + return false; + } + + // This leaves only initialized for-in |var| declarations. ES6 + // forbids these, yet they sadly still occur, rarely, on the + // web. *Don't* assign, and warn about this invalid syntax to + // incrementally move to ES6 semantics. + *forHeadKind = PNK_FORIN; + performAssignment = false; + if (!report(ParseWarning, pc->sc->strict(), initializer, + JSMSG_INVALID_FOR_IN_DECL_WITH_INIT)) + { + return false; + } + + *forInOrOfExpression = expressionAfterForInOrOf(PNK_FORIN, yieldHandling); + if (!*forInOrOfExpression) + return null(); + } else { + *forHeadKind = PNK_FORHEAD; + } + } + + if (*forHeadKind == PNK_FORHEAD) { + // Per Parser::forHeadStart, the semicolon in |for (;| is + // ultimately gotten as Operand. But initializer expressions + // terminate with the absence of an operator gotten as None, so we + // need an exception. + tokenStream.addModifierException(TokenStream::OperandIsNone); + } + } + + if (performAssignment) { + if (!bindBeforeInitializer && !data->bind(name, this)) + return false; + + if (!handler.finishInitializerAssignment(binding, initializer)) + return false; + } + + return true; +} + +template +typename ParseHandler::Node +Parser::declarationName(Node decl, TokenKind tt, BindData* data, + bool initialDeclaration, YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, Node* forInOrOfExpression) +{ + if (tt != TOK_NAME) { + // Anything other than TOK_YIELD or TOK_NAME is an error. + if (tt != TOK_YIELD) { + report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); + return null(); + } + + // TOK_YIELD is only okay if it's treated as a name. + if (!checkYieldNameValidity()) + return null(); + } + + RootedPropertyName name(context, tokenStream.currentName()); + Node binding = newBindingNode(name, handler.declarationIsVar(decl), HoistVars); + if (!binding) + return null(); + MOZ_ASSERT(data->isConst() == handler.declarationIsConst(decl)); + if (data->isConst()) + handler.setFlag(binding, PND_CONST); + data->setNameNode(binding); + + // The '=' context after a variable name in a declaration is an opportunity + // for ASI, and thus for the next token to start an ExpressionStatement: + // + // var foo // VariableDeclaration + // /bar/g; // ExpressionStatement + // + // Therefore get the token here as Operand. + bool matched; + if (!tokenStream.matchToken(&matched, TOK_ASSIGN, TokenStream::Operand)) + return null(); + + if (matched) { + if (!initializerInNameDeclaration(decl, binding, name, data, initialDeclaration, + yieldHandling, forHeadKind, forInOrOfExpression)) + { + return null(); + } + } else { + tokenStream.addModifierException(TokenStream::NoneIsOperand); + + bool constRequiringInitializer = handler.declarationIsConst(decl); + if (initialDeclaration && forHeadKind) { + bool isForIn, isForOf; + if (!matchInOrOf(&isForIn, &isForOf)) + return null(); + + if (isForIn || isForOf) { + // XXX Uncomment this when fixing bug 449811. Until then, + // |for (const ... in/of ...)| remains an error. + //constRequiringInitializer = false; + + *forHeadKind = isForIn ? PNK_FORIN : PNK_FOROF; + } else { + *forHeadKind = PNK_FORHEAD; + } + } + + if (constRequiringInitializer) { + report(ParseError, false, binding, JSMSG_BAD_CONST_DECL); + return null(); + } + + bool bindBeforeInitializer = handler.declarationIsVar(decl); + if (bindBeforeInitializer) { + if (!data->bind(name, this)) + return null(); + } + + if (forHeadKind && *forHeadKind != PNK_FORHEAD) { + *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling); + if (!*forInOrOfExpression) + return null(); + } + + if (!bindBeforeInitializer) { + if (!data->bind(name, this)) + return null(); + } + } + + handler.setLexicalDeclarationOp(binding, data->op()); + return binding; +} + /* * The 'blockObj' parameter is non-null when parsing the 'vars' in a let * expression, block statement, non-top-level let declaration in statement @@ -4439,18 +4809,14 @@ Parser::newBindingNode(PropertyName* name, bool functionScope, Var */ template typename ParseHandler::Node -Parser::variables(YieldHandling yieldHandling, - ParseNodeKind kind, - ForInitLocation location, - bool* psimple, StaticBlockObject* blockObj, VarContext varContext) +Parser::declarationList(YieldHandling yieldHandling, + ParseNodeKind kind, + StaticBlockObject* blockObj /* = nullptr */, + ParseNodeKind* forHeadKind /* = nullptr */, + Node* forInOrOfExpression /* = nullptr */) { - MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET); - - /* - * The simple flag is set if the declaration has the form 'var x', with - * only one variable declared and no initializer expression. - */ - MOZ_ASSERT_IF(psimple, *psimple); + MOZ_ASSERT(kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST); + MOZ_ASSERT_IF(blockObj != nullptr, kind == PNK_LET || kind == PNK_CONST); JSOp op; switch (kind) { @@ -4460,193 +4826,46 @@ Parser::variables(YieldHandling yieldHandling, default: MOZ_CRASH("unknown variable kind"); } - Node pn = handler.newDeclarationList(kind, op); - if (!pn) + Node decl = handler.newDeclarationList(kind, op); + if (!decl) return null(); BindData data(context); if (kind == PNK_VAR) data.initVar(op); else - data.initLexical(varContext, op, blockObj, JSMSG_TOO_MANY_LOCALS); + data.initLexical(HoistVars, op, blockObj, JSMSG_TOO_MANY_LOCALS); - bool first = true; - Node pn2; - while (true) { - do { - if (psimple && !first) - *psimple = false; - first = false; + bool matched; + bool initialDeclaration = true; + do { + MOZ_ASSERT_IF(!initialDeclaration && forHeadKind, + *forHeadKind == PNK_FORHEAD); - TokenKind tt; - if (!tokenStream.getToken(&tt)) - return null(); - if (tt == TOK_LB || tt == TOK_LC) { - if (psimple) - *psimple = false; + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); - pc->inDeclDestructuring = true; - pn2 = primaryExpr(yieldHandling, TripledotProhibited, tt); - pc->inDeclDestructuring = false; - if (!pn2) - return null(); + Node binding = (tt == TOK_LB || tt == TOK_LC) + ? declarationPattern(decl, tt, &data, initialDeclaration, yieldHandling, + forHeadKind, forInOrOfExpression) + : declarationName(decl, tt, &data, initialDeclaration, yieldHandling, + forHeadKind, forInOrOfExpression); + if (!binding) + return null(); - bool parsingForInOrOfInit = false; - if (location == InForInit) { - bool isForIn, isForOf; - if (!matchInOrOf(&isForIn, &isForOf)) - return null(); - parsingForInOrOfInit = isForIn || isForOf; - } + handler.addList(decl, binding); - // See comment below for bindBeforeInitializer in the code that - // handles the non-destructuring case. - bool bindBeforeInitializer = (kind != PNK_LET && kind != PNK_CONST) || - parsingForInOrOfInit; - if (bindBeforeInitializer && !checkDestructuringPattern(&data, pn2)) - return null(); + if (forHeadKind && *forHeadKind != PNK_FORHEAD) + break; - if (parsingForInOrOfInit) { - tokenStream.ungetToken(); - handler.addList(pn, pn2); - break; - } + initialDeclaration = false; - MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL); - - Node init = assignExpr(location == InForInit ? InProhibited : InAllowed, - yieldHandling, TripledotProhibited); - if (!init) - return null(); - - // Ban the nonsensical |for (var V = E1 in E2);| where V is a - // destructuring pattern. See bug 1164741 for background. - if (location == InForInit && kind == PNK_VAR) { - TokenKind afterInit; - if (!tokenStream.peekToken(&afterInit)) - return null(); - if (afterInit == TOK_IN) { - report(ParseError, false, init, JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT, - "in"); - return null(); - } - } - - if (!bindBeforeInitializer && !checkDestructuringPattern(&data, pn2)) - return null(); - - pn2 = handler.newBinary(PNK_ASSIGN, pn2, init); - if (!pn2) - return null(); - - handler.addList(pn, pn2); - break; - } - - if (tt != TOK_NAME) { - if (tt == TOK_YIELD) { - if (!checkYieldNameValidity()) - return null(); - } else { - report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); - return null(); - } - } - - RootedPropertyName name(context, tokenStream.currentName()); - pn2 = newBindingNode(name, kind == PNK_VAR, varContext); - if (!pn2) - return null(); - if (data.isConst()) - handler.setFlag(pn2, PND_CONST); - data.setNameNode(pn2); - - handler.addList(pn, pn2); - - bool matched; - // The '=' context after a variable name in a declaration is an - // opportunity for ASI, and thus for the next token to start an - // ExpressionStatement: - // - // var foo // VariableDeclaration - // /bar/g; // ExpressionStatement - // - // Therefore we must get the token here as Operand. - if (!tokenStream.matchToken(&matched, TOK_ASSIGN, TokenStream::Operand)) - return null(); - if (!matched) { - tokenStream.addModifierException(TokenStream::NoneIsOperand); - - if (data.isConst() && location == NotInForInit) { - report(ParseError, false, null(), JSMSG_BAD_CONST_DECL); - return null(); - } - - if (!data.bind(name, this)) - return null(); - } else { - if (psimple) - *psimple = false; - - // In ES6, lexical bindings may not be accessed until - // initialized. So a declaration of the form |let x = x| results - // in a ReferenceError, as the 'x' on the RHS is accessing the let - // binding before it is initialized. - // - // If we are not parsing a let declaration, bind the name - // now. Otherwise we must wait until after parsing the initializing - // assignment. - bool bindBeforeInitializer = kind != PNK_LET && kind != PNK_CONST; - if (bindBeforeInitializer && !data.bind(name, this)) - return null(); - - Node init = assignExpr(location == InForInit ? InProhibited : InAllowed, - yieldHandling, TripledotProhibited); - if (!init) - return null(); - - // Ignore an initializer if we have a for-in loop declaring a - // |var| with an initializer: |for (var v = ... in ...);|. - // Warn that this syntax is invalid so that developers looking - // in the console know to fix this. ES<6 permitted the - // initializer while ES6 doesn't; ignoring it seems the best - // way to incrementally move to ES6 semantics. - bool performAssignment = true; - if (location == InForInit && kind == PNK_VAR) { - TokenKind afterInit; - if (!tokenStream.peekToken(&afterInit)) - return null(); - if (afterInit == TOK_IN) { - performAssignment = false; - if (!report(ParseWarning, pc->sc->strict(), init, - JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT, "in")) - { - return null(); - } - } - } - - if (performAssignment) { - if (!bindBeforeInitializer && !data.bind(name, this)) - return null(); - - if (!handler.finishInitializerAssignment(pn2, init)) - return null(); - } - } - - handler.setLexicalDeclarationOp(pn2, data.op()); - handler.setEndPosition(pn, pn2); - } while (false); - - bool matched; if (!tokenStream.matchToken(&matched, TOK_COMMA)) return null(); - if (!matched) - break; - } + } while (matched); - return pn; + return decl; } template <> @@ -4760,6 +4979,21 @@ CurrentLexicalStaticBlock(ParseContext* pc) return nullptr; } +template <> +void +Parser::assertCurrentLexicalStaticBlockIs(ParseContext* pc, + Handle blockObj) +{ +} + +template <> +void +Parser::assertCurrentLexicalStaticBlockIs(ParseContext* pc, + Handle blockObj) +{ + MOZ_ASSERT(CurrentLexicalStaticBlock(pc) == blockObj); +} + template <> bool Parser::prepareAndBindInitializedLexicalWithNode(HandlePropertyName name, @@ -4822,12 +5056,13 @@ Parser::lexicalDeclaration(YieldHandling yieldHandling, bool i * * See 8.1.1.1.6 and the note in 13.2.1. */ - ParseNodeKind kind = isConst ? PNK_CONST : PNK_LET; - ParseNode* pn = variables(yieldHandling, kind, NotInForInit, - nullptr, CurrentLexicalStaticBlock(pc), HoistVars); - if (!pn) + ParseNode* decl = + declarationList(yieldHandling, isConst ? PNK_CONST : PNK_LET, + CurrentLexicalStaticBlock(pc)); + if (!decl || !MatchOrInsertSemicolonAfterExpression(tokenStream)) return null(); - return MatchOrInsertSemicolonAfterExpression(tokenStream) ? pn : nullptr; + + return decl; } template <> @@ -5282,7 +5517,7 @@ Parser::exportDeclaration() } case TOK_VAR: - kid = variables(YieldIsName, PNK_VAR, NotInForInit); + kid = declarationList(YieldIsName, PNK_VAR); if (!kid) return null(); if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) @@ -5477,6 +5712,7 @@ Parser::matchInOrOf(bool* isForInp, bool* isForOfp) TokenKind tt; if (!tokenStream.getToken(&tt)) return false; + *isForInp = tt == TOK_IN; *isForOfp = tt == TOK_NAME && tokenStream.currentToken().name() == context->names().of; if (!*isForInp && !*isForOfp) { @@ -5485,51 +5721,179 @@ Parser::matchInOrOf(bool* isForInp, bool* isForOfp) if (tt == TOK_NAME && !checkUnescapedName()) return false; } + + MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp); return true; } -template <> +template bool -Parser::isValidForStatementLHS(ParseNode* pn1, JSVersion version, - bool isForDecl, bool isForEach, - ParseNodeKind headKind) +Parser::validateForInOrOfLHSExpression(Node target) { - if (isForDecl) - return pn1->pn_count < 2 && !pn1->isKind(PNK_CONST); + if (handler.isUnparenthesizedDestructuringPattern(target)) + return checkDestructuringPattern(nullptr, target); - switch (pn1->getKind()) { - case PNK_ARRAY: - case PNK_CALL: - case PNK_DOT: - case PNK_ELEM: - case PNK_NAME: - case PNK_OBJECT: + // All other permitted targets are simple. + if (!reportIfNotValidSimpleAssignmentTarget(target, ForInOrOfTarget)) + return false; + + if (handler.isPropertyAccess(target)) return true; - default: + if (handler.isNameAnyParentheses(target)) { + // The arguments/eval identifiers are simple in non-strict mode code, + // but warn to discourage use nonetheless. + if (!reportIfArgumentsEvalTarget(target)) + return false; + + handler.adjustGetToSet(target); + handler.markAsAssigned(target); + return true; + } + + if (handler.isFunctionCall(target)) + return makeSetCall(target, JSMSG_BAD_FOR_LEFTSIDE); + + report(ParseError, false, target, JSMSG_BAD_FOR_LEFTSIDE); + return false; +} + +template +bool +Parser::forHeadStart(YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, + Node* forInitialPart, + Maybe& letStmt, + MutableHandle blockObj, + Node* forLetImpliedBlock, + Node* forInOrOfExpression) +{ + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP)); + + TokenKind tt; + if (!tokenStream.peekToken(&tt, TokenStream::Operand)) + return null(); + + // Super-duper easy case: |for (;| is a C-style for-loop with no init + // component. + if (tt == TOK_SEMI) { + *forInitialPart = null(); + *forHeadKind = PNK_FORHEAD; + return true; + } + + // Parsing after |for (var| is also relatively simple (from this method's + // point of view). No block-related work complicates matters, so delegate + // to Parser::declaration. + if (tt == TOK_VAR) { + tokenStream.consumeKnownToken(tt, TokenStream::Operand); + + // Pass null for block object because |var| declarations don't use one. + *forInitialPart = declarationList(yieldHandling, PNK_VAR, nullptr, forHeadKind, + forInOrOfExpression); + return *forInitialPart != null(); + } + + // Otherwise we have a lexical declaration or an expression. + + // For-in loop backwards compatibility requires that |let| starting a + // for-loop that's not a (new to ES6) for-of loop, in non-strict mode code, + // parse as an identifier. (|let| in for-of is always a declaration.) + // Thus we must can't just sniff out TOK_CONST/TOK_LET here. :-( + bool parsingLexicalDeclaration = false; + bool letIsIdentifier = false; + if (tt == TOK_LET || tt == TOK_CONST) { + parsingLexicalDeclaration = true; + tokenStream.consumeKnownToken(tt, TokenStream::Operand); + } else if (tt == TOK_NAME && tokenStream.nextName() == context->names().let) { + // Check for the backwards-compatibility corner case in sloppy + // mode like |for (let in e)| where the 'let' token should be + // parsed as an identifier. + if (!peekShouldParseLetDeclaration(&parsingLexicalDeclaration, TokenStream::Operand)) + return false; + + letIsIdentifier = !parsingLexicalDeclaration; + } + + if (parsingLexicalDeclaration) { + handler.disableSyntaxParser(); + + // Set up the block chain for the lexical declaration. + blockObj.set(StaticBlockObject::create(context)); + if (!blockObj) + return false; + blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope()); + letStmt.emplace(*this, StmtType::BLOCK); + *forLetImpliedBlock = pushLetScope(blockObj, *letStmt); + if (!*forLetImpliedBlock) + return false; + (*letStmt)->isForLetBlock = true; + + assertCurrentLexicalStaticBlockIs(pc, blockObj); + *forInitialPart = declarationList(yieldHandling, tt == TOK_CONST ? PNK_CONST : PNK_LET, + blockObj, forHeadKind, forInOrOfExpression); + return *forInitialPart != null(); + } + + // Finally, handle for-loops that start with expressions. Pass + // |InProhibited| so that |in| isn't parsed in a RelationalExpression as a + // binary operator. |in| makes it a for-in loop, *not* an |in| expression. + *forInitialPart = expr(InProhibited, yieldHandling, TripledotProhibited); + if (!*forInitialPart) + return false; + + bool isForIn, isForOf; + if (!matchInOrOf(&isForIn, &isForOf)) + return false; + + // If we don't encounter 'in'/'of', we have a for(;;) loop. We've handled + // the init expression; the caller handles the rest. Allow the Operand + // modifier when regetting: Operand must be used to examine the ';' in + // |for (;|, and our caller handles this case and that. + if (!isForIn && !isForOf) { + *forHeadKind = PNK_FORHEAD; + tokenStream.addModifierException(TokenStream::OperandIsNone); + return true; + } + + MOZ_ASSERT(isForIn != isForOf); + + // In a for-of loop, 'let' that starts the loop head is a |let| keyword, + // per the [lookahead ≠ let] restriction on the LeftHandSideExpression + // variant of such loops. Expressions that start with |let| can't be used + // here. + // + // var let = {}; + // for (let.prop of [1]) // BAD + // break; + // + // See ES6 13.7. + if (isForOf && letIsIdentifier) { + report(ParseError, false, *forInitialPart, JSMSG_LET_STARTING_FOROF_LHS); return false; } + + *forHeadKind = isForIn ? PNK_FORIN : PNK_FOROF; + + if (!validateForInOrOfLHSExpression(*forInitialPart)) + return false; + + // Finally, parse the iterated expression, making the for-loop's closing + // ')' the next token. + *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling); + return *forInOrOfExpression != null(); } -template <> -bool -Parser::checkForHeadConstInitializers(ParseNode* pn1) +template +typename ParseHandler::Node +Parser::cloneForInOrOfDeclarationForAssignment(Node decl) { - if (!pn1->isKind(PNK_CONST)) - return true; - - for (ParseNode* assign = pn1->pn_head; assign; assign = assign->pn_next) { - MOZ_ASSERT(assign->isKind(PNK_ASSIGN) || assign->isKind(PNK_NAME)); - if (assign->isKind(PNK_NAME) && !assign->isAssigned()) - return false; - // PNK_ASSIGN nodes (destructuring assignment) are always assignments. - } - return true; + return cloneLeftHandSide(handler.singleBindingFromDeclaration(decl)); } -template <> -ParseNode* -Parser::forStatement(YieldHandling yieldHandling) +template +typename ParseHandler::Node +Parser::forStatement(YieldHandling yieldHandling) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); uint32_t begin = pos().begin; @@ -5546,7 +5910,7 @@ Parser::forStatement(YieldHandling yieldHandling) if (matched) { iflags = JSITER_FOREACH; isForEach = true; - addTelemetry(JSCompartment::DeprecatedForEach); + //addTelemetry(JSCompartment::DeprecatedForEach); if (versionNumber() < JSVERSION_LATEST) { if (!report(ParseWarning, pc->sc->strict(), null(), JSMSG_DEPRECATED_FOR_EACH)) return null(); @@ -5556,8 +5920,11 @@ Parser::forStatement(YieldHandling yieldHandling) MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); - // True if we have 'for (var/let/const ...)'. - bool isForDecl = false; + // PNK_FORHEAD, PNK_FORIN, or PNK_FOROF depending on the loop type. + ParseNodeKind headKind; + + // |x| in either |for (x; ...; ...)| or |for (x in/of ...)|. + Node startNode; // The next three variables are used to implement `for (let/const ...)`. // @@ -5571,6 +5938,9 @@ Parser::forStatement(YieldHandling yieldHandling) // blockObj is the static block object for the implicit block scope. RootedStaticBlockObject blockObj(context); + // The PNK_LEXICALSCOPE node containing blockObj's ObjectBox. + Node forLetImpliedBlock = null(); + // letStmt is the BLOCK StmtInfo for the implicit block. // // Caution: `letStmt.emplace()` creates some Rooted objects. Rooteds must @@ -5579,116 +5949,41 @@ Parser::forStatement(YieldHandling yieldHandling) // assertions. Maybe letStmt; - // The PNK_LEXICALSCOPE node containing blockObj's ObjectBox. - ParseNode* forLetImpliedBlock = nullptr; + // The expression being iterated over, for for-in/of loops only. Unused + // for for(;;) loops. + Node iteratedExpr; - // True if a 'let' token at the head is parsed as an identifier instead of - // as starting a declaration. - bool letIsIdentifier = false; - - // Set to 'x' in 'for (x; ...; ...)' or 'for (x in ...)'. - ParseNode* pn1; - - TokenStream::Modifier modifier = TokenStream::Operand; + // Parse the entirety of the loop-head for a for-in/of loop (so the next + // token is the closing ')'): + // + // for (... in/of ...) ... + // ^next token + // + // ...OR, parse up to the first ';' in a C-style for-loop: + // + // for (...; ...; ...) ... + // ^next token + // + // In either case the subsequent token can be consistently accessed using + // TokenStream::None semantics. + if (!forHeadStart(yieldHandling, &headKind, &startNode, letStmt, &blockObj, + &forLetImpliedBlock, &iteratedExpr)) { - TokenKind tt; - if (!tokenStream.peekToken(&tt, TokenStream::Operand)) - return null(); - if (tt == TOK_SEMI) { - pn1 = nullptr; - } else { - // Set pn1 to a variable list or an initializing expression. - // - // Pass |InForInit| to Parser::variables when parsing declarations - // to trigger |for|-specific parsing for that one position. In a - // normal variable declaration, any initializer may be an |in| - // expression. But for declarations at the start of a for-loop - // head, initializers can't contain |in|. (Such syntax conflicts - // with ES5's |for (var i = 0 in foo)| syntax, removed in ES6, that - // we "support" by ignoring the |= 0|.) - if (tt == TOK_VAR) { - isForDecl = true; - tokenStream.consumeKnownToken(tt, TokenStream::Operand); - pn1 = variables(yieldHandling, PNK_VAR, InForInit); - } else if (tt == TOK_LET || tt == TOK_CONST || - (tt == TOK_NAME && tokenStream.nextName() == context->names().let)) { - handler.disableSyntaxParser(); - - // Check for the backwards-compatibility corner case in sloppy - // mode like |for (let in e)| where the 'let' token should be - // parsed as an identifier. - bool parseDecl; - if (tt == TOK_NAME) { - if (!peekShouldParseLetDeclaration(&parseDecl, TokenStream::Operand)) - return null(); - letIsIdentifier = !parseDecl; - } else { - parseDecl = true; - tokenStream.consumeKnownToken(tt, TokenStream::Operand); - } - - if (parseDecl) { - bool constDecl = tt == TOK_CONST; - isForDecl = true; - - // Initialize the enclosing scope manually for the call to - // |variables| below. - blockObj = StaticBlockObject::create(context); - if (!blockObj) - return null(); - blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope()); - letStmt.emplace(*this, StmtType::BLOCK); - forLetImpliedBlock = pushLetScope(blockObj, *letStmt); - if (!forLetImpliedBlock) - return null(); - (*letStmt)->isForLetBlock = true; - - MOZ_ASSERT(CurrentLexicalStaticBlock(pc) == blockObj); - pn1 = variables(yieldHandling, constDecl ? PNK_CONST : PNK_LET, InForInit, - nullptr, blockObj, HoistVars); - } else { - pn1 = expr(InProhibited, yieldHandling, TripledotProhibited); - } - } else { - // Pass |InProhibited| when parsing an expression so that |in| - // isn't parsed in a RelationalExpression as a binary operator. - // In this context, |in| is part of a for-in loop -- *not* part - // of a binary expression. - pn1 = expr(InProhibited, yieldHandling, TripledotProhibited); - } - if (!pn1) - return null(); - modifier = TokenStream::None; - } + return null(); } - MOZ_ASSERT_IF(isForDecl, pn1->isArity(PN_LIST)); - MOZ_ASSERT(letStmt.isSome() == (isForDecl && (pn1->isOp(JSOP_DEFLET) || pn1->isOp(JSOP_DEFCONST)))); - - // If there's an |in| keyword here, it's a for-in loop, by dint of careful - // parsing of |pn1|. - ParseNode* pn2; /* forHead->pn_kid2 */ - ParseNode* pn3; /* forHead->pn_kid3 */ - ParseNodeKind headKind = PNK_FORHEAD; - if (pn1) { - bool isForIn, isForOf; - if (!matchInOrOf(&isForIn, &isForOf)) - return null(); - - // In for-in loops, a 'let' token may be used as an identifier for - // backwards-compatibility reasons, e.g., |for (let in e)|. In for-of - // loops, a 'let' token is never parsed as an identifier. Forbid - // trying to parse a for-of loop if we have parsed a 'let' token as an - // identifier above. - // - // See ES6 13.7.5.1. - if (isForIn) - headKind = PNK_FORIN; - else if (isForOf && !letIsIdentifier) - headKind = PNK_FOROF; - } + MOZ_ASSERT(headKind == PNK_FORIN || headKind == PNK_FOROF || headKind == PNK_FORHEAD); + Node pn1; + Node pn2; + Node pn3; + TokenStream::Modifier modifier; if (headKind == PNK_FOROF || headKind == PNK_FORIN) { + // |target| is the LeftHandSideExpression or declaration to which the + // per-iteration value (an arbitrary value exposed by the iteration + // protocol, or a string naming a property) is assigned. + Node target = startNode; + /* * Parse the rest of the for/in or for/of head. * @@ -5700,7 +5995,7 @@ Parser::forStatement(YieldHandling yieldHandling) if (headKind == PNK_FOROF) { forStmt->type = StmtType::FOR_OF_LOOP; if (isForEach) { - report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP); + report(ParseError, false, startNode, JSMSG_BAD_FOR_EACH_LOOP); return null(); } } else { @@ -5708,270 +6003,112 @@ Parser::forStatement(YieldHandling yieldHandling) iflags |= JSITER_ENUMERATE; } - /* Check that the left side of the 'in' or 'of' is valid. */ - if (!isValidForStatementLHS(pn1, versionNumber(), isForDecl, isForEach, headKind)) { - report(ParseError, false, pn1, JSMSG_BAD_FOR_LEFTSIDE); - return null(); - } - /* * After the following if-else, pn2 will point to the name or * destructuring pattern on in's left. pn1 will point to the decl, if * any, else nullptr. Note that the "declaration with initializer" case * rewrites the loop-head, moving the decl and setting pn1 to nullptr. */ - if (isForDecl) { - pn2 = pn1->pn_head; - if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr()) || pn2->isKind(PNK_ASSIGN)) { - MOZ_ASSERT(!(headKind == PNK_FORIN && pn1->isKind(PNK_VAR)), - "Parser::variables should have ignored the " - "initializer in the ES5-sanctioned, ES6-prohibited " - "|for (var ... = ... in ...)| syntax"); + if (handler.isDeclarationList(target)) { + pn1 = target; - // Otherwise, this bizarre |for (const/let x = ... in/of ...)| - // loop isn't valid ES6 and has never been permitted in - // SpiderMonkey. - report(ParseError, false, pn2, JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT, - headKind == PNK_FOROF ? "of" : "in"); + // Make a copy of the declaration that can be passed to + // BytecodeEmitter::emitAssignment. + pn2 = cloneForInOrOfDeclarationForAssignment(target); + if (!pn2) return null(); - } } else { - /* Not a declaration. */ MOZ_ASSERT(!letStmt); - pn2 = pn1; - pn1 = nullptr; + pn1 = null(); + pn2 = target; if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment)) return null(); } - pn3 = (headKind == PNK_FOROF) - ? assignExpr(InAllowed, yieldHandling, TripledotProhibited) - : expr(InAllowed, yieldHandling, TripledotProhibited); - if (!pn3) - return null(); + pn3 = iteratedExpr; + + if (handler.isNameAnyParentheses(pn2)) { + // Beware 'for (arguments in ...)' with or without a 'var'. + handler.markAsAssigned(pn2); + } + + // Parser::declaration consumed everything up to the closing ')'. That + // token follows an {Assignment,}Expression, so the next token must be + // consumed as if an operator continued the expression, i.e. as None. modifier = TokenStream::None; - - if (isForDecl) { - /* - * pn2 is part of a declaration. Make a copy that can be passed to - * BytecodeEmitter::emitAssignment. - */ - pn2 = cloneLeftHandSide(pn2); - if (!pn2) - return null(); - } - - ParseNodeKind kind2 = pn2->getKind(); - MOZ_ASSERT(kind2 != PNK_ASSIGN, "forStatement TOK_ASSIGN"); - - if (kind2 == PNK_NAME) { - /* Beware 'for (arguments in ...)' with or without a 'var'. */ - pn2->markAsAssigned(); - } } else { + Node init = startNode; + if (isForEach) { reportWithOffset(ParseError, false, begin, JSMSG_BAD_FOR_EACH_LOOP); return null(); } - MOZ_ASSERT(headKind == PNK_FORHEAD); + // Look for an operand: |for (;| means we might have already examined + // this semicolon with that modifier. + MUST_MATCH_TOKEN_MOD(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_INIT); - if (letStmt) { - // Ensure here that the previously-unchecked assignment mandate for - // const declarations holds. - if (!checkForHeadConstInitializers(pn1)) { - report(ParseError, false, nullptr, JSMSG_BAD_CONST_DECL); - return null(); - } - } - - /* Parse the loop condition or null into pn2. */ - MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_INIT); TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::Operand)) return null(); + + Node test; + TokenStream::Modifier mod; if (tt == TOK_SEMI) { - pn2 = nullptr; - modifier = TokenStream::Operand; + test = null(); + mod = TokenStream::Operand; } else { - pn2 = expr(InAllowed, yieldHandling, TripledotProhibited); - if (!pn2) + test = expr(InAllowed, yieldHandling, TripledotProhibited); + if (!test) return null(); - modifier = TokenStream::None; + mod = TokenStream::None; } - /* Parse the update expression or null into pn3. */ - MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_COND); + MUST_MATCH_TOKEN_MOD(TOK_SEMI, mod, JSMSG_SEMI_AFTER_FOR_COND); + if (!tokenStream.peekToken(&tt, TokenStream::Operand)) return null(); + + Node update; if (tt == TOK_RP) { - pn3 = nullptr; - modifier = TokenStream::Operand; + update = null(); + mod = TokenStream::Operand; } else { - pn3 = expr(InAllowed, yieldHandling, TripledotProhibited); - if (!pn3) + update = expr(InAllowed, yieldHandling, TripledotProhibited); + if (!update) return null(); - modifier = TokenStream::None; + mod = TokenStream::None; } + + modifier = mod; + pn1 = init; + pn2 = test; + pn3 = update; } MUST_MATCH_TOKEN_MOD(TOK_RP, modifier, JSMSG_PAREN_AFTER_FOR_CTRL); TokenPos headPos(begin, pos().end); - ParseNode* forHead = handler.newForHead(headKind, pn1, pn2, pn3, headPos); + Node forHead = handler.newForHead(headKind, pn1, pn2, pn3, headPos); if (!forHead) return null(); - /* Parse the loop body. */ - ParseNode* body = statement(yieldHandling); + Node body = statement(yieldHandling); if (!body) return null(); - ParseNode* forLoop = handler.newForStatement(begin, forHead, body, iflags); + Node forLoop = handler.newForStatement(begin, forHead, body, iflags); if (!forLoop) return null(); if (forLetImpliedBlock) { - forLetImpliedBlock->pn_expr = forLoop; - forLetImpliedBlock->pn_pos = forLoop->pn_pos; + handler.initForLetBlock(forLetImpliedBlock, forLoop); return forLetImpliedBlock; } return forLoop; } -template <> -SyntaxParseHandler::Node -Parser::forStatement(YieldHandling yieldHandling) -{ - /* - * 'for' statement parsing is fantastically complicated and requires being - * able to inspect the parse tree for previous parts of the 'for'. Syntax - * parsing of 'for' statements is thus done separately, and only handles - * the types of 'for' statements likely to be seen in web content. - */ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); - - AutoPushStmtInfoPC forStmt(*this, StmtType::FOR_LOOP); - - /* Don't parse 'for each' loops. */ - if (allowsForEachIn()) { - TokenKind tt; - if (!tokenStream.peekToken(&tt)) - return null(); - // Not all "yield" tokens are names, but the ones that aren't names are - // invalid in this context anyway. - if (tt == TOK_NAME || tt == TOK_YIELD) { - JS_ALWAYS_FALSE(abortIfSyntaxParser()); - return null(); - } - } - - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); - - /* True if we have 'for (var ...)'. */ - bool isForDecl = false; - bool simpleForDecl = true; - - /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */ - Node lhsNode; - - TokenStream::Modifier modifier = TokenStream::Operand; - { - TokenKind tt; - if (!tokenStream.peekToken(&tt, TokenStream::Operand)) - return null(); - if (tt == TOK_SEMI) { - lhsNode = null(); - } else { - /* Set lhsNode to a var list or an initializing expression. */ - if (tt == TOK_VAR) { - isForDecl = true; - tokenStream.consumeKnownToken(tt, TokenStream::Operand); - lhsNode = variables(yieldHandling, PNK_VAR, InForInit, &simpleForDecl); - } else if (tt == TOK_CONST || tt == TOK_LET || - (tt == TOK_NAME && tokenStream.nextName() == context->names().let)) - { - JS_ALWAYS_FALSE(abortIfSyntaxParser()); - return null(); - } else { - lhsNode = expr(InProhibited, yieldHandling, TripledotProhibited); - } - if (!lhsNode) - return null(); - modifier = TokenStream::None; - } - } - - // If there's an |in| keyword here, it's a for-in loop, by dint of careful - // parsing of |pn1|. - bool isForIn = false, isForOf = false; - if (lhsNode) { - if (!matchInOrOf(&isForIn, &isForOf)) - return null(); - } - if (isForIn || isForOf) { - /* Parse the rest of the for/in or for/of head. */ - forStmt->type = isForOf ? StmtType::FOR_OF_LOOP : StmtType::FOR_IN_LOOP; - - /* Check that the left side of the 'in' or 'of' is valid. */ - if (!isForDecl && - !handler.maybeNameAnyParentheses(lhsNode) && - !handler.isPropertyAccess(lhsNode)) - { - JS_ALWAYS_FALSE(abortIfSyntaxParser()); - return null(); - } - - if (!simpleForDecl) { - JS_ALWAYS_FALSE(abortIfSyntaxParser()); - return null(); - } - - if (!isForDecl && !checkAndMarkAsAssignmentLhs(lhsNode, PlainAssignment)) - return null(); - - if (!(isForIn - ? expr(InAllowed, yieldHandling, TripledotProhibited) - : assignExpr(InAllowed, yieldHandling, TripledotProhibited))) - { - return null(); - } - modifier = TokenStream::None; - } else { - /* Parse the loop condition or null. */ - MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_INIT); - TokenKind tt; - if (!tokenStream.peekToken(&tt, TokenStream::Operand)) - return null(); - modifier = TokenStream::Operand; - if (tt != TOK_SEMI) { - if (!expr(InAllowed, yieldHandling, TripledotProhibited)) - return null(); - modifier = TokenStream::None; - } - - /* Parse the update expression or null. */ - MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_COND); - if (!tokenStream.peekToken(&tt, TokenStream::Operand)) - return null(); - modifier = TokenStream::Operand; - if (tt != TOK_RP) { - if (!expr(InAllowed, yieldHandling, TripledotProhibited)) - return null(); - modifier = TokenStream::None; - } - } - - MUST_MATCH_TOKEN_MOD(TOK_RP, modifier, JSMSG_PAREN_AFTER_FOR_CTRL); - - /* Parse the loop body. */ - if (!statement(yieldHandling)) - return null(); - - return SyntaxParseHandler::NodeGeneric; -} - template typename ParseHandler::Node Parser::switchStatement(YieldHandling yieldHandling) @@ -6591,7 +6728,6 @@ Parser::tryStatement(YieldHandling yieldHandling) if (!catchName) return null(); break; - case TOK_YIELD: if (yieldHandling == YieldIsKeyword) { report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield"); @@ -6955,25 +7091,36 @@ bool Parser::shouldParseLetDeclaration(bool* parseDeclOut) { TokenKind tt; - *parseDeclOut = false; - if (!tokenStream.peekToken(&tt)) return false; - switch (tt) { - case TOK_NAME: - // |let let| is disallowed per ES6 13.3.1.1. - *parseDeclOut = tokenStream.nextName() != context->names().let; - break; - - case TOK_LC: - case TOK_LB: - // |let {| and |let [| are destructuring declarations. + if (tt == TOK_NAME) { + // |let| followed by a name is a lexical declaration. This is so even + // if the name is on a new line. ASI applies *only* if an offending + // token not allowed by the grammar is encountered, and there's no + // [no LineTerminator here] restriction in LexicalDeclaration or + // ForDeclaration forbidding a line break. + // + // It's a tricky point, but this is true *even if* the name is "let", a + // name that can't be bound by LexicalDeclaration or ForDeclaration. + // Per ES6 5.3, static semantics early errors are validated *after* + // determining productions matching the source text. So in this + // example: + // + // let // ASI opportunity...except not + // let; + // + // the text matches LexicalDeclaration. *Then* static semantics in + // ES6 13.3.1.1 (corresponding to the LexicalDeclaration production + // just chosen), per ES6 5.3, are validated to recognize the Script as + // invalid. It can't be evaluated, so a SyntaxError is thrown. *parseDeclOut = true; - break; - - default: - break; + } else if (tt == TOK_LB || tt == TOK_LC) { + *parseDeclOut = true; + } else { + // Whatever we have isn't a declaration. Either it's an expression, or + // it's invalid: expression-parsing code will decide. + *parseDeclOut = false; } return true; @@ -7026,7 +7173,7 @@ Parser::statement(YieldHandling yieldHandling, bool canHaveDirecti // VariableStatement[?Yield] case TOK_VAR: { - Node pn = variables(yieldHandling, PNK_VAR, NotInForInit); + Node pn = declarationList(yieldHandling, PNK_VAR); if (!pn) return null(); if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) @@ -7072,17 +7219,20 @@ Parser::statement(YieldHandling yieldHandling, bool canHaveDirecti } case TOK_NAME: { - // 'let' is a contextual keyword in sloppy node. In strict mode, it is - // always lexed as TOK_LET except following case: + // 'let' is a contextual keyword outside strict mode. In strict mode + // it's always tokenized as TOK_LET except in this one weird case: // - // "use strict" - // let a = 1; + // "use strict" // ExpressionStatement, terminated by ASI + // let a = 1; // LexicalDeclaration // - // There 'let' is lexed as TOK_NAME before parsing directive. + // We can't apply strict mode until we know "use strict" is the entire + // statement, but we can't know "use strict" is the entire statement + // until we see the next token. So 'let' is still TOK_NAME here. if (tokenStream.currentName() == context->names().let) { bool parseDecl; if (!shouldParseLetDeclaration(&parseDecl)) return null(); + if (parseDecl) return lexicalDeclaration(yieldHandling, /* isConst = */ false); } @@ -7204,9 +7354,12 @@ Parser::statement(YieldHandling yieldHandling, bool canHaveDirecti template typename ParseHandler::Node Parser::expr(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, InvokedPrediction invoked) + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked) { - Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling, invoked); + Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling, + possibleError, invoked); if (!pn) return null(); @@ -7220,14 +7373,27 @@ Parser::expr(InHandling inHandling, YieldHandling yieldHandling, if (!seq) return null(); while (true) { - if (handler.isUnparenthesizedYieldExpression(pn)) { - report(ParseError, false, pn, JSMSG_BAD_YIELD_SYNTAX); - return null(); - } - pn = assignExpr(inHandling, yieldHandling, tripledotHandling); + // Additional calls to assignExpr should not reuse the possibleError + // which had been passed into the function. Otherwise we would lose + // information needed to determine whether or not we're dealing with + // a non-recoverable situation. + PossibleError possibleErrorInner(*this); + pn = assignExpr(inHandling, yieldHandling, tripledotHandling, + &possibleErrorInner); if (!pn) return null(); + + // If we find an error here we should report it immedately instead of + // passing it back out of the function. + if (possibleErrorInner.hasError()) { + + // We begin by checking for an outer pending error since it would + // have occurred first. + if (possibleError->checkForExprErrors()) + possibleErrorInner.checkForExprErrors(); + return null(); + } handler.addList(seq, pn); if (!tokenStream.matchToken(&matched, TOK_COMMA)) @@ -7238,6 +7404,19 @@ Parser::expr(InHandling inHandling, YieldHandling yieldHandling, return seq; } +template +typename ParseHandler::Node +Parser::expr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + InvokedPrediction invoked) +{ + PossibleError possibleError(*this); + Node pn = expr(inHandling, yieldHandling, tripledotHandling, &possibleError, invoked); + if (!pn || !possibleError.checkForExprErrors()) + return null(); + return pn; +} + static const JSOp ParseNodeKindToJSOp[] = { JSOP_OR, JSOP_AND, @@ -7325,7 +7504,9 @@ Precedence(ParseNodeKind pnk) { template MOZ_ALWAYS_INLINE typename ParseHandler::Node Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, InvokedPrediction invoked) + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked) { // Shift-reduce parser for the binary operator part of the JS expression // syntax. @@ -7338,7 +7519,7 @@ Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling Node pn; for (;;) { - pn = unaryExpr(yieldHandling, tripledotHandling, invoked); + pn = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked); if (!pn) return pn; @@ -7393,19 +7574,22 @@ Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling template MOZ_ALWAYS_INLINE typename ParseHandler::Node Parser::condExpr1(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, InvokedPrediction invoked) + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked) { - Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, invoked); + Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, possibleError, invoked); if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK)) return condition; - - Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited, + possibleError); if (!thenExpr) return null(); MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); - Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited); + Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited, + possibleError); if (!elseExpr) return null(); @@ -7418,7 +7602,8 @@ Parser::condExpr1(InHandling inHandling, YieldHandling yieldHandli template bool -Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor) +Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor, + PossibleError* possibleError) { MOZ_ASSERT(flavor != KeyedDestructuringAssignment, "destructuring must use special checking/marking code, not " @@ -7430,7 +7615,13 @@ Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor return false; } - return checkDestructuringPattern(nullptr, target); + bool isDestructuring = checkDestructuringPattern(nullptr, target); + // Here we've successfully distinguished between destructuring and + // an object literal. In the case where "CoverInitializedName" + // syntax was used there will be a pending error that needs clearing. + if (possibleError && isDestructuring) + possibleError->setResolved(); + return isDestructuring; } // All other permitted targets are simple. @@ -7440,7 +7631,7 @@ Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor if (handler.isPropertyAccess(target)) return true; - if (handler.maybeNameAnyParentheses(target)) { + if (handler.isNameAnyParentheses(target)) { // The arguments/eval identifiers are simple in non-strict mode code, // but warn to discourage use nonetheless. if (!reportIfArgumentsEvalTarget(target)) @@ -7458,9 +7649,12 @@ Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor template typename ParseHandler::Node Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, InvokedPrediction invoked) + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked) { JS_CHECK_RECURSION(context, return null()); + MOZ_ASSERT(!possibleError->hasError()); // It's very common at this point to have a "detectably simple" expression, // i.e. a name/number/string token followed by one of the following tokens @@ -7510,9 +7704,11 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl TokenStream::Position start(keepAtoms); tokenStream.tell(&start); - Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, invoked); - if (!lhs) + PossibleError possibleErrorInner(*this); + Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked); + if (!lhs) { return null(); + } ParseNodeKind kind; JSOp op; @@ -7532,6 +7728,7 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl case TOK_POWASSIGN: kind = PNK_POWASSIGN; op = JSOP_POW; break; case TOK_ARROW: { + // A line terminator between ArrowParameters and the => should trigger a SyntaxError. tokenStream.ungetToken(); TokenKind next = TOK_EOF; @@ -7594,24 +7791,42 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl default: MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment()); + possibleErrorInner.transferErrorTo(possibleError); tokenStream.ungetToken(); return lhs; } AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment; - if (!checkAndMarkAsAssignmentLhs(lhs, flavor)) + if (!checkAndMarkAsAssignmentLhs(lhs, flavor, &possibleErrorInner)) + return null(); + if (!possibleErrorInner.checkForExprErrors()) return null(); bool saved = pc->inDeclDestructuring; pc->inDeclDestructuring = false; - Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited); + Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited, + possibleError); pc->inDeclDestructuring = saved; if (!rhs) return null(); - return handler.newAssignment(kind, lhs, rhs, pc, op); } +template +typename ParseHandler::Node +Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + InvokedPrediction invoked) +{ + PossibleError possibleError(*this); + Node expr = assignExpr(inHandling, yieldHandling, tripledotHandling, &possibleError, invoked); + if (!expr || !possibleError.checkForExprErrors()) + return null(); + + return expr; +} + + template bool Parser::isValidSimpleAssignmentTarget(Node node, @@ -7621,11 +7836,11 @@ Parser::isValidSimpleAssignmentTarget(Node node, // error for the various syntaxes that fail this, and warning for the // various syntaxes that "pass" this but should not, occurs elsewhere. - if (PropertyName* name = handler.maybeNameAnyParentheses(node)) { + if (handler.isNameAnyParentheses(node)) { if (!pc->sc->strict()) return true; - return name != context->names().arguments && name != context->names().eval; + return !handler.nameIsArgumentsEvalAnyParentheses(node, context); } if (handler.isPropertyAccess(node)) @@ -7643,21 +7858,16 @@ template bool Parser::reportIfArgumentsEvalTarget(Node nameNode) { - PropertyName* name = handler.maybeNameAnyParentheses(nameNode); - MOZ_ASSERT(name, "must only call this function on known names"); - - const char* chars = (name == context->names().arguments) - ? js_arguments_str - : (name == context->names().eval) - ? js_eval_str - : nullptr; + const char* chars = handler.nameIsArgumentsEvalAnyParentheses(nameNode, context); if (!chars) return true; if (!report(ParseStrictError, pc->sc->strict(), nameNode, JSMSG_BAD_STRICT_ASSIGN, chars)) return false; - MOZ_ASSERT(!pc->sc->strict(), "in strict mode an error should have been reported"); + MOZ_ASSERT(!pc->sc->strict(), + "an error should have been reported if this was strict mode " + "code"); return true; } @@ -7671,7 +7881,7 @@ Parser::reportIfNotValidSimpleAssignmentTarget(Node target, Assign if (isValidSimpleAssignmentTarget(target, behavior)) return true; - if (handler.maybeNameAnyParentheses(target)) { + if (handler.isNameAnyParentheses(target)) { // Use a special error if the target is arguments/eval. This ensures // targeting these names is consistently a SyntaxError (which error numbers // below don't guarantee) while giving us a nicer error message. @@ -7701,6 +7911,10 @@ Parser::reportIfNotValidSimpleAssignmentTarget(Node target, Assign case CompoundAssignment: errnum = JSMSG_BAD_LEFTSIDE_OF_ASS; break; + + case ForInOrOfTarget: + errnum = JSMSG_BAD_FOR_LEFTSIDE; + break; } report(ParseError, pc->sc->strict(), target, errnum, extra); @@ -7718,7 +7932,7 @@ Parser::checkAndMarkAsIncOperand(Node target, AssignmentFlavor fla return false; // Mark. - if (handler.maybeNameAnyParentheses(target)) { + if (handler.isNameAnyParentheses(target)) { // Assignment to arguments/eval is allowed outside strict mode code, // but it's dodgy. Report a strict warning (error, if werror was set). if (!reportIfArgumentsEvalTarget(target)) @@ -7737,8 +7951,9 @@ typename ParseHandler::Node Parser::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op, uint32_t begin) { - Node kid = unaryExpr(yieldHandling, TripledotProhibited); - if (!kid) + PossibleError possibleError(*this); + Node kid = unaryExpr(yieldHandling, TripledotProhibited, &possibleError); + if (!kid || !possibleError.checkForExprErrors()) return null(); return handler.newUnary(kind, op, begin, kid); } @@ -7746,7 +7961,7 @@ Parser::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kin template typename ParseHandler::Node Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, - InvokedPrediction invoked) + PossibleError* possibleError, InvokedPrediction invoked) { JS_CHECK_RECURSION(context, return null()); @@ -7778,7 +7993,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t // // Evaluates expression, triggering a runtime ReferenceError for // // the undefined name. // typeof (1, nonExistentName); - Node kid = unaryExpr(yieldHandling, TripledotProhibited); + Node kid = unaryExpr(yieldHandling, TripledotProhibited, possibleError); if (!kid) return null(); @@ -7791,7 +8006,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t TokenKind tt2; if (!tokenStream.getToken(&tt2, TokenStream::Operand)) return null(); - Node pn2 = memberExpr(yieldHandling, TripledotProhibited, tt2, true); + Node pn2 = memberExpr(yieldHandling, TripledotProhibited, possibleError, tt2, true); if (!pn2) return null(); AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment; @@ -7803,13 +8018,13 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t } case TOK_DELETE: { - Node expr = unaryExpr(yieldHandling, TripledotProhibited); + Node expr = unaryExpr(yieldHandling, TripledotProhibited, possibleError); if (!expr) return null(); // Per spec, deleting any unary expression is valid -- it simply // returns true -- except for one case that is illegal in strict mode. - if (handler.maybeNameAnyParentheses(expr)) { + if (handler.isNameAnyParentheses(expr)) { if (!report(ParseStrictError, pc->sc->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND)) return null(); pc->sc->setBindingsAccessedDynamically(); @@ -7819,7 +8034,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t } default: { - Node pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true, + Node pn = memberExpr(yieldHandling, tripledotHandling, possibleError, tt, /* allowCallSyntax = */ true, invoked); if (!pn) return null(); @@ -8177,7 +8392,7 @@ Parser::legacyComprehensionTail(ParseNode* bodyExpr, unsigned return null(); if (matched) { pn2->pn_iflags |= JSITER_FOREACH; - addTelemetry(JSCompartment::DeprecatedForEach); + //addTelemetry(JSCompartment::DeprecatedForEach); if (versionNumber() < JSVERSION_LATEST) { if (!report(ParseWarning, pc->sc->strict(), pn2, JSMSG_DEPRECATED_FOR_EACH)) return null(); @@ -8195,7 +8410,11 @@ Parser::legacyComprehensionTail(ParseNode* bodyExpr, unsigned case TOK_LB: case TOK_LC: pc->inDeclDestructuring = true; - pn3 = primaryExpr(YieldIsKeyword, TripledotProhibited, tt); + + // No possible error is needed here either because we already + // know we are destructuring. + pn3 = primaryExpr(YieldIsKeyword, TripledotProhibited, + nullptr /* = possibleError */, tt); pc->inDeclDestructuring = false; if (!pn3) return null(); @@ -8593,7 +8812,7 @@ Parser::comprehensionFor(GeneratorKind comprehensionKind) blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope()); data.initLexical(DontHoistVars, JSOP_DEFLET, blockObj, JSMSG_TOO_MANY_LOCALS); - Node decls = handler.newList(PNK_LET, lhs); + Node decls = handler.newComprehensionBinding(lhs); if (!decls) return null(); data.setNameNode(lhs); @@ -8802,15 +9021,6 @@ Parser::argumentList(YieldHandling yieldHandling, Node listNode, b return false; } - if (handler.isUnparenthesizedYieldExpression(argNode)) { - TokenKind tt; - if (!tokenStream.peekToken(&tt)) - return false; - if (tt == TOK_COMMA) { - report(ParseError, false, argNode, JSMSG_BAD_YIELD_SYNTAX); - return false; - } - } #if JS_HAS_GENERATOR_EXPRS if (!spread) { if (!tokenStream.matchToken(&matched, TOK_FOR)) @@ -8873,7 +9083,8 @@ Parser::checkAndMarkSuperScope() template typename ParseHandler::Node Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, - TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked) + PossibleError* possibleError, TokenKind tt, + bool allowCallSyntax, InvokedPrediction invoked) { MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); @@ -8897,7 +9108,8 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling // Gotten by tryNewTarget tt = tokenStream.currentToken().type; - Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt, false, PredictInvoked); + Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, + possibleError, tt, false, PredictInvoked); if (!ctorExpr) return null(); @@ -8922,7 +9134,7 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling if (!lhs) return null(); } else { - lhs = primaryExpr(yieldHandling, tripledotHandling, tt, invoked); + lhs = primaryExpr(yieldHandling, tripledotHandling, possibleError, tt, invoked); if (!lhs) return null(); } @@ -8953,7 +9165,7 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling return null(); } } else if (tt == TOK_LB) { - Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited); + Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!propExpr) return null(); @@ -9011,8 +9223,8 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling return null(); JSOp op = JSOP_CALL; - if (PropertyName* name = handler.maybeNameAnyParentheses(lhs)) { - if (tt == TOK_LP && name == context->names().eval) { + if (handler.isNameAnyParentheses(lhs)) { + if (tt == TOK_LP && handler.nameIsEvalAnyParentheses(lhs, context)) { /* Select JSOP_EVAL and flag pc as needsCallObject. */ op = pc->sc->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; pc->sc->setBindingsAccessedDynamically(); @@ -9081,6 +9293,19 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling return lhs; } +template +typename ParseHandler::Node +Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt, + bool allowCallSyntax, InvokedPrediction invoked) +{ + PossibleError possibleError(*this); + Node pn = memberExpr(yieldHandling, tripledotHandling, &possibleError, tt, + allowCallSyntax, invoked); + if (!pn || !possibleError.checkForExprErrors()) + return null(); + return pn; +} + template typename ParseHandler::Node Parser::newName(PropertyName* name) @@ -9453,13 +9678,15 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, return propName; } - if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) { + if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) { if (isGenerator) { report(ParseError, false, null(), JSMSG_BAD_PROP_ID); return null(); } tokenStream.ungetToken(); - *propType = PropertyType::Shorthand; + *propType = tt == TOK_ASSIGN ? + PropertyType::CoverInitializedName : + PropertyType::Shorthand; return propName; } @@ -9500,7 +9727,7 @@ Parser::computedPropertyName(YieldHandling yieldHandling, Node lit template typename ParseHandler::Node -Parser::objectLiteral(YieldHandling yieldHandling) +Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); @@ -9509,6 +9736,7 @@ Parser::objectLiteral(YieldHandling yieldHandling) return null(); bool seenPrototypeMutation = false; + bool seenCoverInitializedName = false; RootedAtom propAtom(context); for (;;) { TokenKind tt; @@ -9567,6 +9795,43 @@ Parser::objectLiteral(YieldHandling yieldHandling) if (!handler.addShorthand(literal, propName, nameExpr)) return null(); + } else if (propType == PropertyType::CoverInitializedName) { + /* + * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand + * with default values, as per ES6 12.14.5 (2016/2/4) + */ + if (!tokenStream.checkForKeyword(propAtom, nullptr)) + return null(); + + Node lhs = identifierName(yieldHandling); + if (!lhs) + return null(); + + tokenStream.consumeKnownToken(TOK_ASSIGN); + Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!rhs) + return null(); + + Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, pc, JSOP_NOP); + if (!propExpr) + return null(); + + if (!handler.addPropertyDefinition(literal, propName, propExpr)) + return null(); + + if (!abortIfSyntaxParser()) + return null(); + + if (!seenCoverInitializedName) { + seenCoverInitializedName = true; + // "shorthand default" or "CoverInitializedName" syntax is only + // valid in the case of destructuring. Here we set a pending error so + // that later in the parse, once we've determined whether or not we're + // destructuring, the error can be reported or ignored appropriately. + if (possibleError) + possibleError->setPending(ParseError, JSMSG_BAD_PROP_ID, false); + } + } else { // FIXME: Implement ES6 function "name" property semantics // (bug 883377). @@ -9666,7 +9931,8 @@ Parser::tryNewTarget(Node &newTarget) template typename ParseHandler::Node Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, - TokenKind tt, InvokedPrediction invoked) + PossibleError* possibleError, TokenKind tt, + InvokedPrediction invoked) { MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); JS_CHECK_RECURSION(context, return null()); @@ -9682,7 +9948,7 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling return arrayInitializer(yieldHandling); case TOK_LC: - return objectLiteral(yieldHandling); + return objectLiteral(yieldHandling, possibleError); case TOK_LP: { TokenKind next; @@ -9714,7 +9980,7 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling return generatorComprehension(begin); } - Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed); + Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError); if (!expr) return null(); MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); @@ -9835,6 +10101,44 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling // sum(x*x for (x in y)); // ok // sum(for (x of y) x*x); // SyntaxError: needs more parens // +template +typename ParseHandler::Node +Parser::exprInParens(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError) +{ + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP)); + uint32_t begin = pos().begin; + uint32_t startYieldOffset = pc->lastYieldOffset; + + Node pn = expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked); + if (!pn) + return null(); + +#if JS_HAS_GENERATOR_EXPRS + bool matched; + if (!tokenStream.matchToken(&matched, TOK_FOR)) + return null(); + if (matched) { + if (pc->lastYieldOffset != startYieldOffset) { + reportWithOffset(ParseError, false, pc->lastYieldOffset, + JSMSG_BAD_GENEXP_BODY, js_yield_str); + return null(); + } + if (handler.isUnparenthesizedCommaExpression(pn)) { + report(ParseError, false, null(), JSMSG_BAD_GENERATOR_SYNTAX); + return null(); + } + pn = legacyGeneratorExpr(pn); + if (!pn) + return null(); + handler.setBeginPosition(pn, begin); + } +#endif /* JS_HAS_GENERATOR_EXPRS */ + + return pn; +} + template typename ParseHandler::Node Parser::exprInParens(InHandling inHandling, YieldHandling yieldHandling, diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index a9cbcf167..d62b6365a 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -4,12 +4,12 @@ * 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/. */ +/* JS parser. */ + #ifndef frontend_Parser_h #define frontend_Parser_h -/* - * JS parser definitions. - */ +#include "mozilla/Maybe.h" #include "jspubtd.h" @@ -364,6 +364,7 @@ enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody }; enum class PropertyType { Normal, Shorthand, + CoverInitializedName, Getter, GetterNoExpressionClosure, Setter, @@ -405,6 +406,72 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter operator StmtInfoPC*() { return &stmt_; } }; + /* + * A class for temporarily stashing errors while parsing continues. + * + * The ability to stash an error is useful for handling situations where we + * aren't able to verify that an error has occurred until later in the parse. + * For instance | ({x=1}) | is always parsed as an object literal with + * a SyntaxError, however, in the case where it is followed by '=>' we rewind + * and reparse it as a valid arrow function. Here a PossibleError would be + * set to 'pending' when the initial SyntaxError was encountered then 'resolved' + * just before rewinding the parser. + * + * When using PossibleError one should set a pending error at the location + * where an error occurs. From that point, the error may be resolved + * (invalidated) or left until the PossibleError is checked. + * + * Ex: + * PossibleError possibleError(*this); + * possibleError.setPending(ParseError, JSMSG_BAD_PROP_ID, false); + * // A JSMSG_BAD_PROP_ID ParseError is reported, returns false. + * possibleError.checkForExprErrors(); + * + * PossibleError possibleError(*this); + * possibleError.setPending(ParseError, JSMSG_BAD_PROP_ID, false); + * possibleError.setResolved(); + * // Returns true, no error is reported. + * possibleError.checkForExprErrors(); + * + * PossibleError possibleError(*this); + * // Returns true, no error is reported. + * possibleError.checkForExprErrors(); + */ + class MOZ_STACK_CLASS PossibleError + { + enum ErrorState { None, Pending }; + ErrorState state_; + + // Error reporting fields. + uint32_t offset_; + unsigned errorNumber_; + ParseReportKind reportKind_; + Parser& parser_; + bool strict_; + + public: + explicit PossibleError(Parser& parser); + + // Set a pending error. + void setPending(ParseReportKind kind, unsigned errorNumber, bool strict); + + // Resolve any pending error. + void setResolved(); + + // Return true if an error is pending without reporting + bool hasError(); + + // If there is a pending error report it and return false, otherwise return + // true. + bool checkForExprErrors(); + + // Pass pending errors between possible error instances. This is useful + // for extending the lifetime of a pending error beyond the scope of + // the PossibleError where it was initially set (keeping in mind that + // PossibleError is a MOZ_STACK_CLASS). + void transferErrorTo(PossibleError* other); + }; + public: ExclusiveContext* const context; LifoAlloc& alloc; @@ -669,7 +736,21 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node ifStatement(YieldHandling yieldHandling); Node doWhileStatement(YieldHandling yieldHandling); Node whileStatement(YieldHandling yieldHandling); + Node forStatement(YieldHandling yieldHandling); + bool forHeadStart(YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, + Node* forInitialPart, + mozilla::Maybe& letStmt, + MutableHandle blockObj, + Node* forLetImpliedBlock, + Node* forInOrOfExpression); + bool validateForInOrOfLHSExpression(Node target); + Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling); + + void assertCurrentLexicalStaticBlockIs(ParseContext* pc, + Handle blockObj); + Node switchStatement(YieldHandling yieldHandling); Node continueStatement(YieldHandling yieldHandling); Node breakStatement(YieldHandling yieldHandling); @@ -685,14 +766,70 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node exportDeclaration(); Node expressionStatement(YieldHandling yieldHandling, InvokedPrediction invoked = PredictUninvoked); - Node variables(YieldHandling yieldHandling, - ParseNodeKind kind, - ForInitLocation location, - bool* psimple = nullptr, StaticBlockObject* blockObj = nullptr, - VarContext varContext = HoistVars); + + // Declaration parsing. The main entrypoint is Parser::declarationList, + // with sub-functionality split out into the remaining methods. + + // |blockObj| may be non-null only when |kind| corresponds to a lexical + // declaration (that is, PNK_LET or PNK_CONST). + // + // The for* parameters, for normal declarations, should be null/ignored. + // They should be non-null only when Parser::forHeadStart parses a + // declaration at the start of a for-loop head. + // + // In this case, on success |*forHeadKind| is PNK_FORHEAD, PNK_FORIN, or + // PNK_FOROF, corresponding to the three for-loop kinds. The precise value + // indicates what was parsed. + // + // If parsing recognized a for(;;) loop, the next token is the ';' within + // the loop-head that separates the init/test parts. + // + // Otherwise, for for-in/of loops, the next token is the ')' ending the + // loop-head. Additionally, the expression that the loop iterates over was + // parsed into |*forInOrOfExpression|. + Node declarationList(YieldHandling yieldHandling, + ParseNodeKind kind, + StaticBlockObject* blockObj = nullptr, + ParseNodeKind* forHeadKind = nullptr, + Node* forInOrOfExpression = nullptr); + + // The items in a declaration list are either patterns or names, with or + // without initializers. These two methods parse a single pattern/name and + // any associated initializer -- and if parsing an |initialDeclaration| + // will, if parsing in a for-loop head (as specified by |forHeadKind| being + // non-null), consume additional tokens up to the closing ')' in a + // for-in/of loop head, returning the iterated expression in + // |*forInOrOfExpression|. (An "initial declaration" is the first + // declaration in a declaration list: |a| but not |b| in |var a, b|, |{c}| + // but not |d| in |let {c} = 3, d|.) + Node declarationPattern(Node decl, TokenKind tt, BindData* data, + bool initialDeclaration, YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, Node* forInOrOfExpression); + Node declarationName(Node decl, TokenKind tt, BindData* data, + bool initialDeclaration, YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, Node* forInOrOfExpression); + + // Having parsed a name (not found in a destructuring pattern) declared by + // a declaration, with the current token being the '=' separating the name + // from its initializer, parse and bind that initializer -- and possibly + // consume trailing in/of and subsequent expression, if so directed by + // |forHeadKind|. + bool initializerInNameDeclaration(Node decl, Node binding, Handle name, + BindData* data, bool initialDeclaration, + YieldHandling yieldHandling, ParseNodeKind* forHeadKind, + Node* forInOrOfExpression); + + Node expr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked = PredictUninvoked); Node expr(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, InvokedPrediction invoked = PredictUninvoked); + Node assignExpr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError, + InvokedPrediction invoked = PredictUninvoked); Node assignExpr(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, InvokedPrediction invoked = PredictUninvoked); @@ -700,16 +837,26 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node yieldExpression(InHandling inHandling); Node condExpr1(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, + PossibleError* possibleError, InvokedPrediction invoked = PredictUninvoked); Node orExpr1(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, - InvokedPrediction invoked = PredictUninvoked); + PossibleError* possibleError, + InvokedPrediction invoked = PredictUninvoked); Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, + PossibleError* possibleError, InvokedPrediction invoked = PredictUninvoked); + Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, + PossibleError* possibleError, TokenKind tt, + bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked); Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked); - Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt, + Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, + PossibleError* possibleError, TokenKind tt, InvokedPrediction invoked = PredictUninvoked); + Node exprInParens(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError); Node exprInParens(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling); @@ -782,10 +929,12 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter CompoundAssignment, KeyedDestructuringAssignment, IncrementAssignment, - DecrementAssignment + DecrementAssignment, + ForInOrOfTarget }; - bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor); + bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor, + PossibleError* possibleError=nullptr); bool matchInOrOf(bool* isForInp, bool* isForOfp); bool checkFunctionArguments(); @@ -801,10 +950,6 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body); bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext* pc); - bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach, - ParseNodeKind headKind); - bool checkForHeadConstInitializers(Node pn1); - // Use when the current token is TOK_NAME and is known to be 'let'. bool shouldParseLetDeclaration(bool* parseDeclOut); @@ -844,7 +989,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node arrayInitializer(YieldHandling yieldHandling); Node newRegExp(); - Node objectLiteral(YieldHandling yieldHandling); + Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError); enum PrepareLexicalKind { PrepareLet, @@ -876,8 +1021,10 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter bool bindUninitialized(BindData* data, HandlePropertyName name, Node pn); bool bindUninitialized(BindData* data, Node pn); bool makeSetCall(Node node, unsigned errnum); - Node cloneDestructuringDefault(Node opn); + + Node cloneForInOrOfDeclarationForAssignment(Node decl); Node cloneLeftHandSide(Node opn); + Node cloneDestructuringDefault(Node opn); Node cloneParseTree(Node opn); Node newNumber(const Token& tok) { diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 687ec59e2..7524b04e7 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -42,11 +42,16 @@ class SyntaxParseHandler NodeGetProp, NodeStringExprStatement, NodeReturn, - NodeHoistableDeclaration, NodeBreak, NodeThrow, NodeEmptyStatement, + NodeVarDeclaration, + NodeLetDeclaration, + NodeConstDeclaration, + + NodeFunctionDefinition, + // This is needed for proper assignment-target handling. ES6 formally // requires function calls *not* pass IsValidSimpleAssignmentTarget, // but at last check there were still sites with |f() = 5| and similar @@ -112,14 +117,6 @@ class SyntaxParseHandler // yields |b| each time it's resumed. NodeUnparenthesizedCommaExpr, - // Yield expressions currently (but not in ES6 -- a SpiderMonkey bug to - // fix) must generally be parenthesized. (See the uses of - // isUnparenthesizedYieldExpression in Parser.cpp for the rare - // exceptions.) Thus we need this to treat |yield 1, 2;| as a syntax - // error and |(yield 1), 2;| as a comma expression that will yield 1, - // then evaluate to 2. - NodeUnparenthesizedYieldExpr, - // Assignment expressions in condition contexts could be typos for // equality checks. (Think |if (x = y)| versus |if (x == y)|.) Thus // we need this to treat |if (x = y)| as a possible typo and @@ -300,7 +297,7 @@ class SyntaxParseHandler bool addShorthand(Node literal, Node name, Node expr) { return true; } bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; } bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; } - Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; } + Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } // Statements @@ -348,10 +345,10 @@ class SyntaxParseHandler bool setLastFunctionArgumentDefault(Node funcpn, Node pn) { return true; } void setLastFunctionArgumentDestructuring(Node funcpn, Node pn) {} - Node newFunctionDefinition() { return NodeHoistableDeclaration; } + Node newFunctionDefinition() { return NodeFunctionDefinition; } void setFunctionBody(Node pn, Node kid) {} void setFunctionBox(Node pn, FunctionBox* funbox) {} - Node newFunctionDefinitionForAnnexB(Node pn, Node assignment) { return NodeHoistableDeclaration; } + Node newFunctionDefinitionForAnnexB(Node pn, Node assignment) { return NodeFunctionDefinition; } void addFunctionArgument(Node pn, Node argpn) {} Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) { @@ -362,10 +359,23 @@ class SyntaxParseHandler return NodeGeneric; } + Node newComprehensionBinding(Node kid) { + // Careful: we're asking this well after the name was parsed, so the + // value returned may not correspond to |kid|'s actual name. But it + // *will* be truthy iff |kid| was a name, so we're safe. + MOZ_ASSERT(isUnparenthesizedName(kid)); + return NodeGeneric; + } + Node newForHead(ParseNodeKind kind, Node decls, Node lhs, Node rhs, const TokenPos& pos) { return NodeGeneric; } + void initForLetBlock(Node forLetImpliedBlock, Node nestedForLoop) { + MOZ_ASSERT(forLetImpliedBlock == NodeGeneric); // per newForStatement + MOZ_ASSERT(nestedForLoop == NodeGeneric); // per newLexicalScope + } + Node newLexicalScope(ObjectBox* blockbox) { return NodeGeneric; } void setLexicalScopeBody(Node block, Node body) {} @@ -391,22 +401,68 @@ class SyntaxParseHandler Node newList(ParseNodeKind kind, JSOp op = JSOP_NOP) { MOZ_ASSERT(kind != PNK_VAR); + MOZ_ASSERT(kind != PNK_LET); + MOZ_ASSERT(kind != PNK_CONST); return NodeGeneric; } Node newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) { - return NodeGeneric; - } - Node newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) { - MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET); - return kind == PNK_VAR ? NodeHoistableDeclaration : NodeGeneric; + return newList(kind, op); } Node newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) { - MOZ_ASSERT(kind != PNK_VAR); - return NodeGeneric; + return newList(kind, op); + } + + Node newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) { + if (kind == PNK_VAR) + return NodeVarDeclaration; + if (kind == PNK_LET) + return NodeLetDeclaration; + MOZ_ASSERT(kind == PNK_CONST); + return NodeConstDeclaration; } Node newDeclarationList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) { - MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET); - return kind == PNK_VAR ? NodeHoistableDeclaration : NodeGeneric; + return newDeclarationList(kind, op); + } + + bool isDeclarationList(Node node) { + return node == NodeVarDeclaration || + node == NodeLetDeclaration || + node == NodeConstDeclaration; + } + + bool declarationIsVar(Node node) { + MOZ_ASSERT(isDeclarationList(node)); + return node == NodeVarDeclaration; + } + + bool declarationIsLet(Node node) { + MOZ_ASSERT(isDeclarationList(node)); + return node == NodeLetDeclaration; + } + + bool declarationIsConst(Node node) { + MOZ_ASSERT(isDeclarationList(node)); + return node == NodeConstDeclaration; + } + + Node singleBindingFromDeclaration(Node decl) { + MOZ_ASSERT(isDeclarationList(decl)); + + // This is, unfortunately, very dodgy. Obviously NodeVarDeclaration + // can store no info on the arbitrary number of bindings it could + // contain. + // + // But this method is called only for cloning for-in/of declarations + // as initialization targets. That context simplifies matters. If the + // binding is a single name, it'll always syntax-parse (or it would + // already have been rejected as assigning/binding a forbidden name). + // Otherwise the binding is a destructuring pattern. But syntax + // parsing would *already* have aborted when it saw a destructuring + // pattern. So we can just say any old thing here, because the only + // time we'll be wrong is a case that syntax parsing has already + // rejected. Use NodeUnparenthesizedName so the SyntaxParseHandler + // Parser::cloneLeftHandSide can assert it sees only this. + return NodeUnparenthesizedName; } Node newCatchList() { @@ -422,7 +478,9 @@ class SyntaxParseHandler list == NodeUnparenthesizedArray || list == NodeUnparenthesizedObject || list == NodeUnparenthesizedCommaExpr || - list == NodeHoistableDeclaration || + list == NodeVarDeclaration || + list == NodeLetDeclaration || + list == NodeConstDeclaration || list == NodeFunctionCall); } @@ -434,10 +492,6 @@ class SyntaxParseHandler return newBinary(kind, lhs, rhs, op); } - bool isUnparenthesizedYieldExpression(Node node) { - return node == NodeUnparenthesizedYieldExpr; - } - bool isUnparenthesizedCommaExpression(Node node) { return node == NodeUnparenthesizedCommaExpr; } @@ -455,7 +509,9 @@ class SyntaxParseHandler } bool isStatementPermittedAfterReturnStatement(Node pn) { - return pn == NodeHoistableDeclaration || pn == NodeBreak || pn == NodeThrow || + return pn == NodeFunctionDefinition || pn == NodeVarDeclaration || + pn == NodeBreak || + pn == NodeThrow || pn == NodeEmptyStatement; } @@ -487,7 +543,6 @@ class SyntaxParseHandler // them to a generic node. if (node == NodeUnparenthesizedString || node == NodeUnparenthesizedCommaExpr || - node == NodeUnparenthesizedYieldExpr || node == NodeUnparenthesizedAssignment || node == NodeUnparenthesizedUnary) { @@ -505,30 +560,35 @@ class SyntaxParseHandler bool isConstant(Node pn) { return false; } - PropertyName* maybeUnparenthesizedName(Node node) { - if (node == NodeUnparenthesizedName || - node == NodeUnparenthesizedArgumentsName || - node == NodeUnparenthesizedEvalName) - { - return lastAtom->asPropertyName(); - } - return nullptr; + bool isUnparenthesizedName(Node node) { + return node == NodeUnparenthesizedArgumentsName || + node == NodeUnparenthesizedEvalName || + node == NodeUnparenthesizedName; } - PropertyName* maybeParenthesizedName(Node node) { - if (node == NodeParenthesizedName || - node == NodeParenthesizedArgumentsName || - node == NodeParenthesizedEvalName) - { - return lastAtom->asPropertyName(); - } - return nullptr; + bool isNameAnyParentheses(Node node) { + if (isUnparenthesizedName(node)) + return true; + return node == NodeParenthesizedArgumentsName || + node == NodeParenthesizedEvalName || + node == NodeParenthesizedName; } - PropertyName* maybeNameAnyParentheses(Node node) { - if (PropertyName* name = maybeUnparenthesizedName(node)) - return name; - return maybeParenthesizedName(node); + bool nameIsEvalAnyParentheses(Node node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this function on known names"); + return node == NodeUnparenthesizedEvalName || node == NodeParenthesizedEvalName; + } + + const char* nameIsArgumentsEvalAnyParentheses(Node node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this method on known names"); + + if (nameIsEvalAnyParentheses(node, cx)) + return js_eval_str; + if (node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName) + return js_arguments_str; + return nullptr; } PropertyName* maybeDottedProperty(Node node) { diff --git a/js/src/irregexp/NativeRegExpMacroAssembler.cpp b/js/src/irregexp/NativeRegExpMacroAssembler.cpp index d0b47f7fb..4255f9595 100644 --- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp +++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp @@ -517,7 +517,7 @@ NativeRegExpMacroAssembler::Backtrack() // Check for an interrupt. Label noInterrupt; masm.branch32(Assembler::Equal, - AbsoluteAddress(runtime->addressOfInterruptUint32()), Imm32(0), + AbsoluteAddress(runtime->addressOfInterruptRegExpJitUint32()), Imm32(0), &noInterrupt); masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0); masm.jump(&exit_label_); diff --git a/js/src/jit-test/lib/syntax.js b/js/src/jit-test/lib/syntax.js index 86f8b4e19..a9d8e8fd4 100644 --- a/js/src/jit-test/lib/syntax.js +++ b/js/src/jit-test/lib/syntax.js @@ -1181,62 +1181,6 @@ function test_syntax(postfixes, check_error, ignore_opts) { test("for each (let x in y "); test("for each (let x in y) "); - // Legacy array comprehensions - - test("[x "); - test("[x for "); - test("[x for ( "); - test("[x for (x "); - test("[x for (x of "); - test("[x for (x of y "); - test("[x for (x of y) "); - test("[x for (x of y) if "); - test("[x for (x of y) if ( "); - test("[x for (x of y) if (x "); - test("[x for (x of y) if (x == "); - test("[x for (x of y) if (x == 1 "); - test("[x for (x of y) if (x == 1) "); - test("[x for (x of y) if (x == 1)] "); - - test("[x for (x in "); - test("[x for (x in y "); - test("[x for (x in y) "); - - test("[x for each "); - test("[x for each ( "); - test("[x for each (x "); - test("[x for each (x in "); - test("[x for each (x in y "); - test("[x for each (x in y) "); - - // Generator expressions - - test("(x "); - test("(x for "); - test("(x for ( "); - test("(x for (x "); - test("(x for (x of "); - test("(x for (x of y "); - test("(x for (x of y) "); - test("(x for (x of y) if "); - test("(x for (x of y) if ( "); - test("(x for (x of y) if (x "); - test("(x for (x of y) if (x == "); - test("(x for (x of y) if (x == 1 "); - test("(x for (x of y) if (x == 1) "); - test("(x for (x of y) if (x == 1)) "); - - test("(x for (x in "); - test("(x for (x in y "); - test("(x for (x in y) "); - - test("(x for each "); - test("(x for each ( "); - test("(x for each (x "); - test("(x for each (x in "); - test("(x for each (x in y "); - test("(x for each (x in y) "); - // asm.js test("(function() { 'use asm'; "); diff --git a/js/src/jit-test/tests/arguments/defaults-strict-mode.js b/js/src/jit-test/tests/arguments/defaults-strict-mode.js index 5242ea513..d95489322 100644 --- a/js/src/jit-test/tests/arguments/defaults-strict-mode.js +++ b/js/src/jit-test/tests/arguments/defaults-strict-mode.js @@ -1,35 +1,42 @@ load(libdir + "asserts.js"); -function f1(f=(function () { return typeof this !== "object"; })) { "use strict"; return f; } +eval(`"use strict"; +function f1(f=(function () { return typeof this !== "object"; })) { return f; } assertEq(f1()(), true); +`); + function f2(f=(function () { "use strict"; return (function () { return typeof this !== "object"; }) })) { assertEq(typeof this, "object"); return f; } assertEq(f2()()(), true); -function f3(f=(function () { return (function () { return typeof this !== "object"; }) })) { "use strict"; return f; } + +eval(`"use strict"; +function f3(f=(function () { return (function () { return typeof this !== "object"; }) })) { return f; } assertEq(f3()()(), true); +`); + // These should be okay. function f4(f=(function () { with (Object) {} }), g=(function () { "use strict"; })) {} function f5(g=(function () { "use strict"; }), f=(function () { with (Object) {} })) {} function f6(f=(function () { return (x for (y in (function g() {}))); })) {} assertThrowsInstanceOf(function () { - eval("function f(a=delete x) { 'use strict'; }"); + eval("'use strict'; function f(a=delete x) { }"); }, SyntaxError); assertThrowsInstanceOf(function () { Math.sin(4); - eval("function f(a='\\251') { 'use strict'; }"); + eval("'use strict'; function f(a='\\251') { }"); }, SyntaxError); assertThrowsInstanceOf(function () { - eval("function f(a='\\251', b=delete x) { 'use strict'; }"); + eval("'use strict'; function f(a='\\251', b=delete x) { }"); }, SyntaxError); assertThrowsInstanceOf(function () { - eval("function f(a=delete x, b='\\251') { 'use strict'; }"); + eval("'use strict'; function f(a=delete x, b='\\251') { }"); }, SyntaxError); assertThrowsInstanceOf(function () { - eval("function f(a=(function () { '\\251'; })) { 'use strict'; }"); + eval("'use strict'; function f(a=(function () { '\\251'; })) { }"); }, SyntaxError); assertThrowsInstanceOf(function () { - eval("function f(a=(function () { with (Object) {} })) { 'use strict'; }"); + eval("'use strict'; function f(a=(function () { with (Object) {} })) { }"); }, SyntaxError); assertThrowsInstanceOf(function () { - eval("function f(a=(function (b, b) {})) { 'use strict'; }"); + eval("'use strict'; function f(a=(function (b, b) {})) { }"); }, SyntaxError); diff --git a/js/src/jit-test/tests/auto-regress/bug487570.js b/js/src/jit-test/tests/auto-regress/bug487570.js deleted file mode 100644 index d8f0b09d1..000000000 --- a/js/src/jit-test/tests/auto-regress/bug487570.js +++ /dev/null @@ -1,13 +0,0 @@ -// |jit-test| error:TypeError - -// Binary: cache/js-dbg-32-86c8e18f20eb-linux -// Flags: -// -(function(){ - for each (var x in new ( - (function (){x})() - for each (y in []) - ) -) -{const functional=undefined} -})() diff --git a/js/src/jit-test/tests/auto-regress/bug488963.js b/js/src/jit-test/tests/auto-regress/bug488963.js deleted file mode 100644 index 40012d10e..000000000 --- a/js/src/jit-test/tests/auto-regress/bug488963.js +++ /dev/null @@ -1,4 +0,0 @@ -// Binary: cache/js-dbg-64-55b6298ff619-linux -// Flags: -// -for(let x in []) {((x = [] for(x in [])) for(y in 0))} diff --git a/js/src/jit-test/tests/auto-regress/bug589093.js b/js/src/jit-test/tests/auto-regress/bug589093.js deleted file mode 100644 index 2c5b7b8ad..000000000 --- a/js/src/jit-test/tests/auto-regress/bug589093.js +++ /dev/null @@ -1,9 +0,0 @@ -// Binary: cache/js-dbg-32-b22e82ce2364-linux -// Flags: -j -// -x = (w for (x in [])) -for (w in [0, 0, 0, 0]) { - (function() { - [c for (z in x)] - })() -} diff --git a/js/src/jit-test/tests/auto-regress/bug622318.js b/js/src/jit-test/tests/auto-regress/bug622318.js deleted file mode 100644 index 62ab5164f..000000000 --- a/js/src/jit-test/tests/auto-regress/bug622318.js +++ /dev/null @@ -1,11 +0,0 @@ -// Binary: cache/js-dbg-32-e0fc487c23f4-linux -// Flags: -j -// -try { - for (window = (0 - for (x in V)); f;) {} -} catch(e) {} -for each(let z in [0, 0, 0, 0, 0, 0, 0, 0, 0]) { - for (v in window) {} -} -var e, V diff --git a/js/src/jit-test/tests/auto-regress/bug691593.js b/js/src/jit-test/tests/auto-regress/bug691593.js deleted file mode 100644 index dd640fdad..000000000 --- a/js/src/jit-test/tests/auto-regress/bug691593.js +++ /dev/null @@ -1,18 +0,0 @@ -// Binary: cache/js-dbg-64-acf3c1fb7c94-linux -// Flags: -// - -genexp = "x * x for (x in [])"; -genexpParened = "(" + genexp + ")"; -needParens(2, "if (1, xx) { }"); -function needParens(section, pat, exp) { - ft = pat.replace(/xx/, genexpParened); - try { - f = new Function(ft); - } catch(e) { } - overParenTest(section, f, exp); -} -function overParenTest(section, f, exp) { - var uf = "" + f; - if (uf.indexOf(genexpParened) != -1) { } -} diff --git a/js/src/jit-test/tests/auto-regress/bug776314.js b/js/src/jit-test/tests/auto-regress/bug776314.js deleted file mode 100644 index 935ac09c6..000000000 --- a/js/src/jit-test/tests/auto-regress/bug776314.js +++ /dev/null @@ -1,6 +0,0 @@ -// Binary: cache/js-dbg-64-defbe00ca091-linux -// Flags: -// -if (typeof disassemble == 'function') { - disassemble(eval(("(function(){(a for each (e in b));})"))); -} diff --git a/js/src/jit-test/tests/auto-regress/bug780405.js b/js/src/jit-test/tests/auto-regress/bug780405.js deleted file mode 100644 index be412e217..000000000 --- a/js/src/jit-test/tests/auto-regress/bug780405.js +++ /dev/null @@ -1,6 +0,0 @@ -// Binary: cache/js-dbg-32-0f66a7ecf8de-linux -// Flags: -// -function n(b = ((function() {}), function() { - (a for (r in [function() {}])) -})) {} diff --git a/js/src/jit-test/tests/basic/bug660538.js b/js/src/jit-test/tests/basic/bug660538.js deleted file mode 100644 index ae854cd75..000000000 --- a/js/src/jit-test/tests/basic/bug660538.js +++ /dev/null @@ -1,10 +0,0 @@ -Error.prototype.__proto__.p = 5; -f = Function("return( \"\" type() != MIRType_Object) + + MDefinition* obj = callInfo.getArg(0); + if (obj->type() != MIRType_Object && obj->type() != MIRType_Value) return InliningStatus_NotInlined; - if (callInfo.getArg(1)->type() != MIRType_Int32) + + MDefinition* arg = callInfo.getArg(1); + if (arg->type() != MIRType_Int32) return InliningStatus_NotInlined; // Don't inline if we don't have a constant slot. - MDefinition* arg = callInfo.getArg(1); if (!arg->isConstantValue()) return InliningStatus_NotInlined; uint32_t slot = arg->constantValue().toPrivateUint32(); @@ -2344,12 +2347,12 @@ IonBuilder::inlineUnsafeSetReservedSlot(CallInfo& callInfo) callInfo.setImplicitlyUsedUnchecked(); MStoreFixedSlot* store = - MStoreFixedSlot::NewBarriered(alloc(), callInfo.getArg(0), slot, callInfo.getArg(2)); + MStoreFixedSlot::NewBarriered(alloc(), obj, slot, callInfo.getArg(2)); current->add(store); current->push(store); if (NeedsPostBarrier(callInfo.getArg(2))) - current->add(MPostWriteBarrier::New(alloc(), callInfo.getArg(0), callInfo.getArg(2))); + current->add(MPostWriteBarrier::New(alloc(), obj, callInfo.getArg(2))); return InliningStatus_Inlined; } @@ -2361,20 +2364,23 @@ IonBuilder::inlineUnsafeGetReservedSlot(CallInfo& callInfo, MIRType knownValueTy trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; } - if (callInfo.getArg(0)->type() != MIRType_Object) + + MDefinition* obj = callInfo.getArg(0); + if (obj->type() != MIRType_Object && obj->type() != MIRType_Value) return InliningStatus_NotInlined; - if (callInfo.getArg(1)->type() != MIRType_Int32) + + MDefinition* arg = callInfo.getArg(1); + if (arg->type() != MIRType_Int32) return InliningStatus_NotInlined; // Don't inline if we don't have a constant slot. - MDefinition* arg = callInfo.getArg(1); if (!arg->isConstantValue()) return InliningStatus_NotInlined; uint32_t slot = arg->constantValue().toPrivateUint32(); callInfo.setImplicitlyUsedUnchecked(); - MLoadFixedSlot* load = MLoadFixedSlot::New(alloc(), callInfo.getArg(0), slot); + MLoadFixedSlot* load = MLoadFixedSlot::New(alloc(), obj, slot); current->add(load); current->push(load); if (knownValueType != MIRType_Value) { diff --git a/js/src/js.msg b/js/src/js.msg index 82405333e..61fca8bd7 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -199,9 +199,10 @@ MSG_DEF(JSMSG_BAD_DESTRUCT_PARENS, 0, JSEXN_SYNTAXERR, "destructuring patter MSG_DEF(JSMSG_BAD_DESTRUCT_DECL, 0, JSEXN_SYNTAXERR, "missing = in destructuring declaration") MSG_DEF(JSMSG_BAD_DUP_ARGS, 0, JSEXN_SYNTAXERR, "duplicate argument names not allowed in this context") MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 0, JSEXN_SYNTAXERR, "invalid for each loop") -MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid for/in left-hand side") -MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 1, JSEXN_TYPEERR, "generator function {0} returns a value") -MSG_DEF(JSMSG_BAD_YIELD_SYNTAX, 0, JSEXN_SYNTAXERR, "yield expression must be parenthesized") +MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid for-in/of left-hand side") +MSG_DEF(JSMSG_LEXICAL_DECL_DEFINES_LET,0, JSEXN_SYNTAXERR, "a lexical declaration can't define a 'let' binding") +MSG_DEF(JSMSG_LET_STARTING_FOROF_LHS, 0, JSEXN_SYNTAXERR, "an expression X in 'for (X of Y)' must not start with 'let'") +MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 1, JSEXN_TYPEERR, "generator function {0} returns a value") MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX, 0, JSEXN_SYNTAXERR, "generator expression must be parenthesized") MSG_DEF(JSMSG_BAD_GENEXP_BODY, 1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression") MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand") @@ -265,7 +266,7 @@ MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage a MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal") MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character") MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module") -MSG_DEF(JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT,1,JSEXN_SYNTAXERR,"for-{0} loop head declarations may not have initializers") +MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers") MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for") MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found") MSG_DEF(JSMSG_LET_CLASS_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a class") @@ -312,7 +313,7 @@ MSG_DEF(JSMSG_PAREN_BEFORE_WITH, 0, JSEXN_SYNTAXERR, "missing ( before wit MSG_DEF(JSMSG_PAREN_IN_PAREN, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical") MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after export specifier list") MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after module specifier list") -MSG_DEF(JSMSG_REDECLARED_CATCH_IDENTIFIER, 1, JSEXN_TYPEERR, "redeclaration of identifier '{0}' in catch") +MSG_DEF(JSMSG_REDECLARED_CATCH_IDENTIFIER, 1, JSEXN_SYNTAXERR, "redeclaration of identifier '{0}' in catch") MSG_DEF(JSMSG_REDECLARED_PARAM, 1, JSEXN_TYPEERR, "redeclaration of formal parameter {0}") MSG_DEF(JSMSG_RESERVED_ID, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier") MSG_DEF(JSMSG_REST_WITH_DEFAULT, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default") diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 1131720cb..4b274cfdb 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3671,6 +3671,20 @@ js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_ return NewCopiedArrayTryUseGroup(cx, group, vp, length); } +bool +js::NewValuePair(JSContext* cx, const Value& val1, const Value& val2, MutableHandleValue rval) +{ + JS::AutoValueArray<2> vec(cx); + vec[0].set(val1); + vec[1].set(val2); + + JSObject* aobj = js::NewDenseCopiedArray(cx, 2, vec.begin()); + if (!aobj) + return false; + rval.setObject(*aobj); + return true; +} + #ifdef DEBUG bool js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp) diff --git a/js/src/jsarray.h b/js/src/jsarray.h index bcd62d175..9e200c6d6 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -123,6 +123,9 @@ extern JSObject* NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, HandleObject proto = nullptr); +extern bool +NewValuePair(JSContext* cx, const Value& val1, const Value& val2, MutableHandleValue rval); + /* * Determines whether a write to the given element on |obj| should fail because * |obj| is an Array with a non-writable length, and writing that element would diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 19bcdc384..fba1f06c0 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -1076,55 +1076,48 @@ ParseDate(const CharT* s, size_t length, ClippedTime* result) * Case 1. The input string contains an English month name. * The form of the string can be month f l, or f month l, or * f l month which each evaluate to the same date. - * If f and l are both greater than or equal to 70, or - * both less than 70, the date is invalid. - * The year is taken to be the greater of the values f, l. - * If the year is greater than or equal to 70 and less than 100, - * it is considered to be the number of years after 1900. + * If f and l are both greater than or equal to 100 the date + * is invalid. + * + * The year is taken to be either the greater of the values f, l or + * whichever is set to zero. If the year is greater than or equal to + * 50 and less than 100, it is considered to be the number of years + * after 1900. If the year is less than 50 it is considered to be the + * number of years after 2000, otherwise it is considered to be the + * number of years after 0. + * * Case 2. The input string is of the form "f/m/l" where f, m and l are - * integers, e.g. 7/16/45. - * Adjust the mon, mday and year values to achieve 100% MSIE - * compatibility. - * a. If 0 <= f < 70, f/m/l is interpreted as month/day/year. - * i. If year < 100, it is the number of years after 1900 - * ii. If year >= 100, it is the number of years after 0. - * b. If 70 <= f < 100 - * i. If m < 70, f/m/l is interpreted as - * year/month/day where year is the number of years after - * 1900. - * ii. If m >= 70, the date is invalid. - * c. If f >= 100 - * i. If m < 70, f/m/l is interpreted as - * year/month/day where year is the number of years after 0. - * ii. If m >= 70, the date is invalid. + * integers, e.g. 7/16/45. mon, mday and year values are adjusted + * to achieve Chrome compatibility. + * + * a. If 0 < f <= 12 and 0 < l <= 31, f/m/l is interpreted as + * month/day/year. + * i. If year < 50, it is the number of years after 2000 + * ii. If year >= 50, it is the number of years after 1900. + * iii. If year >= 100, it is the number of years after 0. + * b. If 31 < f and 0 < m <= 12 and 0 < l <= 31 f/m/l is + * interpreted as year/month/day + * i. If year < 50, it is the number of years after 2000 + * ii. If year >= 50, it is the number of years after 1900. + * iii. If year >= 100, it is the number of years after 0. */ if (seenMonthName) { - if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) + if (mday >= 100 && mon >= 100) return false; - if (mday > year) { + if (year > 0 && (mday == 0 || mday > year)) { int temp = year; year = mday; mday = temp; } - if (year >= 70 && year < 100) { - year += 1900; - } - } else if (mon < 70) { /* (a) month/day/year */ - if (year < 100) { - year += 1900; - } - } else if (mon < 100) { /* (b) year/month/day */ - if (mday < 70) { - int temp = year; - year = mon + 1900; - mon = mday; - mday = temp; - } else { + + if (mday <= 0 || mday > 31) return false; - } - } else { /* (c) year/month/day */ - if (mday < 70) { + } else if (0 < mon && mon <= 12 && 0 < mday && mday <= 31) { + /* (a) month/day/year */ + } else { + /* (b) year/month/day */ + if (mon > 31 && mday <= 12 && year <= 31) { int temp = year; year = mon; mon = mday; @@ -1134,6 +1127,11 @@ ParseDate(const CharT* s, size_t length, ClippedTime* result) } } + if (year < 50) + year += 2000; + else if (year >= 50 && year < 100) + year += 1900; + mon -= 1; /* convert month to 0-based */ if (sec < 0) sec = 0; diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 9cbff88bf..1a8c2f0d6 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -74,15 +74,7 @@ typedef HashSet IdSet; static inline bool NewKeyValuePair(JSContext* cx, jsid id, const Value& val, MutableHandleValue rval) { - JS::AutoValueArray<2> vec(cx); - vec[0].set(IdToValue(id)); - vec[1].set(val); - - JSObject* aobj = NewDenseCopiedArray(cx, 2, vec.begin()); - if (!aobj) - return false; - rval.setObject(*aobj); - return true; + return NewValuePair(cx, IdToValue(id), val, rval); } static inline bool diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 352960af2..38ac4b931 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1590,6 +1590,13 @@ js::str_indexOf(JSContext* cx, unsigned argc, Value* vp) // Step 9 uint32_t start = Min(Max(pos, 0U), textLen); + if (str == searchStr) { + // AngularJS often invokes "false".indexOf("false"). This check should + // be cheap enough to not hurt anything else. + args.rval().setInt32(start == 0 ? 0 : -1); + return true; + } + // Steps 10 and 11 JSLinearString* text = str->ensureLinear(cx); if (!text) @@ -1640,6 +1647,11 @@ js::str_lastIndexOf(JSContext* cx, unsigned argc, Value* vp) if (!pat) return false; + if (textstr == pat) { + args.rval().setInt32(0); + return true; + } + size_t textLen = textstr->length(); size_t patLen = pat->length(); int start = textLen - patLen; // Start searching here diff --git a/js/src/tests/ecma_3/Date/15.9.3.2-1.js b/js/src/tests/ecma_3/Date/15.9.3.2-1.js deleted file mode 100644 index 0aa43cfe3..000000000 --- a/js/src/tests/ecma_3/Date/15.9.3.2-1.js +++ /dev/null @@ -1,60 +0,0 @@ -/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Bob Clary - */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 273292; -var summary = '15.9.3.2 new Date(value)'; -var actual = ''; -var expect = ''; -var date1; -var date2; -var i; -var validDateStrings = [ - "11/69/2004", - "11/70/2004", - "69/69/2004", - "69/69/69", - "69/69/1969", - "70/69/70", - "70/69/1970", - "70/69/2004" - ]; - -var invalidDateStrings = [ - "70/70/70", - "70/70/1970", - "70/70/2004" - ]; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -expect = 0; - -for (i = 0; i < validDateStrings.length; i++) -{ - date1 = new Date(validDateStrings[i]); - date2 = new Date(date1.toDateString()); - actual = date2 - date1; - - reportCompare(expect, actual, inSection(i) + ' ' + - validDateStrings[i]); -} - -expect = true; - -var offset = validDateStrings.length; - -for (i = 0; i < invalidDateStrings.length; i++) -{ - date1 = new Date(invalidDateStrings[i]); - actual = isNaN(date1); - - reportCompare(expect, actual, inSection(i + offset) + ' ' + - invalidDateStrings[i] + ' is invalid.'); -} - diff --git a/js/src/tests/ecma_6/Comprehensions/error-messages.js b/js/src/tests/ecma_6/Comprehensions/error-messages.js index ccbea8d8c..aeacfde27 100644 --- a/js/src/tests/ecma_6/Comprehensions/error-messages.js +++ b/js/src/tests/ecma_6/Comprehensions/error-messages.js @@ -25,7 +25,6 @@ const TOP_YIELD = error("yield").message; const GENERIC = error("(for)").message; const BAD_GENERATOR_SYNTAX = error("(for (x of []) x, 1)").message; const MISSING_SEMI = error("yield 1").message; -const MISSING_PAREN = error("(yield 1)").message; const PAREN_PAREN = error("(foo").message; const FOR_OF_PAREN = error("(for (x of y, z) w)").message; @@ -55,13 +54,13 @@ const cases = [ // yield expressions { expr: "yield 1", top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg" }, { expr: "1, yield 2", top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [FOR_OF_PAREN, 1], desc: "yield w/ arg at end of list" }, - { expr: "yield 1, 2", top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: [YIELD_PAREN, 3], genexp: [FOR_OF_PAREN, 3], desc: "yield w/ arg in list" }, - { expr: "(yield 1)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized" }, - { expr: "(1, yield 2)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized" }, - { expr: "(yield 1, 2)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: [YIELD_PAREN, 3], genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized" }, + { expr: "yield 1, 2", top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [FOR_OF_PAREN, 3], desc: "yield w/ arg in list" }, + { expr: "(yield 1)", top: [PAREN_PAREN, 2], fun: [PAREN_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized" }, + { expr: "(1, yield 2)", top: [PAREN_PAREN, 2], fun: [PAREN_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized" }, + { expr: "(yield 1, 2)", top: [PAREN_PAREN, 2], fun: [PAREN_PAREN, 2], gen: null, genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized" }, // deeply nested yield expressions - { expr: "((((yield 1))))", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield w/ arg" }, + { expr: "((((yield 1))))", top: [PAREN_PAREN, 2], fun: [PAREN_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield w/ arg" }, // arguments { expr: "arguments", top: null, fun: null, gen: null, genexp: null, desc: "arguments in list" }, diff --git a/js/src/tests/ecma_6/Date/two-digit-years.js b/js/src/tests/ecma_6/Date/two-digit-years.js new file mode 100644 index 000000000..5b8ad3d7b --- /dev/null +++ b/js/src/tests/ecma_6/Date/two-digit-years.js @@ -0,0 +1,69 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommonn.org/licenses/publicdomain/ + */ + +/* + * For the sake of cross compatibility with other implementations we + * implement date parsing heuristics which support single and double + * digit years. See bug: 1265136 + */ + +/************** + * BEGIN TEST * + **************/ + +for (let year of Array(100).keys()) { + for (let month of Array(12).keys()) { + for (let day of Array(31).keys()) { + let fullYear = year >= 50 ? year + 1900 : year + 2000; + let fullDate = new Date(`${month + 1}/${day + 1}/${fullYear}`); + + // mm/dd/yy + let d1 = new Date(`${month + 1}/${day + 1}/${year}`); + assertEq(d1.getTime(), fullDate.getTime()) + + // yy/mm/dd + let d2 = new Date(`${year}/${month + 1}/${day + 1}`); + if (year > 31) { + assertEq(d2.getTime(), fullDate.getTime()) + } else if (year > 12) { + assertEq(d2.getTime(), new Date(NaN).getTime()) + } + } + } +} + +assertEq(new Date("99/1/99").getTime(), new Date(NaN).getTime()); +assertEq(new Date("13/13/13").getTime(), new Date(NaN).getTime()); +assertEq(new Date("0/10/0").getTime(), new Date(NaN).getTime()); + +// Written months. +for (let year of Array(1000).keys()) { + let fullDate = new Date(`5/1/${year}`); + let d1 = new Date(`may ${year} 1`); + let d2 = new Date(`may 1 ${year}`); + + let d3 = new Date(`1 may ${year}`); + let d4 = new Date(`${year} may 1`); + + let d5 = new Date(`1 ${year} may`); + let d6 = new Date(`${year} 1 may`); + + assertEq(d1.getTime(), fullDate.getTime()) + assertEq(d2.getTime(), fullDate.getTime()) + assertEq(d3.getTime(), fullDate.getTime()) + assertEq(d4.getTime(), fullDate.getTime()) + assertEq(d5.getTime(), fullDate.getTime()) + assertEq(d6.getTime(), fullDate.getTime()) +} + +assertEq(new Date("may 1999 1999").getTime(), new Date(NaN).getTime()); +assertEq(new Date("may 0 0").getTime(), new Date(NaN).getTime()); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_6/Object/destructuring-shorthand-defaults.js b/js/src/tests/ecma_6/Object/destructuring-shorthand-defaults.js new file mode 100644 index 000000000..6d35b867f --- /dev/null +++ b/js/src/tests/ecma_6/Object/destructuring-shorthand-defaults.js @@ -0,0 +1,102 @@ +/* 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/. */ + +// Ensure that the syntax used in shorthand destructuring with defaults +// e.g. |{x=1, y=2} = {}| properly raises a syntax error within an object +// literal. As per ES6 12.2.6 Object Initializer, "NOTE 3." + +const SYNTAX_ERROR_STMTS = [ + // expressions + "({x={}={}}),", + "({y={x={}={}={}={}={}={}={}={}}={}}),", + "({a=1, b=2, c=3, x=({}={})}),", + "({x=1, y={z={1}}})", + "({x=1} = {y=1});", + "({x: y={z=1}}={})", + "({x=1}),", + "({z=1}={}, {w=2}, {e=3})=>{};", + "({z={x=1}})=>{};", + "({x = ({y=1}) => y})", + "(({x=1})) => x", + // declarations + "let o = {x=1};", + "var j = {x=1};", + "var j = {x={y=1}}={};", + "const z = {x=1};", + "const z = {x={y=1}}={};", + "const {x=1};", + "const {x={y=33}}={};", + "var {x=1};", + "let {x=1};", + "let x, y, {z=1}={}, {w=2}, {e=3};", + // array initialization + "[{x=1, y = ({z=2} = {})}];", + // try/catch + "try {throw 'a';} catch ({x={y=1}}) {}", + // if/else + "if ({k: 1, x={y=2}={}}) {}", + "if (false) {} else if (true) { ({x=1}) }", + // switch + "switch ('c') { case 'c': ({x=1}); }", + // for + "for ({x=1}; 1;) {1}", + "for ({x={y=2}}; 1;) {1}", + "for (var x = 0; x < 2; x++) { ({x=1, y=2}) }", + "for (let x=1;{x=1};){}", + "for (let x=1;{x={y=2}};){}", + "for (let x=1;1;{x=1}){}", + "for (let x=1;1;{x={y=2}}){}", + // while + "while ({x=1}) {1};", + "while ({x={y=2}}={}) {1};", + // with + "with ({x=1}) {};", + "with ({x={y=3}={}}) {};", + "with (Math) { ({x=1}) };" +] + +for (var stmt of SYNTAX_ERROR_STMTS) { + assertThrowsInstanceOf(() => { + eval(stmt); + }, SyntaxError); +} + +const REFERENCE_ERROR_STMTS = [ + "({x} += {});", + "({x = 1}) = {x: 2};", +] + +for (var stmt of REFERENCE_ERROR_STMTS) { + assertThrowsInstanceOf(() => { + eval(stmt); + }, ReferenceError); +} + +// A few tricky but acceptable cases: +// see https://bugzilla.mozilla.org/show_bug.cgi?id=932080#c2 + +assertEq((({a = 0}) => a)({}), 0); +assertEq((({a = 0} = {}) => a)({}), 0); +assertEq((({a = 0} = {}) => a)({a: 1}), 1); + +{ + let x, y; + ({x=1} = {}); + assertEq(x, 1); + ({x=1} = {x: 4}); + assertEq(x, 4); + ({x=1, y=2} = {}) + assertEq(x, 1); + assertEq(y, 2); +} + +{ + let {x={i=1, j=2}={}}={}; + assertDeepEq(x, ({})); + assertEq(i, 1); + assertEq(j, 2); +} + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Statements/for-inof-name-iteration-expression-contains-index-string.js b/js/src/tests/ecma_6/Statements/for-inof-name-iteration-expression-contains-index-string.js new file mode 100644 index 000000000..212b34db9 --- /dev/null +++ b/js/src/tests/ecma_6/Statements/for-inof-name-iteration-expression-contains-index-string.js @@ -0,0 +1,43 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var gTestfile = "for-inof-name-iteration-expression-contains-index-string.js"; +var BUGNUMBER = 1235640; +var summary = + "Don't assert parsing a for-in/of loop whose target is a name, where the " + + "expression being iterated over contains a string containing an index"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function f() +{ + var x; + for (x in "9") + continue; + assertEq(x, "0"); +} + +f(); + +function g() +{ + "use strict"; + var x = "unset"; + for (x in arguments) + continue; + assertEq(x, "unset"); +} + +g(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_7/Object/values.js b/js/src/tests/ecma_7/Object/values.js index 78543c875..f5aaff4b6 100644 --- a/js/src/tests/ecma_7/Object/values.js +++ b/js/src/tests/ecma_7/Object/values.js @@ -3,7 +3,6 @@ * http://creativecommons.org/licenses/publicdomain/ */ -if ("values" in Object) { assertEq(Object.values.length, 1); var o, values; @@ -88,7 +87,6 @@ if ("values" in Object) { assertEq(ownKeysCallCount, 1); assertDeepEq(values, [3, 1]); assertDeepEq(getOwnPropertyDescriptorCalls, ["c", "a"]); -} if (typeof reportCompare === "function") reportCompare(true, true); diff --git a/js/src/tests/js1_5/Date/regress-301738-02.js b/js/src/tests/js1_5/Date/regress-301738-02.js deleted file mode 100644 index 971068545..000000000 --- a/js/src/tests/js1_5/Date/regress-301738-02.js +++ /dev/null @@ -1,130 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 301738; -var summary = 'Date parse compatibilty with MSIE'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -/* - Case 2. The input string is of the form "f/m/l" where f, m and l are - integers, e.g. 7/16/45. - Adjust the mon, mday and year values to achieve 100% MSIE - compatibility. - a. If 0 <= f < 70, f/m/l is interpreted as month/day/year. - i. If year < 100, it is the number of years after 1900 - ii. If year >= 100, it is the number of years after 0. - b. If 70 <= f < 100 - i. If m < 70, f/m/l is interpreted as - year/month/day where year is the number of years after - 1900. - ii. If m >= 70, the date is invalid. - c. If f >= 100 - i. If m < 70, f/m/l is interpreted as - year/month/day where year is the number of years after 0. - ii. If m >= 70, the date is invalid. -*/ - -var f; -var m; -var l; - -function newDate(f, m, l) -{ - return new Date(f + '/' + m + '/' + l); -} - -function newDesc(f, m, l) -{ - return f + '/' + m + '/' + l; -} - -// 2.a.i -f = 0; -m = 0; -l = 0; - -expect = (new Date(l, f-1, m)).toDateString(); -actual = newDate(f, m, l).toDateString(); -reportCompare(expect, actual, newDesc(f, m, l)); - -f = 0; -m = 0; -l = 100; - -expect = (new Date(l, f-1, m)).toDateString(); -actual = newDate(f, m, l).toDateString(); -reportCompare(expect, actual, newDesc(f, m, l)); - -// 2.a.ii -f = 0; -m = 24; -l = 100; - -expect = (new Date(l, f-1, m)).toDateString(); -actual = newDate(f, m, l).toDateString(); -reportCompare(expect, actual, newDesc(f, m, l)); - -f = 0; -m = 24; -l = 2100; - -expect = (new Date(l, f-1, m)).toDateString(); -actual = newDate(f, m, l).toDateString(); -reportCompare(expect, actual, newDesc(f, m, l)); - - -// 2.b.i -f = 70; -m = 24; -l = 100; - -expect = (new Date(f, m-1, l)).toDateString(); -actual = newDate(f, m, l).toDateString(); -reportCompare(expect, actual, newDesc(f, m, l)); - -f = 99; -m = 12; -l = 1; - -expect = (new Date(f, m-1, l)).toDateString(); -actual = newDate(f, m, l).toDateString(); -reportCompare(expect, actual, newDesc(f, m, l)); - -// 2.b.ii. - -f = 99; -m = 70; -l = 1; - -expect = true; -actual = isNaN(newDate(f, m, l)); -reportCompare(expect, actual, newDesc(f, m, l) + ' is an invalid date'); - -// 2.c.i - -f = 100; -m = 12; -l = 1; - -expect = (new Date(f, m-1, l)).toDateString(); -actual = newDate(f, m, l).toDateString(); -reportCompare(expect, actual, newDesc(f, m, l)); - -// 2.c.ii - -f = 100; -m = 70; -l = 1; - -expect = true; -actual = isNaN(newDate(f, m, l)); -reportCompare(expect, actual, newDesc(f, m, l) + ' is an invalid date'); - - diff --git a/js/src/tests/js1_6/Regress/regress-355002.js b/js/src/tests/js1_6/Regress/regress-355002.js index ad120a355..20fc60fdb 100644 --- a/js/src/tests/js1_6/Regress/regress-355002.js +++ b/js/src/tests/js1_6/Regress/regress-355002.js @@ -20,7 +20,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = 'SyntaxError: invalid for/in left-hand side'; + expect = 'SyntaxError: invalid for-in/of left-hand side'; actual = ''; try { diff --git a/js/src/tests/js1_7/geniter/regress-351514.js b/js/src/tests/js1_7/geniter/regress-351514.js deleted file mode 100644 index f07125ea7..000000000 --- a/js/src/tests/js1_7/geniter/regress-351514.js +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 351514; -var summary = 'Finalize yield syntax to match ES4/JS2 proposal'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - expect = /SyntaxError: yield expression must be parenthesized/; - try - { - eval('function f() { yield g(yield 1, 2) };'); - } - catch(ex) - { - actual = ex + ''; - } - - reportMatch(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8/genexps/arguments-property-access-in-generator.js b/js/src/tests/js1_8/genexps/arguments-property-access-in-generator.js deleted file mode 100644 index ce5e3f64a..000000000 --- a/js/src/tests/js1_8/genexps/arguments-property-access-in-generator.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: - * Jeff Walden - */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 721322; -var summary = 'Allow f.arguments in generator expressions'; - -print(BUGNUMBER + ": " + summary); - -/************** - * BEGIN TEST * - **************/ - -eval("(function() { return (f.arguments for (x in [1])); })()"); -eval("(function() { var f = { arguments: 12 }; return [f.arguments for (x in [1])]; })()"); - - -/******************************************************************************/ - -if (typeof reportCompare === "function") - reportCompare(true, true); - -print("Tests complete"); diff --git a/js/src/tests/js1_8/genexps/regress-634472.js b/js/src/tests/js1_8/genexps/regress-634472.js deleted file mode 100644 index 71c0c58f1..000000000 --- a/js/src/tests/js1_8/genexps/regress-634472.js +++ /dev/null @@ -1,166 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ - - -//----------------------------------------------------------------------------- -var BUGNUMBER = 634472; -var summary = 'contextual restrictions for yield and arguments'; -var actual = ''; -var expect = ''; - - -function error(str) { - let base; - try { - // the following line must not be broken up into multiple lines - base = (function(){try{eval('throw new Error()')}catch(e){return e.lineNumber}})(); eval(str); - return null; - } catch (e) { - e.lineNumber = e.lineNumber - base + 1; - return e; - } -} - -const JSMSG_GENEXP_YIELD = error("(function(){((yield) for (x in []))})").message; -const JSMSG_TOP_YIELD = error("yield").message; -const JSMSG_YIELD_PAREN = error("(function(){yield, 1})").message; -const JSMSG_YIELD_FOR = error("(function(){yield for})").message; -const JSMSG_BAD_GENERATOR_SYNTAX = error("(1, x for (x in []))").message; - -const cases = [ - // yield expressions - { expr: "yield", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "simple yield" }, - { expr: "yield 1", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg" }, - { expr: "1, yield", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "simple yield at end of list" }, - { expr: "1, yield 2", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg at end of list" }, - { expr: "yield, 1", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN, desc: "simple yield in list" }, - { expr: "yield 1, 2", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN, desc: "yield w/ arg in list" }, - { expr: "(yield)", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "simple yield, parenthesized" }, - { expr: "(yield 1)", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg, parenthesized" }, - { expr: "(1, yield)", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "simple yield at end of list, parenthesized" }, - { expr: "(1, yield 2)", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized" }, - { expr: "(yield, 1)", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN, desc: "simple yield in list, parenthesized" }, - { expr: "(yield 1, 2)", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN, desc: "yield w/ arg in list, parenthesized" }, - - // deeply nested yield expressions - { expr: "((((yield))))", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "deeply nested yield" }, - { expr: "((((yield 1))))", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "deeply nested yield w/ arg" }, - - // arguments - { expr: "arguments", top: null, fun: null, gen: null, desc: "arguments in list" }, - { expr: "1, arguments", top: null, fun: null, gen: null, desc: "arguments in list" }, - - // yield in generator expressions - { expr: "(yield for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_FOR, gen: JSMSG_YIELD_FOR, desc: "simple yield in genexp" }, - { expr: "(yield 1 for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg in genexp" }, - { expr: "(yield, 1 for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN, desc: "simple yield in list in genexp" }, - { expr: "(yield 1, 2 for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN, desc: "yield w/ arg in list in genexp" }, - { expr: "(1, yield for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_FOR, gen: JSMSG_YIELD_FOR, desc: "simple yield at end of list in genexp" }, - { expr: "(1, yield 2 for (x in []))", top: JSMSG_TOP_YIELD, fun: { simple: JSMSG_GENEXP_YIELD, call: JSMSG_GENEXP_YIELD }, - gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg at end of list in genexp" }, - { expr: "((yield) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "simple yield, parenthesized in genexp" }, - { expr: "((yield 1) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg, parenthesized in genexp" }, - { expr: "(1, (yield) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "simple yield, parenthesized in list in genexp" }, - { expr: "(1, (yield 2) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg, parenthesized in list in genexp" }, - { expr: "((1, yield) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "simple yield at end of list, parenthesized in genexp" }, - { expr: "((1, yield 2) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized in genexp" }, - { expr: "(1, (2, yield) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "simple yield at end of list, parenthesized in list in genexp" }, - { expr: "(1, (2, yield 3) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized in list in genexp" }, - { expr: "((yield, 1) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN, desc: "simple yield in list, parenthesized in genexp" }, - { expr: "((yield 1, 2) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN, desc: "yield w/ arg in list, parenthesized in genexp" }, - { expr: "(1, (yield, 2) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN, desc: "simple yield in list, parenthesized in list in genexp" }, - { expr: "(1, (yield 2, 3) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN, desc: "yield w/ arg in list, parenthesized in list in genexp" }, - - // deeply nested yield in generator expressions - { expr: "((((1, yield 2))) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "deeply nested yield in genexp" }, - { expr: "((((1, yield 2)) for (x in [])) for (y in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "deeply nested yield in multiple genexps" }, - - // arguments in generator expressions - { expr: "(arguments for (x in []))", top: null, fun: null, gen: null, desc: "simple arguments in genexp" }, - { expr: "(1, arguments for (x in []))", top: JSMSG_BAD_GENERATOR_SYNTAX, fun: JSMSG_BAD_GENERATOR_SYNTAX, gen: JSMSG_BAD_GENERATOR_SYNTAX, desc: "arguments in list in genexp" }, - { expr: "((arguments) for (x in []))", top: null, fun: null, gen: null, desc: "arguments, parenthesized in genexp" }, - { expr: "(1, (arguments) for (x in []))", top: JSMSG_BAD_GENERATOR_SYNTAX, fun: JSMSG_BAD_GENERATOR_SYNTAX, gen: JSMSG_BAD_GENERATOR_SYNTAX, desc: "arguments, parenthesized in list in genexp" }, - { expr: "((1, arguments) for (x in []))", top: null, fun: null, gen: null, desc: "arguments in list, parenthesized in genexp" }, - { expr: "(1, (2, arguments) for (x in []))", top: JSMSG_BAD_GENERATOR_SYNTAX, fun: JSMSG_BAD_GENERATOR_SYNTAX, gen: JSMSG_BAD_GENERATOR_SYNTAX, desc: "arguments in list, parenthesized in list in genexp" }, - - // deeply nested arguments in generator expressions - { expr: "((((1, arguments))) for (x in []))", top: null, fun: null, gen: null, desc: "deeply nested arguments in genexp" }, - { expr: "((((1, arguments)) for (x in [])) for (y in []))", top: null, fun: null, gen: null, desc: "deeply nested arguments in multiple genexps" }, - - // legal yield/arguments in nested function - { expr: "((function() { yield }) for (x in []))", top: null, fun: null, gen: null, desc: "legal yield in nested function" }, - { expr: "((function() { arguments }) for (x in []))", top: null, fun: null, gen: null, desc: "legal arguments in nested function" }, - { expr: "((function() arguments) for (x in []))", top: null, fun: null, gen: null, desc: "legal arguments in nested expression-closure" } -]; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function splitKeyword(str) { - return str. - replace(/yield for/, '\nyield for\n'). - replace(/yield ([0-9])/, '\nyield $1\n'). - replace(/yield([^ ]|$)/, '\nyield\n$1'). - replace(/arguments/, '\narguments\n'); -} - -function expectError1(err, ctx, msg) { - reportCompare('object', typeof err, 'exn for: ' + msg); - reportCompare(ctx, err.message, 'exn message for: ' + msg); - if (ctx !== JSMSG_BAD_GENERATOR_SYNTAX) - reportCompare(2, err.lineNumber, 'exn token for: ' + msg); -} - -function expectError(expr, call, wrapCtx, expect, msg) { - let exps = (typeof expect === "string") - ? { simple: expect, call: expect } - : expect; - expectError1(error(wrapCtx(expr)), exps.simple, msg); - if (call) - expectError1(error(wrapCtx(call)), exps.call, 'call argument in ' + msg); -} - -function expectSuccess(err, msg) { - reportCompare(null, err, 'parse: ' + msg); -} - -function atTop(str) { return str } -function inFun(str) { return '(function(){' + str + '})' } -function inGen(str) { return '(y for (y in ' + str + '))' } - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - for (let i = 0, len = cases.length; i < len; i++) { - let {expr, top, fun, gen, desc} = cases[i]; - - let call = (expr[0] === "(") ? ("print" + expr) : null; - - expr = splitKeyword(expr); - if (call) - call = splitKeyword(call); - - if (top) - expectError(expr, call, atTop, top, 'top-level context, ' + desc); - else - expectSuccess(error(expr), 'top-level context, ' + desc); - - if (fun) - expectError(expr, call, inFun, fun, 'function context, ' + desc); - else - expectSuccess(error(inFun(expr)), 'function context, ' + desc); - - if (gen) - expectError(expr, call, inGen, gen, 'genexp context, ' + desc); - else - expectSuccess(error(inGen(expr)), 'genexp context, ' + desc); - } - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8/genexps/regress-380237-02.js b/js/src/tests/js1_8/regress-380237-02.js similarity index 100% rename from js/src/tests/js1_8/genexps/regress-380237-02.js rename to js/src/tests/js1_8/regress-380237-02.js diff --git a/js/src/tests/js1_8_1/regress/regress-452498-052.js b/js/src/tests/js1_8_1/regress/regress-452498-052.js deleted file mode 100644 index 9986ccd61..000000000 --- a/js/src/tests/js1_8_1/regress/regress-452498-052.js +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 452498; -var summary = 'TM: upvar2 regression tests'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - -// ------- Comment #52 From Jason Orendorff - -// Crash in NoteLValue, called from BindDestructuringVar. -// NoteLValue assumes pn->pn_lexdef is non-null, but here -// pn is itself the definition of x. - for (var [x] in null) ; - -// This one only crashes when executed from a file. -// Assertion failure: pn != dn->dn_uses, at ../jsparse.cpp:1131 - for (var f in null) - ; - var f = 1; - (f) - -// Assertion failure: pnu->pn_cookie == FREE_UPVAR_COOKIE, at ../jsemit.cpp:1815 -// In EmitEnterBlock. x has one use, which is pnu here. -// pnu is indeed a name, but pnu->pn_cookie is 0. - try { eval('let (x = 1) { var x; }'); } catch(ex) {} - -// Assertion failure: cg->upvars.lookup(atom), at ../jsemit.cpp:1992 -// atom="x", upvars is empty. - (1 for each (x in x)); - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/regress/regress-452498-068.js b/js/src/tests/js1_8_1/regress/regress-452498-068.js deleted file mode 100644 index 261313103..000000000 --- a/js/src/tests/js1_8_1/regress/regress-452498-068.js +++ /dev/null @@ -1,69 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 452498; -var summary = 'TM: upvar2 regression tests'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - -// ===== - - foo = "" + new Function("while(\u3056){let \u3056 = x}"); - -// ===== - - function a(){ let c; eval("let c, y"); } - a(); - -// ===== - - try - { - {x: 1e+81 ? c : arguments} - } - catch(ex) - { - } - -// ===== - - (function(q){return q;} for each (\u3056 in [])) - -// ===== - - function f(){ var c; eval("{var c = NaN, c;}"); } - f(); - -// ===== - try - { - eval( - ' x\n' + - ' let(x) {\n' + - ' var x\n' - ); - } - catch(ex) - { - } - -// ===== - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/regress/regress-452498-082.js b/js/src/tests/js1_8_1/regress/regress-452498-082.js index 2327a02cb..5568cf8f5 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-082.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-082.js @@ -44,34 +44,6 @@ function test() } g("for (var x = 0; x < 3; ++x)(new (function(){})());"); -// ===== - - try - { - (function(){new (function ({}, x) { yield (x(1e-81) for (x4 in undefined)) })()})(); - } - catch(ex) - { - } -// ===== - - try - { - (function(){[(function ([y]) { })() for each (x in [])];})(); - } - catch(ex) - { - } -// ===== - - try - { - eval('(function(){for(var x2 = [function(id) { return id } for each (x in []) if ([])] in functional) function(){};})();'); - } - catch(ex) - { - } - // ===== try { @@ -104,21 +76,6 @@ function test() var f = new Function("[] = [( '' )()];"); "" + f; -// ===== - - try - { - eval( - 'for(let x;' + - ' ([,,,]' + - ' .toExponential(new Function(), (function(){}))); [] = {})' + - ' for(var [x, x] = * in this.__defineSetter__("", function(){}));' - ); - } - catch(ex) - { - } - // ===== reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_8_1/regress/regress-452498-098.js b/js/src/tests/js1_8_1/regress/regress-452498-098.js deleted file mode 100644 index cf8c71510..000000000 --- a/js/src/tests/js1_8_1/regress/regress-452498-098.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 452498; -var summary = 'TM: upvar2 regression tests'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - -// ------- Comment #98 From Gary Kwong [:nth10sd] - - uneval(function(){(Number(0) for each (NaN in []) for each (x4 in this))}); -// Assertion failure: pos == 0, at ../jsopcode.cpp:2963 - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/regress/regress-452498-099.js b/js/src/tests/js1_8_1/regress/regress-452498-099.js deleted file mode 100644 index 3468a4993..000000000 --- a/js/src/tests/js1_8_1/regress/regress-452498-099.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 452498; -var summary = 'TM: upvar2 regression tests'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - -// ------- Comment #99 From Gary Kwong [:nth10sd] - - try - { - eval("(function x(){x.(this)} )();"); - } - catch(ex) - { - } - -// Assertion failure: (uint32_t)(index_) < atoms_->length, at ../jsinterp.cpp:327 -// Crash [@ js_FullTestPropertyCache] at null in opt, -j not required. - -// ===== - - try - { - (function(){try {x} finally {}; ([x in []] for each (x in x))})(); - } - catch(ex) - { - } - -// Assertion failure: lexdep->frameLevel() <= funbox->level, at ../jsparse.cpp:1735 -// Crash [@ BindNameToSlot] near null in opt, -j not required. - -// ===== - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/regress/regress-452498-102.js b/js/src/tests/js1_8_1/regress/regress-452498-102.js index 81e6ae5db..0aa60e4ec 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-102.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-102.js @@ -55,24 +55,6 @@ function test() } // Assertion failure: regs.sp == StackBase(fp), at ../jsinterp.cpp:2984 -// ===== - try - { - do {x} while([[] for (x in []) ]); - } - catch(ex) - { - } -// Assertion failure: !(pnu->pn_dflags & PND_BOUND), at ../jsemit.cpp:1818 -// ===== - - try - { - {x} ((x=[] for (x in []))); x; - } - catch(ex) - { - } // Assertion failure: cg->staticLevel >= level, at ../jsemit.cpp:2014 // Crash [@ BindNameToSlot] in opt without -j diff --git a/js/src/tests/js1_8_1/regress/regress-452498-130.js b/js/src/tests/js1_8_1/regress/regress-452498-130.js deleted file mode 100644 index f2f578e3e..000000000 --- a/js/src/tests/js1_8_1/regress/regress-452498-130.js +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 452498; -var summary = 'TM: upvar2 regression tests'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - -// ------- Comment #130 From Gary Kwong [:nth10sd] - -// Does not require -j: -// ===== - ((function x()x in []) for (y in [])) - -// Assertion failure: !(pnu->pn_dflags & PND_BOUND), at ../jsemit.cpp:1818 -// ===== - - -// Requires -j: -// ===== - for (var x = 0; x < 3; ++x) { new function(){} } - -// Assertion failure: cx->bailExit, at ../jstracer.cpp:4672 -// Opt crash [@ LeaveTree] near null - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/regress/regress-452498-135.js b/js/src/tests/js1_8_1/regress/regress-452498-135.js index bcd49f49b..b2bf72fab 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-135.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-135.js @@ -32,34 +32,6 @@ function test() } } -// Assertion failure: fp2->fun && fp2->script, at ../jsinterp.cpp:5633 -// Opt crash [@ js_Interpret] -// === - - try - { - (x for each (c in [])) - x - } - catch(ex) - { - } - -// Assertion failure: ss->printer->pcstack, at ../jsopcode.cpp:909 -// === - try - { - (function(){for(; (this); ((window for (x in [])) for (y in []))) 0}); - } - catch(ex) - { - } -// Assertion failure: level >= tc->staticLevel, at ../jsparse.cpp:5773 -// === - eval(uneval( function(){ - ((function()y)() for each (x in this)) - } )) - // Debug & opt crash [@ BindNameToSlot] // -j is required: diff --git a/js/src/tests/js1_8_1/regress/regress-452498-138.js b/js/src/tests/js1_8_1/regress/regress-452498-138.js deleted file mode 100644 index 5b81d7ee6..000000000 --- a/js/src/tests/js1_8_1/regress/regress-452498-138.js +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 452498; -var summary = 'TM: upvar2 regression tests'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - -// ------- Comment #138 From Gary Kwong [:nth10sd] - -// Does not require -j: -// === - ((function x(){ yield (x = undefined) } ) for (y in /x/)); - -// Assertion failure: lexdep->frameLevel() <= funbox->level, at ../jsparse.cpp:1820 -// === - try - { - for(let x in ( x for (y in x) for each (x in []) )) y; - } - catch(ex) - { - } -// Assertion failure: cg->upvars.lookup(atom), at ../jsemit.cpp:2034 -// === - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/regress/regress-507424.js b/js/src/tests/js1_8_1/regress/regress-507424.js deleted file mode 100644 index 5c4dbe741..000000000 --- a/js/src/tests/js1_8_1/regress/regress-507424.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 507424; -var summary = 'TM: assert with regexp literal inside closure' -var actual = ''; -var expect = 'do not crash'; - - -//----------------------------------------------------------------------------- -start_test(); -jit(true); - -(new Function("'a'.replace(/a/,function(x){return(/x/ for each(y in[x]))})"))(); - -jit(false); -actual = 'do not crash' -finish_test(); -//----------------------------------------------------------------------------- - -function start_test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); -} - -function finish_test() -{ - reportCompare(expect, actual, summary); - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/regress/regress-515885.js b/js/src/tests/js1_8_1/regress/regress-515885.js deleted file mode 100644 index 60f3512de..000000000 --- a/js/src/tests/js1_8_1/regress/regress-515885.js +++ /dev/null @@ -1,6 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ -var it = (x for (x in [function(){}])); -it.next(); - -reportCompare("no assertion failure", "no assertion failure", "See bug 515885."); diff --git a/js/src/tests/js1_8_1/strict/generator-eval-arguments.js b/js/src/tests/js1_8_1/strict/generator-eval-arguments.js deleted file mode 100644 index f1c5259e7..000000000 --- a/js/src/tests/js1_8_1/strict/generator-eval-arguments.js +++ /dev/null @@ -1,38 +0,0 @@ -// |reftest| skip-if(Android) -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ - -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/* - * In strict mode, generator expressions may not locally bind 'eval' - * or 'arguments.' - */ -assertEq(testLenientAndStrict('(1 for (eval in []))', - parsesSuccessfully, - parseRaisesException(SyntaxError)), - true); -assertEq(testLenientAndStrict('(1 for ([eval] in []))', - parsesSuccessfully, - parseRaisesException(SyntaxError)), - true); -assertEq(testLenientAndStrict('(1 for ({x:eval} in []))', - parsesSuccessfully, - parseRaisesException(SyntaxError)), - true); -assertEq(testLenientAndStrict('(1 for (arguments in []))', - parsesSuccessfully, - parseRaisesException(SyntaxError)), - true); -assertEq(testLenientAndStrict('(1 for ([arguments] in []))', - parsesSuccessfully, - parseRaisesException(SyntaxError)), - true); -assertEq(testLenientAndStrict('(1 for ({x:arguments} in []))', - parsesSuccessfully, - parseRaisesException(SyntaxError)), - true); - -reportCompare(true, true); diff --git a/js/src/tests/js1_8_5/extensions/regress-627859.js b/js/src/tests/js1_8_5/extensions/regress-627859.js deleted file mode 100644 index 5a29fcea5..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627859.js +++ /dev/null @@ -1,23 +0,0 @@ -// -*- indent-tabs-mode: nil; js-indent-level: 4 -*- -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var x = 42; -function a() { - var x; - function b() { - x = 43; - // When jsparse.cpp's CompExprTransplanter transplants the - // comprehension expression 'x' into the scope of the 'for' loop, - // it must not bring the placeholder definition node for the - // assignment to x above along with it. If it does, x won't appear - // in b's lexdeps, we'll never find out that the assignment refers - // to a's x, and we'll generate an assignment to the global x. - (x for (x in [])); - } - b(); -} -a(); -assertEq(x, 42); - -reportCompare(true, true); diff --git a/js/src/tests/js1_8_5/reflect-parse/basicBuilder.js b/js/src/tests/js1_8_5/reflect-parse/basicBuilder.js index 007d983d1..5956c2ec8 100644 --- a/js/src/tests/js1_8_5/reflect-parse/basicBuilder.js +++ b/js/src/tests/js1_8_5/reflect-parse/basicBuilder.js @@ -39,8 +39,6 @@ assertGlobalExpr("(function() { })", 11, { functionExpression: () => 11 }); assertGlobalExpr("[1,2,3]", 12, { arrayExpression: () => 12 }); assertGlobalExpr("({ x: y })", 13, { objectExpression: () => 13 }); assertGlobalExpr("this", 14, { thisExpression: () => 14 }); -assertGlobalExpr("[x for (x in y)]", 17, { comprehensionExpression: () => 17 }); -assertGlobalExpr("(x for (x in y))", 18, { generatorExpression: () => 18 }); assertGlobalExpr("(function() { yield 42 })", genFunExpr("legacy", null, [], blockStmt([exprStmt(19)])), { yieldExpression: () => 19 }); assertGlobalStmt("switch (x) { case y: }", switchStmt(ident("x"), [1]), { switchCase: () => 1 }); @@ -50,9 +48,6 @@ assertGlobalStmt("try { } catch (e) { }", tryStmt(blockStmt([]), [], 2, null), { assertGlobalStmt("try { } catch (e if e instanceof A) { } catch (e if e instanceof B) { }", tryStmt(blockStmt([]), [2, 2], null, null), { catchClause: () => 2 }); -assertGlobalExpr("[x for (y in z) for (x in y)]", - compExpr(ident("x"), [3, 3], null, "legacy"), - { comprehensionBlock: () => 3 }); assertGlobalExpr("({ x: y } = z)", aExpr("=", 1, ident("z")), { objectPattern: () => 1 }); assertGlobalExpr("({ x: y } = z)", aExpr("=", objPatt([2]), ident("z")), { propertyPattern: () => 2 }); diff --git a/js/src/tests/js1_8_5/reflect-parse/generatorExpressions.js b/js/src/tests/js1_8_5/reflect-parse/generatorExpressions.js index cdd4bb33b..e186c9ba8 100644 --- a/js/src/tests/js1_8_5/reflect-parse/generatorExpressions.js +++ b/js/src/tests/js1_8_5/reflect-parse/generatorExpressions.js @@ -1,79 +1,33 @@ // |reftest| skip-if(!xulRuntime.shell) function test() { -// generator expressions - -assertExpr("( x for (x in foo))", - genExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], null, "legacy")); -assertExpr("( [x,y] for (x in foo) for (y in bar))", - genExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], null, "legacy")); -assertExpr("( [x,y,z] for (x in foo) for (y in bar) for (z in baz))", - genExpr(arrExpr([ident("x"), ident("y"), ident("z")]), - [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar")), compBlock(ident("z"), ident("baz"))], - null, - "legacy")); - -assertExpr("( x for (x in foo) if (p))", - genExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], ident("p"), "legacy")); -assertExpr("( [x,y] for (x in foo) for (y in bar) if (p))", - genExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], ident("p"), "legacy")); -assertExpr("( [x,y,z] for (x in foo) for (y in bar) for (z in baz) if (p) )", - genExpr(arrExpr([ident("x"), ident("y"), ident("z")]), - [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar")), compBlock(ident("z"), ident("baz"))], - ident("p"), - "legacy")); - -assertExpr("( x for each (x in foo))", - genExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], null, "legacy")); -assertExpr("( [x,y] for each (x in foo) for each (y in bar))", - genExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], null, "legacy")); -assertExpr("( [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz))", - genExpr(arrExpr([ident("x"), ident("y"), ident("z")]), - [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar")), compEachBlock(ident("z"), ident("baz"))], - null, - "legacy")); - -assertExpr("( x for each (x in foo) if (p))", - genExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], ident("p"), "legacy")); -assertExpr("( [x,y] for each (x in foo) for each (y in bar) if (p))", - genExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], ident("p"), "legacy")); -assertExpr("( [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz) if (p) )", - genExpr(arrExpr([ident("x"), ident("y"), ident("z")]), - [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar")), compEachBlock(ident("z"), ident("baz"))], - ident("p"), - "legacy")); - -// Generator expressions using for-of can be written in two different styles. -function assertLegacyAndModernGenExpr(expr, body, blocks, filter) { - assertExpr(expr, genExpr(body, blocks, filter, "legacy")); - - // Transform the legacy genexpr to a modern genexpr and test it that way - // too. +// Translate legacy genexprs into less legacy genexprs and test them. +function assertFormerlyES6GenExpr(expr, body, blocks, filter) { let match = expr.match(/^\((.*?) for (.*)\)$/); assertEq(match !== null, true); let expr2 = "(for " + match[2] + " " + match[1] + ")"; assertExpr(expr2, genExpr(body, blocks, filter, "modern")); } -assertLegacyAndModernGenExpr("( x for (x of foo))", - ident("x"), [compOfBlock(ident("x"), ident("foo"))], null); -assertLegacyAndModernGenExpr("( [x,y] for (x of foo) for (y of bar))", - arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], null); -assertLegacyAndModernGenExpr("( [x,y,z] for (x of foo) for (y of bar) for (z of baz))", - arrExpr([ident("x"), ident("y"), ident("z")]), - [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))], - null); +assertFormerlyES6GenExpr("( x for (x of foo))", + ident("x"), [compOfBlock(ident("x"), ident("foo"))], null); +assertFormerlyES6GenExpr("( [x,y] for (x of foo) for (y of bar))", + arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], null); +assertFormerlyES6GenExpr("( [x,y,z] for (x of foo) for (y of bar) for (z of baz))", + arrExpr([ident("x"), ident("y"), ident("z")]), + [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))], + null); -assertLegacyAndModernGenExpr("( x for (x of foo) if (p))", - ident("x"), [compOfBlock(ident("x"), ident("foo"))], ident("p")); -assertLegacyAndModernGenExpr("( [x,y] for (x of foo) for (y of bar) if (p))", - arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], ident("p")); -assertLegacyAndModernGenExpr("( [x,y,z] for (x of foo) for (y of bar) for (z of baz) if (p) )", - arrExpr([ident("x"), ident("y"), ident("z")]), - [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))], - ident("p")); +assertFormerlyES6GenExpr("( x for (x of foo) if (p))", + ident("x"), [compOfBlock(ident("x"), ident("foo"))], ident("p")); +assertFormerlyES6GenExpr("( [x,y] for (x of foo) for (y of bar) if (p))", + arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], ident("p")); +assertFormerlyES6GenExpr("( [x,y,z] for (x of foo) for (y of bar) for (z of baz) if (p) )", + arrExpr([ident("x"), ident("y"), ident("z")]), + [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))], + ident("p")); -// Modern generator comprehension with multiple ComprehensionIf. +// FormerlyES6 generator comprehension with multiple ComprehensionIf. assertExpr("(for (x of foo) x)", genExpr(ident("x"), [compOfBlock(ident("x"), ident("foo"))], null, "modern")); diff --git a/js/src/tests/js1_8_5/regress/regress-576847.js b/js/src/tests/js1_8_5/regress/regress-576847.js deleted file mode 100644 index ba9828c03..000000000 --- a/js/src/tests/js1_8_5/regress/regress-576847.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/* Don't crash. */ -try { - eval("function f(){}(((f)for(x in function(){}))())"); - var threwTypeError = false; -} catch (x) { - var threwTypeError = x instanceof TypeError; -} -assertEq(threwTypeError, true); - -/* Properly bind f. */ -assertEq(eval("function f() {}; var i = (f for (f in [1])); uneval([n for (n in i)])"), - '["0"]'); - -reportCompare(true, true); diff --git a/js/src/tests/js1_8_5/regress/regress-620750.js b/js/src/tests/js1_8_5/regress/regress-620750.js deleted file mode 100644 index 1bac543d5..000000000 --- a/js/src/tests/js1_8_5/regress/regress-620750.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ -var expect = true; -var actual = expect; - -function f() { - 'use strict'; - for (; false; (0 for each (t in eval("")))) { } -} -fs = "" + f; -try { - eval("(" + fs + ")"); -} catch (e) { - actual = false; -} - -reportCompare(expect, actual, "ok"); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index a3a3fda41..79f27f643 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -144,6 +144,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) entryMonitor(nullptr), parentRuntime(parentRuntime), interrupt_(false), + interruptRegExpJit_(false), telemetryCallback(nullptr), handlingSignal(false), interruptCallback(nullptr), @@ -641,11 +642,11 @@ JSRuntime::requestInterrupt(InterruptMode mode) jitStackLimit_ = UINTPTR_MAX; if (mode == JSRuntime::RequestInterruptUrgent) { - // If this interrupt is urgent (slow script dialog and garbage - // collection among others), take additional steps to - // interrupt corner cases where the above fields are not - // regularly polled. Wake both ilooping JIT code and - // futexWait. + // If this interrupt is urgent (slow script dialog for instance), take + // additional steps to interrupt corner cases where the above fields are + // not regularly polled. Wake ilooping Ion code, irregexp JIT code and + // Atomics.wait() + interruptRegExpJit_ = true; fx.lock(); if (fx.isWaiting()) fx.wake(FutexRuntime::WakeForJSInterrupt); @@ -660,6 +661,7 @@ JSRuntime::handleInterrupt(JSContext* cx) MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); if (interrupt_ || jitStackLimit_ == UINTPTR_MAX) { interrupt_ = false; + interruptRegExpJit_ = false; resetJitStackLimit(); return InvokeInterruptCallback(cx); } diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 9df53087c..d91e9ceb0 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -794,6 +794,7 @@ struct JSRuntime : public JS::shadow::Runtime, private: mozilla::Atomic interrupt_; + mozilla::Atomic interruptRegExpJit_; /* Call this to accumulate telemetry data. */ JSAccumulateTelemetryDataCallback telemetryCallback; @@ -841,6 +842,10 @@ struct JSRuntime : public JS::shadow::Runtime, static_assert(sizeof(interrupt_) == sizeof(uint32_t), "Assumed by JIT callers"); return &interrupt_; } + void* addressOfInterruptRegExpJitUint32() { // see bug 1386199 + static_assert(sizeof(interruptRegExpJit_) == sizeof(uint32_t), "Assumed by JIT callers"); + return &interruptRegExpJit_; + } /* Set when handling a signal for a thread associated with this runtime. */ bool handlingSignal; diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index fa796bcda..2815d3102 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,11 +29,11 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 336; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 338; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 434, +static_assert(JSErr_Limit == 435, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " diff --git a/netwerk/protocol/http/Http2Stream.cpp b/netwerk/protocol/http/Http2Stream.cpp index cbde1ddb2..fea9b6155 100644 --- a/netwerk/protocol/http/Http2Stream.cpp +++ b/netwerk/protocol/http/Http2Stream.cpp @@ -576,8 +576,7 @@ Http2Stream::GenerateOpen() firstFrameFlags |= Http2Session::kFlag_END_STREAM; } else if (head->IsPost() || head->IsPut() || - head->IsConnect() || - head->IsOptions()) { + head->IsConnect()) { // place fin in a data frame even for 0 length messages for iterop } else if (!mRequestBodyLenRemaining) { // for other HTTP extension methods, rely on the content-length diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index c430ad085..183684b68 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -181,7 +181,8 @@ HttpBaseChannel::Init(nsIURI *aURI, rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); if (NS_FAILED(rv)) return rv; - rv = gHttpHandler->AddStandardRequestHeaders(&mRequestHead.Headers(), isHTTPS); + rv = gHttpHandler->AddStandardRequestHeaders(&mRequestHead.Headers(), isHTTPS, +hostLine); if (NS_FAILED(rv)) return rv; nsAutoCString type; diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 2877e3f40..b8582e07e 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -421,14 +421,28 @@ nsHttpHandler::InitConnectionMgr() } nsresult -nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request, bool isSecure) +nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request, + bool isSecure, + const nsACString &hostLine) { nsresult rv; - // Add the "User-Agent" header - rv = request->SetHeader(nsHttp::User_Agent, UserAgent(), - false, nsHttpHeaderArray::eVarietyDefault); - if (NS_FAILED(rv)) return rv; + // Add the "User-Agent" header (unless we have blacklisted this site and + // we aren't using a custom user agent; see TenFourFox issue 422). + if (mUserAgentOverride || ( + !hostLine.EqualsLiteral("i.imgur.com") && + !hostLine.EqualsLiteral("imgur.com") && + 1)) { + rv = request->SetHeader(nsHttp::User_Agent, UserAgent(), + false, nsHttpHeaderArray::eVarietyDefault); + if (NS_FAILED(rv)) return rv; + } +#if DEBUG + else { + fprintf(stderr, "User agent header suppressed for host: %s\n", + PromiseFlatCString(hostLine).get()); + } +#endif // MIME based content negotiation lives! // Add the "Accept" header. Note, this is set as an override because the diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index 6bd27389e..0e825700c 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -67,7 +67,8 @@ public: nsHttpHandler(); nsresult Init(); - nsresult AddStandardRequestHeaders(nsHttpHeaderArray *, bool isSecure); + nsresult AddStandardRequestHeaders(nsHttpHeaderArray *, bool isSecure, + const nsACString &hostLine); nsresult AddConnectionHeader(nsHttpHeaderArray *, uint32_t capabilities); bool IsAcceptableEncoding(const char *encoding, bool isSecure); diff --git a/toolkit/components/places/AsyncFaviconHelpers.cpp b/toolkit/components/places/AsyncFaviconHelpers.cpp index e65a8f48d..b76d419b9 100644 --- a/toolkit/components/places/AsyncFaviconHelpers.cpp +++ b/toolkit/components/places/AsyncFaviconHelpers.cpp @@ -674,6 +674,7 @@ AsyncFetchAndSetIconForPage::OnStopRequest(nsIRequest* aRequest, mIcon.expiration = GetExpirationTimeFromChannel(channel); +#if(0) // Telemetry probes to measure the favicon file sizes for each different file type. // This allow us to measure common file sizes while also observing each type popularity. if (mIcon.mimeType.EqualsLiteral("image/png")) { @@ -700,6 +701,7 @@ AsyncFetchAndSetIconForPage::OnStopRequest(nsIRequest* aRequest, else { mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_OTHER_SIZES, mIcon.data.Length()); } +#endif rv = OptimizeIconSize(mIcon, favicons); NS_ENSURE_SUCCESS(rv, rv); diff --git a/toolkit/components/places/PlacesDBUtils.jsm b/toolkit/components/places/PlacesDBUtils.jsm index 13abab935..c2ddfefe1 100644 --- a/toolkit/components/places/PlacesDBUtils.jsm +++ b/toolkit/components/places/PlacesDBUtils.jsm @@ -55,11 +55,13 @@ this.PlacesDBUtils = { else { // All tasks have been completed. // Telemetry the time it took for maintenance, if a start time exists. +/* if (aTasks._telemetryStart) { Services.telemetry.getHistogramById("PLACES_IDLE_MAINTENANCE_TIME_MS") .add(Date.now() - aTasks._telemetryStart); aTasks._telemetryStart = 0; } +*/ if (aTasks.callback) { let scope = aTasks.scope || Cu.getGlobalForObject(aTasks.callback); @@ -92,7 +94,7 @@ this.PlacesDBUtils = { , this.checkCoherence , this._refreshUI ]); - tasks._telemetryStart = Date.now(); + //tasks._telemetryStart = Date.now(); tasks.callback = function() { Services.prefs.setIntPref("places.database.lastMaintenance", parseInt(Date.now() / 1000)); @@ -870,6 +872,7 @@ this.PlacesDBUtils = { telemetry: function PDBU_telemetry(aTasks, aHealthReportCallback=null) { let tasks = new Tasks(aTasks); +/* let isTelemetry = !aHealthReportCallback; @@ -948,7 +951,7 @@ this.PlacesDBUtils = { }, { histogram: "PLACES_DATABASE_PAGESIZE_B", - query: "PRAGMA page_size /* PlacesDBUtils.jsm PAGESIZE_B */" }, + query: "PRAGMA page_size" }, { histogram: "PLACES_DATABASE_SIZE_PER_PAGE_B", query: "PRAGMA page_count", @@ -1053,6 +1056,7 @@ this.PlacesDBUtils = { ); } +*/ PlacesDBUtils._executeTasks(tasks); }, diff --git a/toolkit/components/places/UnifiedComplete.js b/toolkit/components/places/UnifiedComplete.js index 29aeb197a..d45e28758 100644 --- a/toolkit/components/places/UnifiedComplete.js +++ b/toolkit/components/places/UnifiedComplete.js @@ -254,8 +254,10 @@ Cu.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); +/* XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm"); +*/ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Preferences", @@ -859,9 +861,11 @@ Search.prototype = { if (!this.pending) return; +/* TelemetryStopwatch.start(TELEMETRY_1ST_RESULT, this); if (this._searchString) TelemetryStopwatch.start(TELEMETRY_6_FIRST_RESULTS, this); +*/ // Since we call the synchronous parseSubmissionURL function later, we must // wait for the initialization of PlacesSearchAutocompleteProvider first. @@ -1339,7 +1343,7 @@ Search.prototype = { }, _onResultRow: function (row) { - TelemetryStopwatch.finish(TELEMETRY_1ST_RESULT, this); + //TelemetryStopwatch.finish(TELEMETRY_1ST_RESULT, this); let queryType = row.getResultByIndex(QUERYINDEX_QUERYTYPE); let match; switch (queryType) { @@ -1430,8 +1434,10 @@ Search.prototype = { match.style, match.finalCompleteValue); +/* if (this._result.matchCount == 6) TelemetryStopwatch.finish(TELEMETRY_6_FIRST_RESULTS, this); +*/ this.notifyResults(true); }, @@ -1939,8 +1945,10 @@ UnifiedComplete.prototype = { * results or not. */ finishSearch: function (notify=false) { +/* TelemetryStopwatch.cancel(TELEMETRY_1ST_RESULT, this); TelemetryStopwatch.cancel(TELEMETRY_6_FIRST_RESULTS, this); +*/ // Clear state now to avoid race conditions, see below. let search = this._currentSearch; this._lastLowResultsSearchSuggestion = search._lastLowResultsSearchSuggestion; diff --git a/toolkit/components/places/nsPlacesAutoComplete.js b/toolkit/components/places/nsPlacesAutoComplete.js index 9f8f56030..8e24137e0 100644 --- a/toolkit/components/places/nsPlacesAutoComplete.js +++ b/toolkit/components/places/nsPlacesAutoComplete.js @@ -8,8 +8,10 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); +/* XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm"); +*/ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", @@ -1525,7 +1527,7 @@ urlInlineComplete.prototype = { let query = this._hostQuery; query.params.search_string = this._currentSearchString.toLowerCase(); // This is just to measure the delay to reach the UI, not the query time. - TelemetryStopwatch.start(DOMAIN_QUERY_TELEMETRY); + //TelemetryStopwatch.start(DOMAIN_QUERY_TELEMETRY); let wrapper = new AutoCompleteStatementCallbackWrapper(this, { handleResult: aResultSet => { if (this._pendingSearch != pendingSearch) @@ -1555,7 +1557,7 @@ urlInlineComplete.prototype = { handleCompletion: aReason => { if (this._pendingSearch != pendingSearch) return; - TelemetryStopwatch.finish(DOMAIN_QUERY_TELEMETRY); + //TelemetryStopwatch.finish(DOMAIN_QUERY_TELEMETRY); this._finishSearch(); } }, this._db);