author | Dorel Luca <dluca@mozilla.com> |
Thu, 14 Dec 2017 11:58:34 +0200 | |
changeset 396435 | 16bcfaad13e10377b1e5ba557da0545b4b3b4f75 |
parent 396434 | bcdf5a72af42869141893e69aa1c3b0b4149c3e9 (current diff) |
parent 396366 | 8062887ff0d9382ea84177f2c21f62dc0e613d9e (diff) |
child 396436 | 70eb26bf87605ed49e36e4c193801ac89e477936 |
push id | 98313 |
push user | nbeleuzu@mozilla.com |
push date | Fri, 15 Dec 2017 01:48:15 +0000 |
treeherder | mozilla-inbound@cebcea19cfb0 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 59.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
intl/lwbrk/nsILineBreaker.h | file | annotate | diff | comparison | revisions | |
intl/lwbrk/nsIWordBreaker.h | file | annotate | diff | comparison | revisions | |
intl/lwbrk/nsJISx4051LineBreaker.cpp | file | annotate | diff | comparison | revisions | |
intl/lwbrk/nsJISx4051LineBreaker.h | file | annotate | diff | comparison | revisions | |
intl/lwbrk/nsSampleWordBreaker.cpp | file | annotate | diff | comparison | revisions | |
intl/lwbrk/nsSampleWordBreaker.h | file | annotate | diff | comparison | revisions | |
js/src/tests/test262/built-ins/RegExp/incomplete_hex_unicode_escape.js | file | annotate | diff | comparison | revisions | |
js/src/tests/test262/language/expressions/typeof/native-no-call.js | file | annotate | diff | comparison | revisions | |
taskcluster/ci/release-notify-publish/kind.yml | file | annotate | diff | comparison | revisions |
--- a/build/sparse-profiles/taskgraph +++ b/build/sparse-profiles/taskgraph @@ -1,15 +1,16 @@ %include build/sparse-profiles/mach [include] # These files are read as part of generating the taskgraph. path:browser/locales/l10n-changesets.json path:mobile/locales/l10n-changesets.json path:browser/config/version_display.txt +path:browser/config/version.txt # Lots of random files in here are read. Just pull in the whole thing. path:build/ # TODO remove once bug 1402010 is resolved and test manifests aren't # processed in Files() reading mode in moz.build files. path:layout/tools/reftest/ path:testing/web-platform/tests/tools/
--- a/devtools/server/actors/highlighters.css +++ b/devtools/server/actors/highlighters.css @@ -617,17 +617,18 @@ position: absolute; overflow: visible; } :-moz-native-anonymous .shapes-polygon, :-moz-native-anonymous .shapes-ellipse, :-moz-native-anonymous .shapes-rect, :-moz-native-anonymous .shapes-bounding-box, -:-moz-native-anonymous .shapes-rotate-line { +:-moz-native-anonymous .shapes-rotate-line, +:-moz-native-anonymous .shapes-quad { fill: transparent; stroke: var(--highlighter-guide-color); shape-rendering: geometricPrecision; vector-effect: non-scaling-stroke; } :-moz-native-anonymous .shapes-markers { fill: #fff;
--- a/devtools/server/actors/highlighters/shapes.js +++ b/devtools/server/actors/highlighters/shapes.js @@ -115,16 +115,106 @@ class ShapesHighlighter extends AutoRefr "id": "shape-container", "class": "shape-container", "viewBox": "0 0 100 100", "preserveAspectRatio": "none" }, prefix: this.ID_CLASS_PREFIX }); + // This clipPath and its children make sure the element quad outline + // is only shown when the shape extends past the element quads. + let clipSvg = createSVGNode(this.win, { + nodeType: "clipPath", + parent: mainSvg, + attributes: { + "id": "clip-path", + "class": "clip-path", + }, + prefix: this.ID_CLASS_PREFIX + }); + + createSVGNode(this.win, { + nodeType: "polygon", + parent: clipSvg, + attributes: { + "id": "clip-polygon", + "class": "clip-polygon", + "hidden": "true" + }, + prefix: this.ID_CLASS_PREFIX + }); + + createSVGNode(this.win, { + nodeType: "ellipse", + parent: clipSvg, + attributes: { + "id": "clip-ellipse", + "class": "clip-ellipse", + "hidden": true + }, + prefix: this.ID_CLASS_PREFIX + }); + + createSVGNode(this.win, { + nodeType: "rect", + parent: clipSvg, + attributes: { + "id": "clip-rect", + "class": "clip-rect", + "hidden": true + }, + prefix: this.ID_CLASS_PREFIX + }); + + // Rectangle that displays the element quads. Only shown for shape-outside. + // Only the parts of the rectangle's outline that overlap with the shape is shown. + createSVGNode(this.win, { + nodeType: "rect", + parent: mainSvg, + attributes: { + "id": "quad", + "class": "quad", + "hidden": "true", + "clip-path": "url(#shapes-clip-path)", + "x": 0, + "y": 0, + "width": 100, + "height": 100 + }, + prefix: this.ID_CLASS_PREFIX + }); + + // clipPath that corresponds to the element's quads. Only applied for shape-outside. + // This ensures only the parts of the shape that are within the element's quads are + // outlined by a solid line. + let shapeClipSvg = createSVGNode(this.win, { + nodeType: "clipPath", + parent: mainSvg, + attributes: { + "id": "quad-clip-path", + "class": "quad-clip-path", + }, + prefix: this.ID_CLASS_PREFIX + }); + + createSVGNode(this.win, { + nodeType: "rect", + parent: shapeClipSvg, + attributes: { + "id": "quad-clip", + "class": "quad-clip", + "x": -1, + "y": -1, + "width": 102, + "height": 102 + }, + prefix: this.ID_CLASS_PREFIX + }); + let mainGroup = createSVGNode(this.win, { nodeType: "g", parent: mainSvg, attributes: { "id": "group", }, prefix: this.ID_CLASS_PREFIX }); @@ -160,16 +250,54 @@ class ShapesHighlighter extends AutoRefr attributes: { "id": "rect", "class": "rect", "hidden": true }, prefix: this.ID_CLASS_PREFIX }); + // Dashed versions of each shape. Only shown for the parts of the shape + // that extends past the element's quads. + createSVGNode(this.win, { + nodeType: "polygon", + parent: mainGroup, + attributes: { + "id": "dashed-polygon", + "class": "polygon", + "hidden": "true", + "stroke-dasharray": "5, 5", + }, + prefix: this.ID_CLASS_PREFIX + }); + + createSVGNode(this.win, { + nodeType: "ellipse", + parent: mainGroup, + attributes: { + "id": "dashed-ellipse", + "class": "ellipse", + "hidden": "true", + "stroke-dasharray": "5, 5", + }, + prefix: this.ID_CLASS_PREFIX + }); + + createSVGNode(this.win, { + nodeType: "rect", + parent: mainGroup, + attributes: { + "id": "dashed-rect", + "class": "rect", + "hidden": "true", + "stroke-dasharray": "5, 5", + }, + prefix: this.ID_CLASS_PREFIX + }); + createSVGNode(this.win, { nodeType: "path", parent: mainGroup, attributes: { "id": "bounding-box", "class": "bounding-box", "stroke-dasharray": "5, 5", "hidden": true @@ -314,24 +442,25 @@ class ShapesHighlighter extends AutoRefr let { target, type, pageX, pageY } = event; // For events on highlighted nodes in an iframe, when the event takes place // outside the iframe. Check if event target belongs to the iframe. If it doesn't, // adjust pageX/pageY to be relative to the iframe rather than the parent. let nodeDocument = this.currentNode.ownerDocument; if (target !== nodeDocument && target.ownerDocument !== nodeDocument) { let [xOffset, yOffset] = getFrameOffsets(target.ownerGlobal, this.currentNode); + let zoom = getCurrentZoom(this.win); // xOffset/yOffset are relative to the viewport, so first find the top/left // edges of the viewport relative to the page. let viewportLeft = pageX - event.clientX; let viewportTop = pageY - event.clientY; // Also adjust for scrolling in the iframe. let { scrollTop, scrollLeft } = nodeDocument.documentElement; - pageX -= viewportLeft + xOffset - scrollLeft; - pageY -= viewportTop + yOffset - scrollTop; + pageX -= viewportLeft + xOffset / zoom - scrollLeft; + pageY -= viewportTop + yOffset / zoom - scrollTop; } switch (type) { case "pagehide": // If a page hide event is triggered for current window's highlighter, hide the // highlighter. if (target.defaultView === this.win) { this.destroy(); @@ -2007,16 +2136,23 @@ class ShapesHighlighter extends AutoRefr _hideShapes() { this.getElement("ellipse").setAttribute("hidden", true); this.getElement("polygon").setAttribute("hidden", true); this.getElement("rect").setAttribute("hidden", true); this.getElement("bounding-box").setAttribute("hidden", true); this.getElement("markers").setAttribute("d", ""); this.getElement("markers-outline").setAttribute("d", ""); this.getElement("rotate-line").setAttribute("d", ""); + this.getElement("quad").setAttribute("hidden", true); + this.getElement("clip-ellipse").setAttribute("hidden", true); + this.getElement("clip-polygon").setAttribute("hidden", true); + this.getElement("clip-rect").setAttribute("hidden", true); + this.getElement("dashed-polygon").setAttribute("hidden", true); + this.getElement("dashed-ellipse").setAttribute("hidden", true); + this.getElement("dashed-rect").setAttribute("hidden", true); } /** * Update the highlighter for the current node. Called whenever the element's quads * or CSS shape has changed. * @returns {Boolean} whether the highlighter was successfully updated */ _update() { @@ -2041,16 +2177,32 @@ class ShapesHighlighter extends AutoRefr } else if (this.shapeType === "circle") { this._updateCircleShape(width, height, zoom); } else if (this.shapeType === "ellipse") { this._updateEllipseShape(width, height, zoom); } else if (this.shapeType === "inset") { this._updateInsetShape(width, height, zoom); } + if (this.property === "shape-outside") { + // For shape-outside, the element's quads are displayed for the parts that overlap + // with the shape, and the parts of the shape that extend past the element's quads + // are shown with a dashed line. + let quadRect = this.getElement("quad"); + quadRect.removeAttribute("hidden"); + + this.getElement("polygon").setAttribute("clip-path", "url(#shapes-quad-clip-path)"); + this.getElement("ellipse").setAttribute("clip-path", "url(#shapes-quad-clip-path)"); + this.getElement("rect").setAttribute("clip-path", "url(#shapes-quad-clip-path)"); + } else { + this.getElement("polygon").removeAttribute("clip-path"); + this.getElement("ellipse").removeAttribute("clip-path"); + this.getElement("rect").removeAttribute("clip-path"); + } + let { width: winWidth, height: winHeight } = this._winDimensions; root.removeAttribute("hidden"); root.setAttribute("style", `position:absolute; width:${winWidth}px;height:${winHeight}px; overflow:hidden;`); this._handleMarkerHover(this.hoveredPoint); setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement); @@ -2079,27 +2231,49 @@ class ShapesHighlighter extends AutoRefr if (this.shapeType === "polygon") { let points = this.coordinates.map(point => point.join(",")).join(" "); let polygonEl = this.getElement("polygon"); polygonEl.setAttribute("points", points); polygonEl.removeAttribute("hidden"); + let clipPolygon = this.getElement("clip-polygon"); + clipPolygon.setAttribute("points", points); + clipPolygon.removeAttribute("hidden"); + + let dashedPolygon = this.getElement("dashed-polygon"); + dashedPolygon.setAttribute("points", points); + dashedPolygon.removeAttribute("hidden"); + markerPoints.push(rotatePoint); let rotateLine = `M ${center.join(" ")} L ${rotatePoint.join(" ")}`; this.getElement("rotate-line").setAttribute("d", rotateLine); } else if (this.shapeType === "circle" || this.shapeType === "ellipse") { let { rx, ry, cx, cy } = this.coordinates; let ellipseEl = this.getElement("ellipse"); ellipseEl.setAttribute("rx", rx); ellipseEl.setAttribute("ry", ry); ellipseEl.setAttribute("cx", cx); ellipseEl.setAttribute("cy", cy); ellipseEl.removeAttribute("hidden"); + + let clipEllipse = this.getElement("clip-ellipse"); + clipEllipse.setAttribute("rx", rx); + clipEllipse.setAttribute("ry", ry); + clipEllipse.setAttribute("cx", cx); + clipEllipse.setAttribute("cy", cy); + clipEllipse.removeAttribute("hidden"); + + let dashedEllipse = this.getElement("dashed-ellipse"); + dashedEllipse.setAttribute("rx", rx); + dashedEllipse.setAttribute("ry", ry); + dashedEllipse.setAttribute("cx", cx); + dashedEllipse.setAttribute("cy", cy); + dashedEllipse.removeAttribute("hidden"); } this._drawMarkers(markerPoints, width, height, zoom); } /** * Update the SVG polygon to fit the CSS polygon. * @param {Number} width the width of the element quads * @param {Number} height the height of the element quads @@ -2108,16 +2282,23 @@ class ShapesHighlighter extends AutoRefr _updatePolygonShape(width, height, zoom) { // Draw and show the polygon. let points = this.coordinates.map(point => point.join(",")).join(" "); let polygonEl = this.getElement("polygon"); polygonEl.setAttribute("points", points); polygonEl.removeAttribute("hidden"); + let clipPolygon = this.getElement("clip-polygon"); + clipPolygon.setAttribute("points", points); + clipPolygon.removeAttribute("hidden"); + + let dashedPolygon = this.getElement("dashed-polygon"); + dashedPolygon.setAttribute("points", points); + dashedPolygon.removeAttribute("hidden"); this._drawMarkers(this.coordinates, width, height, zoom); } /** * Update the SVG ellipse to fit the CSS circle. * @param {Number} width the width of the element quads * @param {Number} height the height of the element quads * @param {Number} zoom the zoom level of the window @@ -2126,16 +2307,30 @@ class ShapesHighlighter extends AutoRefr let { rx, ry, cx, cy } = this.coordinates; let ellipseEl = this.getElement("ellipse"); ellipseEl.setAttribute("rx", rx); ellipseEl.setAttribute("ry", ry); ellipseEl.setAttribute("cx", cx); ellipseEl.setAttribute("cy", cy); ellipseEl.removeAttribute("hidden"); + let clipEllipse = this.getElement("clip-ellipse"); + clipEllipse.setAttribute("rx", rx); + clipEllipse.setAttribute("ry", ry); + clipEllipse.setAttribute("cx", cx); + clipEllipse.setAttribute("cy", cy); + clipEllipse.removeAttribute("hidden"); + + let dashedEllipse = this.getElement("dashed-ellipse"); + dashedEllipse.setAttribute("rx", rx); + dashedEllipse.setAttribute("ry", ry); + dashedEllipse.setAttribute("cx", cx); + dashedEllipse.setAttribute("cy", cy); + dashedEllipse.removeAttribute("hidden"); + this._drawMarkers([[cx, cy], [cx + rx, cy]], width, height, zoom); } /** * Update the SVG ellipse to fit the CSS ellipse. * @param {Number} width the width of the element quads * @param {Number} height the height of the element quads * @param {Number} zoom the zoom level of the window @@ -2144,16 +2339,29 @@ class ShapesHighlighter extends AutoRefr let { rx, ry, cx, cy } = this.coordinates; let ellipseEl = this.getElement("ellipse"); ellipseEl.setAttribute("rx", rx); ellipseEl.setAttribute("ry", ry); ellipseEl.setAttribute("cx", cx); ellipseEl.setAttribute("cy", cy); ellipseEl.removeAttribute("hidden"); + let clipEllipse = this.getElement("clip-ellipse"); + clipEllipse.setAttribute("rx", rx); + clipEllipse.setAttribute("ry", ry); + clipEllipse.setAttribute("cx", cx); + clipEllipse.setAttribute("cy", cy); + clipEllipse.removeAttribute("hidden"); + + let dashedEllipse = this.getElement("dashed-ellipse"); + dashedEllipse.setAttribute("rx", rx); + dashedEllipse.setAttribute("ry", ry); + dashedEllipse.setAttribute("cx", cx); + dashedEllipse.setAttribute("cy", cy); + dashedEllipse.removeAttribute("hidden"); let markerCoords = [ [cx, cy], [cx + rx, cy], [cx, cy + ry] ]; this._drawMarkers(markerCoords, width, height, zoom); } /** * Update the SVG rect to fit the CSS inset. * @param {Number} width the width of the element quads * @param {Number} height the height of the element quads @@ -2163,16 +2371,30 @@ class ShapesHighlighter extends AutoRefr let { top, left, right, bottom } = this.coordinates; let rectEl = this.getElement("rect"); rectEl.setAttribute("x", left); rectEl.setAttribute("y", top); rectEl.setAttribute("width", 100 - left - right); rectEl.setAttribute("height", 100 - top - bottom); rectEl.removeAttribute("hidden"); + let clipRect = this.getElement("clip-rect"); + clipRect.setAttribute("x", left); + clipRect.setAttribute("y", top); + clipRect.setAttribute("width", 100 - left - right); + clipRect.setAttribute("height", 100 - top - bottom); + clipRect.removeAttribute("hidden"); + + let dashedRect = this.getElement("dashed-rect"); + dashedRect.setAttribute("x", left); + dashedRect.setAttribute("y", top); + dashedRect.setAttribute("width", 100 - left - right); + dashedRect.setAttribute("height", 100 - top - bottom); + dashedRect.removeAttribute("hidden"); + let centerX = (left + (100 - right)) / 2; let centerY = (top + (100 - bottom)) / 2; let markerCoords = [[centerX, top], [100 - right, centerY], [centerX, 100 - bottom], [left, centerY]]; this._drawMarkers(markerCoords, width, height, zoom); } /**
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -145,17 +145,16 @@ #include "nsIFragmentContentSink.h" #include "nsContainerFrame.h" #include "nsIHTMLDocument.h" #include "nsIIdleService.h" #include "nsIImageLoadingContent.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIIOService.h" -#include "nsILineBreaker.h" #include "nsILoadContext.h" #include "nsILoadGroup.h" #include "nsIMemoryReporter.h" #include "nsIMIMEHeaderParam.h" #include "nsIMIMEService.h" #include "nsINode.h" #include "mozilla/dom/NodeInfo.h" #include "nsIObjectLoadingContent.h" @@ -176,20 +175,18 @@ #include "nsIScrollable.h" #include "nsIStreamConverterService.h" #include "nsIStringBundle.h" #include "nsIURI.h" #include "nsIURIWithPrincipal.h" #include "nsIURL.h" #include "nsIWebNavigation.h" #include "nsIWindowMediator.h" -#include "nsIWordBreaker.h" #include "nsIXPConnect.h" #include "nsJSUtils.h" -#include "nsLWBrkCIID.h" #include "nsMappedAttributes.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsNodeInfoManager.h" #include "NullPrincipal.h" #include "nsParserCIID.h" #include "nsParserConstants.h" #include "nsPIDOMWindow.h" @@ -262,18 +259,18 @@ nsIUUIDGenerator *nsContentUtils::sUUIDG nsIConsoleService *nsContentUtils::sConsoleService; nsDataHashtable<nsRefPtrHashKey<nsAtom>, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr; nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr; nsTArray<RefPtr<nsAtom>>* nsContentUtils::sUserDefinedEvents = nullptr; nsIStringBundleService *nsContentUtils::sStringBundleService; nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT]; nsIContentPolicy *nsContentUtils::sContentPolicyService; bool nsContentUtils::sTriedToGetContentPolicy = false; -nsILineBreaker *nsContentUtils::sLineBreaker; -nsIWordBreaker *nsContentUtils::sWordBreaker; +RefPtr<mozilla::intl::LineBreaker> nsContentUtils::sLineBreaker; +RefPtr<mozilla::intl::WordBreaker> nsContentUtils::sWordBreaker; nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nullptr; uint32_t nsContentUtils::sScriptBlockerCount = 0; uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0; AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners = nullptr; uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0; nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr; bool nsContentUtils::sIsHandlingKeyBoardEvent = false; @@ -602,21 +599,19 @@ nsContentUtils::Init() nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService); if (NS_FAILED(rv)) { // This makes life easier, but we can live without it. sIOService = nullptr; } - rv = CallGetService(NS_LBRK_CONTRACTID, &sLineBreaker); - NS_ENSURE_SUCCESS(rv, rv); - - rv = CallGetService(NS_WBRK_CONTRACTID, &sWordBreaker); - NS_ENSURE_SUCCESS(rv, rv); + sLineBreaker = mozilla::intl::LineBreaker::Create(); + + sWordBreaker = mozilla::intl::WordBreaker::Create(); if (!InitializeEventTable()) return NS_ERROR_FAILURE; if (!sEventListenerManagersHash) { static const PLDHashTableOps hash_table_ops = { PLDHashTable::HashVoidPtrKeyStub, @@ -2115,18 +2110,18 @@ nsContentUtils::Shutdown() NS_IF_RELEASE(sStringBundleService); NS_IF_RELEASE(sConsoleService); sXPConnect = nullptr; NS_IF_RELEASE(sSecurityManager); NS_IF_RELEASE(sSystemPrincipal); NS_IF_RELEASE(sNullSubjectPrincipal); NS_IF_RELEASE(sIOService); NS_IF_RELEASE(sUUIDGenerator); - NS_IF_RELEASE(sLineBreaker); - NS_IF_RELEASE(sWordBreaker); + sLineBreaker = nullptr; + sWordBreaker = nullptr; NS_IF_RELEASE(sBidiKeyboard); delete sAtomEventTable; sAtomEventTable = nullptr; delete sStringEventTable; sStringEventTable = nullptr; delete sUserDefinedEvents; sUserDefinedEvents = nullptr;
--- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -29,16 +29,18 @@ #include "nsContentListDeclarations.h" #include "nsMathUtils.h" #include "nsTArrayForwardDeclare.h" #include "Units.h" #include "mozilla/dom/AutocompleteInfoBinding.h" #include "mozilla/dom/BindingDeclarations.h" // For CallerType #include "mozilla/dom/ScriptSettings.h" #include "mozilla/FloatingPoint.h" +#include "mozilla/intl/LineBreaker.h" +#include "mozilla/intl/WordBreaker.h" #include "mozilla/net/ReferrerPolicy.h" #include "mozilla/Logging.h" #include "mozilla/NotNull.h" #include "mozilla/Maybe.h" #include "mozilla/RangeBoundary.h" #include "nsIContentPolicy.h" #include "nsIDocument.h" #include "nsIDOMMouseEvent.h" @@ -75,17 +77,16 @@ class nsIDOMKeyEvent; class nsIDOMNode; class nsIDragSession; class nsIEventTarget; class nsIFragmentContentSink; class nsIFrame; class nsIImageLoadingContent; class nsIInterfaceRequestor; class nsIIOService; -class nsILineBreaker; class nsILoadInfo; class nsILoadGroup; class nsIMessageBroadcaster; class nsNameSpaceManager; class nsIObserver; class nsIParser; class nsIPluginTag; class nsIPresShell; @@ -95,17 +96,16 @@ class nsIRunnable; class nsIScriptContext; class nsIScriptSecurityManager; class nsIStringBundle; class nsIStringBundleService; class nsISupportsHashKey; class nsIURI; class nsIUUIDGenerator; class nsIWidget; -class nsIWordBreaker; class nsIXPConnect; class nsNodeInfoManager; class nsPIDOMWindowInner; class nsPIDOMWindowOuter; class nsPresContext; class nsStringBuffer; class nsStringHashKey; class nsTextFragment; @@ -747,24 +747,24 @@ public: // system principal, and true for a null principal. // This version checks the permission for an exact host match on // the principal static bool IsExactSitePermDeny(nsIPrincipal* aPrincipal, const char* aType); // Returns true if aDoc1 and aDoc2 have equal NodePrincipal()s. static bool HaveEqualPrincipals(nsIDocument* aDoc1, nsIDocument* aDoc2); - static nsILineBreaker* LineBreaker() + static mozilla::intl::LineBreaker* LineBreaker() { - return sLineBreaker; + return sLineBreaker.get(); } - static nsIWordBreaker* WordBreaker() + static mozilla::intl::WordBreaker* WordBreaker() { - return sWordBreaker; + return sWordBreaker.get(); } /** * Regster aObserver as a shutdown observer. A strong reference is held * to aObserver until UnregisterShutdownObserver is called. */ static void RegisterShutdownObserver(nsIObserver* aObserver); static void UnregisterShutdownObserver(nsIObserver* aObserver); @@ -3389,18 +3389,18 @@ private: static nsTArray<RefPtr<nsAtom>>* sUserDefinedEvents; static nsIStringBundleService* sStringBundleService; static nsIStringBundle* sStringBundles[PropertiesFile_COUNT]; static nsIContentPolicy* sContentPolicyService; static bool sTriedToGetContentPolicy; - static nsILineBreaker* sLineBreaker; - static nsIWordBreaker* sWordBreaker; + static RefPtr<mozilla::intl::LineBreaker> sLineBreaker; + static RefPtr<mozilla::intl::WordBreaker> sWordBreaker; static nsIBidiKeyboard* sBidiKeyboard; static bool sInitialized; static uint32_t sScriptBlockerCount; static uint32_t sDOMNodeRemovedSuppressCount; // Not an nsCOMArray because removing elements from those is slower
--- a/dom/base/nsGlobalWindowCommands.cpp +++ b/dom/base/nsGlobalWindowCommands.cpp @@ -26,17 +26,16 @@ #include "nsIWebNavigation.h" #include "nsIContentViewerEdit.h" #include "nsIContentViewer.h" #include "nsFocusManager.h" #include "nsCopySupport.h" #include "nsIClipboard.h" #include "ContentEventHandler.h" #include "nsContentUtils.h" -#include "nsIWordBreaker.h" #include "mozilla/Attributes.h" #include "mozilla/BasicEvents.h" #include "mozilla/TextEvents.h" #include "mozilla/dom/Selection.h" #include "mozilla/layers/KeyboardMap.h" #include "nsIClipboardDragDropHooks.h" #include "nsIClipboardDragDropHookList.h" @@ -1124,22 +1123,22 @@ nsLookUpDictionaryCommand::DoCommandPara handler.OnQueryTextContent(&textContent); if (NS_WARN_IF(!textContent.mSucceeded || textContent.mReply.mString.IsEmpty())) { return NS_ERROR_FAILURE; } // XXX nsIWordBreaker doesn't use contextual breaker. // If OS provides it, widget should use it if contextual breaker is needed. - nsCOMPtr<nsIWordBreaker> wordBreaker = nsContentUtils::WordBreaker(); + RefPtr<mozilla::intl::WordBreaker> wordBreaker = nsContentUtils::WordBreaker(); if (NS_WARN_IF(!wordBreaker)) { return NS_ERROR_FAILURE; } - nsWordRange range = + mozilla::intl::WordRange range = wordBreaker->FindWord(textContent.mReply.mString.get(), textContent.mReply.mString.Length(), charAt.mReply.mOffset - offset); if (range.mEnd == range.mBegin) { return NS_ERROR_FAILURE; } range.mBegin += offset; range.mEnd += offset;
--- a/dom/base/nsHTMLContentSerializer.cpp +++ b/dom/base/nsHTMLContentSerializer.cpp @@ -22,17 +22,16 @@ #include "nsIServiceManager.h" #include "nsIDocumentEncoder.h" #include "nsGkAtoms.h" #include "nsIURI.h" #include "nsNetUtil.h" #include "nsEscape.h" #include "nsCRT.h" #include "nsContentUtils.h" -#include "nsLWBrkCIID.h" #include "nsIScriptElement.h" #include "nsAttrName.h" #include "nsIDocShell.h" #include "nsIEditor.h" #include "nsIHTMLEditor.h" #include "mozilla/dom/Element.h" #include "nsParserConstants.h"
--- a/dom/base/nsLineBreaker.cpp +++ b/dom/base/nsLineBreaker.cpp @@ -1,28 +1,30 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsLineBreaker.h" #include "nsContentUtils.h" -#include "nsILineBreaker.h" #include "gfxTextRun.h" // for the gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_* values #include "nsHyphenationManager.h" #include "nsHyphenator.h" #include "mozilla/gfx/2D.h" +#include "mozilla/intl/LineBreaker.h" + +using mozilla::intl::LineBreaker; nsLineBreaker::nsLineBreaker() : mCurrentWordLanguage(nullptr), mCurrentWordContainsMixedLang(false), mCurrentWordContainsComplexChar(false), mAfterBreakableSpace(false), mBreakHere(false), - mWordBreak(nsILineBreaker::kWordBreak_Normal) + mWordBreak(LineBreaker::kWordBreak_Normal) { } nsLineBreaker::~nsLineBreaker() { NS_ASSERTION(mCurrentWord.Length() == 0, "Should have Reset() before destruction!"); } @@ -64,17 +66,17 @@ nsLineBreaker::FlushCurrentWord() return NS_ERROR_OUT_OF_MEMORY; nsTArray<bool> capitalizationState; if (!mCurrentWordContainsComplexChar) { // For break-strict set everything internal to "break", otherwise // to "no break"! memset(breakState.Elements(), - mWordBreak == nsILineBreaker::kWordBreak_BreakAll ? + mWordBreak == LineBreaker::kWordBreak_BreakAll ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE, length*sizeof(uint8_t)); } else { nsContentUtils::LineBreaker()-> GetJISx4051Breaks(mCurrentWord.Elements(), length, mWordBreak, breakState.Elements()); } @@ -231,17 +233,17 @@ nsLineBreaker::AppendText(nsAtom* aHyphe for (;;) { char16_t ch = aText[offset]; bool isSpace = IsSpace(ch); bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE); if (aSink && !noBreaksNeeded) { breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) || - (mWordBreak == nsILineBreaker::kWordBreak_BreakAll) ? + (mWordBreak == LineBreaker::kWordBreak_BreakAll) ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE; } mBreakHere = false; mAfterBreakableSpace = isBreakableSpace; if (isSpace || ch == '\n') { if (offset > wordStart && aSink) { @@ -398,17 +400,17 @@ nsLineBreaker::AppendText(nsAtom* aHyphe bool isSpace = IsSpace(ch); bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE); if (aSink) { // Consider word-break style. Since the break position of CJK scripts // will be set by nsILineBreaker, we don't consider CJK at this point. breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) || - (mWordBreak == nsILineBreaker::kWordBreak_BreakAll) ? + (mWordBreak == LineBreaker::kWordBreak_BreakAll) ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE; } mBreakHere = false; mAfterBreakableSpace = isBreakableSpace; if (isSpace) { if (offset > wordStart && wordHasComplexChar) {
--- a/dom/base/nsLineBreaker.h +++ b/dom/base/nsLineBreaker.h @@ -4,17 +4,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef NSLINEBREAKER_H_ #define NSLINEBREAKER_H_ #include "nsString.h" #include "nsTArray.h" -#include "nsILineBreaker.h" class nsAtom; class nsHyphenator; /** * A receiver of line break data. */ class nsILineBreakSink { @@ -48,54 +47,55 @@ public: * with each text chunk, which might happen during the corresponding AppendText * call, or might happen during a later AppendText call or even a Reset() * call. * * The linebreak results MUST NOT depend on how the text is broken up * into AppendText calls. * * The current strategy is that we break the overall text into - * whitespace-delimited "words". Then those words are passed to the nsILineBreaker + * whitespace-delimited "words". Then those words are passed to the LineBreaker * service for deeper analysis if they contain a "complex" character as described * below. * * This class also handles detection of which characters should be capitalized * for text-transform:capitalize. This is a good place to handle that because * we have all the context we need. */ class nsLineBreaker { public: nsLineBreaker(); ~nsLineBreaker(); - static inline bool IsSpace(char16_t u) { return NS_IsSpace(u); } + static inline bool IsSpace(char16_t u) { return mozilla::intl::NS_IsSpace(u); } static inline bool IsComplexASCIIChar(char16_t u) { return !((0x0030 <= u && u <= 0x0039) || (0x0041 <= u && u <= 0x005A) || (0x0061 <= u && u <= 0x007A) || (0x000a == u)); } static inline bool IsComplexChar(char16_t u) { return IsComplexASCIIChar(u) || - NS_NeedsPlatformNativeHandling(u) || + mozilla::intl::NS_NeedsPlatformNativeHandling(u) || (0x1100 <= u && u <= 0x11ff) || // Hangul Jamo (0x2000 <= u && u <= 0x21ff) || // Punctuations and Symbols (0x2e80 <= u && u <= 0xd7ff) || // several CJK blocks (0xf900 <= u && u <= 0xfaff) || // CJK Compatibility Idographs (0xff00 <= u && u <= 0xffef); // Halfwidth and Fullwidth Forms } // Break opportunities exist at the end of each run of breakable whitespace // (see IsSpace above). Break opportunities can also exist between pairs of - // non-whitespace characters, as determined by nsILineBreaker. We pass a whitespace- - // delimited word to nsILineBreaker if it contains at least one character + // non-whitespace characters, as determined by mozilla::intl::LineBreaker. + // We pass a whitespace- + // delimited word to LineBreaker if it contains at least one character // matching IsComplexChar. // We provide flags to control on a per-chunk basis where breaks are allowed. // At any character boundary, exactly one text chunk governs whether a // break is allowed at that boundary. // // We operate on text after whitespace processing has been applied, so // other characters (e.g. tabs and newlines) may have been converted to // spaces. @@ -167,17 +167,17 @@ public: * @param aTrailingBreak this is set to true when there is a break opportunity * at the end of the text. This will normally only be declared true when there * is breakable whitespace at the end. */ nsresult Reset(bool* aTrailingBreak); /* * Set word-break mode for linebreaker. This is set by word-break property. - * @param aMode is nsILineBreaker::kWordBreak_* value. + * @param aMode is LineBreaker::kWordBreak_* value. */ void SetWordBreak(uint8_t aMode) { mWordBreak = aMode; } private: // This is a list of text sources that make up the "current word" (i.e., // run of text which does not contain any whitespace). All the mLengths // are are nonzero, these cannot overlap. struct TextItem {
--- a/dom/base/nsPlainTextSerializer.cpp +++ b/dom/base/nsPlainTextSerializer.cpp @@ -6,17 +6,16 @@ /* * nsIContentSerializer implementation that can be used with an * nsIDocumentEncoder to convert a DOM into plaintext in a nice way * (eg for copy/paste as plaintext). */ #include "nsPlainTextSerializer.h" -#include "nsLWBrkCIID.h" #include "nsIServiceManager.h" #include "nsGkAtoms.h" #include "nsNameSpaceManager.h" #include "nsTextFragment.h" #include "nsContentUtils.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsCRT.h"
--- a/dom/base/nsPlainTextSerializer.h +++ b/dom/base/nsPlainTextSerializer.h @@ -9,22 +9,22 @@ * nsIDocumentEncoder to convert a DOM into plaintext in a nice way * (eg for copy/paste as plaintext). */ #ifndef nsPlainTextSerializer_h__ #define nsPlainTextSerializer_h__ #include "mozilla/Attributes.h" +#include "mozilla/intl/LineBreaker.h" #include "nsCOMPtr.h" #include "nsAtom.h" #include "nsCycleCollectionParticipant.h" #include "nsIContentSerializer.h" #include "nsIDocumentEncoder.h" -#include "nsILineBreaker.h" #include "nsString.h" #include "nsTArray.h" #include <stack> class nsIContent; namespace mozilla { @@ -226,17 +226,17 @@ private: // The stack for ordered lists int32_t *mOLStack; uint32_t mOLStackIndex; uint32_t mULCount; nsString mLineBreak; - nsCOMPtr<nsILineBreaker> mLineBreaker; + RefPtr<mozilla::intl::LineBreaker> mLineBreaker; // Conveniance constant. It would be nice to have it as a const static // variable, but that causes issues with OpenBSD and module unloading. const nsString kSpace; // If nsIDocumentEncoder::OutputNonTextContentAsPlaceholder is set, the child // nodes of specific nodes - <iframe>, <canvas>, etc. should be ignored. // mIgnoredChildNodeLevel is used to tell if current node is an ignorable
--- a/dom/base/nsXHTMLContentSerializer.cpp +++ b/dom/base/nsXHTMLContentSerializer.cpp @@ -22,17 +22,16 @@ #include "nsIServiceManager.h" #include "nsIDocumentEncoder.h" #include "nsGkAtoms.h" #include "nsIURI.h" #include "nsNetUtil.h" #include "nsEscape.h" #include "nsCRT.h" #include "nsContentUtils.h" -#include "nsLWBrkCIID.h" #include "nsIScriptElement.h" #include "nsStubMutationObserver.h" #include "nsAttrName.h" #include "nsComputedDOMStyle.h" #include "mozilla/dom/Element.h" static const int32_t kLongLineLen = 128;
--- a/dom/base/nsXMLContentSerializer.cpp +++ b/dom/base/nsXMLContentSerializer.cpp @@ -24,18 +24,18 @@ #include "nsNameSpaceManager.h" #include "nsTextFragment.h" #include "nsString.h" #include "mozilla/Sprintf.h" #include "nsUnicharUtils.h" #include "nsCRT.h" #include "nsContentUtils.h" #include "nsAttrName.h" -#include "nsILineBreaker.h" #include "mozilla/dom/Element.h" +#include "mozilla/intl/LineBreaker.h" #include "nsParserConstants.h" #include "mozilla/Encoding.h" using namespace mozilla; using namespace mozilla::dom; #define kXMLNS "xmlns" @@ -1595,17 +1595,18 @@ nsXMLContentSerializer::AppendWrapped_No } else { // we must wrap onceAgainBecauseWeAddedBreakInFront = false; bool foundWrapPosition = false; int32_t wrapPosition = 0; if (mAllowLineBreaking) { - nsILineBreaker *lineBreaker = nsContentUtils::LineBreaker(); + mozilla::intl::LineBreaker* lineBreaker = + nsContentUtils::LineBreaker(); wrapPosition = lineBreaker->Prev(aSequenceStart, (aEnd - aSequenceStart), (aPos - aSequenceStart) + 1); if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) { foundWrapPosition = true; } else {
--- a/dom/events/test/pointerevents/test_bug1403055.html +++ b/dom/events/test/pointerevents/test_bug1403055.html @@ -15,21 +15,21 @@ https://bugzilla.mozilla.org/show_bug.cg <p id="display"></p> <div id="target0" style="width: 50px; height: 50px; background: green"></div> <div id="target1" style="width: 50px; height: 50px; background: red"></div> <div id="done" style="width: 50px; height: 50px; background: black"></div> <script type="text/javascript"> /** Test for Bug 1403055 **/ SimpleTest.waitForExplicitFinish(); -function runTests() { - let target0 = window.document.getElementById("target0"); - let target1 = window.document.getElementById("target1"); - let done = window.document.getElementById("done"); +var target0 = window.document.getElementById("target0"); +var target1 = window.document.getElementById("target1"); +var done = window.document.getElementById("done"); +function withoutImplicitlyPointerCaptureForTouch() { let target0_events = ["pointerover", "pointerenter", "pointerdown", "pointermove"]; let target1_events = ["pointerover", "pointerenter", "pointermove", "pointercancel", "pointerout", "pointerleave"]; target0_events.forEach((elem, index, arr) => { target0.addEventListener(elem, (event) => { is(event.type, target0_events[0], "receive " + event.type + " on target0"); target0_events = target0_events.filter(item => item !== event.type); }, { once: true }); @@ -40,26 +40,53 @@ function runTests() { is(event.type, target1_events[0], "receive " + event.type + " on target1"); target1_events = target1_events.filter(item => item !== event.type); }, { once: true }); }); done.addEventListener("mouseup", () => { ok(target0_events.length == 0, " should receive " + target0_events + " on target0"); ok(target1_events.length == 0, " should receive " + target1_events + " on target1"); + SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true], + ["dom.w3c_pointer_events.implicit_capture", true]]}, + withImplicitlyPointerCaptureForTouch); + }, {once : true}); + + synthesizeTouch(target0, 5, 5, { type: "touchstart" }); + synthesizeTouch(target0, 5, 5, { type: "touchmove" }); + synthesizeTouch(target1, 5, 5, { type: "touchmove" }); + synthesizeTouch(target1, 5, 5, { type: "touchcancel" }); + synthesizeMouseAtCenter(done, { type: "mousedown" }); + synthesizeMouseAtCenter(done, { type: "mouseup" }); +} + +function withImplicitlyPointerCaptureForTouch() { + let target0_events = ["pointerover", "pointerenter", "pointerdown", "pointermove", "pointercancel", "pointerout", "pointerleave"]; + + target0_events.forEach((elem, index, arr) => { + target0.addEventListener(elem, (event) => { + is(event.type, target0_events[0], "receive " + event.type + " on target0"); + target0_events = target0_events.filter(item => item !== event.type); + }, { once: true }); + }); + + done.addEventListener("mouseup", () => { + ok(target0_events.length == 0, " should receive " + target0_events + " on target0"); SimpleTest.finish(); - }); + }, {once : true}); synthesizeTouch(target0, 5, 5, { type: "touchstart" }); synthesizeTouch(target0, 5, 5, { type: "touchmove" }); synthesizeTouch(target1, 5, 5, { type: "touchmove" }); synthesizeTouch(target1, 5, 5, { type: "touchcancel" }); synthesizeMouseAtCenter(done, { type: "mousedown" }); synthesizeMouseAtCenter(done, { type: "mouseup" }); } SimpleTest.waitForFocus(() => { - SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true]]}, runTests); + SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true], + ["dom.w3c_pointer_events.implicit_capture", false]]}, + withoutImplicitlyPointerCaptureForTouch); }); </script> </body> </html>
--- a/dom/events/test/pointerevents/touch_action_helpers.js +++ b/dom/events/test/pointerevents/touch_action_helpers.js @@ -1,15 +1,24 @@ // Some common helpers function touchActionSetup(testDriver) { add_completion_callback(subtestDone); document.body.addEventListener('touchend', testDriver, { passive: true }); } +function touchActionSetupAndWaitTestDone(testDriver) { + let testDone = new Promise(resolve => { + add_completion_callback(resolve); + }); + + document.body.addEventListener('touchend', testDriver, { passive: true }); + return testDone; +} + function touchScrollRight(aSelector = '#target0', aX = 20, aY = 20) { var target = document.querySelector(aSelector); return ok(synthesizeNativeTouchDrag(target, aX + 40, aY, -40, 0), "Synthesized horizontal drag"); } function touchScrollDown(aSelector = '#target0', aX = 20, aY = 20) { var target = document.querySelector(aSelector); return ok(synthesizeNativeTouchDrag(target, aX, aY + 40, 0, -40), "Synthesized vertical drag"); @@ -30,21 +39,23 @@ function waitForResetScrollLeft(aSelecto } }); }); } // The main body functions to simulate the input events required for the named test function* pointerevent_touch_action_auto_css_touch_manual(testDriver) { - touchActionSetup(testDriver); + let testDone = touchActionSetupAndWaitTestDone(testDriver); yield touchScrollRight(); yield waitForApzFlushedRepaints(testDriver); yield touchScrollDown(); + yield testDone.then(testDriver); + subtestDone(); } function* pointerevent_touch_action_button_test_touch_manual(testDriver) { touchActionSetup(testDriver); yield touchScrollDown(); yield waitForApzFlushedRepaints(testDriver); yield setTimeout(testDriver, 2 * scrollReturnInterval); @@ -98,21 +109,23 @@ function* pointerevent_touch_action_inhe yield touchScrollDown('#target0 > div div'); yield waitForApzFlushedRepaints(testDriver); yield touchScrollRight('#target0 > div div'); yield waitForApzFlushedRepaints(testDriver); yield tapComplete(); } function* pointerevent_touch_action_inherit_highest_parent_none_touch_manual(testDriver) { - touchActionSetup(testDriver); + let testDone = touchActionSetupAndWaitTestDone(testDriver); yield touchScrollDown('#target0 > div'); yield waitForApzFlushedRepaints(testDriver); yield touchScrollRight('#target0 > div'); + yield testDone.then(testDriver); + subtestDone(); } function* pointerevent_touch_action_inherit_parent_none_touch_manual(testDriver) { touchActionSetup(testDriver); yield touchScrollDown(); yield waitForApzFlushedRepaints(testDriver); yield touchScrollRight(); @@ -146,21 +159,23 @@ function* pointerevent_touch_action_pan_ yield touchScrollDown('#target0 > div div'); yield waitForApzFlushedRepaints(testDriver); yield touchScrollRight('#target0 > div div'); yield waitForApzFlushedRepaints(testDriver); yield tapComplete(); } function* pointerevent_touch_action_pan_x_pan_y_touch_manual(testDriver) { - touchActionSetup(testDriver); + let testDone = touchActionSetupAndWaitTestDone(testDriver); yield touchScrollDown(); yield waitForApzFlushedRepaints(testDriver); yield touchScrollRight(); + yield testDone.then(testDriver); + subtestDone(); } function* pointerevent_touch_action_pan_y_css_touch_manual(testDriver) { touchActionSetup(testDriver); yield touchScrollDown(); yield waitForApzFlushedRepaints(testDriver); yield touchScrollRight();
--- a/dom/media/AudioSegment.h +++ b/dom/media/AudioSegment.h @@ -182,17 +182,19 @@ struct AudioChunk { if (aOther.mChannelData[channel] != AddAudioSampleOffset(mChannelData[channel], mBufferFormat, int32_t(mDuration))) { return false; } } } return true; } - bool IsNull() const { return mBuffer == nullptr; } + bool IsNull() const { + return mBuffer == nullptr; + } void SetNull(StreamTime aDuration) { mBuffer = nullptr; mChannelData.Clear(); mDuration = aDuration; mVolume = 1.0f; mBufferFormat = AUDIO_FORMAT_SILENCE; mPrincipalHandle = PRINCIPAL_HANDLE_NONE; @@ -328,54 +330,63 @@ public: } mDuration += c.mDuration; } } void ResampleChunks(SpeexResamplerState* aResampler, uint32_t aInRate, uint32_t aOutRate); - void AppendFrames(already_AddRefed<ThreadSharedObject> aBuffer, const nsTArray<const float*>& aChannelData, int32_t aDuration, const PrincipalHandle& aPrincipalHandle) { AudioChunk* chunk = AppendChunk(aDuration); chunk->mBuffer = aBuffer; + + MOZ_ASSERT(chunk->mBuffer || aChannelData.IsEmpty(), "Appending invalid data ?"); + for (uint32_t channel = 0; channel < aChannelData.Length(); ++channel) { chunk->mChannelData.AppendElement(aChannelData[channel]); } chunk->mBufferFormat = AUDIO_FORMAT_FLOAT32; #ifdef MOZILLA_INTERNAL_API chunk->mTimeStamp = TimeStamp::Now(); #endif chunk->mPrincipalHandle = aPrincipalHandle; } void AppendFrames(already_AddRefed<ThreadSharedObject> aBuffer, const nsTArray<const int16_t*>& aChannelData, int32_t aDuration, const PrincipalHandle& aPrincipalHandle) { AudioChunk* chunk = AppendChunk(aDuration); chunk->mBuffer = aBuffer; + + MOZ_ASSERT(chunk->mBuffer || aChannelData.IsEmpty(), "Appending invalid data ?"); + for (uint32_t channel = 0; channel < aChannelData.Length(); ++channel) { chunk->mChannelData.AppendElement(aChannelData[channel]); } chunk->mBufferFormat = AUDIO_FORMAT_S16; #ifdef MOZILLA_INTERNAL_API chunk->mTimeStamp = TimeStamp::Now(); #endif chunk->mPrincipalHandle = aPrincipalHandle; + } // Consumes aChunk, and returns a pointer to the persistent copy of aChunk // in the segment. AudioChunk* AppendAndConsumeChunk(AudioChunk* aChunk) { AudioChunk* chunk = AppendChunk(aChunk->mDuration); chunk->mBuffer = aChunk->mBuffer.forget(); chunk->mChannelData.SwapElements(aChunk->mChannelData); + + MOZ_ASSERT(chunk->mBuffer || aChunk->mChannelData.IsEmpty(), "Appending invalid data ?"); + chunk->mVolume = aChunk->mVolume; chunk->mBufferFormat = aChunk->mBufferFormat; #ifdef MOZILLA_INTERNAL_API chunk->mTimeStamp = TimeStamp::Now(); #endif chunk->mPrincipalHandle = aChunk->mPrincipalHandle; return chunk; }
--- a/dom/media/StreamTracks.h +++ b/dom/media/StreamTracks.h @@ -11,27 +11,27 @@ #include "TrackID.h" namespace mozilla { inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate, TrackRate aInRate, TrackTicks aTicks) { - NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); - NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); - NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); + MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); + MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); + MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); return (aTicks * aOutRate) / aInRate; } inline TrackTicks RateConvertTicksRoundUp(TrackRate aOutRate, TrackRate aInRate, TrackTicks aTicks) { - NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); - NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); - NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); + MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); + MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); + MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); return (aTicks * aOutRate + aInRate - 1) / aInRate; } /** * This object contains the decoded data for a stream's tracks. * A StreamTracks can be appended to. Logically a StreamTracks only gets longer, * but we also have the ability to "forget" data before a certain time that * we know won't be used again. (We prune a whole number of seconds internally.)
--- a/dom/media/tests/mochitest/test_getUserMedia_mediaElementCapture_audio.html +++ b/dom/media/tests/mochitest/test_getUserMedia_mediaElementCapture_audio.html @@ -11,17 +11,17 @@ createHTML({ bug: "1259788", title: "Test CaptureStream audio content on HTMLMediaElement playing a gUM MediaStream", visible: true }); var audioContext; var gUMAudioElement; var analyser; -runTest(() => getUserMedia({audio: true}) +runTest(() => getUserMedia({audio: { echoCancellation: false }}) .then(stream => { gUMAudioElement = createMediaElement("audio", "gUMAudio"); gUMAudioElement.srcObject = stream; audioContext = new AudioContext(); info("Capturing"); analyser = new AudioStreamAnalyser(audioContext,
--- a/dom/media/webrtc/AudioOutputObserver.h +++ b/dom/media/webrtc/AudioOutputObserver.h @@ -12,23 +12,23 @@ namespace webrtc { class SingleRwFifo; } namespace mozilla { typedef struct FarEndAudioChunk_ { - uint16_t mSamples; + size_t mSamples; bool mOverrun; - int16_t mData[1]; // variable-length + AudioDataValue mData[1]; // variable-length } FarEndAudioChunk; // This class is used to packetize and send the mixed audio from an MSG, in -// int16, to the AEC module of WebRTC.org. +// float, to the AEC module of WebRTC.org. class AudioOutputObserver { public: AudioOutputObserver(); NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioOutputObserver); void Clear();
--- a/dom/media/webrtc/MediaEngine.h +++ b/dom/media/webrtc/MediaEngine.h @@ -48,18 +48,16 @@ public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEngine) static const int DEFAULT_VIDEO_FPS = 30; static const int DEFAULT_43_VIDEO_WIDTH = 640; static const int DEFAULT_43_VIDEO_HEIGHT = 480; static const int DEFAULT_169_VIDEO_WIDTH = 1280; static const int DEFAULT_169_VIDEO_HEIGHT = 720; - static const int DEFAULT_SAMPLE_RATE = 32000; - // This allows using whatever rate the graph is using for the // MediaStreamTrack. This is useful for microphone data, we know it's already // at the correct rate for insertion in the MSG. static const int USE_GRAPH_RATE = -1; /* Populate an array of video sources in the nsTArray. Also include devices * that are currently unavailable. */ virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
--- a/dom/media/webrtc/MediaEngineDefault.cpp +++ b/dom/media/webrtc/MediaEngineDefault.cpp @@ -21,17 +21,16 @@ #ifdef MOZ_WIDGET_ANDROID #include "nsISupportsUtils.h" #endif #ifdef MOZ_WEBRTC #include "YuvStamper.h" #endif -#define AUDIO_RATE mozilla::MediaEngine::DEFAULT_SAMPLE_RATE #define DEFAULT_AUDIO_TIMER_MS 10 namespace mozilla { using namespace mozilla::gfx; NS_IMPL_ISUPPORTS(MediaEngineDefaultVideoSource, nsITimerCallback, nsINamed) /** * Default video source. @@ -329,16 +328,17 @@ MediaEngineDefaultVideoSource::NotifyPul * Default audio source. */ NS_IMPL_ISUPPORTS0(MediaEngineDefaultAudioSource) MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource() : MediaEngineAudioSource(kReleased) , mLastNotify(0) + , mFreq(1000) {} MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource() {} void MediaEngineDefaultAudioSource::GetName(nsAString& aName) const { @@ -379,20 +379,18 @@ MediaEngineDefaultAudioSource::Allocate( } // Mock failure for automated tests. if (aConstraints.mDeviceId.IsString() && aConstraints.mDeviceId.GetAsString().EqualsASCII("bad device")) { return NS_ERROR_FAILURE; } + mFreq = aPrefs.mFreq ? aPrefs.mFreq : 1000; mState = kAllocated; - // generate sine wave (default 1KHz) - mSineGenerator = new SineWaveGenerator(AUDIO_RATE, - static_cast<uint32_t>(aPrefs.mFreq ? aPrefs.mFreq : 1000)); *aOutHandle = nullptr; return NS_OK; } nsresult MediaEngineDefaultAudioSource::Deallocate(AllocationHandle* aHandle) { MOZ_ASSERT(!aHandle); @@ -406,19 +404,25 @@ MediaEngineDefaultAudioSource::Deallocat nsresult MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID, const PrincipalHandle& aPrincipalHandle) { if (mState != kAllocated) { return NS_ERROR_FAILURE; } + + if (!mSineGenerator) { + // generate sine wave (default 1KHz) + mSineGenerator = new SineWaveGenerator(aStream->GraphRate(), mFreq); + } + // AddTrack will take ownership of segment AudioSegment* segment = new AudioSegment(); - aStream->AddAudioTrack(aID, AUDIO_RATE, 0, segment, SourceMediaStream::ADDTRACK_QUEUED); + aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED); // Remember TrackID so we can finish later mTrackID = aID; mLastNotify = 0; mState = kStarted; return NS_OK; } @@ -464,17 +468,17 @@ MediaEngineDefaultAudioSource::NotifyPul SourceMediaStream *aSource, TrackID aID, StreamTime aDesiredTime, const PrincipalHandle& aPrincipalHandle) { MOZ_ASSERT(aID == mTrackID); AudioSegment segment; // avoid accumulating rounding errors - TrackTicks desired = aSource->TimeToTicksRoundUp(AUDIO_RATE, aDesiredTime); + TrackTicks desired = aSource->TimeToTicksRoundUp(aGraph->GraphRate(), aDesiredTime); TrackTicks delta = desired - mLastNotify; mLastNotify += delta; AppendToSegment(segment, delta, aPrincipalHandle); aSource->AppendToTrack(mTrackID, &segment); } void MediaEngineDefault::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
--- a/dom/media/webrtc/MediaEngineDefault.h +++ b/dom/media/webrtc/MediaEngineDefault.h @@ -183,18 +183,19 @@ public: NS_DECL_THREADSAFE_ISUPPORTS protected: ~MediaEngineDefaultAudioSource(); TrackID mTrackID; TrackTicks mLastNotify; // Accessed in ::Start(), then on NotifyPull (from MSG thread) + uint32_t mFreq; // ditto - // Created on Allocate, then accessed from NotifyPull (MSG thread) + // Created on Start, then accessed from NotifyPull (MSG thread) nsAutoPtr<SineWaveGenerator> mSineGenerator; }; class MediaEngineDefault : public MediaEngine { typedef MediaEngine Super; public:
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp +++ b/dom/media/webrtc/MediaEngineWebRTC.cpp @@ -102,17 +102,16 @@ void AudioInputCubeb::UpdateDeviceList() StaticMutexAutoLock lock(sMutex); // swap state cubeb_device_collection_destroy(cubebContext, &mDevices); mDevices = devices; } MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs) : mMutex("mozilla::MediaEngineWebRTC"), - mVoiceEngine(nullptr), mAudioInput(nullptr), mFullDuplex(aPrefs.mFullDuplex), mDelayAgnostic(aPrefs.mDelayAgnostic), mExtendedFilter(aPrefs.mExtendedFilter), mHasTabVideoSource(false) { nsCOMPtr<nsIComponentRegistrar> compMgr; NS_GetComponentRegistrar(getter_AddRefs(compMgr)); @@ -275,53 +274,21 @@ MediaEngineWebRTC::EnumerateAudioDevices if (aMediaSource == dom::MediaSourceEnum::AudioCapture) { RefPtr<MediaEngineWebRTCAudioCaptureSource> audioCaptureSource = new MediaEngineWebRTCAudioCaptureSource(nullptr); aASources->AppendElement(audioCaptureSource); return; } -#ifdef MOZ_WIDGET_ANDROID - JavaVM* jvm = mozilla::jni::GetVM(); - jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef(); - - if (webrtc::VoiceEngine::SetAndroidObjects(jvm, (void*)context) != 0) { - LOG(("VoiceEngine:SetAndroidObjects Failed")); - return; - } -#endif - - if (!mVoiceEngine) { - mVoiceEngine = webrtc::VoiceEngine::Create(); - if (!mVoiceEngine) { + if (!mAudioInput) { + if (!SupportsDuplex()) { return; } - } - - ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - if (!ptrVoEBase) { - return; - } - - // Always re-init the voice engine, since if we close the last use we - // DeInitEngine() and Terminate(), which shuts down Process() - but means - // we have to Init() again before using it. Init() when already inited is - // just a no-op, so call always. - if (ptrVoEBase->Init() < 0) { - return; - } - - if (!mAudioInput) { - if (SupportsDuplex()) { - // The platform_supports_full_duplex. - mAudioInput = new mozilla::AudioInputCubeb(mVoiceEngine); - } else { - mAudioInput = new mozilla::AudioInputWebRTC(mVoiceEngine); - } + mAudioInput = new mozilla::AudioInputCubeb(); } int nDevices = 0; mAudioInput->GetNumOfRecordingDevices(nDevices); int i; #if defined(MOZ_WIDGET_ANDROID) i = 0; // Bug 1037025 - let the OS handle defaulting for now on android/b2g #else @@ -339,36 +306,26 @@ MediaEngineWebRTC::EnumerateAudioDevices int error = mAudioInput->GetRecordingDeviceName(i, deviceName, uniqueId); if (error) { LOG((" AudioInput::GetRecordingDeviceName: Failed %d", error)); continue; } if (uniqueId[0] == '\0') { // Mac and Linux don't set uniqueId! - MOZ_ASSERT(sizeof(deviceName) == sizeof(uniqueId)); // total paranoia strcpy(uniqueId, deviceName); // safe given assert and initialization/error-check } RefPtr<MediaEngineAudioSource> aSource; NS_ConvertUTF8toUTF16 uuid(uniqueId); if (mAudioSources.Get(uuid, getter_AddRefs(aSource))) { // We've already seen this device, just append. aASources->AppendElement(aSource.get()); } else { - AudioInput* audioinput = mAudioInput; - if (SupportsDuplex()) { - // The platform_supports_full_duplex. - - // For cubeb, it has state (the selected ID) - // XXX just use the uniqueID for cubeb and support it everywhere, and get rid of this - // XXX Small window where the device list/index could change! - audioinput = new mozilla::AudioInputCubeb(mVoiceEngine, i); - } - aSource = new MediaEngineWebRTCMicrophoneSource(mVoiceEngine, audioinput, + aSource = new MediaEngineWebRTCMicrophoneSource(new mozilla::AudioInputCubeb(i), i, deviceName, uniqueId, mDelayAgnostic, mExtendedFilter); mAudioSources.Put(uuid, aSource); // Hashtable takes ownership. aASources->AppendElement(aSource); } } } @@ -396,20 +353,13 @@ MediaEngineWebRTC::Shutdown() MediaEngineAudioSource* source = iter.UserData(); if (source) { source->Shutdown(); } } mVideoSources.Clear(); mAudioSources.Clear(); - if (mVoiceEngine) { - mVoiceEngine->SetTraceCallback(nullptr); - webrtc::VoiceEngine::Delete(mVoiceEngine); - } - - mVoiceEngine = nullptr; - mozilla::camera::Shutdown(); AudioInputCubeb::CleanupGlobalData(); } }
--- a/dom/media/webrtc/MediaEngineWebRTC.h +++ b/dom/media/webrtc/MediaEngineWebRTC.h @@ -136,17 +136,17 @@ protected: virtual ~MediaEngineWebRTCAudioCaptureSource() {} nsCString mUUID; }; // Small subset of VoEHardware class AudioInput { public: - explicit AudioInput(webrtc::VoiceEngine* aVoiceEngine) : mVoiceEngine(aVoiceEngine) {}; + AudioInput() = default; // Threadsafe because it's referenced from an MicrophoneSource, which can // had references to it on other threads. NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioInput) virtual int GetNumOfRecordingDevices(int& aDevices) = 0; virtual int GetRecordingDeviceName(int aIndex, char (&aStrNameUTF8)[128], char aStrGuidUTF8[128]) = 0; virtual int GetRecordingDeviceStatus(bool& aIsAvailable) = 0; @@ -155,25 +155,23 @@ public: virtual void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) = 0; virtual void StopRecording(SourceMediaStream *aStream) = 0; virtual int SetRecordingDevice(int aIndex) = 0; virtual void SetUserChannelCount(uint32_t aChannels) = 0; protected: // Protected destructor, to discourage deletion outside of Release(): virtual ~AudioInput() {} - - webrtc::VoiceEngine* mVoiceEngine; }; class AudioInputCubeb final : public AudioInput { public: - explicit AudioInputCubeb(webrtc::VoiceEngine* aVoiceEngine, int aIndex = 0) : - AudioInput(aVoiceEngine), mSelectedDevice(aIndex), mInUseCount(0) + explicit AudioInputCubeb(int aIndex = 0) : + AudioInput(), mSelectedDevice(aIndex), mInUseCount(0) { if (!mDeviceIndexes) { mDeviceIndexes = new nsTArray<int>; mDeviceNames = new nsTArray<nsCString>; mDefaultDevice = -1; } } @@ -309,24 +307,17 @@ public: { #ifdef MOZ_WIDGET_ANDROID // OpenSL ES does not support enumerating devices. MOZ_ASSERT(mDevices.count == 0); #else MOZ_ASSERT(mDevices.count > 0); #endif - if (mInUseCount == 0) { - ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoEXMedia; - ptrVoEXMedia = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine); - if (ptrVoEXMedia) { - ptrVoEXMedia->SetExternalRecordingStatus(true); - } - mAnyInUse = true; - } + mAnyInUse = true; mInUseCount++; // Always tell the stream we're using it for input aStream->OpenAudioInput(mSelectedDevice, aListener); } void StopRecording(SourceMediaStream *aStream) { aStream->CloseAudioInput(); @@ -364,87 +355,16 @@ private: static int mDefaultDevice; // -1 == not set static nsTArray<nsCString>* mDeviceNames; static cubeb_device_collection mDevices; static bool mAnyInUse; static StaticMutex sMutex; static uint32_t sUserChannelCount; }; -class AudioInputWebRTC final : public AudioInput -{ -public: - explicit AudioInputWebRTC(webrtc::VoiceEngine* aVoiceEngine) : AudioInput(aVoiceEngine) {} - - int GetNumOfRecordingDevices(int& aDevices) - { - ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase; - ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - if (!ptrVoEBase) { - return 1; - } - aDevices = ptrVoEBase->audio_device_module()->RecordingDevices(); - return 0; - } - - int GetRecordingDeviceName(int aIndex, char (&aStrNameUTF8)[128], - char aStrGuidUTF8[128]) - { - ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase; - ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - if (!ptrVoEBase) { - return 1; - } - return ptrVoEBase->audio_device_module()->RecordingDeviceName(aIndex, - aStrNameUTF8, - aStrGuidUTF8); - } - - int GetRecordingDeviceStatus(bool& aIsAvailable) - { - ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase; - ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - if (!ptrVoEBase) { - return 1; - } - return ptrVoEBase->audio_device_module()->RecordingIsAvailable(&aIsAvailable); - } - - void GetChannelCount(uint32_t& aChannels) - { - aChannels = 1; // default to mono - } - - int GetMaxAvailableChannels(uint32_t& aChannels) - { - aChannels = 1; - return 0; - } - - void SetUserChannelCount(uint32_t aChannels) - {} - - void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) {} - void StopRecording(SourceMediaStream *aStream) {} - - int SetRecordingDevice(int aIndex) - { - ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase; - ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - if (!ptrVoEBase) { - return 1; - } - return ptrVoEBase->audio_device_module()->SetRecordingDevice(aIndex); - } - -protected: - // Protected destructor, to discourage deletion outside of Release(): - ~AudioInputWebRTC() {} -}; - class WebRTCAudioDataListener : public AudioDataListener { protected: // Protected destructor, to discourage deletion outside of Release(): virtual ~WebRTCAudioDataListener() {} public: explicit WebRTCAudioDataListener(MediaEngineAudioSource* aAudioSource) @@ -485,23 +405,21 @@ public: mAudioSource = nullptr; } private: Mutex mMutex; RefPtr<MediaEngineAudioSource> mAudioSource; }; -class MediaEngineWebRTCMicrophoneSource : public MediaEngineAudioSource, - public webrtc::VoEMediaProcess +class MediaEngineWebRTCMicrophoneSource : public MediaEngineAudioSource { typedef MediaEngineAudioSource Super; public: - MediaEngineWebRTCMicrophoneSource(webrtc::VoiceEngine* aVoiceEnginePtr, - mozilla::AudioInput* aAudioInput, + MediaEngineWebRTCMicrophoneSource(mozilla::AudioInput* aAudioInput, int aIndex, const char* name, const char* uuid, bool aDelayAgnostic, bool aExtendedFilter); void GetName(nsAString& aName) const override; void GetUUID(nsACString& aUUID) const override; @@ -545,114 +463,109 @@ public: { return NS_ERROR_NOT_IMPLEMENTED; } uint32_t GetBestFitnessDistance( const nsTArray<const NormalizedConstraintSet*>& aConstraintSets, const nsString& aDeviceId) const override; - // VoEMediaProcess. - virtual void Process(int channel, webrtc::ProcessingTypes type, - int16_t audio10ms[], size_t length, - int samplingFreq, bool isStereo) override; - void Shutdown() override; NS_DECL_THREADSAFE_ISUPPORTS protected: ~MediaEngineWebRTCMicrophoneSource() {} private: nsresult UpdateSingleSource(const AllocationHandle* aHandle, const NormalizedConstraints& aNetConstraints, const NormalizedConstraints& aNewConstraint, const MediaEnginePrefs& aPrefs, const nsString& aDeviceId, const char** aOutBadConstraint) override; + + void UpdateAECSettingsIfNeeded(bool aEnable, webrtc::EcModes aMode); + void UpdateAGCSettingsIfNeeded(bool aEnable, webrtc::AgcModes aMode); + void UpdateNSSettingsIfNeeded(bool aEnable, webrtc::NsModes aMode); + void SetLastPrefs(const MediaEnginePrefs& aPrefs); // These allocate/configure and release the channel bool AllocChannel(); void FreeChannel(); - // These start/stop VoEBase and associated interfaces - bool InitEngine(); - void DeInitEngine(); - - // This is true when all processing is disabled, we can skip - // packetization, resampling and other processing passes. - bool PassThrough() { - return mSkipProcessing; - } template<typename T> void InsertInGraph(const T* aBuffer, size_t aFrames, uint32_t aChannels); void PacketizeAndProcess(MediaStreamGraph* aGraph, const AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels); - webrtc::VoiceEngine* mVoiceEngine; + + // This is true when all processing is disabled, we can skip + // packetization, resampling and other processing passes. + bool PassThrough() { + return mSkipProcessing; + } + void SetPassThrough(bool aPassThrough) { + mSkipProcessing = aPassThrough; + } + RefPtr<mozilla::AudioInput> mAudioInput; RefPtr<WebRTCAudioDataListener> mListener; - RefPtr<AudioOutputObserver> mAudioOutputObserver; - // Note: shared across all microphone sources - we don't want to Terminate() - // the VoEBase until there are no active captures + // Note: shared across all microphone sources static int sChannelsOpen; - static ScopedCustomReleasePtr<webrtc::VoEBase> mVoEBase; - static ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERender; - static ScopedCustomReleasePtr<webrtc::VoENetwork> mVoENetwork; - static ScopedCustomReleasePtr<webrtc::VoEAudioProcessing> mVoEProcessing; + const UniquePtr<webrtc::AudioProcessing> mAudioProcessing; + const RefPtr<AudioOutputObserver> mAudioOutputObserver; // accessed from the GraphDriver thread except for deletion - nsAutoPtr<AudioPacketizer<AudioDataValue, int16_t>> mPacketizer; + nsAutoPtr<AudioPacketizer<AudioDataValue, float>> mPacketizer; ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERenderListener; // mMonitor protects mSources[] and mPrinicpalIds[] access/changes, and // transitions of mState from kStarted to kStopped (which are combined with // EndTrack()). mSources[] and mPrincipalHandles[] are accessed from webrtc // threads. Monitor mMonitor; nsTArray<RefPtr<SourceMediaStream>> mSources; nsTArray<PrincipalHandle> mPrincipalHandles; // Maps to mSources. int mCapIndex; - int mChannel; bool mDelayAgnostic; bool mExtendedFilter; MOZ_INIT_OUTSIDE_CTOR TrackID mTrackID; bool mStarted; nsString mDeviceName; nsCString mDeviceUUID; int32_t mSampleFrequency; uint64_t mTotalFrames; uint64_t mLastLogFrames; - NullTransport *mNullTransport; - - nsTArray<int16_t> mInputBuffer; // mSkipProcessing is true if none of the processing passes are enabled, // because of prefs or constraints. This allows simply copying the audio into // the MSG, skipping resampling and the whole webrtc.org code. + // This is read and written to only on the MSG thread. bool mSkipProcessing; // To only update microphone when needed, we keep track of previous settings. MediaEnginePrefs mLastPrefs; - AlignedShortBuffer mInputDownmixBuffer; + AlignedFloatBuffer mInputBuffer; + AlignedFloatBuffer mDeinterleavedBuffer; + AlignedAudioBuffer mInputDownmixBuffer; }; class MediaEngineWebRTC : public MediaEngine { typedef MediaEngine Super; public: explicit MediaEngineWebRTC(MediaEnginePrefs& aPrefs); @@ -671,17 +584,16 @@ public: nsTArray<RefPtr<MediaEngineAudioSource>>*) override; private: ~MediaEngineWebRTC() {} nsCOMPtr<nsIThread> mThread; // gUM runnables can e.g. Enumerate from multiple threads Mutex mMutex; - webrtc::VoiceEngine* mVoiceEngine; RefPtr<mozilla::AudioInput> mAudioInput; bool mFullDuplex; bool mDelayAgnostic; bool mExtendedFilter; bool mHasTabVideoSource; // Store devices we've already seen in a hashtable for quick return. // Maps UUID to MediaEngineSource (one set for audio, one for video).
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -5,29 +5,28 @@ #include "MediaEngineWebRTC.h" #include <stdio.h> #include <algorithm> #include "mozilla/Assertions.h" #include "MediaTrackConstraints.h" #include "mtransport/runnable_utils.h" #include "nsAutoPtr.h" #include "AudioConverter.h" +#include "MediaStreamGraphImpl.h" // scoped_ptr.h uses FF #ifdef FF #undef FF #endif #include "webrtc/modules/audio_device/opensl/single_rw_fifo.h" +#include "webrtc/voice_engine/voice_engine_defines.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/common_audio/include/audio_util.h" -#define CHANNELS 1 -#define ENCODING "L16" -#define DEFAULT_PORT 5555 - -#define SAMPLE_RATE(freq) ((freq)*2*8) // bps, 16-bit samples -#define SAMPLE_LENGTH(freq) (((freq)*10)/1000) +using namespace webrtc; // These are restrictions from the webrtc.org code #define MAX_CHANNELS 2 #define MAX_SAMPLING_FREQ 48000 // Hz - multiple of 100 #define MAX_AEC_FIFO_DEPTH 200 // ms - multiple of 10 static_assert(!(MAX_AEC_FIFO_DEPTH % 10), "Invalid MAX_AEC_FIFO_DEPTH"); @@ -48,31 +47,27 @@ LogModule* AudioLogModule() { /** * Webrtc microphone source source. */ NS_IMPL_ISUPPORTS0(MediaEngineWebRTCMicrophoneSource) NS_IMPL_ISUPPORTS0(MediaEngineWebRTCAudioCaptureSource) int MediaEngineWebRTCMicrophoneSource::sChannelsOpen = 0; -ScopedCustomReleasePtr<webrtc::VoEBase> MediaEngineWebRTCMicrophoneSource::mVoEBase; -ScopedCustomReleasePtr<webrtc::VoEExternalMedia> MediaEngineWebRTCMicrophoneSource::mVoERender; -ScopedCustomReleasePtr<webrtc::VoENetwork> MediaEngineWebRTCMicrophoneSource::mVoENetwork; -ScopedCustomReleasePtr<webrtc::VoEAudioProcessing> MediaEngineWebRTCMicrophoneSource::mVoEProcessing; AudioOutputObserver::AudioOutputObserver() : mPlayoutFreq(0) , mPlayoutChannels(0) , mChunkSize(0) , mSaved(nullptr) , mSamplesSaved(0) , mDownmixBuffer(MAX_SAMPLING_FREQ * MAX_CHANNELS / 100) { // Buffers of 10ms chunks - mPlayoutFifo = new webrtc::SingleRwFifo(MAX_AEC_FIFO_DEPTH/10); + mPlayoutFifo = new SingleRwFifo(MAX_AEC_FIFO_DEPTH/10); } AudioOutputObserver::~AudioOutputObserver() { Clear(); free(mSaved); mSaved = nullptr; } @@ -138,38 +133,38 @@ AudioOutputObserver::InsertFarEnd(const aOverran = false; } // Rechunk to 10ms. // The AnalyzeReverseStream() and WebRtcAec_BufferFarend() functions insist on 10ms // samples per call. Annoying... while (aFrames) { if (!mSaved) { mSaved = (FarEndAudioChunk *) moz_xmalloc(sizeof(FarEndAudioChunk) + - (mChunkSize * channels - 1)*sizeof(int16_t)); + (mChunkSize * channels - 1)*sizeof(AudioDataValue)); mSaved->mSamples = mChunkSize; mSaved->mOverrun = aOverran; aOverran = false; } uint32_t to_copy = mChunkSize - mSamplesSaved; if (to_copy > aFrames) { to_copy = aFrames; } - int16_t* dest = &(mSaved->mData[mSamplesSaved * channels]); + AudioDataValue* dest = &(mSaved->mData[mSamplesSaved * channels]); if (aChannels > MAX_CHANNELS) { AudioConverter converter(AudioConfig(aChannels, 0), AudioConfig(channels, 0)); converter.Process(mDownmixBuffer, aBuffer, to_copy); ConvertAudioSamples(mDownmixBuffer.Data(), dest, to_copy * channels); } else { ConvertAudioSamples(aBuffer, dest, to_copy * channels); } #ifdef LOG_FAREND_INSERTION if (fp) { - fwrite(&(mSaved->mData[mSamplesSaved * aChannels]), to_copy * aChannels, sizeof(int16_t), fp); + fwrite(&(mSaved->mData[mSamplesSaved * aChannels]), to_copy * aChannels, sizeof(AudioDataValue), fp); } #endif aFrames -= to_copy; mSamplesSaved += to_copy; aBuffer += to_copy * aChannels; if (mSamplesSaved >= mChunkSize) { int free_slots = mPlayoutFifo->capacity() - mPlayoutFifo->size(); @@ -182,41 +177,38 @@ AudioOutputObserver::InsertFarEnd(const mSaved = nullptr; mSamplesSaved = 0; } } } } MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource( - webrtc::VoiceEngine* aVoiceEnginePtr, mozilla::AudioInput* aAudioInput, int aIndex, const char* name, const char* uuid, bool aDelayAgnostic, bool aExtendedFilter) : MediaEngineAudioSource(kReleased) - , mVoiceEngine(aVoiceEnginePtr) , mAudioInput(aAudioInput) + , mAudioProcessing(AudioProcessing::Create()) + , mAudioOutputObserver(new AudioOutputObserver()) , mMonitor("WebRTCMic.Monitor") , mCapIndex(aIndex) - , mChannel(-1) , mDelayAgnostic(aDelayAgnostic) , mExtendedFilter(aExtendedFilter) , mTrackID(TRACK_NONE) , mStarted(false) - , mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE) + , mSampleFrequency(MediaEngine::USE_GRAPH_RATE) , mTotalFrames(0) , mLastLogFrames(0) - , mNullTransport(nullptr) , mSkipProcessing(false) , mInputDownmixBuffer(MAX_SAMPLING_FREQ * MAX_CHANNELS / 100) { - MOZ_ASSERT(aVoiceEnginePtr); MOZ_ASSERT(aAudioInput); mDeviceName.Assign(NS_ConvertUTF8toUTF16(name)); mDeviceUUID.Assign(uuid); mListener = new mozilla::WebRTCAudioDataListener(this); mSettings->mEchoCancellation.Construct(0); mSettings->mAutoGainControl.Construct(0); mSettings->mNoiseSuppression.Construct(0); mSettings->mChannelCount.Construct(0); @@ -270,16 +262,150 @@ MediaEngineWebRTCMicrophoneSource::Resta aOutBadConstraint); } bool operator == (const MediaEnginePrefs& a, const MediaEnginePrefs& b) { return !memcmp(&a, &b, sizeof(MediaEnginePrefs)); }; +// This does an early return in case of error. +#define HANDLE_APM_ERROR(fn) \ +do { \ + int rv = fn; \ + if (rv != AudioProcessing::kNoError) { \ + MOZ_ASSERT_UNREACHABLE("APM error in " #fn); \ + return; \ + } \ +} while(0); + +void MediaEngineWebRTCMicrophoneSource::UpdateAECSettingsIfNeeded(bool aEnable, EcModes aMode) +{ + using webrtc::EcModes; + + EchoCancellation::SuppressionLevel level; + + switch(aMode) { + case EcModes::kEcUnchanged: + level = mAudioProcessing->echo_cancellation()->suppression_level(); + break; + case EcModes::kEcConference: + level = EchoCancellation::kHighSuppression; + break; + case EcModes::kEcDefault: + level = EchoCancellation::kModerateSuppression; + break; + case EcModes::kEcAec: + level = EchoCancellation::kModerateSuppression; + break; + case EcModes::kEcAecm: + // No suppression level to set for the mobile echo canceller + break; + default: + MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, ("Bad EcMode value")); + MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config" + " for the echo cancelation mode."); + // fall back to something sensible in release + level = EchoCancellation::kModerateSuppression; + break; + } + + // AECm and AEC are mutually exclusive. + if (aMode == EcModes::kEcAecm) { + HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->Enable(false)); + HANDLE_APM_ERROR(mAudioProcessing->echo_control_mobile()->Enable(aEnable)); + } else { + HANDLE_APM_ERROR(mAudioProcessing->echo_control_mobile()->Enable(false)); + HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->Enable(aEnable)); + HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->set_suppression_level(level)); + } +} + +void +MediaEngineWebRTCMicrophoneSource::UpdateAGCSettingsIfNeeded(bool aEnable, AgcModes aMode) +{ +#if defined(WEBRTC_IOS) || defined(ATA) || defined(WEBRTC_ANDROID) + if (aMode == kAgcAdaptiveAnalog) { + MOZ_LOG(GetMediaManagerLog(), + LogLevel::Error, + ("Invalid AGC mode kAgcAdaptiveAnalog on mobile")); + MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config" + " for the auto gain, on mobile."); + aMode = kAgcDefault; + } +#endif + GainControl::Mode mode = kDefaultAgcMode; + + switch (aMode) { + case AgcModes::kAgcDefault: + mode = kDefaultAgcMode; + break; + case AgcModes::kAgcUnchanged: + mode = mAudioProcessing->gain_control()->mode(); + break; + case AgcModes::kAgcFixedDigital: + mode = GainControl::Mode::kFixedDigital; + break; + case AgcModes::kAgcAdaptiveAnalog: + mode = GainControl::Mode::kAdaptiveAnalog; + break; + case AgcModes::kAgcAdaptiveDigital: + mode = GainControl::Mode::kAdaptiveDigital; + break; + default: + MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config" + " for the auto gain."); + // This is a good fallback, it works regardless of the platform. + mode = GainControl::Mode::kAdaptiveDigital; + break; + } + + HANDLE_APM_ERROR(mAudioProcessing->gain_control()->set_mode(mode)); + HANDLE_APM_ERROR(mAudioProcessing->gain_control()->Enable(aEnable)); +} + +void +MediaEngineWebRTCMicrophoneSource::UpdateNSSettingsIfNeeded(bool aEnable, NsModes aMode) +{ + NoiseSuppression::Level nsLevel; + + switch (aMode) { + case NsModes::kNsDefault: + nsLevel = kDefaultNsMode; + break; + case NsModes::kNsUnchanged: + nsLevel = mAudioProcessing->noise_suppression()->level(); + break; + case NsModes::kNsConference: + nsLevel = NoiseSuppression::kHigh; + break; + case NsModes::kNsLowSuppression: + nsLevel = NoiseSuppression::kLow; + break; + case NsModes::kNsModerateSuppression: + nsLevel = NoiseSuppression::kModerate; + break; + case NsModes::kNsHighSuppression: + nsLevel = NoiseSuppression::kHigh; + break; + case NsModes::kNsVeryHighSuppression: + nsLevel = NoiseSuppression::kVeryHigh; + break; + default: + MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config" + " for the noise suppression."); + // Pick something sensible as a faillback in release. + nsLevel = NoiseSuppression::kModerate; + } + HANDLE_APM_ERROR(mAudioProcessing->noise_suppression()->set_level(nsLevel)); + HANDLE_APM_ERROR(mAudioProcessing->noise_suppression()->Enable(aEnable)); +} + +#undef HANDLE_APM_ERROR + nsresult MediaEngineWebRTCMicrophoneSource::UpdateSingleSource( const AllocationHandle* aHandle, const NormalizedConstraints& aNetConstraints, const NormalizedConstraints& aNewConstraint, /* Ignored */ const MediaEnginePrefs& aPrefs, const nsString& aDeviceId, const char** aOutBadConstraint) @@ -313,22 +439,17 @@ MediaEngineWebRTCMicrophoneSource::Updat prefs.mAecOn ? prefs.mAec : -1, prefs.mAgcOn ? prefs.mAgc : -1, prefs.mNoiseOn ? prefs.mNoise : -1, prefs.mChannels)); switch (mState) { case kReleased: MOZ_ASSERT(aHandle); - if (sChannelsOpen == 0) { - if (!InitEngine()) { - LOG(("Audio engine is not initalized")); - return NS_ERROR_FAILURE; - } - } else { + if (sChannelsOpen != 0) { // Until we fix (or wallpaper) support for multiple mic input // (Bug 1238038) fail allocation for a second device return NS_ERROR_FAILURE; } if (mAudioInput->SetRecordingDevice(mCapIndex)) { return NS_ERROR_FAILURE; } mAudioInput->SetUserChannelCount(prefs.mChannels); @@ -350,29 +471,29 @@ MediaEngineWebRTCMicrophoneSource::Updat case kStarted: if (prefs == mLastPrefs) { return NS_OK; } if (prefs.mChannels != mLastPrefs.mChannels) { MOZ_ASSERT(mSources.Length() > 0); + // If the channel count changed, tell the MSG to open a new driver with + // the correct channel count. auto& source = mSources.LastElement(); mAudioInput->SetUserChannelCount(prefs.mChannels); // Get validated number of channel uint32_t channelCount = 0; mAudioInput->GetChannelCount(channelCount); MOZ_ASSERT(channelCount > 0 && mLastPrefs.mChannels > 0); - // Check if new validated channels is the same as previous - if (static_cast<uint32_t>(mLastPrefs.mChannels) != channelCount && + if (mLastPrefs.mChannels != prefs.mChannels && !source->OpenNewAudioCallbackDriver(mListener)) { + MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, ("Could not open a new AudioCallbackDriver for input")); return NS_ERROR_FAILURE; } - // Update settings - prefs.mChannels = channelCount; } if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) { MonitorAutoLock lock(mMonitor); if (mSources.IsEmpty()) { LOG(("Audio device %d reallocated", mCapIndex)); } else { LOG(("Audio device %d allocated shared", mCapIndex)); @@ -381,74 +502,72 @@ MediaEngineWebRTCMicrophoneSource::Updat break; default: LOG(("Audio device %d in ignored state %d", mCapIndex, mState)); break; } if (sChannelsOpen > 0) { - int error; + UpdateAGCSettingsIfNeeded(prefs.mAgcOn, static_cast<AgcModes>(prefs.mAgc)); + UpdateNSSettingsIfNeeded(prefs.mNoiseOn, static_cast<NsModes>(prefs.mNoise)); + UpdateAECSettingsIfNeeded(prefs.mAecOn, static_cast<EcModes>(prefs.mAec)); - error = mVoEProcessing->SetEcStatus(prefs.mAecOn, (webrtc::EcModes)prefs.mAec); - if (error) { - LOG(("%s Error setting Echo Status: %d ",__FUNCTION__, error)); - // Overhead of capturing all the time is very low (<0.1% of an audio only call) - if (prefs.mAecOn) { - error = mVoEProcessing->SetEcMetricsStatus(true); - if (error) { - LOG(("%s Error setting Echo Metrics: %d ",__FUNCTION__, error)); - } - } - } - error = mVoEProcessing->SetAgcStatus(prefs.mAgcOn, (webrtc::AgcModes)prefs.mAgc); - if (error) { - LOG(("%s Error setting AGC Status: %d ",__FUNCTION__, error)); - } - error = mVoEProcessing->SetNsStatus(prefs.mNoiseOn, (webrtc::NsModes)prefs.mNoise); - if (error) { - LOG(("%s Error setting NoiseSuppression Status: %d ",__FUNCTION__, error)); - } - } - - // we don't allow switching from non-fast-path to fast-path on the fly yet - if (mState != kStarted) { - mSkipProcessing = !(prefs.mAecOn || prefs.mAgcOn || prefs.mNoiseOn); - if (mSkipProcessing) { - mSampleFrequency = MediaEngine::USE_GRAPH_RATE; - } else { - // make sure we route a copy of the mixed audio output of this MSG to the - // AEC - if (!mAudioOutputObserver) { - mAudioOutputObserver = new AudioOutputObserver(); - } - } + webrtc::Config config; + config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(mExtendedFilter)); + config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(mDelayAgnostic)); + mAudioProcessing->SetExtraOptions(config); } SetLastPrefs(prefs); return NS_OK; } +#undef HANDLE_APM_ERROR + void -MediaEngineWebRTCMicrophoneSource::SetLastPrefs( - const MediaEnginePrefs& aPrefs) +MediaEngineWebRTCMicrophoneSource::SetLastPrefs(const MediaEnginePrefs& aPrefs) { mLastPrefs = aPrefs; RefPtr<MediaEngineWebRTCMicrophoneSource> that = this; NS_DispatchToMainThread(media::NewRunnableFrom([that, aPrefs]() mutable { that->mSettings->mEchoCancellation.Value() = aPrefs.mAecOn; that->mSettings->mAutoGainControl.Value() = aPrefs.mAgcOn; that->mSettings->mNoiseSuppression.Value() = aPrefs.mNoiseOn; that->mSettings->mChannelCount.Value() = aPrefs.mChannels; + + class Message : public ControlMessage { + public: + Message(MediaEngineWebRTCMicrophoneSource* aSource, + bool aPassThrough) + : ControlMessage(nullptr) + , mMicrophoneSource(aSource) + , mPassThrough(aPassThrough) + {} + + void Run() override + { + mMicrophoneSource->SetPassThrough(mPassThrough); + } + + protected: + RefPtr<MediaEngineWebRTCMicrophoneSource> mMicrophoneSource; + bool mPassThrough; + }; + + bool passThrough = !(aPrefs.mAecOn || aPrefs.mAgcOn || aPrefs.mNoiseOn); + if (!that->mSources.IsEmpty()) { + that->mSources[0]->GraphImpl()->AppendMessage(MakeUnique<Message>(that, passThrough)); + } + return NS_OK; })); } - nsresult MediaEngineWebRTCMicrophoneSource::Deallocate(AllocationHandle* aHandle) { AssertIsOnOwningThread(); Super::Deallocate(aHandle); if (!mRegisteredHandles.Length()) { @@ -488,17 +607,17 @@ MediaEngineWebRTCMicrophoneSource::Start mPrincipalHandles.AppendElement(aPrincipalHandle); MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); } AudioSegment* segment = new AudioSegment(); if (mSampleFrequency == MediaEngine::USE_GRAPH_RATE) { mSampleFrequency = aStream->GraphRate(); } - aStream->AddAudioTrack(aID, mSampleFrequency, 0, segment, SourceMediaStream::ADDTRACK_QUEUED); + aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED); // XXX Make this based on the pref. aStream->RegisterForAudioMixing(); LOG(("Start audio for stream %p", aStream)); if (!mListener) { mListener = new mozilla::WebRTCAudioDataListener(this); } @@ -509,34 +628,20 @@ MediaEngineWebRTCMicrophoneSource::Start return NS_OK; } mState = kStarted; mTrackID = aID; // Make sure logger starts before capture AsyncLatencyLogger::Get(true); - if (mAudioOutputObserver) { - mAudioOutputObserver->Clear(); - } - - if (mVoEBase->StartReceive(mChannel)) { - return NS_ERROR_FAILURE; - } + mAudioOutputObserver->Clear(); - // Must be *before* StartSend() so it will notice we selected external input (full_duplex) mAudioInput->StartRecording(aStream, mListener); - if (mVoEBase->StartSend(mChannel)) { - return NS_ERROR_FAILURE; - } - - // Attach external media processor, so this::Process will be called. - mVoERender->RegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel, *this); - return NS_OK; } nsresult MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID) { AssertIsOnOwningThread(); { @@ -555,38 +660,27 @@ MediaEngineWebRTCMicrophoneSource::Stop( if (!mSources.IsEmpty()) { mAudioInput->StopRecording(aSource); return NS_OK; } if (mState != kStarted) { return NS_ERROR_FAILURE; } - if (!mVoEBase) { - return NS_ERROR_FAILURE; - } mState = kStopped; } if (mListener) { // breaks a cycle, since the WebRTCAudioDataListener has a RefPtr to us mListener->Shutdown(); mListener = nullptr; } mAudioInput->StopRecording(aSource); - mVoERender->DeRegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel); - - if (mVoEBase->StopSend(mChannel)) { - return NS_ERROR_FAILURE; - } - if (mVoEBase->StopReceive(mChannel)) { - return NS_ERROR_FAILURE; - } return NS_OK; } void MediaEngineWebRTCMicrophoneSource::NotifyPull(MediaStreamGraph *aGraph, SourceMediaStream *aSource, TrackID aID, StreamTime aDesiredTime, @@ -598,58 +692,209 @@ MediaEngineWebRTCMicrophoneSource::Notif void MediaEngineWebRTCMicrophoneSource::NotifyOutputData(MediaStreamGraph* aGraph, AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels) { - if (mAudioOutputObserver) { + if (!PassThrough()) { mAudioOutputObserver->InsertFarEnd(aBuffer, aFrames, false, - aRate, aChannels); + aRate, aChannels); } } +// Only called if we're not in passthrough mode void MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph, const AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels) { - // This will call Process() with data coming out of the AEC/NS/AGC/etc chain + MOZ_ASSERT(!PassThrough(), "This should be bypassed when in PassThrough mode."); + size_t offset = 0; + if (!mPacketizer || mPacketizer->PacketSize() != aRate/100u || mPacketizer->Channels() != aChannels) { // It's ok to drop the audio still in the packetizer here. mPacketizer = - new AudioPacketizer<AudioDataValue, int16_t>(aRate/100, aChannels); + new AudioPacketizer<AudioDataValue, float>(aRate/100, aChannels); + } + + // On initial capture, throw away all far-end data except the most recent sample + // since it's already irrelevant and we want to keep avoid confusing the AEC far-end + // input code with "old" audio. + if (!mStarted) { + mStarted = true; + while (mAudioOutputObserver->Size() > 1) { + free(mAudioOutputObserver->Pop()); // only call if size() > 0 + } } + // Feed the far-end audio data (speakers) to the feedback input of the AEC. + while (mAudioOutputObserver->Size() > 0) { + // Bug 1414837: This will call `free()`, and we should remove it. + // Pop gives ownership. + nsAutoPtr<FarEndAudioChunk> buffer(mAudioOutputObserver->Pop()); // only call if size() > 0 + if (!buffer) { + continue; + } + AudioDataValue* packetDataPointer = buffer->mData; + AutoTArray<float*, MAX_CHANNELS> deinterleavedPacketDataChannelPointers; + AudioDataValue* interleavedFarend = nullptr; + uint32_t channelCountFarend = 0; + uint32_t framesPerPacketFarend = 0; + + // Downmix from aChannels to MAX_CHANNELS if needed + if (mAudioOutputObserver->PlayoutChannels() > MAX_CHANNELS) { + AudioConverter converter(AudioConfig(aChannels, 0, AudioConfig::FORMAT_DEFAULT), + AudioConfig(MAX_CHANNELS, 0, AudioConfig::FORMAT_DEFAULT)); + framesPerPacketFarend = + buffer->mSamples; + framesPerPacketFarend = + converter.Process(mInputDownmixBuffer, + packetDataPointer, + framesPerPacketFarend); + interleavedFarend = mInputDownmixBuffer.Data(); + channelCountFarend = MAX_CHANNELS; + deinterleavedPacketDataChannelPointers.SetLength(MAX_CHANNELS); + } else { + uint32_t outputChannels = mAudioOutputObserver->PlayoutChannels(); + interleavedFarend = packetDataPointer; + channelCountFarend = outputChannels; + framesPerPacketFarend = buffer->mSamples; + deinterleavedPacketDataChannelPointers.SetLength(outputChannels); + } + + MOZ_ASSERT(interleavedFarend && + (channelCountFarend == 1 || channelCountFarend == 2) && + framesPerPacketFarend); + + if (mInputBuffer.Length() < framesPerPacketFarend * channelCountFarend) { + mInputBuffer.SetLength(framesPerPacketFarend * channelCountFarend); + } + + offset = 0; + for (size_t i = 0; i < deinterleavedPacketDataChannelPointers.Length(); ++i) { + deinterleavedPacketDataChannelPointers[i] = mInputBuffer.Data() + offset; + offset += framesPerPacketFarend; + } + + // Deinterleave, prepare a channel pointers array, with enough storage for + // the frames. + // + // If this is a platform that uses s16 for audio input and output, + // convert to floats, the APM API we use only accepts floats. + DeinterleaveAndConvertBuffer(interleavedFarend, + framesPerPacketFarend, + channelCountFarend, + deinterleavedPacketDataChannelPointers.Elements()); + + // Having the same config for input and output means we potentially save + // some CPU. + StreamConfig inputConfig(mAudioOutputObserver->PlayoutFrequency(), + channelCountFarend, + false /* we don't use typing detection*/); + StreamConfig outputConfig = inputConfig; + + // Passing the same pointers here saves a copy inside this function. + int err = + mAudioProcessing->ProcessReverseStream(deinterleavedPacketDataChannelPointers.Elements(), + inputConfig, + outputConfig, + deinterleavedPacketDataChannelPointers.Elements()); + + if (err) { + MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, + ("error in audio ProcessReverseStream(): %d", err)); + return; + } + } + + // Packetize our input data into 10ms chunks, deinterleave into planar channel + // buffers, process, and append to the right MediaStreamTrack. mPacketizer->Input(aBuffer, static_cast<uint32_t>(aFrames)); while (mPacketizer->PacketsAvailable()) { uint32_t samplesPerPacket = mPacketizer->PacketSize() * mPacketizer->Channels(); if (mInputBuffer.Length() < samplesPerPacket) { mInputBuffer.SetLength(samplesPerPacket); } - int16_t* packet = mInputBuffer.Elements(); + if (mDeinterleavedBuffer.Length() < samplesPerPacket) { + mDeinterleavedBuffer.SetLength(samplesPerPacket); + } + float* packet = mInputBuffer.Data(); mPacketizer->Output(packet); - if (aChannels > MAX_CHANNELS) { - AudioConverter converter(AudioConfig(aChannels, 0, AudioConfig::FORMAT_S16), - AudioConfig(MAX_CHANNELS, 0, AudioConfig::FORMAT_S16)); - converter.Process(mInputDownmixBuffer, packet, mPacketizer->PacketSize()); - mVoERender->ExternalRecordingInsertData(mInputDownmixBuffer.Data(), - mPacketizer->PacketSize() * MAX_CHANNELS, - aRate, 0); - } else { - mVoERender->ExternalRecordingInsertData(packet, samplesPerPacket, aRate, 0); + // Deinterleave the input data + // Prepare an array pointing to deinterleaved channels. + AutoTArray<float*, 8> deinterleavedPacketizedInputDataChannelPointers; + deinterleavedPacketizedInputDataChannelPointers.SetLength(aChannels); + offset = 0; + for (size_t i = 0; i < deinterleavedPacketizedInputDataChannelPointers.Length(); ++i) { + deinterleavedPacketizedInputDataChannelPointers[i] = mDeinterleavedBuffer.Data() + offset; + offset += mPacketizer->PacketSize(); + } + + // Deinterleave to mInputBuffer, pointed to by inputBufferChannelPointers. + Deinterleave(packet, mPacketizer->PacketSize(), aChannels, + deinterleavedPacketizedInputDataChannelPointers.Elements()); + + StreamConfig inputConfig(aRate, + aChannels, + false /* we don't use typing detection*/); + StreamConfig outputConfig = inputConfig; + + // Bug 1404965: Get the right delay here, it saves some work down the line. + mAudioProcessing->set_stream_delay_ms(0); + + // Bug 1414837: find a way to not allocate here. + RefPtr<SharedBuffer> buffer = + SharedBuffer::Create(mPacketizer->PacketSize() * aChannels * sizeof(float)); + AudioSegment segment; + + // Prepare channel pointers to the SharedBuffer created above. + AutoTArray<float*, 8> processedOutputChannelPointers; + AutoTArray<const float*, 8> processedOutputChannelPointersConst; + processedOutputChannelPointers.SetLength(aChannels); + processedOutputChannelPointersConst.SetLength(aChannels); + + offset = 0; + for (size_t i = 0; i < processedOutputChannelPointers.Length(); ++i) { + processedOutputChannelPointers[i] = static_cast<float*>(buffer->Data()) + offset; + processedOutputChannelPointersConst[i] = static_cast<float*>(buffer->Data()) + offset; + offset += mPacketizer->PacketSize(); + } + + mAudioProcessing->ProcessStream(deinterleavedPacketizedInputDataChannelPointers.Elements(), + inputConfig, + outputConfig, + processedOutputChannelPointers.Elements()); + MonitorAutoLock lock(mMonitor); + if (mState != kStarted) + return; + + for (size_t i = 0; i < mSources.Length(); ++i) { + if (!mSources[i]) { // why ?! + continue; + } + + // We already have planar audio data of the right format. Insert into the + // MSG. + MOZ_ASSERT(processedOutputChannelPointers.Length() == aChannels); + RefPtr<SharedBuffer> other = buffer; + segment.AppendFrames(other.forget(), + processedOutputChannelPointersConst, + mPacketizer->PacketSize(), + mPrincipalHandles[i]); + mSources[i]->AppendToTrack(mTrackID, &segment); } } } template<typename T> void MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer, size_t aFrames, @@ -666,33 +911,33 @@ MediaEngineWebRTCMicrophoneSource::Inser MOZ_LOG(AudioLogModule(), LogLevel::Debug, ("%p: Inserting %zu samples into graph, total frames = %" PRIu64, (void*)this, aFrames, mTotalFrames)); mLastLogFrames = mTotalFrames; } } size_t len = mSources.Length(); - for (size_t i = 0; i < len; i++) { + for (size_t i = 0; i < len; ++i) { if (!mSources[i]) { continue; } TimeStamp insertTime; // Make sure we include the stream and the track. // The 0:1 is a flag to note when we've done the final insert for a given input block. LogTime(AsyncLatencyLogger::AudioTrackInsertion, LATENCY_STREAM_ID(mSources[i].get(), mTrackID), (i+1 < len) ? 0 : 1, insertTime); // Bug 971528 - Support stereo capture in gUM MOZ_ASSERT(aChannels >= 1 && aChannels <= 8, "Support up to 8 channels"); - nsAutoPtr<AudioSegment> segment(new AudioSegment()); + AudioSegment segment; RefPtr<SharedBuffer> buffer = SharedBuffer::Create(aFrames * aChannels * sizeof(T)); AutoTArray<const T*, 8> channels; if (aChannels == 1) { PodCopy(static_cast<T*>(buffer->Data()), aBuffer, aFrames); channels.AppendElement(static_cast<T*>(buffer->Data())); } else { channels.SetLength(aChannels); @@ -708,21 +953,21 @@ MediaEngineWebRTCMicrophoneSource::Inser DeinterleaveAndConvertBuffer(aBuffer, aFrames, aChannels, write_channels.Elements()); } MOZ_ASSERT(aChannels == channels.Length()); - segment->AppendFrames(buffer.forget(), channels, aFrames, + segment.AppendFrames(buffer.forget(), channels, aFrames, mPrincipalHandles[i]); - segment->GetStartTime(insertTime); + segment.GetStartTime(insertTime); - mSources[i]->AppendToTrack(mTrackID, segment); + mSources[i]->AppendToTrack(mTrackID, &segment); } } // Called back on GraphDriver thread! // Note this can be called back after ::Shutdown() void MediaEngineWebRTCMicrophoneSource::NotifyInputData(MediaStreamGraph* aGraph, const AudioDataValue* aBuffer, @@ -736,174 +981,64 @@ MediaEngineWebRTCMicrophoneSource::Notif InsertInGraph<AudioDataValue>(aBuffer, aFrames, aChannels); } else { PacketizeAndProcess(aGraph, aBuffer, aFrames, aRate, aChannels); } } #define ResetProcessingIfNeeded(_processing) \ do { \ - webrtc::_processing##Modes mode; \ - int rv = mVoEProcessing->Get##_processing##Status(enabled, mode); \ - if (rv) { \ - NS_WARNING("Could not get the status of the " \ - #_processing " on device change."); \ - return; \ - } \ + bool enabled = mAudioProcessing->_processing()->is_enabled(); \ \ if (enabled) { \ - rv = mVoEProcessing->Set##_processing##Status(!enabled); \ + int rv = mAudioProcessing->_processing()->Enable(!enabled); \ + if (rv) { \ + NS_WARNING("Could not reset the status of the " \ + #_processing " on device change."); \ + return; \ + } \ + rv = mAudioProcessing->_processing()->Enable(enabled); \ if (rv) { \ NS_WARNING("Could not reset the status of the " \ #_processing " on device change."); \ return; \ } \ \ - rv = mVoEProcessing->Set##_processing##Status(enabled); \ - if (rv) { \ - NS_WARNING("Could not reset the status of the " \ - #_processing " on device change."); \ - return; \ - } \ } \ } while(0) void MediaEngineWebRTCMicrophoneSource::DeviceChanged() { // Reset some processing - bool enabled; - ResetProcessingIfNeeded(Agc); - ResetProcessingIfNeeded(Ec); - ResetProcessingIfNeeded(Ns); + ResetProcessingIfNeeded(gain_control); + ResetProcessingIfNeeded(echo_cancellation); + ResetProcessingIfNeeded(noise_suppression); } -bool -MediaEngineWebRTCMicrophoneSource::InitEngine() -{ - MOZ_ASSERT(!mVoEBase); - mVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); - - mVoEBase->Init(); - webrtc::Config config; - config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(mExtendedFilter)); - config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(mDelayAgnostic)); - mVoEBase->audio_processing()->SetExtraOptions(config); - - mVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine); - if (mVoERender) { - mVoENetwork = webrtc::VoENetwork::GetInterface(mVoiceEngine); - if (mVoENetwork) { - mVoEProcessing = webrtc::VoEAudioProcessing::GetInterface(mVoiceEngine); - if (mVoEProcessing) { - mNullTransport = new NullTransport(); - return true; - } - } - } - DeInitEngine(); - return false; -} - -// This shuts down the engine when no channel is open -void -MediaEngineWebRTCMicrophoneSource::DeInitEngine() -{ - if (mVoEBase) { - mVoEBase->Terminate(); - delete mNullTransport; - mNullTransport = nullptr; - - mVoEProcessing = nullptr; - mVoENetwork = nullptr; - mVoERender = nullptr; - mVoEBase = nullptr; - } -} - -// This shuts down the engine when no channel is open. // mState records if a channel is allocated (slightly redundantly to mChannel) void MediaEngineWebRTCMicrophoneSource::FreeChannel() { if (mState != kReleased) { - if (mChannel != -1) { - MOZ_ASSERT(mVoENetwork && mVoEBase); - if (mVoENetwork) { - mVoENetwork->DeRegisterExternalTransport(mChannel); - } - if (mVoEBase) { - mVoEBase->DeleteChannel(mChannel); - } - mChannel = -1; - } mState = kReleased; MOZ_ASSERT(sChannelsOpen > 0); - if (--sChannelsOpen == 0) { - DeInitEngine(); - } + --sChannelsOpen; } } bool MediaEngineWebRTCMicrophoneSource::AllocChannel() { - MOZ_ASSERT(mVoEBase); - - mChannel = mVoEBase->CreateChannel(); - if (mChannel >= 0) { - if (!mVoENetwork->RegisterExternalTransport(mChannel, *mNullTransport)) { - mSampleFrequency = MediaEngine::DEFAULT_SAMPLE_RATE; - LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency)); - - // Check for availability. - if (!mAudioInput->SetRecordingDevice(mCapIndex)) { - // Because of the permission mechanism of B2G, we need to skip the status - // check here. - bool avail = false; - mAudioInput->GetRecordingDeviceStatus(avail); - if (!avail) { - if (sChannelsOpen == 0) { - DeInitEngine(); - } - return false; - } + mSampleFrequency = MediaEngine::USE_GRAPH_RATE; + LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency)); - // Set "codec" to PCM, 32kHz on device's channels - ScopedCustomReleasePtr<webrtc::VoECodec> ptrVoECodec(webrtc::VoECodec::GetInterface(mVoiceEngine)); - if (ptrVoECodec) { - webrtc::CodecInst codec; - strcpy(codec.plname, ENCODING); - codec.channels = CHANNELS; - uint32_t maxChannels = 0; - if (mAudioInput->GetMaxAvailableChannels(maxChannels) == 0) { - MOZ_ASSERT(maxChannels); - codec.channels = std::min<uint32_t>(maxChannels, MAX_CHANNELS); - } - MOZ_ASSERT(mSampleFrequency == 16000 || mSampleFrequency == 32000); - codec.rate = SAMPLE_RATE(mSampleFrequency); - codec.plfreq = mSampleFrequency; - codec.pacsize = SAMPLE_LENGTH(mSampleFrequency); - codec.pltype = 0; // Default payload type - - if (!ptrVoECodec->SetSendCodec(mChannel, codec)) { - mState = kAllocated; - sChannelsOpen++; - return true; - } - } - } - } - } - mVoEBase->DeleteChannel(mChannel); - mChannel = -1; - if (sChannelsOpen == 0) { - DeInitEngine(); - } - return false; + mState = kAllocated; + sChannelsOpen++; + return true; } void MediaEngineWebRTCMicrophoneSource::Shutdown() { Super::Shutdown(); if (mListener) { // breaks a cycle, since the WebRTCAudioDataListener has a RefPtr to us @@ -931,56 +1066,16 @@ MediaEngineWebRTCMicrophoneSource::Shutd } while (mRegisteredHandles.Length()) { MOZ_ASSERT(mState == kAllocated || mState == kStopped); // on last Deallocate(), FreeChannel()s and DeInit()s if all channels are released Deallocate(mRegisteredHandles[0].get()); } MOZ_ASSERT(mState == kReleased); - - mAudioInput = nullptr; -} - -typedef int16_t sample; - -void -MediaEngineWebRTCMicrophoneSource::Process(int channel, - webrtc::ProcessingTypes type, - sample *audio10ms, size_t length, - int samplingFreq, bool isStereo) -{ - MOZ_ASSERT(!PassThrough(), "This should be bypassed when in PassThrough mode."); - // On initial capture, throw away all far-end data except the most recent sample - // since it's already irrelevant and we want to keep avoid confusing the AEC far-end - // input code with "old" audio. - if (!mStarted) { - mStarted = true; - while (mAudioOutputObserver->Size() > 1) { - free(mAudioOutputObserver->Pop()); // only call if size() > 0 - } - } - - while (mAudioOutputObserver->Size() > 0) { - FarEndAudioChunk *buffer = mAudioOutputObserver->Pop(); // only call if size() > 0 - if (buffer) { - int length = buffer->mSamples; - int res = mVoERender->ExternalPlayoutData(buffer->mData, - mAudioOutputObserver->PlayoutFrequency(), - mAudioOutputObserver->PlayoutChannels(), - length); - free(buffer); - if (res == -1) { - return; - } - } - } - - uint32_t channels = isStereo ? 2 : 1; - InsertInGraph<int16_t>(audio10ms, length, channels); } void MediaEngineWebRTCAudioCaptureSource::GetName(nsAString &aName) const { aName.AssignLiteral("AudioCapture"); }
--- a/dom/media/webrtc/moz.build +++ b/dom/media/webrtc/moz.build @@ -34,21 +34,22 @@ if CONFIG['MOZ_WEBRTC']: 'RTCCertificate.cpp', 'RTCIdentityProviderRegistrar.cpp', ] # MediaEngineWebRTC.cpp needs to be built separately. SOURCES += [ 'MediaEngineWebRTC.cpp', ] LOCAL_INCLUDES += [ + '..', '/dom/base', '/media/libyuv/libyuv/include', '/media/webrtc/signaling/src/common', '/media/webrtc/signaling/src/common/browser_logging', - '/media/webrtc/trunk', + '/media/webrtc/trunk' ] XPIDL_SOURCES += [ 'nsITabSource.idl' ] UNIFIED_SOURCES += [ 'MediaEngineDefault.cpp',
--- a/dom/network/TCPSocket.cpp +++ b/dom/network/TCPSocket.cpp @@ -1009,20 +1009,20 @@ NS_IMETHODIMP TCPSocket::OnTransportStatus(nsITransport* aTransport, nsresult aStatus, int64_t aProgress, int64_t aProgressMax) { if (static_cast<uint32_t>(aStatus) != nsISocketTransport::STATUS_CONNECTED_TO) { return NS_OK; } mReadyState = TCPReadyState::Open; + nsresult rv = CreateInputStreamPump(); + NS_ENSURE_SUCCESS(rv, rv); FireEvent(NS_LITERAL_STRING("open")); - nsresult rv = CreateInputStreamPump(); - NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP TCPSocket::OnInputStreamReady(nsIAsyncInputStream* aStream) { // Only used for detecting if the connection was refused.
--- a/editor/libeditor/InternetCiter.cpp +++ b/editor/libeditor/InternetCiter.cpp @@ -6,18 +6,17 @@ #include "InternetCiter.h" #include "nsAString.h" #include "nsCOMPtr.h" #include "nsCRT.h" #include "nsDebug.h" #include "nsDependentSubstring.h" #include "nsError.h" -#include "nsILineBreaker.h" -#include "nsLWBrkCIID.h" +#include "mozilla/intl/LineBreaker.h" #include "nsServiceManagerUtils.h" #include "nsString.h" #include "nsStringIterator.h" namespace mozilla { const char16_t gt ('>'); const char16_t space (' '); @@ -162,18 +161,20 @@ InternetCiter::Rewrap(const nsAString& a #ifdef DEBUG int32_t cr = aInString.FindChar(char16_t('\r')); NS_ASSERTION((cr < 0), "Rewrap: CR in string gotten from DOM!\n"); #endif /* DEBUG */ aOutString.Truncate(); nsresult rv; - nsCOMPtr<nsILineBreaker> lineBreaker = do_GetService(NS_LBRK_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); + + RefPtr<mozilla::intl::LineBreaker> lineBreaker = + mozilla::intl::LineBreaker::Create(); + MOZ_ASSERT(lineBreaker); // Loop over lines in the input string, rewrapping each one. uint32_t length; uint32_t posInString = 0; uint32_t outStringCol = 0; uint32_t citeLevel = 0; const nsPromiseFlatString &tString = PromiseFlatString(aInString); length = tString.Length();
--- a/editor/txtsvc/nsTextServicesDocument.cpp +++ b/editor/txtsvc/nsTextServicesDocument.cpp @@ -25,17 +25,17 @@ #include "nsIEditor.h" // for nsIEditor, etc #include "nsINode.h" // for nsINode #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor #include "nsISelection.h" // for nsISelection #include "nsISelectionController.h" // for nsISelectionController, etc #include "nsISupportsBase.h" // for nsISupports #include "nsISupportsUtils.h" // for NS_IF_ADDREF, NS_ADDREF, etc #include "nsITextServicesFilter.h" // for nsITextServicesFilter -#include "nsIWordBreaker.h" // for nsWordRange, nsIWordBreaker +#include "mozilla/intl/WordBreaker.h" // for WordRange, WordBreaker #include "nsRange.h" // for nsRange #include "nsStaticAtom.h" // for NS_STATIC_ATOM_SETUP, etc #include "nsString.h" // for nsString, nsAutoString #include "nsTextServicesDocument.h" #include "nscore.h" // for nsresult, NS_IMETHODIMP, etc #define LOCK_DOC(doc) #define UNLOCK_DOC(doc) @@ -3149,18 +3149,18 @@ nsTextServicesDocument::FindWordBounds(n uint32_t strOffset = entry->mStrOffset + aNodeOffset - entry->mNodeOffset; // Now we use the word breaker to find the beginning and end // of the word from our calculated string offset. const char16_t *str = aBlockStr->get(); uint32_t strLen = aBlockStr->Length(); - nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker(); - nsWordRange res = wordBreaker->FindWord(str, strLen, strOffset); + mozilla::intl::WordBreaker* wordBreaker = nsContentUtils::WordBreaker(); + mozilla::intl::WordRange res = wordBreaker->FindWord(str, strLen, strOffset); if (res.mBegin > strLen) { return str ? NS_ERROR_ILLEGAL_VALUE : NS_ERROR_NULL_POINTER; } // Strip out the NBSPs at the ends while (res.mBegin <= res.mEnd && IS_NBSP_CHAR(str[res.mBegin])) { res.mBegin++; }
--- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -238,28 +238,30 @@ ContentClient::BeginPaint(PaintedLayer* // types, or we failed to unrotate the buffer when requested. In any case, // we need to allocate a new one and prepare it for drawing. if (!dest.mCanReuseBuffer) { uint32_t bufferFlags = 0; if (dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { bufferFlags |= BUFFER_COMPONENT_ALPHA; } - RefPtr<RotatedBuffer> newBuffer = CreateBuffer(result.mContentType, - dest.mBufferRect, - bufferFlags); + RefPtr<RotatedBuffer> newBuffer; + if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) { + newBuffer = CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags); - if (!newBuffer) { - if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) { + if (!newBuffer) { gfxCriticalNote << "Failed buffer for " << dest.mBufferRect.x << ", " << dest.mBufferRect.y << ", " << dest.mBufferRect.Width() << ", " << dest.mBufferRect.Height(); } + } + + if (!newBuffer) { Clear(); return result; } if (!newBuffer->Lock(lockMode)) { gfxCriticalNote << "Failed to lock new back buffer."; Clear(); return result;
--- a/intl/build/nsI18nModule.cpp +++ b/intl/build/nsI18nModule.cpp @@ -1,61 +1,47 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/ModuleUtils.h" -// lwbrk -#include "nsLWBrkCIID.h" -#include "nsJISx4051LineBreaker.h" -#include "nsSampleWordBreaker.h" - // string bundles (intl) #include "nsStringBundleService.h" #include "nsStringBundleTextOverride.h" // locale #include "nsLocaleConstructors.h" // uconv -NS_GENERIC_FACTORY_CONSTRUCTOR(nsJISx4051LineBreaker) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsSampleWordBreaker) - NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStringBundleService, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStringBundleTextOverride, Init) NS_DEFINE_NAMED_CID(MOZ_LOCALESERVICE_CID); NS_DEFINE_NAMED_CID(MOZ_OSPREFERENCES_CID); -NS_DEFINE_NAMED_CID(NS_LBRK_CID); -NS_DEFINE_NAMED_CID(NS_WBRK_CID); NS_DEFINE_NAMED_CID(NS_STRINGBUNDLESERVICE_CID); NS_DEFINE_NAMED_CID(NS_STRINGBUNDLETEXTOVERRIDE_CID); NS_DEFINE_NAMED_CID(NS_COLLATIONFACTORY_CID); NS_DEFINE_NAMED_CID(NS_COLLATION_CID); static const mozilla::Module::CIDEntry kIntlCIDs[] = { { &kMOZ_LOCALESERVICE_CID, false, nullptr, mozilla::intl::LocaleServiceConstructor }, { &kMOZ_OSPREFERENCES_CID, false, nullptr, mozilla::intl::OSPreferencesConstructor }, - { &kNS_LBRK_CID, false, nullptr, nsJISx4051LineBreakerConstructor }, - { &kNS_WBRK_CID, false, nullptr, nsSampleWordBreakerConstructor }, { &kNS_STRINGBUNDLESERVICE_CID, false, nullptr, nsStringBundleServiceConstructor }, { &kNS_STRINGBUNDLETEXTOVERRIDE_CID, false, nullptr, nsStringBundleTextOverrideConstructor }, { &kNS_COLLATIONFACTORY_CID, false, nullptr, nsCollationFactoryConstructor }, { &kNS_COLLATION_CID, false, nullptr, nsCollationConstructor }, { nullptr } }; static const mozilla::Module::ContractIDEntry kIntlContracts[] = { { MOZ_LOCALESERVICE_CONTRACTID, &kMOZ_LOCALESERVICE_CID }, { MOZ_OSPREFERENCES_CONTRACTID, &kMOZ_OSPREFERENCES_CID }, - { NS_LBRK_CONTRACTID, &kNS_LBRK_CID }, - { NS_WBRK_CONTRACTID, &kNS_WBRK_CID }, { NS_STRINGBUNDLE_CONTRACTID, &kNS_STRINGBUNDLESERVICE_CID }, { NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID, &kNS_STRINGBUNDLETEXTOVERRIDE_CID }, { NS_COLLATIONFACTORY_CONTRACTID, &kNS_COLLATIONFACTORY_CID }, { NS_COLLATION_CONTRACTID, &kNS_COLLATION_CID }, { nullptr } }; static const mozilla::Module kIntlModule = {
rename from intl/lwbrk/nsJISx4051LineBreaker.cpp rename to intl/lwbrk/LineBreaker.cpp --- a/intl/lwbrk/nsJISx4051LineBreaker.cpp +++ b/intl/lwbrk/LineBreaker.cpp @@ -1,23 +1,29 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -#include "nsJISx4051LineBreaker.h" +#include "mozilla/intl/LineBreaker.h" #include "jisx4051class.h" #include "nsComplexBreaker.h" #include "nsTArray.h" #include "nsUnicodeProperties.h" using namespace mozilla::unicode; +using namespace mozilla::intl; + +/*static*/ +already_AddRefed<LineBreaker> +LineBreaker::Create() +{ + return RefPtr<LineBreaker>(new LineBreaker()).forget(); +} /* Simplification of Pair Table in JIS X 4051 1. The Origion Table - in 4.1.3 In JIS x 4051. The pair table is defined as below @@ -577,26 +583,16 @@ static bool GetPairConservative(int8_t c1, int8_t c2) { NS_ASSERTION(c1 < MAX_CLASSES ,"illegal classes 1"); NS_ASSERTION(c2 < MAX_CLASSES ,"illegal classes 2"); return (0 == ((gPairConservative[c1] >> c2) & 0x0001)); } -nsJISx4051LineBreaker::nsJISx4051LineBreaker() -{ -} - -nsJISx4051LineBreaker::~nsJISx4051LineBreaker() -{ -} - -NS_IMPL_ISUPPORTS(nsJISx4051LineBreaker, nsILineBreaker) - class ContextState { public: ContextState(const char16_t* aText, uint32_t aLength) : mUniText(aText) , mText(nullptr) , mLength(aLength) { Init(); @@ -873,18 +869,18 @@ ContextualAnalysis(char32_t prev, char32 NS_ERROR("Forgot to handle the current character!"); } } return GetClass(cur); } int32_t -nsJISx4051LineBreaker::WordMove(const char16_t* aText, uint32_t aLen, - uint32_t aPos, int8_t aDirection) +LineBreaker::WordMove(const char16_t* aText, uint32_t aLen, + uint32_t aPos, int8_t aDirection) { bool textNeedsJISx4051 = false; int32_t begin, end; for (begin = aPos; begin > 0 && !NS_IsSpace(aText[begin - 1]); --begin) { if (IS_CJK_CHAR(aText[begin]) || NS_NeedsPlatformNativeHandling(aText[begin])) { textNeedsJISx4051 = true; } @@ -902,55 +898,55 @@ nsJISx4051LineBreaker::WordMove(const ch // (This is required for serializers. See Bug #344816.) // Also fall back to this when out of memory. if (aDirection < 0) { ret = (begin == int32_t(aPos)) ? begin - 1 : begin; } else { ret = end; } } else { - GetJISx4051Breaks(aText + begin, end - begin, nsILineBreaker::kWordBreak_Normal, + GetJISx4051Breaks(aText + begin, end - begin, LineBreaker::kWordBreak_Normal, breakState.Elements()); ret = aPos; do { ret += aDirection; } while (begin < ret && ret < end && !breakState[ret - begin]); } return ret; } int32_t -nsJISx4051LineBreaker::Next(const char16_t* aText, uint32_t aLen, - uint32_t aPos) +LineBreaker::Next(const char16_t* aText, uint32_t aLen, + uint32_t aPos) { NS_ASSERTION(aText, "aText shouldn't be null"); NS_ASSERTION(aLen > aPos, "Bad position passed to nsJISx4051LineBreaker::Next"); int32_t nextPos = WordMove(aText, aLen, aPos, 1); return nextPos < int32_t(aLen) ? nextPos : NS_LINEBREAKER_NEED_MORE_TEXT; } int32_t -nsJISx4051LineBreaker::Prev(const char16_t* aText, uint32_t aLen, - uint32_t aPos) +LineBreaker::Prev(const char16_t* aText, uint32_t aLen, + uint32_t aPos) { NS_ASSERTION(aText, "aText shouldn't be null"); NS_ASSERTION(aLen >= aPos && aPos > 0, "Bad position passed to nsJISx4051LineBreaker::Prev"); int32_t prevPos = WordMove(aText, aLen, aPos, -1); return prevPos > 0 ? prevPos : NS_LINEBREAKER_NEED_MORE_TEXT; } void -nsJISx4051LineBreaker::GetJISx4051Breaks(const char16_t* aChars, uint32_t aLength, - uint8_t aWordBreak, - uint8_t* aBreakBefore) +LineBreaker::GetJISx4051Breaks(const char16_t* aChars, uint32_t aLength, + uint8_t aWordBreak, + uint8_t* aBreakBefore) { uint32_t cur; int8_t lastClass = CLASS_NONE; ContextState state(aChars, aLength); for (cur = 0; cur < aLength; ++cur, state.AdvanceIndex()) { char32_t ch = state.GetUnicodeCharAt(cur); uint32_t chLen = ch > 0xFFFFu ? 2 : 1; @@ -981,20 +977,20 @@ nsJISx4051LineBreaker::GetJISx4051Breaks state.NotifyNonHyphenCharacter(ch); cl = GetClass(ch); } bool allowBreak = false; if (cur > 0) { NS_ASSERTION(CLASS_COMPLEX != lastClass || CLASS_COMPLEX != cl, "Loop should have prevented adjacent complex chars here"); - if (aWordBreak == nsILineBreaker::kWordBreak_Normal) { + if (aWordBreak == LineBreaker::kWordBreak_Normal) { allowBreak = (state.UseConservativeBreaking()) ? GetPairConservative(lastClass, cl) : GetPair(lastClass, cl); - } else if (aWordBreak == nsILineBreaker::kWordBreak_BreakAll) { + } else if (aWordBreak == LineBreaker::kWordBreak_BreakAll) { allowBreak = true; } } aBreakBefore[cur] = allowBreak; if (allowBreak) state.NotifyBreakBefore(); lastClass = cl; if (CLASS_COMPLEX == cl) { @@ -1009,20 +1005,20 @@ nsJISx4051LineBreaker::GetJISx4051Breaks if (c > 0xFFFFU) { // it was a surrogate pair ++end; } } NS_GetComplexLineBreaks(aChars + cur, end - cur, aBreakBefore + cur); // We have to consider word-break value again for complex characters - if (aWordBreak != nsILineBreaker::kWordBreak_Normal) { + if (aWordBreak != LineBreaker::kWordBreak_Normal) { // Respect word-break property for (uint32_t i = cur; i < end; i++) - aBreakBefore[i] = (aWordBreak == nsILineBreaker::kWordBreak_BreakAll); + aBreakBefore[i] = (aWordBreak == LineBreaker::kWordBreak_BreakAll); } // restore breakability at chunk begin, which was always set to false // by the complex line breaker aBreakBefore[cur] = allowBreak; cur = end - 1; } @@ -1033,19 +1029,19 @@ nsJISx4051LineBreaker::GetJISx4051Breaks ++cur; aBreakBefore[cur] = false; state.AdvanceIndex(); } } } void -nsJISx4051LineBreaker::GetJISx4051Breaks(const uint8_t* aChars, uint32_t aLength, - uint8_t aWordBreak, - uint8_t* aBreakBefore) +LineBreaker::GetJISx4051Breaks(const uint8_t* aChars, uint32_t aLength, + uint8_t aWordBreak, + uint8_t* aBreakBefore) { uint32_t cur; int8_t lastClass = CLASS_NONE; ContextState state(aChars, aLength); for (cur = 0; cur < aLength; ++cur, state.AdvanceIndex()) { char32_t ch = aChars[cur]; int8_t cl; @@ -1059,20 +1055,20 @@ nsJISx4051LineBreaker::GetJISx4051Breaks if (ch == U_EQUAL) state.NotifySeenEqualsSign(); state.NotifyNonHyphenCharacter(ch); cl = GetClass(ch); } bool allowBreak = false; if (cur > 0) { - if (aWordBreak == nsILineBreaker::kWordBreak_Normal) { + if (aWordBreak == LineBreaker::kWordBreak_Normal) { allowBreak = (state.UseConservativeBreaking()) ? GetPairConservative(lastClass, cl) : GetPair(lastClass, cl); - } else if (aWordBreak == nsILineBreaker::kWordBreak_BreakAll) { + } else if (aWordBreak == LineBreaker::kWordBreak_BreakAll) { allowBreak = true; } } aBreakBefore[cur] = allowBreak; if (allowBreak) state.NotifyBreakBefore(); lastClass = cl; }
rename from intl/lwbrk/nsILineBreaker.h rename to intl/lwbrk/LineBreaker.h --- a/intl/lwbrk/nsILineBreaker.h +++ b/intl/lwbrk/LineBreaker.h @@ -1,59 +1,60 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef nsILineBreaker_h__ -#define nsILineBreaker_h__ - -#include "nsISupports.h" +#ifndef mozilla_intl_LineBreaker_h__ +#define mozilla_intl_LineBreaker_h__ #include "nscore.h" +#include "nsISupports.h" #define NS_LINEBREAKER_NEED_MORE_TEXT -1 -// {0x4b0b9e04-6ffb-4647-aa5f-2fa2ebd883e8} -#define NS_ILINEBREAKER_IID \ -{0x4b0b9e04, 0x6ffb, 0x4647, \ - {0xaa, 0x5f, 0x2f, 0xa2, 0xeb, 0xd8, 0x83, 0xe8}} +namespace mozilla { +namespace intl { -class nsILineBreaker : public nsISupports +class LineBreaker { public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_ILINEBREAKER_IID) + NS_INLINE_DECL_REFCOUNTING(LineBreaker) enum { kWordBreak_Normal = 0, // default kWordBreak_BreakAll = 1, // break all kWordBreak_KeepAll = 2 // always keep }; - virtual int32_t Next( const char16_t* aText, uint32_t aLen, - uint32_t aPos) = 0; + static already_AddRefed<LineBreaker> Create(); - virtual int32_t Prev( const char16_t* aText, uint32_t aLen, - uint32_t aPos) = 0; + int32_t Next(const char16_t* aText, uint32_t aLen, uint32_t aPos); + + int32_t Prev( const char16_t* aText, uint32_t aLen, uint32_t aPos); // Call this on a word with whitespace at either end. We will apply JISx4051 // rules to find breaks inside the word. aBreakBefore is set to the break- // before status of each character; aBreakBefore[0] will always be false // because we never return a break before the first character. // aLength is the length of the aText array and also the length of the aBreakBefore // output array. - virtual void GetJISx4051Breaks(const char16_t* aText, uint32_t aLength, - uint8_t aWordBreak, - uint8_t* aBreakBefore) = 0; - virtual void GetJISx4051Breaks(const uint8_t* aText, uint32_t aLength, - uint8_t aWordBreak, - uint8_t* aBreakBefore) = 0; + void GetJISx4051Breaks(const char16_t* aText, uint32_t aLength, + uint8_t aWordBreak, + uint8_t* aBreakBefore); + void GetJISx4051Breaks(const uint8_t* aText, uint32_t aLength, + uint8_t aWordBreak, + uint8_t* aBreakBefore); + +private: + ~LineBreaker() { } + + int32_t WordMove(const char16_t* aText, uint32_t aLen, uint32_t aPos, + int8_t aDirection); }; -NS_DEFINE_STATIC_IID_ACCESSOR(nsILineBreaker, NS_ILINEBREAKER_IID) - static inline bool NS_IsSpace(char16_t u) { return u == 0x0020 || // SPACE u == 0x0009 || // CHARACTER TABULATION u == 0x000D || // CARRIAGE RETURN (0x2000 <= u && u <= 0x2006) || // EN QUAD, EM QUAD, EN SPACE, // EM SPACE, THREE-PER-EM SPACE, @@ -67,9 +68,12 @@ NS_IsSpace(char16_t u) static inline bool NS_NeedsPlatformNativeHandling(char16_t aChar) { return (0x0e01 <= aChar && aChar <= 0x0fff) || // Thai, Lao, Tibetan (0x1780 <= aChar && aChar <= 0x17ff); // Khmer } -#endif /* nsILineBreaker_h__ */ +} // namespace intl +} // namespace mozilla + +#endif /* mozilla_intl_LineBreaker_h__ */
rename from intl/lwbrk/nsSampleWordBreaker.cpp rename to intl/lwbrk/WordBreaker.cpp --- a/intl/lwbrk/nsSampleWordBreaker.cpp +++ b/intl/lwbrk/WordBreaker.cpp @@ -1,26 +1,28 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/intl/WordBreaker.h" -#include "nsSampleWordBreaker.h" +using mozilla::intl::WordBreaker; +using mozilla::intl::WordBreakClass; +using mozilla::intl::WordRange; -nsSampleWordBreaker::nsSampleWordBreaker() +/*static*/ +already_AddRefed<WordBreaker> +WordBreaker::Create() { -} -nsSampleWordBreaker::~nsSampleWordBreaker() -{ + return RefPtr<WordBreaker>(new WordBreaker()).forget(); } -NS_IMPL_ISUPPORTS(nsSampleWordBreaker, nsIWordBreaker) -bool nsSampleWordBreaker::BreakInBetween( +bool WordBreaker::BreakInBetween( const char16_t* aText1 , uint32_t aTextLen1, const char16_t* aText2 , uint32_t aTextLen2) { NS_PRECONDITION( nullptr != aText1, "null ptr"); NS_PRECONDITION( nullptr != aText2, "null ptr"); if(!aText1 || !aText2 || (0 == aTextLen1) || (0 == aTextLen2)) return false; @@ -37,18 +39,18 @@ bool nsSampleWordBreaker::BreakInBetween // we change the beginning of IS_HAN from 0x4e00 to 0x3400 to relfect Unicode 3.0 #define IS_HAN(c) (( 0x3400 <= (c)) && ((c) <= 0x9fff))||(( 0xf900 <= (c)) && ((c) <= 0xfaff)) #define IS_KATAKANA(c) (( 0x30A0 <= (c)) && ((c) <= 0x30FF)) #define IS_HIRAGANA(c) (( 0x3040 <= (c)) && ((c) <= 0x309F)) #define IS_HALFWIDTHKATAKANA(c) (( 0xFF60 <= (c)) && ((c) <= 0xFF9F)) #define IS_THAI(c) (0x0E00 == (0xFF80 & (c) )) // Look at the higest 9 bits -/* static */ nsWordBreakClass -nsIWordBreaker::GetClass(char16_t c) +/* static */ WordBreakClass +WordBreaker::GetClass(char16_t c) { // begin of the hack if (IS_ALPHABETICAL_SCRIPT(c)) { if(IS_ASCII(c)) { if(ASCII_IS_SPACE(c)) { return kWbClassSpace; } else if(ASCII_IS_ALPHA(c) || ASCII_IS_DIGIT(c)) { @@ -71,35 +73,35 @@ nsIWordBreaker::GetClass(char16_t c) } else if(IS_HIRAGANA(c)) { return kWbClassHiraganaLetter; } else if(IS_HALFWIDTHKATAKANA(c)) { return kWbClassHWKatakanaLetter; } else { return kWbClassAlphaLetter; } } - return static_cast<nsWordBreakClass>(0); + return static_cast<WordBreakClass>(0); } -nsWordRange nsSampleWordBreaker::FindWord( +WordRange WordBreaker::FindWord( const char16_t* aText , uint32_t aTextLen, uint32_t aOffset) { - nsWordRange range; + WordRange range; NS_PRECONDITION( nullptr != aText, "null ptr"); NS_PRECONDITION( 0 != aTextLen, "len = 0"); NS_PRECONDITION( aOffset <= aTextLen, "aOffset > aTextLen"); range.mBegin = aTextLen + 1; range.mEnd = aTextLen + 1; if(!aText || aOffset > aTextLen) return range; - nsWordBreakClass c = GetClass(aText[aOffset]); + WordBreakClass c = GetClass(aText[aOffset]); uint32_t i; // Scan forward range.mEnd--; for(i = aOffset +1;i <= aTextLen; i++) { if( c != GetClass(aText[i])) { range.mEnd = i; @@ -120,20 +122,20 @@ nsWordRange nsSampleWordBreaker::FindWor if(kWbClassThaiLetter == c) { // need to call Thai word breaker from here // we should pass the whole Thai segment to the thai word breaker to find a shorter answer } return range; } -int32_t nsSampleWordBreaker::NextWord( +int32_t WordBreaker::NextWord( const char16_t* aText, uint32_t aLen, uint32_t aPos) { - nsWordBreakClass c1, c2; + WordBreakClass c1, c2; uint32_t cur = aPos; if (cur == aLen) return NS_WORDBREAKER_NEED_MORE_TEXT; c1 = GetClass(aText[cur]); for(cur++; cur <aLen; cur++) { c2 = GetClass(aText[cur]);
rename from intl/lwbrk/nsIWordBreaker.h rename to intl/lwbrk/WordBreaker.h --- a/intl/lwbrk/nsIWordBreaker.h +++ b/intl/lwbrk/WordBreaker.h @@ -1,53 +1,54 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef nsIWordBreaker_h__ -#define nsIWordBreaker_h__ - -#include "nsISupports.h" +#ifndef mozilla_intl_WordBreaker_h__ +#define mozilla_intl_WordBreaker_h__ #include "nscore.h" +#include "nsISupports.h" #define NS_WORDBREAKER_NEED_MORE_TEXT -1 -// {E86B3379-BF89-11d2-B3AF-00805F8A6670} -#define NS_IWORDBREAKER_IID \ -{ 0xe86b3379, 0xbf89, 0x11d2, \ - { 0xb3, 0xaf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70 } } +namespace mozilla { +namespace intl { typedef struct { uint32_t mBegin; uint32_t mEnd; -} nsWordRange; +} WordRange; -enum nsWordBreakClass : uint8_t { +enum WordBreakClass : uint8_t { kWbClassSpace = 0, kWbClassAlphaLetter, kWbClassPunct, kWbClassHanLetter, kWbClassKatakanaLetter, kWbClassHiraganaLetter, kWbClassHWKatakanaLetter, kWbClassThaiLetter }; -class nsIWordBreaker : public nsISupports +class WordBreaker { public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWORDBREAKER_IID) + NS_INLINE_DECL_REFCOUNTING(WordBreaker) + + static already_AddRefed<WordBreaker> Create(); - virtual bool BreakInBetween(const char16_t* aText1 , uint32_t aTextLen1, - const char16_t* aText2 , - uint32_t aTextLen2) = 0; - virtual nsWordRange FindWord(const char16_t* aText1 , uint32_t aTextLen1, - uint32_t aOffset) = 0; - virtual int32_t NextWord(const char16_t* aText, uint32_t aLen, - uint32_t aPos) = 0; + bool BreakInBetween(const char16_t* aText1, uint32_t aTextLen1, + const char16_t* aText2, uint32_t aTextLen2); + WordRange FindWord(const char16_t* aText1, uint32_t aTextLen1, + uint32_t aOffset); + int32_t NextWord(const char16_t* aText, uint32_t aLen, uint32_t aPos); - static nsWordBreakClass GetClass(char16_t aChar); + static WordBreakClass GetClass(char16_t aChar); + +private: + ~WordBreaker() { } }; -NS_DEFINE_STATIC_IID_ACCESSOR(nsIWordBreaker, NS_IWORDBREAKER_IID) +} // namespace intl +} // namespace mozilla -#endif /* nsIWordBreaker_h__ */ +#endif /* mozilla_intl_WordBreaker_h__ */
--- a/intl/lwbrk/gtest/TestLineBreak.cpp +++ b/intl/lwbrk/gtest/TestLineBreak.cpp @@ -4,24 +4,21 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <stdio.h> #include "nsXPCOM.h" #include "nsIComponentManager.h" #include "nsISupports.h" #include "nsServiceManagerUtils.h" -#include "nsILineBreaker.h" -#include "nsIWordBreaker.h" -#include "nsLWBrkCIID.h" #include "nsString.h" #include "gtest/gtest.h" -NS_DEFINE_CID(kLBrkCID, NS_LBRK_CID); -NS_DEFINE_CID(kWBrkCID, NS_WBRK_CID); +#include "mozilla/intl/LineBreaker.h" +#include "mozilla/intl/WordBreaker.h" static char teng1[] = // 1 2 3 4 5 6 7 //01234567890123456789012345678901234567890123456789012345678901234567890123456789 "This is a test to test(reasonable) line break. This 0.01123 = 45 x 48."; static uint32_t lexp1[] = { 4,7,9,14,17,34,39,40,41,42,49,54,62,64,67,69,73 @@ -103,17 +100,17 @@ Check(const char* in, const uint32_t* ou } printf("\n"); } return ok; } bool -TestASCIILB(nsILineBreaker *lb, +TestASCIILB(mozilla::intl::LineBreaker *lb, const char* in, const uint32_t* out, uint32_t outlen) { NS_ConvertASCIItoUTF16 eng1(in); uint32_t i; uint32_t res[256]; int32_t curr; @@ -123,17 +120,17 @@ TestASCIILB(nsILineBreaker *lb, curr = lb->Next(eng1.get(), eng1.Length(), curr); res[i] = curr != NS_LINEBREAKER_NEED_MORE_TEXT ? curr : eng1.Length(); } return Check(in, out, outlen, i, res); } bool -TestASCIIWB(nsIWordBreaker *lb, +TestASCIIWB(mozilla::intl::WordBreaker *lb, const char* in, const uint32_t* out, uint32_t outlen) { NS_ConvertASCIItoUTF16 eng1(in); uint32_t i; uint32_t res[256]; int32_t curr = 0; @@ -144,64 +141,49 @@ TestASCIIWB(nsIWordBreaker *lb, res [i] = curr != NS_WORDBREAKER_NEED_MORE_TEXT ? curr : eng1.Length(); } return Check(in, out, outlen, i, res); } TEST(LineBreak, LineBreaker) { - nsILineBreaker *t = nullptr; - nsresult res = CallGetService(kLBrkCID, &t); - ASSERT_TRUE(NS_SUCCEEDED(res) && t); - NS_IF_RELEASE(t); + RefPtr<mozilla::intl::LineBreaker> t = mozilla::intl::LineBreaker::Create(); - res = CallGetService(kLBrkCID, &t); - ASSERT_TRUE(NS_SUCCEEDED(res) && t); + ASSERT_TRUE(t); ASSERT_TRUE(TestASCIILB(t, teng1, lexp1, sizeof(lexp1) / sizeof(uint32_t))); ASSERT_TRUE(TestASCIILB(t, teng2, lexp2, sizeof(lexp2) / sizeof(uint32_t))); ASSERT_TRUE(TestASCIILB(t, teng3, lexp3, sizeof(lexp3) / sizeof(uint32_t))); - - NS_RELEASE(t); } TEST(LineBreak, WordBreaker) { - nsIWordBreaker *t = nullptr; - nsresult res = CallGetService(kWBrkCID, &t); - ASSERT_TRUE(NS_SUCCEEDED(res) && t); - NS_IF_RELEASE(t); - - res = CallGetService(kWBrkCID, &t); - ASSERT_TRUE(NS_SUCCEEDED(res) && t); + RefPtr<mozilla::intl::WordBreaker> t = mozilla::intl::WordBreaker::Create(); + ASSERT_TRUE(t); ASSERT_TRUE(TestASCIIWB(t, teng1, wexp1, sizeof(wexp1) / sizeof(uint32_t))); ASSERT_TRUE(TestASCIIWB(t, teng2, wexp2, sizeof(wexp2) / sizeof(uint32_t))); ASSERT_TRUE(TestASCIIWB(t, teng3, wexp3, sizeof(wexp3) / sizeof(uint32_t))); - - NS_RELEASE(t); } // 012345678901234 static const char wb0[] = "T"; static const char wb1[] = "h"; static const char wb2[] = "is is a int"; static const char wb3[] = "ernationali"; static const char wb4[] = "zation work."; static const char* wb[] = { wb0, wb1, wb2, wb3, wb4 }; void TestPrintWordWithBreak() { uint32_t numOfFragment = sizeof(wb) / sizeof(char*); - nsIWordBreaker* wbk = nullptr; - - CallGetService(kWBrkCID, &wbk); + RefPtr<mozilla::intl::WordBreaker> wbk = mozilla::intl::WordBreaker::Create(); nsAutoString result; for (uint32_t i = 0; i < numOfFragment; i++) { NS_ConvertASCIItoUTF16 fragText(wb[i]); int32_t cur = 0; cur = wbk->NextWord(fragText.get(), fragText.Length(), cur); @@ -226,32 +208,28 @@ TestPrintWordWithBreak() if (canBreak) { result.Append('^'); } fragText.Assign(nextFragText); } } ASSERT_STREQ("is^ ^is^ ^a^ ^ is a intzation^ ^work^ation work.", NS_ConvertUTF16toUTF8(result).get()); - - NS_IF_RELEASE(wbk); } void TestFindWordBreakFromPosition(uint32_t fragN, uint32_t offset, const char* expected) { uint32_t numOfFragment = sizeof(wb) / sizeof(char*); - nsIWordBreaker* wbk = nullptr; - - CallGetService(kWBrkCID, &wbk); + RefPtr<mozilla::intl::WordBreaker> wbk = mozilla::intl::WordBreaker::Create(); NS_ConvertASCIItoUTF16 fragText(wb[fragN]); - nsWordRange res = wbk->FindWord(fragText.get(), fragText.Length(), offset); + mozilla::intl::WordRange res = wbk->FindWord(fragText.get(), fragText.Length(), offset); bool canBreak; nsAutoString result(Substring(fragText, res.mBegin, res.mEnd-res.mBegin)); if ((uint32_t)fragText.Length() == res.mEnd) { // if we hit the end of the fragment nsAutoString curFragText = fragText; for(uint32_t p = fragN +1; p < numOfFragment ;p++) @@ -259,17 +237,17 @@ TestFindWordBreakFromPosition(uint32_t f NS_ConvertASCIItoUTF16 nextFragText(wb[p]); canBreak = wbk->BreakInBetween(curFragText.get(), curFragText.Length(), nextFragText.get(), nextFragText.Length()); if (canBreak) { break; } - nsWordRange r = wbk->FindWord(nextFragText.get(), nextFragText.Length(), + mozilla::intl::WordRange r = wbk->FindWord(nextFragText.get(), nextFragText.Length(), 0); result.Append(Substring(nextFragText, r.mBegin, r.mEnd - r.mBegin)); if ((uint32_t)nextFragText.Length() != r.mEnd) { break; } nextFragText.Assign(curFragText); @@ -283,32 +261,30 @@ TestFindWordBreakFromPosition(uint32_t f NS_ConvertASCIItoUTF16 prevFragText(wb[p-1]); canBreak = wbk->BreakInBetween(prevFragText.get(), prevFragText.Length(), curFragText.get(), curFragText.Length()); if (canBreak) { break; } - nsWordRange r = wbk->FindWord(prevFragText.get(), prevFragText.Length(), + mozilla::intl::WordRange r = wbk->FindWord(prevFragText.get(), prevFragText.Length(), prevFragText.Length()); result.Insert(Substring(prevFragText, r.mBegin, r.mEnd - r.mBegin), 0); if (0 != r.mBegin) { break; } prevFragText.Assign(curFragText); } } ASSERT_STREQ(expected, NS_ConvertUTF16toUTF8(result).get()) << "FindWordBreakFromPosition(" << fragN << ", " << offset << ")"; - - NS_IF_RELEASE(wbk); } TEST(LineBreak, WordBreakUsage) { TestPrintWordWithBreak(); TestFindWordBreakFromPosition(0, 0, "This"); TestFindWordBreakFromPosition(1, 0, "his"); TestFindWordBreakFromPosition(2, 0, "is");
--- a/intl/lwbrk/moz.build +++ b/intl/lwbrk/moz.build @@ -1,25 +1,24 @@ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # 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/. TEST_DIRS += ['gtest'] -EXPORTS += [ - 'nsILineBreaker.h', - 'nsIWordBreaker.h', - 'nsLWBrkCIID.h', +EXPORTS.mozilla.intl += [ + 'LineBreaker.h', + 'WordBreaker.h', ] UNIFIED_SOURCES += [ - 'nsJISx4051LineBreaker.cpp', - 'nsSampleWordBreaker.cpp', + 'LineBreaker.cpp', + 'WordBreaker.cpp', ] if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: SOURCES += [ 'nsPangoBreaker.cpp', ] CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS'] elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
deleted file mode 100644 --- a/intl/lwbrk/nsJISx4051LineBreaker.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef nsJISx4051LineBreaker_h__ -#define nsJISx4051LineBreaker_h__ - - -#include "nsILineBreaker.h" - -class nsJISx4051LineBreaker : public nsILineBreaker -{ - NS_DECL_ISUPPORTS - -private: - virtual ~nsJISx4051LineBreaker(); - -public: - nsJISx4051LineBreaker(); - - int32_t Next( const char16_t* aText, uint32_t aLen, uint32_t aPos) override; - - int32_t Prev( const char16_t* aText, uint32_t aLen, uint32_t aPos) override; - - virtual void GetJISx4051Breaks(const char16_t* aText, uint32_t aLength, - uint8_t aBreakMode, - uint8_t* aBreakBefore) override; - virtual void GetJISx4051Breaks(const uint8_t* aText, uint32_t aLength, - uint8_t aBreakMode, - uint8_t* aBreakBefore) override; - -private: - int32_t WordMove(const char16_t* aText, uint32_t aLen, uint32_t aPos, - int8_t aDirection); -}; - -#endif /* nsJISx4051LineBreaker_h__ */
deleted file mode 100644 --- a/intl/lwbrk/nsSampleWordBreaker.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef nsSampleWordBreaker_h__ -#define nsSampleWordBreaker_h__ - - -#include "nsIWordBreaker.h" - -class nsSampleWordBreaker : public nsIWordBreaker -{ - NS_DECL_ISUPPORTS -public: - - nsSampleWordBreaker() ; - - bool BreakInBetween(const char16_t* aText1 , uint32_t aTextLen1, - const char16_t* aText2 , uint32_t aTextLen2) override; - nsWordRange FindWord(const char16_t* aText1 , uint32_t aTextLen1, - uint32_t aOffset) override; - - int32_t NextWord(const char16_t* aText, uint32_t aLen, uint32_t aPos) override; - -protected: - virtual ~nsSampleWordBreaker(); -}; - -#endif /* nsSampleWordBreaker_h__ */
--- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -1199,17 +1199,17 @@ GlobalObject::initModuleProto(JSContext* #undef DEFINE_GETTER_FUNCTIONS #undef DEFINE_STRING_ACCESSOR_METHOD #undef DEFINE_ARRAY_SLOT_ACCESSOR /////////////////////////////////////////////////////////////////////////// // ModuleBuilder ModuleBuilder::ModuleBuilder(JSContext* cx, HandleModuleObject module, - const frontend::TokenStream& tokenStream) + const frontend::TokenStreamAnyChars& tokenStream) : cx_(cx), module_(cx, module), tokenStream_(tokenStream), requestedModuleSpecifiers_(cx, AtomSet(cx)), requestedModules_(cx, RequestedModuleVector(cx)), importedBoundNames_(cx, AtomVector(cx)), importEntries_(cx, ImportEntryVector(cx)), exportEntries_(cx, ExportEntryVector(cx)), @@ -1320,17 +1320,17 @@ ModuleBuilder::processImport(frontend::P RootedAtom importName(cx_, spec->pn_left->pn_atom); RootedAtom localName(cx_, spec->pn_right->pn_atom); if (!importedBoundNames_.append(localName)) return false; uint32_t line; uint32_t column; - tokenStream_.lineNumAndColumnIndex(spec->pn_left->pn_pos.begin, &line, &column); + tokenStream_.lineAndColumnAt(spec->pn_left->pn_pos.begin, &line, &column); RootedImportEntryObject importEntry(cx_); importEntry = ImportEntryObject::create(cx_, module, importName, localName, line, column); if (!importEntry || !importEntries_.append(importEntry)) return false; } return true; @@ -1458,47 +1458,47 @@ ModuleBuilder::hasExportedName(JSAtom* n } bool ModuleBuilder::appendExportEntry(HandleAtom exportName, HandleAtom localName, ParseNode* node) { uint32_t line = 0; uint32_t column = 0; if (node) - tokenStream_.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column); + tokenStream_.lineAndColumnAt(node->pn_pos.begin, &line, &column); Rooted<ExportEntryObject*> exportEntry(cx_); exportEntry = ExportEntryObject::create(cx_, exportName, nullptr, nullptr, localName, line, column); return exportEntry && exportEntries_.append(exportEntry); } bool ModuleBuilder::appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest, HandleAtom importName, ParseNode* node) { uint32_t line; uint32_t column; - tokenStream_.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column); + tokenStream_.lineAndColumnAt(node->pn_pos.begin, &line, &column); Rooted<ExportEntryObject*> exportEntry(cx_); exportEntry = ExportEntryObject::create(cx_, exportName, moduleRequest, importName, nullptr, line, column); return exportEntry && exportEntries_.append(exportEntry); } bool ModuleBuilder::maybeAppendRequestedModule(HandleAtom specifier, ParseNode* node) { if (requestedModuleSpecifiers_.has(specifier)) return true; uint32_t line; uint32_t column; - tokenStream_.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column); + tokenStream_.lineAndColumnAt(node->pn_pos.begin, &line, &column); JSContext* cx = cx_; RootedRequestedModuleObject req(cx, RequestedModuleObject::create(cx, specifier, line, column)); if (!req) return false; return FreezeObject(cx, req) && requestedModules_.append(req) &&
--- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -21,17 +21,17 @@ namespace js { class ModuleEnvironmentObject; class ModuleObject; namespace frontend { class ParseNode; -class TokenStream; +class TokenStreamAnyChars; } /* namespace frontend */ typedef Rooted<ModuleObject*> RootedModuleObject; typedef Handle<ModuleObject*> HandleModuleObject; typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject; typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject; class ImportEntryObject : public NativeObject @@ -345,17 +345,17 @@ class ModuleObject : public NativeObject }; // Process a module's parse tree to collate the import and export data used when // creating a ModuleObject. class MOZ_STACK_CLASS ModuleBuilder { public: explicit ModuleBuilder(JSContext* cx, HandleModuleObject module, - const frontend::TokenStream& tokenStream); + const frontend::TokenStreamAnyChars& tokenStream); bool init(); bool processImport(frontend::ParseNode* pn); bool processExport(frontend::ParseNode* pn); bool processExportFrom(frontend::ParseNode* pn); bool hasExportedName(JSAtom* name) const; @@ -375,17 +375,17 @@ class MOZ_STACK_CLASS ModuleBuilder using RootedAtomVector = JS::Rooted<AtomVector>; using RootedImportEntryVector = JS::Rooted<ImportEntryVector>; using RootedExportEntryVector = JS::Rooted<ExportEntryVector>; using RootedRequestedModuleVector = JS::Rooted<RequestedModuleVector>; using RootedAtomSet = JS::Rooted<AtomSet>; JSContext* cx_; RootedModuleObject module_; - const frontend::TokenStream& tokenStream_; + const frontend::TokenStreamAnyChars& tokenStream_; RootedAtomSet requestedModuleSpecifiers_; RootedRequestedModuleVector requestedModules_; RootedAtomVector importedBoundNames_; RootedImportEntryVector importEntries_; RootedExportEntryVector exportEntries_; RootedExportEntryVector localExportEntries_; RootedExportEntryVector indirectExportEntries_; RootedExportEntryVector starExportEntries_;
--- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -1732,17 +1732,17 @@ class ASTSerializer {} bool init(HandleObject userobj) { return builder.init(userobj); } void setParser(Parser<FullParseHandler, char16_t>* p) { parser = p; - builder.setTokenStream(&p->tokenStream); + builder.setTokenStream(&p->anyChars); } bool program(ParseNode* pn, MutableHandleValue dst); }; } /* anonymous namespace */ AssignmentOperator @@ -1909,17 +1909,17 @@ ASTSerializer::blockStatement(ParseNode* NodeVector stmts(cx); return statements(pn, stmts) && builder.blockStatement(stmts, &pn->pn_pos, dst); } bool ASTSerializer::program(ParseNode* pn, MutableHandleValue dst) { - MOZ_ASSERT(parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin) == lineno); + MOZ_ASSERT(parser->anyChars.srcCoords.lineNum(pn->pn_pos.begin) == lineno); NodeVector stmts(cx); return statements(pn, stmts) && builder.program(stmts, &pn->pn_pos, dst); } bool ASTSerializer::sourceElement(ParseNode* pn, MutableHandleValue dst) @@ -3486,17 +3486,17 @@ reflect_parse(JSContext* cx, uint32_t ar } else { if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global())) return false; Rooted<ModuleObject*> module(cx, ModuleObject::create(cx)); if (!module) return false; - ModuleBuilder builder(cx, module, parser.tokenStream); + ModuleBuilder builder(cx, module, parser.anyChars); if (!builder.init()) return false; ModuleSharedContext modulesc(cx, module, &cx->global()->emptyGlobalScope(), builder); pn = parser.moduleBody(&modulesc); if (!pn) return false;
--- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -327,17 +327,17 @@ function extraRootedPointers() return [ 'ModuleValidator', 'JSErrorResult', 'WrappableJSErrorResult', // These are not actually rooted, but are only used in the context of // AutoKeepAtoms. 'js::frontend::TokenStream', - 'js::frontend::TokenStream::Position', + 'js::frontend::TokenStreamAnyChars', 'mozilla::ErrorResult', 'mozilla::IgnoredErrorResult', 'mozilla::dom::binding_detail::FastErrorResult', ]; } function isRootedGCPointerTypeName(name)
--- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -100,18 +100,17 @@ AutoFrontendTraceLog::AutoFrontendTraceL { // If the tokenizer hasn't yet gotten any tokens, use the line and column // numbers from CompileOptions. uint32_t line, column; if (errorReporter.hasTokenizationStarted()) { line = errorReporter.options().lineno; column = errorReporter.options().column; } else { - uint32_t offset = errorReporter.offset(); - errorReporter.lineNumAndColumnIndex(offset, &line, &column); + errorReporter.currentLineAndColumn(&line, &column); } frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(), line, column); frontendLog_.emplace(logger_, *frontendEvent_); typeLog_.emplace(logger_, id); } #else { } #endif @@ -132,17 +131,17 @@ AutoFrontendTraceLog::AutoFrontendTraceL #endif AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id, const ErrorReporter& errorReporter, ParseNode* pn) #ifdef JS_TRACE_LOGGING : logger_(TraceLoggerForCurrentThread(cx)) { uint32_t line, column; - errorReporter.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column); + errorReporter.lineAndColumnAt(pn->pn_pos.begin, &line, &column); frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(), line, column); frontendLog_.emplace(logger_, *frontendEvent_); typeLog_.emplace(logger_, id); } #else { } #endif @@ -274,17 +273,17 @@ BytecodeCompiler::emplaceEmitter(Maybe<B bool BytecodeCompiler::handleParseFailure(const Directives& newDirectives) { if (parser->hadAbortedSyntaxParse()) { // Hit some unrecoverable ambiguity during an inner syntax parse. // Syntax parsing has now been disabled in the parser, so retry // the parse. parser->clearAbortedSyntaxParse(); - } else if (parser->tokenStream.hadError() || directives == newDirectives) { + } else if (parser->anyChars.hadError() || directives == newDirectives) { return false; } parser->tokenStream.seek(startPosition); // Assignment must be monotonic to prevent reparsing iloops MOZ_ASSERT_IF(directives.strict(), newDirectives.strict()); MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); @@ -398,17 +397,17 @@ BytecodeCompiler::compileModule() if (!module) return nullptr; if (!createScript()) return nullptr; module->init(script); - ModuleBuilder builder(cx, module, parser->tokenStream); + ModuleBuilder builder(cx, module, parser->anyChars); if (!builder.init()) return nullptr; ModuleSharedContext modulesc(cx, module, enclosingScope, builder); ParseNode* pn = parser->moduleBody(&modulesc); if (!pn) return nullptr;
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3519,18 +3519,18 @@ BytecodeEmitter::maybeSetSourceMap() /* * Source map URLs passed as a compile option (usually via a HTTP source map * header) override any source map urls passed as comment pragmas. */ if (parser.options().sourceMapURL()) { // Warn about the replacement, but use the new one. if (parser.ss()->hasSourceMapURL()) { - if (!parser.reportNoOffset(ParseWarning, false, JSMSG_ALREADY_HAS_PRAGMA, - parser.ss()->filename(), "//# sourceMappingURL")) + if (!parser.warningNoOffset(JSMSG_ALREADY_HAS_PRAGMA, + parser.ss()->filename(), "//# sourceMappingURL")) { return false; } } if (!parser.ss()->setSourceMapURL(cx, parser.options().sourceMapURL())) return false; } @@ -3576,20 +3576,17 @@ BytecodeEmitter::reportError(ParseNode* bool BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...) { TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos; va_list args; va_start(args, errorNumber); - // FIXME: parser.tokenStream() should be a TokenStreamAnyChars for bug 1351107, - // but caused problems, cf. bug 1363116. - bool result = parser.tokenStream() - .reportExtraWarningErrorNumberVA(nullptr, pos.begin, errorNumber, args); + bool result = parser.reportExtraWarningErrorNumberVA(nullptr, pos.begin, errorNumber, &args); va_end(args); return result; } bool BytecodeEmitter::emitNewInit(JSProtoKey key) {
--- a/js/src/frontend/EitherParser.h +++ b/js/src/frontend/EitherParser.h @@ -11,223 +11,263 @@ #ifndef frontend_EitherParser_h #define frontend_EitherParser_h #include "mozilla/Attributes.h" #include "mozilla/IndexSequence.h" #include "mozilla/Move.h" #include "mozilla/Tuple.h" +#include "mozilla/TypeTraits.h" #include "mozilla/Variant.h" #include "frontend/Parser.h" #include "frontend/TokenStream.h" namespace js { + +namespace detail { + +template<template <class Parser> class GetThis, + template <class This> class MemberFunction, + typename... Args> +struct InvokeMemberFunction +{ + mozilla::Tuple<typename mozilla::Decay<Args>::Type...> args; + + template<class This, size_t... Indices> + auto + matchInternal(This* obj, mozilla::IndexSequence<Indices...>) + -> decltype(((*obj).*(MemberFunction<This>::get()))(mozilla::Get<Indices>(args)...)) + { + return ((*obj).*(MemberFunction<This>::get()))(mozilla::Get<Indices>(args)...); + } + + public: + template<typename... ActualArgs> + explicit InvokeMemberFunction(ActualArgs&&... actualArgs) + : args { mozilla::Forward<ActualArgs>(actualArgs)... } + {} + + template<class Parser> + auto + match(Parser* parser) + -> decltype(this->matchInternal(GetThis<Parser>::get(parser), + typename mozilla::IndexSequenceFor<Args...>::Type())) + { + return this->matchInternal(GetThis<Parser>::get(parser), + typename mozilla::IndexSequenceFor<Args...>::Type()); + } +}; + +// |this|-computing templates. + +template<class Parser> +struct GetParser +{ + static Parser* get(Parser* parser) { return parser; } +}; + +template<class Parser> +struct GetParseHandler +{ + static auto get(Parser* parser) -> decltype(&parser->handler) { + return &parser->handler; + } +}; + +template<class Parser> +struct GetTokenStream +{ + static auto get(Parser* parser) -> decltype(&parser->tokenStream) { + return &parser->tokenStream; + } +}; + +// Member function-computing templates. + +template<class Parser> +struct ParserOptions +{ + static constexpr auto get() -> decltype(&Parser::options) { + return &Parser::options; + } +}; + +template<class TokenStream> +struct TokenStreamComputeErrorMetadata +{ + static constexpr auto get() -> decltype(&TokenStream::computeErrorMetadata) { + return &TokenStream::computeErrorMetadata; + } +}; + +template<class Parser> +struct ParserNewObjectBox +{ + static constexpr auto get() -> decltype(&Parser::newObjectBox) { + return &Parser::newObjectBox; + } +}; + +template<class Handler> +struct HandlerSingleBindingFromDeclaration +{ + static constexpr auto get() -> decltype(&Handler::singleBindingFromDeclaration) { + return &Handler::singleBindingFromDeclaration; + } +}; + +template<class Handler> +struct HandlerIsDeclarationList +{ + static constexpr auto get() -> decltype(&Handler::isDeclarationList) { + return &Handler::isDeclarationList; + } +}; + +template<class Handler> +struct HandlerIsSuperBase +{ + static constexpr auto get() -> decltype(&Handler::isSuperBase) { + return &Handler::isSuperBase; + } +}; + +template<class TokenStream> +struct TokenStreamReportError +{ + static constexpr auto get() -> decltype(&TokenStream::reportError) { + return &TokenStream::reportError; + } +}; + +template<class TokenStream> +struct TokenStreamReportExtraWarning +{ + static constexpr auto get() -> decltype(&TokenStream::reportExtraWarningErrorNumberVA) { + return &TokenStream::reportExtraWarningErrorNumberVA; + } +}; + +// Generic matchers. + +struct TokenStreamMatcher +{ + template<class Parser> + frontend::TokenStreamAnyChars& match(Parser* parser) { + return parser->anyChars; + } +}; + +struct ScriptSourceMatcher +{ + template<class Parser> + ScriptSource* match(Parser* parser) { + return parser->ss; + } +}; + +struct ParserBaseMatcher +{ + template<class Parser> + frontend::ParserBase& match(Parser* parser) { + return *static_cast<frontend::ParserBase*>(parser); + } +}; + +} // namespace detail + namespace frontend { template<class ParseHandler> class EitherParser { - const mozilla::Variant<Parser<ParseHandler, char16_t>* const> parser; + mozilla::Variant<Parser<ParseHandler, char16_t>* const> parser; using Node = typename ParseHandler::Node; + template<template <class Parser> class GetThis, + template <class This> class GetMemberFunction, + typename... StoredArgs> + using InvokeMemberFunction = + detail::InvokeMemberFunction<GetThis, GetMemberFunction, StoredArgs...>; + public: template<class Parser> explicit EitherParser(Parser* parser) : parser(parser) {} - private: - struct TokenStreamMatcher - { - template<class Parser> - TokenStream& match(Parser* parser) { - return parser->tokenStream; - } - }; - - public: - TokenStream& tokenStream() { - return parser.match(TokenStreamMatcher()); + TokenStreamAnyChars& tokenStream() { + return parser.match(detail::TokenStreamMatcher()); } - const TokenStream& tokenStream() const { - return parser.match(TokenStreamMatcher()); - } - - private: - struct ScriptSourceMatcher - { - template<class Parser> - ScriptSource* match(Parser* parser) { - return parser->ss; - } - }; - - public: - ScriptSource* ss() { - return parser.match(ScriptSourceMatcher()); + const TokenStreamAnyChars& tokenStream() const { + return parser.match(detail::TokenStreamMatcher()); } - private: - struct OptionsMatcher - { - template<class Parser> - const JS::ReadOnlyCompileOptions& match(Parser* parser) { - return parser->options(); - } - }; - - public: - const JS::ReadOnlyCompileOptions& options() { - return parser.match(OptionsMatcher()); + ScriptSource* ss() { + return parser.match(detail::ScriptSourceMatcher()); } - private: - struct ComputeErrorMetadataMatcher - { - ErrorMetadata* metadata; - uint32_t offset; - - ComputeErrorMetadataMatcher(ErrorMetadata* metadata, uint32_t offset) - : metadata(metadata), offset(offset) - {} - - template<class Parser> - MOZ_MUST_USE bool match(Parser* parser) { - return parser->tokenStream.computeErrorMetadata(metadata, offset); - } - }; - - public: - MOZ_MUST_USE bool computeErrorMetadata(ErrorMetadata* metadata, uint32_t offset) { - return parser.match(ComputeErrorMetadataMatcher(metadata, offset)); + const JS::ReadOnlyCompileOptions& options() { + InvokeMemberFunction<detail::GetParser, detail::ParserOptions> optionsMatcher; + return parser.match(mozilla::Move(optionsMatcher)); } - private: - struct NewObjectBoxMatcher - { - JSObject* obj; - - explicit NewObjectBoxMatcher(JSObject* obj) : obj(obj) {} - - template<class Parser> - ObjectBox* match(Parser* parser) { - return parser->newObjectBox(obj); - } - }; - - public: - ObjectBox* newObjectBox(JSObject* obj) { - return parser.match(NewObjectBoxMatcher(obj)); + MOZ_MUST_USE bool computeErrorMetadata(ErrorMetadata* metadata, uint32_t offset) { + InvokeMemberFunction<detail::GetTokenStream, detail::TokenStreamComputeErrorMetadata, + ErrorMetadata*, uint32_t> + matcher { metadata, offset }; + return parser.match(mozilla::Move(matcher)); } - private: - struct SingleBindingFromDeclarationMatcher - { - Node decl; - - explicit SingleBindingFromDeclarationMatcher(Node decl) : decl(decl) {} - - template<class Parser> - Node match(Parser* parser) { - return parser->handler.singleBindingFromDeclaration(decl); - } - }; - - public: - Node singleBindingFromDeclaration(Node decl) { - return parser.match(SingleBindingFromDeclarationMatcher(decl)); + ObjectBox* newObjectBox(JSObject* obj) { + InvokeMemberFunction<detail::GetParser, detail::ParserNewObjectBox, + JSObject*> + matcher { obj }; + return parser.match(mozilla::Move(matcher)); } - private: - struct IsDeclarationListMatcher - { - Node node; - - explicit IsDeclarationListMatcher(Node node) : node(node) {} - - template<class Parser> - bool match(Parser* parser) { - return parser->handler.isDeclarationList(node); - } - }; - - public: - bool isDeclarationList(Node node) { - return parser.match(IsDeclarationListMatcher(node)); + Node singleBindingFromDeclaration(Node decl) { + InvokeMemberFunction<detail::GetParseHandler, detail::HandlerSingleBindingFromDeclaration, + Node> + matcher { decl }; + return parser.match(mozilla::Move(matcher)); } - private: - struct IsSuperBaseMatcher - { - Node node; - - explicit IsSuperBaseMatcher(Node node) : node(node) {} - - template<class Parser> - bool match(Parser* parser) { - return parser->handler.isSuperBase(node); - } - }; - - public: - bool isSuperBase(Node node) { - return parser.match(IsSuperBaseMatcher(node)); + bool isDeclarationList(Node node) { + InvokeMemberFunction<detail::GetParseHandler, detail::HandlerIsDeclarationList, + Node> + matcher { node }; + return parser.match(mozilla::Move(matcher)); } - private: - template<typename Function, class This, typename... Args, size_t... Indices> - static auto - CallGenericFunction(Function func, This* obj, - mozilla::Tuple<Args...>& args, mozilla::IndexSequence<Indices...>) - -> decltype(((*obj).*func)(mozilla::Get<Indices>(args)...)) - { - return ((*obj).*func)(mozilla::Get<Indices>(args)...); + bool isSuperBase(Node node) { + InvokeMemberFunction<detail::GetParseHandler, detail::HandlerIsSuperBase, + Node> + matcher { node }; + return parser.match(mozilla::Move(matcher)); } - template<typename... StoredArgs> - struct ReportErrorMatcher - { - mozilla::Tuple<StoredArgs...> args; - - template<typename... Args> - explicit ReportErrorMatcher(Args&&... actualArgs) - : args { mozilla::Forward<Args>(actualArgs)... } - {} - - template<class Parser> - void match(Parser* parser) { - return CallGenericFunction(&TokenStream::reportError, - &parser->tokenStream, - args, - typename mozilla::IndexSequenceFor<StoredArgs...>::Type()); - } - }; - - public: template<typename... Args> void reportError(Args&&... args) { - ReportErrorMatcher<typename mozilla::Decay<Args>::Type...> + InvokeMemberFunction<detail::GetTokenStream, detail::TokenStreamReportError, Args...> matcher { mozilla::Forward<Args>(args)... }; return parser.match(mozilla::Move(matcher)); } - private: - struct ParserBaseMatcher - { - template<class Parser> - ParserBase& match(Parser* parser) { - return *parser; - } - }; + template<typename... Args> + MOZ_MUST_USE bool warningNoOffset(Args&&... args) { + return parser.match(detail::ParserBaseMatcher()).warningNoOffset(mozilla::Forward<Args>(args)...); + } - public: template<typename... Args> - MOZ_MUST_USE bool reportNoOffset(Args&&... args) { - return parser.match(ParserBaseMatcher()).reportNoOffset(mozilla::Forward<Args>(args)...); + MOZ_MUST_USE bool reportExtraWarningErrorNumberVA(Args&&... args) { + InvokeMemberFunction<detail::GetTokenStream, detail::TokenStreamReportExtraWarning, Args...> + matcher { mozilla::Forward<Args>(args)... }; + return parser.match(mozilla::Move(matcher)); } }; } /* namespace frontend */ } /* namespace js */ #endif /* frontend_EitherParser_h */
--- a/js/src/frontend/ErrorReporter.h +++ b/js/src/frontend/ErrorReporter.h @@ -1,21 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + #ifndef frontend_ErrorReporter_h #define frontend_ErrorReporter_h +#include <stdarg.h> // for va_list +#include <stddef.h> // for size_t +#include <stdint.h> // for uint32_t + +#include "jsapi.h" // for JS::ReadOnlyCompileOptions + namespace js { namespace frontend { class ErrorReporter { public: - virtual const ReadOnlyCompileOptions& options() const = 0; - virtual void lineNumAndColumnIndex(size_t offset, uint32_t* line, uint32_t* column) const = 0; - virtual size_t offset() const = 0; + virtual const JS::ReadOnlyCompileOptions& options() const = 0; + + virtual void lineAndColumnAt(size_t offset, uint32_t* line, uint32_t* column) const = 0; + virtual void currentLineAndColumn(uint32_t* line, uint32_t* column) const = 0; + virtual bool hasTokenizationStarted() const = 0; - virtual void reportErrorNoOffset(unsigned errorNumber, ...) = 0; + virtual void reportErrorNoOffsetVA(unsigned errorNumber, va_list args) = 0; virtual const char* getFilename() const = 0; + + void reportErrorNoOffset(unsigned errorNumber, ...) { + va_list args; + va_start(args, errorNumber); + + reportErrorNoOffsetVA(errorNumber, args); + + va_end(args); + } }; } // namespace frontend } // namespace js #endif // frontend_ErrorReporter_h
--- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -492,20 +492,21 @@ Boolish(ParseNode* pn) } default: return Unknown; } } static bool -Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser); +Fold(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, char16_t>& parser); static bool -FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser) +FoldCondition(JSContext* cx, ParseNode** nodePtr, + GeneralParser<FullParseHandler, char16_t>& parser) { // Conditions fold like any other expression... if (!Fold(cx, nodePtr, parser)) return false; // ...but then they sometimes can be further folded to constants. ParseNode* node = *nodePtr; Truthiness t = Boolish(node); @@ -525,17 +526,17 @@ FoldCondition(JSContext* cx, ParseNode** } node->setArity(PN_NULLARY); } return true; } static bool -FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldTypeOfExpr(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_TYPEOFEXPR)); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode*& expr = node->pn_kid; if (!Fold(cx, &expr, parser)) return false; @@ -560,17 +561,17 @@ FoldTypeOfExpr(JSContext* cx, ParseNode* node->setOp(JSOP_NOP); node->pn_atom = result; } return true; } static bool -FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldDeleteExpr(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_DELETEEXPR)); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode*& expr = node->pn_kid; if (!Fold(cx, &expr, parser)) return false; @@ -582,17 +583,18 @@ FoldDeleteExpr(JSContext* cx, ParseNode* node->setArity(PN_NULLARY); node->setOp(JSOP_TRUE); } return true; } static bool -FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldDeleteElement(JSContext* cx, ParseNode* node, + GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_DELETEELEM)); MOZ_ASSERT(node->isArity(PN_UNARY)); MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM)); ParseNode*& expr = node->pn_kid; if (!Fold(cx, &expr, parser)) return false; @@ -606,17 +608,18 @@ FoldDeleteElement(JSContext* cx, ParseNo MOZ_ASSERT(expr->isKind(PNK_ELEM) || expr->isKind(PNK_DOT)); if (expr->isKind(PNK_DOT)) node->setKind(PNK_DELETEPROP); return true; } static bool -FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldDeleteProperty(JSContext* cx, ParseNode* node, + GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_DELETEPROP)); MOZ_ASSERT(node->isArity(PN_UNARY)); MOZ_ASSERT(node->pn_kid->isKind(PNK_DOT)); ParseNode*& expr = node->pn_kid; #ifdef DEBUG ParseNodeKind oldKind = expr->getKind(); @@ -627,17 +630,17 @@ FoldDeleteProperty(JSContext* cx, ParseN MOZ_ASSERT(expr->isKind(oldKind), "kind should have remained invariant under folding"); return true; } static bool -FoldNot(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldNot(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_NOT)); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode*& expr = node->pn_kid; if (!FoldCondition(cx, &expr, parser)) return false; @@ -661,17 +664,18 @@ FoldNot(JSContext* cx, ParseNode* node, node->setArity(PN_NULLARY); node->setOp(newval ? JSOP_TRUE : JSOP_FALSE); } return true; } static bool -FoldUnaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldUnaryArithmetic(JSContext* cx, ParseNode* node, + GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_BITNOT) || node->isKind(PNK_POS) || node->isKind(PNK_NEG), "need a different method for this node kind"); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode*& expr = node->pn_kid; if (!Fold(cx, &expr, parser)) return false; @@ -694,37 +698,38 @@ FoldUnaryArithmetic(JSContext* cx, Parse node->setArity(PN_NULLARY); node->pn_dval = d; } return true; } static bool -FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldIncrementDecrement(JSContext* cx, ParseNode* node, + GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_PREINCREMENT) || node->isKind(PNK_POSTINCREMENT) || node->isKind(PNK_PREDECREMENT) || node->isKind(PNK_POSTDECREMENT)); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode*& target = node->pn_kid; - MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls)); + MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, PermitAssignmentToFunctionCalls)); if (!Fold(cx, &target, parser)) return false; - MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls)); + MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, PermitAssignmentToFunctionCalls)); return true; } static bool -FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser) +FoldAndOr(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser) { ParseNode* node = *nodePtr; MOZ_ASSERT(node->isKind(PNK_AND) || node->isKind(PNK_OR)); MOZ_ASSERT(node->isArity(PN_LIST)); bool isOrNode = node->isKind(PNK_OR); ParseNode** elem = &node->pn_head; @@ -796,17 +801,18 @@ FoldAndOr(JSContext* cx, ParseNode** nod node->setArity(PN_NULLARY); parser.freeTree(node); } return true; } static bool -FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser) +FoldConditional(JSContext* cx, ParseNode** nodePtr, + GeneralParser<FullParseHandler, char16_t>& parser) { ParseNode** nextNode = nodePtr; do { // |nextNode| on entry points to the C?T:F expression to be folded. // Reset it to exit the loop in the common case where F isn't another // ?: expression. nodePtr = nextNode; @@ -865,17 +871,17 @@ FoldConditional(JSContext* cx, ParseNode parser.freeTree(discarded); } while (nextNode); return true; } static bool -FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser) +FoldIf(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser) { ParseNode** nextNode = nodePtr; do { // |nextNode| on entry points to the initial |if| to be folded. Reset // it to exit the loop when the |else| arm isn't another |if|. nodePtr = nextNode; nextNode = nullptr; @@ -966,17 +972,17 @@ FoldIf(JSContext* cx, ParseNode** nodePt parser.freeTree(node); } } while (nextNode); return true; } static bool -FoldFunction(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldFunction(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_FUNCTION)); MOZ_ASSERT(node->isArity(PN_CODE)); // Don't constant-fold inside "use asm" code, as this could create a parse // tree that doesn't type-check as asm.js. if (node->pn_funbox->useAsmOrInsideUseAsm()) return true; @@ -1028,28 +1034,29 @@ ComputeBinary(ParseNodeKind kind, double MOZ_ASSERT(kind == PNK_LSH || kind == PNK_RSH); int32_t i = ToInt32(left); uint32_t j = ToUint32(right) & 31; return int32_t((kind == PNK_LSH) ? uint32_t(i) << j : i >> j); } static bool -FoldModule(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldModule(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_MODULE)); MOZ_ASSERT(node->isArity(PN_CODE)); ParseNode*& moduleBody = node->pn_body; MOZ_ASSERT(moduleBody); return Fold(cx, &moduleBody, parser); } static bool -FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldBinaryArithmetic(JSContext* cx, ParseNode* node, + GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_SUB) || node->isKind(PNK_STAR) || node->isKind(PNK_LSH) || node->isKind(PNK_RSH) || node->isKind(PNK_URSH) || node->isKind(PNK_DIV) || node->isKind(PNK_MOD)); @@ -1110,17 +1117,18 @@ FoldBinaryArithmetic(JSContext* cx, Pars parser.freeTree(elem); } } return true; } static bool -FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldExponentiation(JSContext* cx, ParseNode* node, + GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_POW)); MOZ_ASSERT(node->isArity(PN_LIST)); MOZ_ASSERT(node->pn_count >= 2); // Fold each operand, ideally into a number. ParseNode** listp = &node->pn_head; for (; *listp; listp = &(*listp)->pn_next) { @@ -1153,17 +1161,17 @@ FoldExponentiation(JSContext* cx, ParseN node->setKind(PNK_NUMBER); node->setArity(PN_NULLARY); node->setOp(JSOP_DOUBLE); node->pn_dval = ecmaPow(d1, d2); return true; } static bool -FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler, char16_t>& parser) +FoldList(JSContext* cx, ParseNode* list, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(list->isArity(PN_LIST)); ParseNode** elem = &list->pn_head; for (; *elem; elem = &(*elem)->pn_next) { if (!Fold(cx, elem, parser)) return false; } @@ -1172,31 +1180,31 @@ FoldList(JSContext* cx, ParseNode* list, list->pn_tail = elem; list->checkListConsistency(); return true; } static bool -FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldReturn(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_RETURN)); MOZ_ASSERT(node->isArity(PN_UNARY)); if (ParseNode*& expr = node->pn_kid) { if (!Fold(cx, &expr, parser)) return false; } return true; } static bool -FoldTry(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldTry(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_TRY)); MOZ_ASSERT(node->isArity(PN_TERNARY)); ParseNode*& statements = node->pn_kid1; if (!Fold(cx, &statements, parser)) return false; @@ -1209,17 +1217,17 @@ FoldTry(JSContext* cx, ParseNode* node, if (!Fold(cx, &finally, parser)) return false; } return true; } static bool -FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldCatch(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_CATCH)); MOZ_ASSERT(node->isArity(PN_BINARY)); if (ParseNode*& declPattern = node->pn_left) { if (!Fold(cx, &declPattern, parser)) return false; } @@ -1228,17 +1236,17 @@ FoldCatch(JSContext* cx, ParseNode* node if (!Fold(cx, &statements, parser)) return false; } return true; } static bool -FoldClass(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldClass(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_CLASS)); MOZ_ASSERT(node->isArity(PN_TERNARY)); if (ParseNode*& classNames = node->pn_kid1) { if (!Fold(cx, &classNames, parser)) return false; } @@ -1248,17 +1256,17 @@ FoldClass(JSContext* cx, ParseNode* node return false; } ParseNode*& body = node->pn_kid3; return Fold(cx, &body, parser); } static bool -FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser) +FoldElement(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser) { ParseNode* node = *nodePtr; MOZ_ASSERT(node->isKind(PNK_ELEM)); MOZ_ASSERT(node->isArity(PN_BINARY)); ParseNode*& expr = node->pn_left; if (!Fold(cx, &expr, parser)) @@ -1316,17 +1324,17 @@ FoldElement(JSContext* cx, ParseNode** n node->setArity(PN_UNARY); node->pn_kid = key; parser.freeTree(node); return true; } static bool -FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser) +FoldAdd(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser) { ParseNode* node = *nodePtr; MOZ_ASSERT(node->isKind(PNK_ADD)); MOZ_ASSERT(node->isArity(PN_LIST)); MOZ_ASSERT(node->pn_count >= 2); // Generically fold all operands first. @@ -1466,17 +1474,17 @@ FoldAdd(JSContext* cx, ParseNode** nodeP node->setOp(JSOP_TRUE); parser.freeTree(node); } return true; } static bool -FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldCall(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_SUPERCALL) || node->isKind(PNK_TAGGED_TEMPLATE)); MOZ_ASSERT(node->isArity(PN_LIST)); // Don't fold a parenthesized callable component in an invocation, as this // might cause a different |this| value to be used, changing semantics: // @@ -1500,28 +1508,28 @@ FoldCall(JSContext* cx, ParseNode* node, // If the last node in the list was replaced, pn_tail points into the wrong node. node->pn_tail = listp; node->checkListConsistency(); return true; } static bool -FoldForInOrOf(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldForInOrOf(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_FORIN) || node->isKind(PNK_FOROF)); MOZ_ASSERT(node->isArity(PN_TERNARY)); MOZ_ASSERT(!node->pn_kid2); return Fold(cx, &node->pn_kid1, parser) && Fold(cx, &node->pn_kid3, parser); } static bool -FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldForHead(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_FORHEAD)); MOZ_ASSERT(node->isArity(PN_TERNARY)); if (ParseNode*& init = node->pn_kid1) { if (!Fold(cx, &init, parser)) return false; } @@ -1540,46 +1548,47 @@ FoldForHead(JSContext* cx, ParseNode* no if (!Fold(cx, &update, parser)) return false; } return true; } static bool -FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldDottedProperty(JSContext* cx, ParseNode* node, + GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_DOT)); MOZ_ASSERT(node->isArity(PN_NAME)); // Iterate through a long chain of dotted property accesses to find the // most-nested non-dotted property node, then fold that. ParseNode** nested = &node->pn_expr; while ((*nested)->isKind(PNK_DOT)) { MOZ_ASSERT((*nested)->isArity(PN_NAME)); nested = &(*nested)->pn_expr; } return Fold(cx, nested, parser); } static bool -FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser) +FoldName(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser) { MOZ_ASSERT(node->isKind(PNK_NAME)); MOZ_ASSERT(node->isArity(PN_NAME)); if (!node->pn_expr) return true; return Fold(cx, &node->pn_expr, parser); } bool -Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser) +Fold(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, char16_t>& parser) { if (!CheckRecursionLimit(cx)) return false; ParseNode* pn = *pnp; switch (pn->getKind()) { case PNK_NOP: @@ -1859,22 +1868,23 @@ Fold(JSContext* cx, ParseNode** pnp, Par } MOZ_CRASH("shouldn't reach here"); return false; } template<typename CharT> bool -frontend::FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, CharT>* parser) +frontend::FoldConstants(JSContext* cx, ParseNode** pnp, + GeneralParser<FullParseHandler, CharT>* parser) { // Don't constant-fold inside "use asm" code, as this could create a parse // tree that doesn't type-check as asm.js. if (parser->pc->useAsmOrInsideUseAsm()) return true; AutoTraceLog traceLog(TraceLoggerForCurrentThread(cx), TraceLogger_BytecodeFoldConstants); return Fold(cx, pnp, *parser); } template bool frontend::FoldConstants(JSContext* cx, ParseNode** pnp, - Parser<FullParseHandler, char16_t>* parser); + GeneralParser<FullParseHandler, char16_t>* parser);
--- a/js/src/frontend/FoldConstants.h +++ b/js/src/frontend/FoldConstants.h @@ -8,39 +8,39 @@ #define frontend_FoldConstants_h #include "frontend/SyntaxParseHandler.h" namespace js { namespace frontend { class FullParseHandler; -template <class ParseHandler, typename CharT> class Parser; +template <class ParseHandler, typename CharT> class GeneralParser; // Perform constant folding on the given AST. For example, the program // `print(2 + 2)` would become `print(4)`. // // pnp is the address of a pointer variable that points to the root node of the // AST. On success, *pnp points to the root node of the new tree, which may be // the same node (unchanged or modified in place) or a new node. // // Usage: // pn = parser->statement(); // if (!pn) // return false; // if (!FoldConstants(cx, &pn, parser)) // return false; template<typename CharT> MOZ_MUST_USE bool -FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, CharT>* parser); +FoldConstants(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, CharT>* parser); template<typename CharT> inline MOZ_MUST_USE bool FoldConstants(JSContext* cx, typename SyntaxParseHandler::Node* pnp, - Parser<SyntaxParseHandler, CharT>* parser) + GeneralParser<SyntaxParseHandler, CharT>* parser) { return true; } } /* namespace frontend */ } /* namespace js */ #endif /* frontend_FoldConstants_h */
--- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -11,16 +11,19 @@ #include "mozilla/PodOperations.h" #include <string.h> #include "frontend/ParseNode.h" #include "frontend/SharedContext.h" namespace js { + +class RegExpObject; + namespace frontend { // Parse handler used when generating a full parse tree for all code which the // parser encounters. class FullParseHandler { ParseNodeAllocator allocator; @@ -216,36 +219,47 @@ class FullParseHandler // Expressions ParseNode* newArrayLiteral(uint32_t begin) { return new_<ListNode>(PNK_ARRAY, TokenPos(begin, begin + 1)); } MOZ_MUST_USE bool addElision(ParseNode* literal, const TokenPos& pos) { + MOZ_ASSERT(literal->isKind(PNK_ARRAY)); + MOZ_ASSERT(literal->isArity(PN_LIST)); + ParseNode* elision = new_<NullaryNode>(PNK_ELISION, pos); if (!elision) return false; + literal->append(elision); literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST; return true; } MOZ_MUST_USE bool addSpreadElement(ParseNode* literal, uint32_t begin, ParseNode* inner) { + MOZ_ASSERT(literal->isKind(PNK_ARRAY)); + MOZ_ASSERT(literal->isArity(PN_LIST)); + ParseNode* spread = newSpread(begin, inner); if (!spread) return false; + literal->append(spread); literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST; return true; } void addArrayElement(ParseNode* literal, ParseNode* element) { + MOZ_ASSERT(literal->isArity(PN_LIST)); + if (!element->isConstant()) literal->pn_xflags |= PNX_NONCONST; + literal->append(element); } ParseNode* newCall(const TokenPos& pos) { return new_<ListNode>(PNK_CALL, JSOP_CALL, pos); } ParseNode* newSuperCall(ParseNode* callee) { @@ -277,16 +291,19 @@ class FullParseHandler ParseNode* newPosHolder(const TokenPos& pos) { return new_<NullaryNode>(PNK_POSHOLDER, pos); } ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) { return new_<UnaryNode>(PNK_SUPERBASE, pos, thisName); } MOZ_MUST_USE bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) { + MOZ_ASSERT(literal->isKind(PNK_OBJECT)); + MOZ_ASSERT(literal->isArity(PN_LIST)); + // Object literals with mutated [[Prototype]] are non-constant so that // singleton objects will have Object.prototype as their [[Prototype]]. setListFlag(literal, PNX_NONCONST); ParseNode* mutation = newUnary(PNK_MUTATEPROTO, begin, expr); if (!mutation) return false; literal->append(mutation); @@ -333,27 +350,29 @@ class FullParseHandler return false; literal->append(spread); return true; } MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn, AccessorType atype) { + MOZ_ASSERT(literal->isKind(PNK_OBJECT)); MOZ_ASSERT(literal->isArity(PN_LIST)); MOZ_ASSERT(key->isKind(PNK_NUMBER) || key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING) || key->isKind(PNK_COMPUTED_NAME)); - literal->pn_xflags |= PNX_NONCONST; ParseNode* propdef = newBinary(PNK_COLON, key, fn, AccessorTypeToJSOp(atype)); if (!propdef) return false; + literal->append(propdef); + literal->pn_xflags |= PNX_NONCONST; return true; } MOZ_MUST_USE bool addClassMethodDefinition(ParseNode* methodList, ParseNode* key, ParseNode* fn, AccessorType atype, bool isStatic) { MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST)); MOZ_ASSERT(key->isKind(PNK_NUMBER) || @@ -426,24 +445,26 @@ class FullParseHandler list->append(casepn); if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS) list->pn_xflags |= PNX_FUNCDEFS; } MOZ_MUST_USE bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) { MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST)); + MOZ_ASSERT(stmtList->isArity(PN_LIST)); TokenPos yieldPos(stmtList->pn_pos.begin, stmtList->pn_pos.begin + 1); ParseNode* makeGen = new_<NullaryNode>(PNK_GENERATOR, yieldPos); if (!makeGen) return false; MOZ_ASSERT(genName->getOp() == JSOP_GETNAME); genName->setOp(JSOP_SETNAME); + ParseNode* genInit = newBinary(PNK_ASSIGN, genName, makeGen); if (!genInit) return false; ParseNode* initialYield = newInitialYieldExpression(yieldPos.begin, genInit); if (!initialYield) return false; @@ -645,19 +666,19 @@ class FullParseHandler } bool isExpressionClosure(ParseNode* node) const { return node->isKind(PNK_FUNCTION) && node->pn_funbox->isExprBody() && !node->pn_funbox->isArrow(); } - void setFunctionFormalParametersAndBody(ParseNode* pn, ParseNode* kid) { + void setFunctionFormalParametersAndBody(ParseNode* funcNode, ParseNode* kid) { MOZ_ASSERT_IF(kid, kid->isKind(PNK_PARAMSBODY)); - pn->pn_body = kid; + funcNode->pn_body = kid; } void setFunctionBox(ParseNode* pn, FunctionBox* funbox) { MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); pn->pn_funbox = funbox; funbox->functionNode = pn; } void addFunctionFormalParameter(ParseNode* pn, ParseNode* argpn) { pn->pn_body->append(argpn); @@ -857,16 +878,19 @@ class FullParseHandler return lazyOuterFunction_->closedOverBindings()[lazyClosedOverBindingIndex++]; } }; inline bool FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn, ParseNode* defaultValue) { + MOZ_ASSERT(funcpn->isKind(PNK_FUNCTION)); + MOZ_ASSERT(funcpn->isArity(PN_CODE)); + ParseNode* arg = funcpn->pn_body->last(); ParseNode* pn = newBinary(PNK_ASSIGN, arg, defaultValue); if (!pn) return false; checkAndSetIsDirectRHSAnonFunction(defaultValue); funcpn->pn_body->pn_pos.end = pn->pn_pos.end;
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -16,16 +16,17 @@ * * This parser attempts no error recovery. */ #include "frontend/Parser.h" #include "mozilla/Range.h" #include "mozilla/Sprintf.h" +#include "mozilla/TypeTraits.h" #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsfun.h" #include "jsopcode.h" #include "jsscript.h" #include "jstypes.h" @@ -583,267 +584,235 @@ FunctionBox::initWithEnclosingScope(Scop } else { computeAllowSyntax(enclosingScope); computeThisBinding(enclosingScope); } computeInWith(enclosingScope); } +template <class ParseHandler, typename CharT> +inline typename GeneralParser<ParseHandler, CharT>::FinalParser* +GeneralParser<ParseHandler, CharT>::asFinalParser() +{ + static_assert(mozilla::IsBaseOf<GeneralParser<ParseHandler, CharT>, FinalParser>::value, + "inheritance relationship required by the static_cast<> below"); + + return static_cast<FinalParser*>(this); +} + +template <class ParseHandler, typename CharT> +inline const typename GeneralParser<ParseHandler, CharT>::FinalParser* +GeneralParser<ParseHandler, CharT>::asFinalParser() const +{ + static_assert(mozilla::IsBaseOf<GeneralParser<ParseHandler, CharT>, FinalParser>::value, + "inheritance relationship required by the static_cast<> below"); + + return static_cast<const FinalParser*>(this); +} + +template<class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::abortIfSyntaxParser() +{ + return asFinalParser()->abortIfSyntaxParser(); +} + +template <class ParseHandler, typename CharT> void -ParserBase::error(unsigned errorNumber, ...) +GeneralParser<ParseHandler, CharT>::error(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); ErrorMetadata metadata; if (tokenStream.computeErrorMetadata(&metadata, pos().begin)) ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args); va_end(args); } +template <class ParseHandler, typename CharT> void -ParserBase::errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...) +GeneralParser<ParseHandler, CharT>::errorWithNotes(UniquePtr<JSErrorNotes> notes, + unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); ErrorMetadata metadata; if (tokenStream.computeErrorMetadata(&metadata, pos().begin)) { ReportCompileError(context, Move(metadata), Move(notes), JSREPORT_ERROR, errorNumber, args); } va_end(args); } +template <class ParseHandler, typename CharT> void -ParserBase::errorAt(uint32_t offset, unsigned errorNumber, ...) +GeneralParser<ParseHandler, CharT>::errorAt(uint32_t offset, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); ErrorMetadata metadata; if (tokenStream.computeErrorMetadata(&metadata, offset)) ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args); va_end(args); } +template <class ParseHandler, typename CharT> void -ParserBase::errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset, - unsigned errorNumber, ...) +GeneralParser<ParseHandler, CharT>::errorWithNotesAt(UniquePtr<JSErrorNotes> notes, + uint32_t offset, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); ErrorMetadata metadata; if (tokenStream.computeErrorMetadata(&metadata, offset)) { ReportCompileError(context, Move(metadata), Move(notes), JSREPORT_ERROR, errorNumber, args); } va_end(args); } +template <class ParseHandler, typename CharT> bool -ParserBase::warning(unsigned errorNumber, ...) +GeneralParser<ParseHandler, CharT>::warning(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); ErrorMetadata metadata; bool result = tokenStream.computeErrorMetadata(&metadata, pos().begin) && - tokenStream.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args); + anyChars.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args); va_end(args); return result; } +template <class ParseHandler, typename CharT> bool -ParserBase::warningAt(uint32_t offset, unsigned errorNumber, ...) +GeneralParser<ParseHandler, CharT>::warningAt(uint32_t offset, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); ErrorMetadata metadata; bool result = tokenStream.computeErrorMetadata(&metadata, offset); if (result) { result = - tokenStream.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, - args); + anyChars.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args); } va_end(args); return result; } +template <class ParseHandler, typename CharT> bool -ParserBase::extraWarning(unsigned errorNumber, ...) +GeneralParser<ParseHandler, CharT>::extraWarning(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); bool result = - tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin, errorNumber, args); + tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin, errorNumber, &args); va_end(args); return result; } +template <class ParseHandler, typename CharT> bool -ParserBase::extraWarningAt(uint32_t offset, unsigned errorNumber, ...) +GeneralParser<ParseHandler, CharT>::extraWarningAt(uint32_t offset, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); bool result = - tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, args); + tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, &args); va_end(args); return result; } +template <class ParseHandler, typename CharT> bool -ParserBase::strictModeError(unsigned errorNumber, ...) +GeneralParser<ParseHandler, CharT>::strictModeError(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); bool res = tokenStream.reportStrictModeErrorNumberVA(nullptr, pos().begin, pc->sc()->strict(), - errorNumber, args); + errorNumber, &args); va_end(args); return res; } +template <class ParseHandler, typename CharT> bool -ParserBase::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...) +GeneralParser<ParseHandler, CharT>::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); bool res = tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, pc->sc()->strict(), - errorNumber, args); + errorNumber, &args); va_end(args); return res; } bool -ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...) +ParserBase::warningNoOffset(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = false; - switch (kind) { - case ParseError: - case ParseWarning: { - ErrorMetadata metadata; - tokenStream.computeErrorMetadataNoOffset(&metadata); - - if (kind == ParseError) { - ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, - args); - MOZ_ASSERT(!result); - } else { - result = - tokenStream.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, - args); - } - - break; - } - case ParseExtraWarning: - result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, TokenStream::NoOffset, - errorNumber, args); - break; - case ParseStrictError: - result = tokenStream.reportStrictModeErrorNumberVA(nullptr, TokenStream::NoOffset, strict, - errorNumber, args); - break; - } + ErrorMetadata metadata; + anyChars.computeErrorMetadataNoOffset(&metadata); + + bool result = + anyChars.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args); va_end(args); return result; } -#define ABORTED_SYNTAX_PARSE_SENTINEL (SyntaxParser*)(0x1) - -template <> -bool -Parser<FullParseHandler, char16_t>::hadAbortedSyntaxParse() -{ - return false; -} - -template <> -bool -Parser<SyntaxParseHandler, char16_t>::hadAbortedSyntaxParse() -{ - return syntaxParser_ == ABORTED_SYNTAX_PARSE_SENTINEL; -} - -template <> void -Parser<FullParseHandler, char16_t>::clearAbortedSyntaxParse() -{ -} - -template <> -void -Parser<SyntaxParseHandler, char16_t>::clearAbortedSyntaxParse() -{ - syntaxParser_ = nullptr; -} - -template <> -void -Parser<FullParseHandler, char16_t>::disableSyntaxParser() -{ - syntaxParser_ = nullptr; -} - -template <> -void -Parser<SyntaxParseHandler, char16_t>::disableSyntaxParser() -{ -} - -template <> -inline bool -Parser<FullParseHandler, char16_t>::abortIfSyntaxParser() -{ - disableSyntaxParser(); - return true; -} - -template <> -inline bool -Parser<SyntaxParseHandler, char16_t>::abortIfSyntaxParser() -{ - syntaxParser_ = ABORTED_SYNTAX_PARSE_SENTINEL; - return false; -} - -#undef ABORTED_SYNTAX_PARSE_SENTINEL +ParserBase::errorNoOffset(unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); + + ErrorMetadata metadata; + anyChars.computeErrorMetadataNoOffset(&metadata); + + ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args); + + va_end(args); +} ParserBase::ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options, const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames) : context(cx), alloc(alloc), - tokenStream(cx, options, chars, length, thisForCtor()), + anyChars(cx, options, thisForCtor()), traceListHead(nullptr), pc(nullptr), usedNames(usedNames), ss(nullptr), keepAtoms(cx), foldConstants(foldConstants), #ifdef DEBUG checkOptionsCalled(false), @@ -865,68 +834,70 @@ ParserBase::~ParserBase() * next GC) to avoid unnecessary OOMs. */ alloc.freeAllIfHugeAndUnused(); context->frontendCollectionPool().removeActiveCompilation(); } template <class ParseHandler, typename CharT> -Parser<ParseHandler, CharT>::Parser(JSContext* cx, LifoAlloc& alloc, - const ReadOnlyCompileOptions& options, - const CharT* chars, size_t length, - bool foldConstants, - UsedNameTracker& usedNames, - SyntaxParser* syntaxParser, - LazyScript* lazyOuterFunction) +GeneralParser<ParseHandler, CharT>::GeneralParser(JSContext* cx, LifoAlloc& alloc, + const ReadOnlyCompileOptions& options, + const CharT* chars, size_t length, + bool foldConstants, + UsedNameTracker& usedNames, + SyntaxParser* syntaxParser, + LazyScript* lazyOuterFunction) : ParserBase(cx, alloc, options, chars, length, foldConstants, usedNames), AutoGCRooter(cx, PARSER), syntaxParser_(syntaxParser), + tokenStream(cx, options, chars, length), handler(cx, alloc, lazyOuterFunction) -{ - // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings - // which are not generated if functions are parsed lazily. Note that the - // standard "use strict" does not inhibit lazy parsing. - if (options.extraWarningsOption) - disableSyntaxParser(); -} +{} template<class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::checkOptions() +GeneralParser<ParseHandler, CharT>::checkOptions() { #ifdef DEBUG checkOptionsCalled = true; #endif - return tokenStream.checkOptions(); -} - -template <class ParseHandler, typename CharT> -Parser<ParseHandler, CharT>::~Parser() + return anyChars.checkOptions(); +} + +template <class ParseHandler, typename CharT> +GeneralParser<ParseHandler, CharT>::~GeneralParser() { MOZ_ASSERT(checkOptionsCalled); } -template <> +template <typename CharT> void -Parser<SyntaxParseHandler, char16_t>::setAwaitHandling(AwaitHandling awaitHandling) -{ - awaitHandling_ = awaitHandling; -} - -template <> +Parser<SyntaxParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling) +{ + this->awaitHandling_ = awaitHandling; +} + +template <typename CharT> void -Parser<FullParseHandler, char16_t>::setAwaitHandling(AwaitHandling awaitHandling) -{ - awaitHandling_ = awaitHandling; +Parser<FullParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling) +{ + this->awaitHandling_ = awaitHandling; if (syntaxParser_) syntaxParser_->setAwaitHandling(awaitHandling); } +template <class ParseHandler, typename CharT> +inline void +GeneralParser<ParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling) +{ + asFinalParser()->setAwaitHandling(awaitHandling); +} + ObjectBox* ParserBase::newObjectBox(JSObject* obj) { MOZ_ASSERT(obj); /* * We use JSContext.tempLifoAlloc to allocate parsed objects and place them * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc @@ -943,20 +914,21 @@ ParserBase::newObjectBox(JSObject* obj) traceListHead = objbox; return objbox; } template <class ParseHandler, typename CharT> FunctionBox* -Parser<ParseHandler, CharT>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart, - Directives inheritedDirectives, - GeneratorKind generatorKind, - FunctionAsyncKind asyncKind) +GeneralParser<ParseHandler, CharT>::newFunctionBox(Node fn, JSFunction* fun, + uint32_t toStringStart, + Directives inheritedDirectives, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind) { MOZ_ASSERT(fun); /* * We use JSContext.tempLifoAlloc to allocate parsed objects and place them * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc * arenas containing the entries must be alive until we are done with * scanning, parsing and code generation for the whole script or top-level @@ -986,33 +958,33 @@ ModuleSharedContext::ModuleSharedContext bindings(cx), builder(builder) { thisBinding_ = ThisBinding::Module; } template <class ParseHandler, typename CharT> void -Parser<ParseHandler, CharT>::trace(JSTracer* trc) +GeneralParser<ParseHandler, CharT>::trace(JSTracer* trc) { ObjectBox::TraceList(trc, traceListHead); } void TraceParser(JSTracer* trc, AutoGCRooter* parser) { - static_cast<Parser<FullParseHandler, char16_t>*>(parser)->trace(trc); + static_cast<GeneralParser<FullParseHandler, char16_t>*>(parser)->trace(trc); } /* * Parse a top-level JS script. */ template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::parse() +GeneralParser<ParseHandler, CharT>::parse() { MOZ_ASSERT(checkOptionsCalled); Directives directives(options().strictOption); GlobalSharedContext globalsc(context, ScopeKind::Global, directives, options().extraWarningsOption); SourceParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr); if (!globalpc.init()) @@ -1076,27 +1048,27 @@ ParserBase::hasValidSimpleStrictParamete if (!isValidStrictBinding(name->asPropertyName())) return false; } return true; } template <class ParseHandler, typename CharT> void -Parser<ParseHandler, CharT>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber, - uint32_t openedPos) +GeneralParser<ParseHandler, CharT>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber, + uint32_t openedPos) { auto notes = MakeUnique<JSErrorNotes>(); if (!notes) { ReportOutOfMemory(pc->sc()->context); return; } uint32_t line, column; - tokenStream.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column); + anyChars.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column); const size_t MaxWidth = sizeof("4294967295"); char columnNumber[MaxWidth]; SprintfLiteral(columnNumber, "%" PRIu32, column); char lineNumber[MaxWidth]; SprintfLiteral(lineNumber, "%" PRIu32, line); if (!notes->addNoteASCII(pc->sc()->context, @@ -1107,18 +1079,19 @@ Parser<ParseHandler, CharT>::reportMissi return; } errorWithNotes(Move(notes), errorNumber); } template <class ParseHandler, typename CharT> void -Parser<ParseHandler, CharT>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, - TokenPos pos, uint32_t prevPos) +GeneralParser<ParseHandler, CharT>::reportRedeclaration(HandlePropertyName name, + DeclarationKind prevKind, + TokenPos pos, uint32_t prevPos) { JSAutoByteString bytes; if (!AtomToPrintableString(context, name, &bytes)) return; if (prevPos == DeclaredNameInfo::npos) { errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr()); return; @@ -1126,17 +1099,17 @@ Parser<ParseHandler, CharT>::reportRedec auto notes = MakeUnique<JSErrorNotes>(); if (!notes) { ReportOutOfMemory(pc->sc()->context); return; } uint32_t line, column; - tokenStream.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column); + anyChars.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column); const size_t MaxWidth = sizeof("4294967295"); char columnNumber[MaxWidth]; SprintfLiteral(columnNumber, "%" PRIu32, column); char lineNumber[MaxWidth]; SprintfLiteral(lineNumber, "%" PRIu32, line); if (!notes->addNoteASCII(pc->sc()->context, @@ -1158,20 +1131,20 @@ Parser<ParseHandler, CharT>::reportRedec // // The 'disallowDuplicateParams' bool indicates whether the use of another // feature (destructuring or default arguments) disables duplicate arguments. // (ECMA-262 requires us to support duplicate parameter names, but, for newer // features, we consider the code to have "opted in" to higher standards and // forbid duplicates.) template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::notePositionalFormalParameter(Node fn, HandlePropertyName name, - uint32_t beginPos, - bool disallowDuplicateParams, - bool* duplicatedParam) +GeneralParser<ParseHandler, CharT>::notePositionalFormalParameter(Node fn, HandlePropertyName name, + uint32_t beginPos, + bool disallowDuplicateParams, + bool* duplicatedParam) { if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) { if (disallowDuplicateParams) { error(JSMSG_BAD_DUP_ARGS); return false; } // Strict-mode disallows duplicate args. We may not know whether we are @@ -1203,17 +1176,18 @@ Parser<ParseHandler, CharT>::notePositio return false; handler.addFunctionFormalParameter(fn, paramNode); return true; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::noteDestructuredPositionalFormalParameter(Node fn, Node destruct) +GeneralParser<ParseHandler, CharT>::noteDestructuredPositionalFormalParameter(Node fn, + Node destruct) { // Append an empty name to the positional formals vector to keep track of // argument slots when making FunctionScope::Data. if (!pc->positionalFormalParameterNames().append(nullptr)) { ReportOutOfMemory(context); return false; } @@ -1449,19 +1423,19 @@ ParseContext::annexBAppliesToLexicalFunc // If an early error would have occurred already, this function should not // exhibit Annex B.3.3 semantics. return !redeclaredKind; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt, - DeclarationKind kind, - TokenPos pos) +GeneralParser<ParseHandler, CharT>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt, + DeclarationKind kind, + TokenPos pos) { MOZ_ASSERT(DeclarationKindIsLexical(kind)); // It is an early error to declare a lexical binding not directly // within a block. if (!StatementKindIsBraced(stmt.kind()) && stmt.kind() != StatementKind::ForLoopLexicalHead) { @@ -1473,18 +1447,18 @@ Parser<ParseHandler, CharT>::checkLexica return false; } return true; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::noteDeclaredName(HandlePropertyName name, DeclarationKind kind, - TokenPos pos) +GeneralParser<ParseHandler, CharT>::noteDeclaredName(HandlePropertyName name, DeclarationKind kind, + TokenPos pos) { // The asm.js validator does all its own symbol-table management so, as an // optimization, avoid doing any work here. if (pc->useAsmOrInsideUseAsm()) return true; switch (kind) { case DeclarationKind::Var: @@ -1644,17 +1618,17 @@ Parser<ParseHandler, CharT>::noteDeclare break; } return true; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::noteUsedName(HandlePropertyName name) +GeneralParser<ParseHandler, CharT>::noteUsedName(HandlePropertyName name) { // If the we are delazifying, the LazyScript already has all the // closed-over info for bindings and there's no need to track used names. if (handler.canSkipLazyClosedOverBindings()) return true; // The asm.js validator does all its own symbol-table management so, as an // optimization, avoid doing any work here. @@ -1669,26 +1643,26 @@ Parser<ParseHandler, CharT>::noteUsedNam if (pc->sc()->isGlobalContext() && scope == &pc->varScope()) return true; return usedNames.noteUse(context, name, pc->scriptId(), scope->id()); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::hasUsedName(HandlePropertyName name) +GeneralParser<ParseHandler, CharT>::hasUsedName(HandlePropertyName name) { if (UsedNamePtr p = usedNames.lookup(name)) return p->value().isUsedInScript(pc->scriptId()); return false; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope) +GeneralParser<ParseHandler, CharT>::propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope) { // Now that we have all the declared names in the scope, check which // functions should exhibit Annex B semantics. if (!scope.propagateAndMarkAnnexBFunctionBoxes(pc)) return false; if (handler.canSkipLazyClosedOverBindings()) { // Scopes are nullptr-delimited in the LazyScript closed over bindings @@ -1720,19 +1694,19 @@ Parser<ParseHandler, CharT>::propagateFr if (isSyntaxParser && !pc->closedOverBindingsForLazy().append(nullptr)) { ReportOutOfMemory(context); return false; } return true; } -template <> +template <typename CharT> bool -Parser<FullParseHandler, char16_t>::checkStatementsEOF() +Parser<FullParseHandler, CharT>::checkStatementsEOF() { // This is designed to be paired with parsing a statement list at the top // level. // // The statementList() call breaks on TOK_RC, so make sure we've // reached EOF here. TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::Operand)) @@ -2105,40 +2079,50 @@ ParserBase::newLexicalScopeData(ParseCon bindings->constStart = cursor - start; PodCopy(cursor, consts.begin(), consts.length()); bindings->length = numBindings; } return Some(bindings); } -template <> +template <typename CharT> SyntaxParseHandler::Node -Parser<SyntaxParseHandler, char16_t>::finishLexicalScope(ParseContext::Scope& scope, Node body) +Parser<SyntaxParseHandler, CharT>::finishLexicalScope(ParseContext::Scope& scope, Node body) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) return null(); + return body; } -template <> +template <typename CharT> ParseNode* -Parser<FullParseHandler, char16_t>::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body) +Parser<FullParseHandler, CharT>::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) return nullptr; + Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(scope); if (!bindings) return nullptr; + return handler.newLexicalScope(*bindings, body); } -template <> +template <class ParseHandler, typename CharT> +inline typename ParseHandler::Node +GeneralParser<ParseHandler, CharT>::finishLexicalScope(ParseContext::Scope& scope, Node body) +{ + return asFinalParser()->finishLexicalScope(scope, body); +} + +template <typename CharT> ParseNode* -Parser<FullParseHandler, char16_t>::evalBody(EvalSharedContext* evalsc) +Parser<FullParseHandler, CharT>::evalBody(EvalSharedContext* evalsc) { SourceParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr); if (!evalpc.init()) return nullptr; ParseContext::VarScope varScope(this); if (!varScope.init(pc)) return nullptr; @@ -2196,19 +2180,19 @@ Parser<FullParseHandler, char16_t>::eval Maybe<EvalScope::Data*> bindings = newEvalScopeData(pc->varScope()); if (!bindings) return nullptr; evalsc->bindings = *bindings; return body; } -template <> +template <typename CharT> ParseNode* -Parser<FullParseHandler, char16_t>::globalBody(GlobalSharedContext* globalsc) +Parser<FullParseHandler, CharT>::globalBody(GlobalSharedContext* globalsc) { SourceParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr); if (!globalpc.init()) return nullptr; ParseContext::VarScope varScope(this); if (!varScope.init(pc)) return nullptr; @@ -2232,35 +2216,35 @@ Parser<FullParseHandler, char16_t>::glob Maybe<GlobalScope::Data*> bindings = newGlobalScopeData(pc->varScope()); if (!bindings) return nullptr; globalsc->bindings = *bindings; return body; } -template <> +template <typename CharT> ParseNode* -Parser<FullParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc) +Parser<FullParseHandler, CharT>::moduleBody(ModuleSharedContext* modulesc) { MOZ_ASSERT(checkOptionsCalled); SourceParseContext modulepc(this, modulesc, nullptr); if (!modulepc.init()) return null(); ParseContext::VarScope varScope(this); if (!varScope.init(pc)) return nullptr; Node mn = handler.newModule(pos()); if (!mn) return null(); - AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, AwaitIsModuleKeyword); + AutoAwaitIsKeyword<FullParseHandler, CharT> awaitIsKeyword(this, AwaitIsModuleKeyword); ParseNode* pn = statementList(YieldIsName); if (!pn) return null(); MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST)); mn->pn_body = pn; TokenKind tt; @@ -2301,35 +2285,35 @@ Parser<FullParseHandler, char16_t>::modu Maybe<ModuleScope::Data*> bindings = newModuleScopeData(modulepc.varScope()); if (!bindings) return nullptr; modulesc->bindings = *bindings; return mn; } -template <> +template <typename CharT> SyntaxParseHandler::Node -Parser<SyntaxParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc) +Parser<SyntaxParseHandler, CharT>::moduleBody(ModuleSharedContext* modulesc) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return SyntaxParseHandler::NodeFailure; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::hasUsedFunctionSpecialName(HandlePropertyName name) +GeneralParser<ParseHandler, CharT>::hasUsedFunctionSpecialName(HandlePropertyName name) { MOZ_ASSERT(name == context->names().arguments || name == context->names().dotThis); return hasUsedName(name) || pc->functionBox()->bindingsAccessedDynamically(); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::declareFunctionThis() +GeneralParser<ParseHandler, CharT>::declareFunctionThis() { // The asm.js validator does all its own symbol-table management so, as an // optimization, avoid doing any work here. if (pc->useAsmOrInsideUseAsm()) return true; // Derived class constructors emit JSOP_CHECKRETURN, which requires // '.this' to be bound. @@ -2354,79 +2338,79 @@ Parser<ParseHandler, CharT>::declareFunc funbox->setHasThisBinding(); } return true; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::newInternalDotName(HandlePropertyName name) +GeneralParser<ParseHandler, CharT>::newInternalDotName(HandlePropertyName name) { Node nameNode = newName(name); if (!nameNode) return null(); if (!noteUsedName(name)) return null(); return nameNode; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::newThisName() +GeneralParser<ParseHandler, CharT>::newThisName() { return newInternalDotName(context->names().dotThis); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::newDotGeneratorName() +GeneralParser<ParseHandler, CharT>::newDotGeneratorName() { return newInternalDotName(context->names().dotGenerator); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::declareDotGeneratorName() +GeneralParser<ParseHandler, CharT>::declareDotGeneratorName() { // The special '.generator' binding must be on the function scope, as // generators expect to find it on the CallObject. ParseContext::Scope& funScope = pc->functionScope(); HandlePropertyName dotGenerator = context->names().dotGenerator; AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator); if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var, DeclaredNameInfo::npos)) { return false; } return true; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::finishFunctionScopes(bool isStandaloneFunction) +GeneralParser<ParseHandler, CharT>::finishFunctionScopes(bool isStandaloneFunction) { FunctionBox* funbox = pc->functionBox(); if (funbox->hasParameterExprs) { if (!propagateFreeNamesAndMarkClosedOverBindings(pc->functionScope())) return false; } if (funbox->function()->isNamedLambda() && !isStandaloneFunction) { if (!propagateFreeNamesAndMarkClosedOverBindings(pc->namedLambdaScope())) return false; } return true; } -template <> +template <typename CharT> bool -Parser<FullParseHandler, char16_t>::finishFunction(bool isStandaloneFunction /* = false */) +Parser<FullParseHandler, CharT>::finishFunction(bool isStandaloneFunction /* = false */) { if (!finishFunctionScopes(isStandaloneFunction)) return false; FunctionBox* funbox = pc->functionBox(); bool hasParameterExprs = funbox->hasParameterExprs; if (hasParameterExprs) { @@ -2449,19 +2433,19 @@ Parser<FullParseHandler, char16_t>::fini if (!bindings) return false; funbox->namedLambdaBindings().set(*bindings); } return true; } -template <> +template <typename CharT> bool -Parser<SyntaxParseHandler, char16_t>::finishFunction(bool isStandaloneFunction /* = false */) +Parser<SyntaxParseHandler, CharT>::finishFunction(bool isStandaloneFunction /* = false */) { // The LazyScript for a lazily parsed function needs to know its set of // free variables and inner functions so that when it is fully parsed, we // can skip over any already syntax parsed inner functions and still // retain correct scope information. if (!finishFunctionScopes(isStandaloneFunction)) return false; @@ -2509,41 +2493,48 @@ Parser<SyntaxParseHandler, char16_t>::fi // Flags that need to copied back into the parser when we do the full // parse. PropagateTransitiveParseFlags(funbox, lazy); fun->initLazyScript(lazy); return true; } +template <class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::finishFunction(bool isStandaloneFunction /* = false */) +{ + return asFinalParser()->finishFunction(isStandaloneFunction); +} + static YieldHandling GetYieldHandling(GeneratorKind generatorKind) { if (generatorKind == GeneratorKind::NotGenerator) return YieldIsName; return YieldIsKeyword; } static AwaitHandling GetAwaitHandling(FunctionAsyncKind asyncKind) { if (asyncKind == FunctionAsyncKind::SyncFunction) return AwaitIsName; return AwaitIsKeyword; } -template <> +template <typename CharT> ParseNode* -Parser<FullParseHandler, char16_t>::standaloneFunction(HandleFunction fun, - HandleScope enclosingScope, - const Maybe<uint32_t>& parameterListEnd, - GeneratorKind generatorKind, - FunctionAsyncKind asyncKind, - Directives inheritedDirectives, - Directives* newDirectives) +Parser<FullParseHandler, CharT>::standaloneFunction(HandleFunction fun, + HandleScope enclosingScope, + const Maybe<uint32_t>& parameterListEnd, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, + Directives inheritedDirectives, + Directives* newDirectives) { MOZ_ASSERT(checkOptionsCalled); // Skip prelude. TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); if (asyncKind == FunctionAsyncKind::AsyncFunction) { @@ -2558,20 +2549,20 @@ Parser<FullParseHandler, char16_t>::stan if (generatorKind == GeneratorKind::Generator) { MOZ_ASSERT(tt == TOK_MUL); if (!tokenStream.getToken(&tt)) return null(); } // Skip function name, if present. if (TokenKindIsPossibleIdentifierName(tt)) { - MOZ_ASSERT(tokenStream.currentName() == fun->explicitName()); + MOZ_ASSERT(anyChars.currentName() == fun->explicitName()); } else { MOZ_ASSERT(fun->explicitName() == nullptr); - tokenStream.ungetToken(); + anyChars.ungetToken(); } Node fn = handler.newFunctionStatement(pos()); if (!fn) return null(); ParseNode* argsbody = handler.newList(PNK_PARAMSBODY, pos()); if (!argsbody) @@ -2586,17 +2577,17 @@ Parser<FullParseHandler, char16_t>::stan SourceParseContext funpc(this, funbox, newDirectives); if (!funpc.init()) return null(); funpc.setIsStandaloneFunctionBody(); YieldHandling yieldHandling = GetYieldHandling(generatorKind); AwaitHandling awaitHandling = GetAwaitHandling(asyncKind); - AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, awaitHandling); + AutoAwaitIsKeyword<FullParseHandler, CharT> awaitIsKeyword(this, awaitHandling); if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement, parameterListEnd, /* isStandaloneFunction = */ true)) { return null(); } if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); @@ -2608,17 +2599,17 @@ Parser<FullParseHandler, char16_t>::stan if (!FoldConstants(context, &fn, this)) return null(); return fn; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::declareFunctionArgumentsObject() +GeneralParser<ParseHandler, CharT>::declareFunctionArgumentsObject() { FunctionBox* funbox = pc->functionBox(); ParseContext::Scope& funScope = pc->functionScope(); ParseContext::Scope& varScope = pc->varScope(); bool hasExtraBodyVarScope = &funScope != &varScope; // Time to implement the odd semantics of 'arguments'. @@ -2685,18 +2676,19 @@ Parser<ParseHandler, CharT>::declareFunc funbox->setDefinitelyNeedsArgsObj(); } return true; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::functionBody(InHandling inHandling, YieldHandling yieldHandling, - FunctionSyntaxKind kind, FunctionBodyType type) +GeneralParser<ParseHandler, CharT>::functionBody(InHandling inHandling, + YieldHandling yieldHandling, + FunctionSyntaxKind kind, FunctionBodyType type) { MOZ_ASSERT(pc->isFunctionBox()); #ifdef DEBUG uint32_t startYieldOffset = pc->lastYieldOffset; #endif Node pn; @@ -2849,17 +2841,17 @@ ParserBase::newFunction(HandleAtom atom, fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(false)); #endif } return fun; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::matchOrInsertSemicolon() +GeneralParser<ParseHandler, CharT>::matchOrInsertSemicolon() { TokenKind tt = TOK_EOF; if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand)) return false; if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { /* * When current token is `await` and it's outside of async function, * it's possibly intended to be an await expression. @@ -2867,37 +2859,37 @@ Parser<ParseHandler, CharT>::matchOrInse * await f(); * ^ * | * tried to insert semicolon here * * Detect this situation and throw an understandable error. Otherwise * we'd throw a confusing "unexpected token: (unexpected token)" error. */ - if (!pc->isAsync() && tokenStream.currentToken().type == TOK_AWAIT) { + if (!pc->isAsync() && anyChars.currentToken().type == TOK_AWAIT) { error(JSMSG_AWAIT_OUTSIDE_ASYNC); return false; } - if (!yieldExpressionsSupported() && tokenStream.currentToken().type == TOK_YIELD) { + if (!yieldExpressionsSupported() && anyChars.currentToken().type == TOK_YIELD) { error(JSMSG_YIELD_OUTSIDE_GENERATOR); return false; } /* Advance the scanner for proper error location reporting. */ tokenStream.consumeKnownToken(tt, TokenStream::Operand); error(JSMSG_UNEXPECTED_TOKEN_NO_EXPECT, TokenKindToDesc(tt)); return false; } bool matched; return tokenStream.matchToken(&matched, TOK_SEMI, TokenStream::Operand); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::leaveInnerFunction(ParseContext* outerpc) +GeneralParser<ParseHandler, CharT>::leaveInnerFunction(ParseContext* outerpc) { MOZ_ASSERT(pc != outerpc); // If the current function allows super.property but cannot have a home // object, i.e., it is an arrow function, we need to propagate the flag to // the outer ParseContext. if (pc->superScopeNeedsHomeObject()) { if (!pc->isArrowFunction()) @@ -2907,28 +2899,28 @@ Parser<ParseHandler, CharT>::leaveInnerF } // Lazy functions inner to another lazy function need to be remembered by // the inner function so that if the outer function is eventually parsed // we do not need any further parsing or processing of the inner function. // // Append the inner function here unconditionally; the vector is only used // if the Parser using outerpc is a syntax parsing. See - // Parser<SyntaxParseHandler>::finishFunction. + // GeneralParser<SyntaxParseHandler>::finishFunction. if (!outerpc->innerFunctionsForLazy.append(pc->functionBox()->function())) return false; PropagateTransitiveParseFlags(pc->functionBox(), outerpc->sc()); return true; } template <class ParseHandler, typename CharT> JSAtom* -Parser<ParseHandler, CharT>::prefixAccessorName(PropertyType propType, HandleAtom propAtom) +GeneralParser<ParseHandler, CharT>::prefixAccessorName(PropertyType propType, HandleAtom propAtom) { RootedAtom prefix(context); if (propType == PropertyType::Setter || propType == PropertyType::SetterNoExpressionClosure) { prefix = context->names().setPrefix; } else { MOZ_ASSERT(propType == PropertyType::Getter || propType == PropertyType::GetterNoExpressionClosure); prefix = context->names().getPrefix; } @@ -2937,19 +2929,19 @@ Parser<ParseHandler, CharT>::prefixAcces if (!str) return nullptr; return AtomizeString(context, str); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandling, - FunctionSyntaxKind kind, - Node funcpn) +GeneralParser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandling, + FunctionSyntaxKind kind, + Node funcpn) { FunctionBox* funbox = pc->functionBox(); bool parenFreeArrow = false; // Modifier for the following tokens. // TokenStream::None for the following cases: // async a => 1 // ^ @@ -2961,23 +2953,23 @@ Parser<ParseHandler, CharT>::functionArg // ^ // // function f(a) {} // ^ // // TokenStream::Operand for the following case: // a => 1 // ^ - TokenStream::Modifier firstTokenModifier = TokenStream::None; + Modifier firstTokenModifier = TokenStream::None; // Modifier for the the first token in each argument. // can be changed to TokenStream::None for the following case: // async a => 1 // ^ - TokenStream::Modifier argModifier = TokenStream::Operand; + Modifier argModifier = TokenStream::Operand; if (kind == Arrow) { TokenKind tt; // In async function, the first token after `async` is already gotten // with TokenStream::None. // In sync function, the first token is already gotten with // TokenStream::Operand. firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand; if (!tokenStream.peekToken(&tt, firstTokenModifier)) @@ -2997,17 +2989,17 @@ Parser<ParseHandler, CharT>::functionArg error(kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL); return false; } firstTokenPos = pos(); // Record the start of function source (for FunctionToString). If we // are parenFreeArrow, we will set this below, after consuming the NAME. - funbox->setStart(tokenStream); + funbox->setStart(anyChars); } else { // When delazifying, we may not have a current token and pos() is // garbage. In that case, substitute the first token's position. if (!tokenStream.peekTokenPos(&firstTokenPos, firstTokenModifier)) return false; } Node argsbody = handler.newList(PNK_PARAMSBODY, firstTokenPos); @@ -3100,17 +3092,17 @@ Parser<ParseHandler, CharT>::functionArg default: { if (!TokenKindIsPossibleIdentifier(tt)) { error(JSMSG_MISSING_FORMAL); return false; } if (parenFreeArrow) - funbox->setStart(tokenStream); + funbox->setStart(anyChars); RootedPropertyName name(context, bindingIdentifier(yieldHandling)); if (!name) return false; if (!notePositionalFormalParameter(funcpn, name, pos().begin, disallowDuplicateParams, &duplicatedParam)) { @@ -3215,29 +3207,30 @@ Parser<ParseHandler, CharT>::functionArg } else if (IsSetterKind(kind)) { error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); return false; } return true; } -template <> +template <typename CharT> bool -Parser<FullParseHandler, char16_t>::skipLazyInnerFunction(ParseNode* pn, uint32_t toStringStart, - FunctionSyntaxKind kind, bool tryAnnexB) +Parser<FullParseHandler, CharT>::skipLazyInnerFunction(ParseNode* funcNode, uint32_t toStringStart, + FunctionSyntaxKind kind, bool tryAnnexB) { // When a lazily-parsed function is called, we only fully parse (and emit) // that function, not any of its nested children. The initial syntax-only // parse recorded the free variables of nested functions and their extents, // so we can skip over them after accounting for their free variables. RootedFunction fun(context, handler.nextLazyInnerFunction()); - FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, Directives(/* strict = */ false), - fun->generatorKind(), fun->asyncKind()); + FunctionBox* funbox = + newFunctionBox(funcNode, fun, toStringStart, Directives(/* strict = */ false), + fun->generatorKind(), fun->asyncKind()); if (!funbox) return false; LazyScript* lazy = fun->lazyScript(); if (lazy->needsHomeObject()) funbox->setNeedsHomeObject(); if (lazy->isExprBody()) funbox->setIsExprBody(); @@ -3259,30 +3252,39 @@ Parser<FullParseHandler, char16_t>::skip // Append possible Annex B function box only upon successfully parsing. if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox)) return false; return true; } -template <> +template <typename CharT> bool -Parser<SyntaxParseHandler, char16_t>::skipLazyInnerFunction(Node pn, uint32_t toStringStart, - FunctionSyntaxKind kind, - bool tryAnnexB) +Parser<SyntaxParseHandler, CharT>::skipLazyInnerFunction(Node funcNode, uint32_t toStringStart, + FunctionSyntaxKind kind, + bool tryAnnexB) { MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing"); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, - Node nodeList, - TokenKind* ttp) +GeneralParser<ParseHandler, CharT>::skipLazyInnerFunction(Node funcNode, uint32_t toStringStart, + FunctionSyntaxKind kind, + bool tryAnnexB) +{ + return asFinalParser()->skipLazyInnerFunction(funcNode, toStringStart, kind, tryAnnexB); +} + +template <class ParseHandler, typename CharT> +bool +GeneralParser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, + Node nodeList, + TokenKind* ttp) { Node pn = expr(InAllowed, yieldHandling, TripledotProhibited); if (!pn) return false; handler.addList(nodeList, pn); TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) @@ -3292,18 +3294,18 @@ Parser<ParseHandler, CharT>::addExprAndG return false; } return tokenStream.getToken(ttp, TokenStream::TemplateTail); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList, - TokenKind tt) +GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList, + TokenKind tt) { Node callSiteObjNode = handler.newCallSiteObject(pos().begin); if (!callSiteObjNode) return false; handler.addList(nodeList, callSiteObjNode); while (true) { if (!appendToCallSiteObj(callSiteObjNode)) @@ -3315,17 +3317,17 @@ Parser<ParseHandler, CharT>::taggedTempl return false; } handler.setEndPosition(nodeList, callSiteObjNode); return true; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling) { Node pn = noSubstitutionUntaggedTemplate(); if (!pn) return null(); Node nodeList = handler.newList(PNK_TEMPLATE_STRING_LIST, pn); if (!nodeList) return null(); @@ -3341,32 +3343,33 @@ Parser<ParseHandler, CharT>::templateLit handler.addList(nodeList, pn); } while (tt == TOK_TEMPLATE_HEAD); return nodeList; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::functionDefinition(Node pn, uint32_t toStringStart, - InHandling inHandling, YieldHandling yieldHandling, - HandleAtom funName, FunctionSyntaxKind kind, - GeneratorKind generatorKind, - FunctionAsyncKind asyncKind, - bool tryAnnexB /* = false */) +GeneralParser<ParseHandler, CharT>::functionDefinition(Node funcNode, uint32_t toStringStart, + InHandling inHandling, YieldHandling yieldHandling, + HandleAtom funName, FunctionSyntaxKind kind, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, + bool tryAnnexB /* = false */) { MOZ_ASSERT_IF(kind == Statement, funName); // When fully parsing a LazyScript, we do not fully reparse its inner // functions, which are also lazy. Instead, their free variables and // source extents are recorded and may be skipped. if (handler.canSkipLazyInnerFunctions()) { - if (!skipLazyInnerFunction(pn, toStringStart, kind, tryAnnexB)) - return null(); - return pn; + if (!skipLazyInnerFunction(funcNode, toStringStart, kind, tryAnnexB)) + return null(); + + return funcNode; } RootedObject proto(context); if (generatorKind == GeneratorKind::Generator || asyncKind == FunctionAsyncKind::AsyncFunction) { // If we are off thread, the generator meta-objects have // already been created by js::StartOffThreadParseTask, so cx will not @@ -3382,230 +3385,260 @@ Parser<ParseHandler, CharT>::functionDef // Speculatively parse using the directives of the parent parsing context. // If a directive is encountered (e.g., "use strict") that changes how the // function should have been parsed, we backup and reparse with the new set // of directives. Directives directives(pc); Directives newDirectives = directives; - TokenStream::Position start(keepAtoms); + typename TokenStream::Position start(keepAtoms); tokenStream.tell(&start); // Parse the inner function. The following is a loop as we may attempt to // reparse a function due to failed syntax parsing and encountering new // "use foo" directives. while (true) { - if (trySyntaxParseInnerFunction(pn, fun, toStringStart, inHandling, yieldHandling, kind, - generatorKind, asyncKind, tryAnnexB, directives, + if (trySyntaxParseInnerFunction(funcNode, fun, toStringStart, inHandling, yieldHandling, + kind, generatorKind, asyncKind, tryAnnexB, directives, &newDirectives)) { break; } // Return on error. - if (tokenStream.hadError() || directives == newDirectives) + if (anyChars.hadError() || directives == newDirectives) return null(); // Assignment must be monotonic to prevent infinitely attempting to // reparse. MOZ_ASSERT_IF(directives.strict(), newDirectives.strict()); MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); directives = newDirectives; tokenStream.seek(start); // functionFormalParametersAndBody may have already set pn->pn_body before failing. - handler.setFunctionFormalParametersAndBody(pn, null()); - } - - return pn; -} - -template <> + handler.setFunctionFormalParametersAndBody(funcNode, null()); + } + + return funcNode; +} + +template <typename CharT> bool -Parser<FullParseHandler, char16_t>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun, - uint32_t toStringStart, - InHandling inHandling, - YieldHandling yieldHandling, - FunctionSyntaxKind kind, - GeneratorKind generatorKind, - FunctionAsyncKind asyncKind, - bool tryAnnexB, - Directives inheritedDirectives, - Directives* newDirectives) +Parser<FullParseHandler, CharT>::trySyntaxParseInnerFunction(ParseNode* funcNode, + HandleFunction fun, + uint32_t toStringStart, + InHandling inHandling, + YieldHandling yieldHandling, + FunctionSyntaxKind kind, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, + bool tryAnnexB, + Directives inheritedDirectives, + Directives* newDirectives) { // Try a syntax parse for this inner function. do { // If we're assuming this function is an IIFE, always perform a full // parse to avoid the overhead of a lazy syntax-only parse. Although // the prediction may be incorrect, IIFEs are common enough that it // pays off for lots of code. - if (pn->isLikelyIIFE() && + if (funcNode->isLikelyIIFE() && generatorKind == GeneratorKind::NotGenerator && asyncKind == FunctionAsyncKind::SyncFunction) { break; } if (!syntaxParser_) break; UsedNameTracker::RewindToken token = usedNames.getRewindToken(); // Move the syntax parser to the current position in the stream. - TokenStream::Position position(keepAtoms); + typename TokenStream::Position position(keepAtoms); tokenStream.tell(&position); - if (!syntaxParser_->tokenStream.seek(position, tokenStream)) + if (!syntaxParser_->tokenStream.seek(position, anyChars)) return false; // Make a FunctionBox before we enter the syntax parser, because |pn| // still expects a FunctionBox to be attached to it during BCE, and // the syntax parser cannot attach one to it. - FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives, + FunctionBox* funbox = newFunctionBox(funcNode, fun, toStringStart, inheritedDirectives, generatorKind, asyncKind); if (!funbox) return false; funbox->initWithEnclosingParseContext(pc, kind); - if (!syntaxParser_->innerFunction(SyntaxParseHandler::NodeGeneric, - pc, funbox, inHandling, yieldHandling, kind, - newDirectives)) + if (!syntaxParser_->innerFunctionForFunctionBox(SyntaxParseHandler::NodeGeneric, + pc, funbox, inHandling, yieldHandling, + kind, newDirectives)) { if (syntaxParser_->hadAbortedSyntaxParse()) { // Try again with a full parse. UsedNameTracker needs to be // rewound to just before we tried the syntax parse for // correctness. syntaxParser_->clearAbortedSyntaxParse(); usedNames.rewind(token); MOZ_ASSERT_IF(!syntaxParser_->context->helperThread(), !syntaxParser_->context->isExceptionPending()); break; } return false; } // Advance this parser over tokens processed by the syntax parser. syntaxParser_->tokenStream.tell(&position); - if (!tokenStream.seek(position, syntaxParser_->tokenStream)) + if (!tokenStream.seek(position, syntaxParser_->anyChars)) return false; // Update the end position of the parse node. - pn->pn_pos.end = tokenStream.currentToken().pos.end; + funcNode->pn_pos.end = anyChars.currentToken().pos.end; // Append possible Annex B function box only upon successfully parsing. - if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox)) - return false; + if (tryAnnexB) { + if (!pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox)) + return false; + } return true; } while (false); // We failed to do a syntax parse above, so do the full parse. - return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind, + return innerFunction(funcNode, pc, fun, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); } -template <> +template <typename CharT> bool -Parser<SyntaxParseHandler, char16_t>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun, - uint32_t toStringStart, - InHandling inHandling, - YieldHandling yieldHandling, - FunctionSyntaxKind kind, - GeneratorKind generatorKind, - FunctionAsyncKind asyncKind, - bool tryAnnexB, - Directives inheritedDirectives, - Directives* newDirectives) +Parser<SyntaxParseHandler, CharT>::trySyntaxParseInnerFunction(Node funcNode, HandleFunction fun, + uint32_t toStringStart, + InHandling inHandling, + YieldHandling yieldHandling, + FunctionSyntaxKind kind, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, + bool tryAnnexB, + Directives inheritedDirectives, + Directives* newDirectives) { // This is already a syntax parser, so just parse the inner function. - return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind, + return innerFunction(funcNode, pc, fun, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); } template <class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::trySyntaxParseInnerFunction(Node funcNode, HandleFunction fun, + uint32_t toStringStart, + InHandling inHandling, + YieldHandling yieldHandling, + FunctionSyntaxKind kind, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, + bool tryAnnexB, + Directives inheritedDirectives, + Directives* newDirectives) +{ + return asFinalParser()->trySyntaxParseInnerFunction(funcNode, fun, toStringStart, inHandling, + yieldHandling, kind, generatorKind, + asyncKind, tryAnnexB, inheritedDirectives, + newDirectives); +} + +template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, - InHandling inHandling, - YieldHandling yieldHandling, FunctionSyntaxKind kind, - Directives* newDirectives) +GeneralParser<ParseHandler, CharT>::innerFunctionForFunctionBox(Node funcNode, + ParseContext* outerpc, + FunctionBox* funbox, + InHandling inHandling, + YieldHandling yieldHandling, + FunctionSyntaxKind kind, + Directives* newDirectives) { // Note that it is possible for outerpc != this->pc, as we may be // attempting to syntax parse an inner function from an outer full // parser. In that case, outerpc is a SourceParseContext from the full parser // instead of the current top of the stack of the syntax parser. // Push a new ParseContext. SourceParseContext funpc(this, funbox, newDirectives); if (!funpc.init()) return false; - if (!functionFormalParametersAndBody(inHandling, yieldHandling, pn, kind)) + if (!functionFormalParametersAndBody(inHandling, yieldHandling, funcNode, kind)) return false; return leaveInnerFunction(outerpc); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, - uint32_t toStringStart, InHandling inHandling, - YieldHandling yieldHandling, FunctionSyntaxKind kind, - GeneratorKind generatorKind, - FunctionAsyncKind asyncKind, bool tryAnnexB, - Directives inheritedDirectives, - Directives* newDirectives) +GeneralParser<ParseHandler, CharT>::innerFunction(Node funcNode, ParseContext* outerpc, + HandleFunction fun, uint32_t toStringStart, + InHandling inHandling, + YieldHandling yieldHandling, + FunctionSyntaxKind kind, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, bool tryAnnexB, + Directives inheritedDirectives, + Directives* newDirectives) { // Note that it is possible for outerpc != this->pc, as we may be // attempting to syntax parse an inner function from an outer full // parser. In that case, outerpc is a SourceParseContext from the full parser // instead of the current top of the stack of the syntax parser. - FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives, + FunctionBox* funbox = newFunctionBox(funcNode, fun, toStringStart, inheritedDirectives, generatorKind, asyncKind); if (!funbox) return false; funbox->initWithEnclosingParseContext(outerpc, kind); - if (!innerFunction(pn, outerpc, funbox, inHandling, yieldHandling, kind, newDirectives)) + if (!innerFunctionForFunctionBox(funcNode, outerpc, funbox, inHandling, yieldHandling, kind, + newDirectives)) + { return false; + } // Append possible Annex B function box only upon successfully parsing. - if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox)) - return false; + if (tryAnnexB) { + if (!pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox)) + return false; + } return true; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj) +GeneralParser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj) { Node cookedNode = noSubstitutionTaggedTemplate(); if (!cookedNode) return false; JSAtom* atom = tokenStream.getRawTemplateStringAtom(); if (!atom) return false; Node rawNode = handler.newTemplateStringLiteral(atom, pos()); if (!rawNode) return false; handler.addToCallSiteObject(callSiteObj, rawNode, cookedNode); return true; } -template <> +template <typename CharT> ParseNode* -Parser<FullParseHandler, char16_t>::standaloneLazyFunction(HandleFunction fun, - uint32_t toStringStart, - bool strict, - GeneratorKind generatorKind, - FunctionAsyncKind asyncKind) +Parser<FullParseHandler, CharT>::standaloneLazyFunction(HandleFunction fun, uint32_t toStringStart, + bool strict, GeneratorKind generatorKind, + FunctionAsyncKind asyncKind) { MOZ_ASSERT(checkOptionsCalled); Node pn = handler.newFunctionStatement(pos()); if (!pn) return null(); Directives directives(strict); @@ -3616,23 +3649,23 @@ Parser<FullParseHandler, char16_t>::stan funbox->initFromLazyFunction(); Directives newDirectives = directives; SourceParseContext funpc(this, funbox, &newDirectives); if (!funpc.init()) return null(); // Our tokenStream has no current token, so pn's position is garbage. - // Substitute the position of the first token in our source. If the function - // is a not-async arrow, use TokenStream::Operand to keep + // Substitute the position of the first token in our source. If the + // function is a not-async arrow, use TokenStream::Operand to keep // verifyConsistentModifier from complaining (we will use // TokenStream::Operand in functionArguments). - TokenStream::Modifier modifier = (fun->isArrow() && - asyncKind == FunctionAsyncKind::SyncFunction) - ? TokenStream::Operand : TokenStream::None; + Modifier modifier = (fun->isArrow() && asyncKind == FunctionAsyncKind::SyncFunction) + ? TokenStream::Operand + : TokenStream::None; if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier)) return null(); YieldHandling yieldHandling = GetYieldHandling(generatorKind); FunctionSyntaxKind syntaxKind = Statement; if (fun->isClassConstructor()) syntaxKind = ClassConstructor; else if (fun->isMethod()) @@ -3652,36 +3685,37 @@ Parser<FullParseHandler, char16_t>::stan if (!FoldConstants(context, &pn, this)) return null(); return pn; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling inHandling, - YieldHandling yieldHandling, - Node pn, FunctionSyntaxKind kind, - const Maybe<uint32_t>& parameterListEnd /* = Nothing() */, - bool isStandaloneFunction /* = false */) +GeneralParser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling inHandling, + YieldHandling yieldHandling, + Node pn, + FunctionSyntaxKind kind, + const Maybe<uint32_t>& parameterListEnd /* = Nothing() */, + bool isStandaloneFunction /* = false */) { // Given a properly initialized parse context, try to parse an actual // function without concern for conversion to strict mode, use of lazy // parsing and such. FunctionBox* funbox = pc->functionBox(); RootedFunction fun(context, funbox->function()); // See below for an explanation why arrow function parameters and arrow // function bodies are parsed with different yield/await settings. { AwaitHandling awaitHandling = funbox->isAsync() || (kind == Arrow && awaitIsKeyword()) ? AwaitIsKeyword : AwaitIsName; - AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, awaitHandling); + AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, awaitHandling); if (!functionArguments(yieldHandling, kind, pn)) return false; } Maybe<ParseContext::VarScope> varScope; if (funbox->hasParameterExprs) { varScope.emplace(this); if (!varScope->init(pc)) @@ -3728,17 +3762,17 @@ Parser<ParseHandler, CharT>::functionFor if (!warnOnceAboutExprClosure()) return false; #else error(JSMSG_CURLY_BEFORE_BODY); return false; #endif } - tokenStream.ungetToken(); + anyChars.ungetToken(); bodyType = ExpressionBody; funbox->setIsExprBody(); } else { openedPos = pos().begin; } // Arrow function parameters inherit yieldHandling from the enclosing // context, but the arrow body doesn't. E.g. in |(a = yield) => yield|, @@ -3746,17 +3780,17 @@ Parser<ParseHandler, CharT>::functionFor // whether the arrow function is enclosed in a generator function or not. // Whereas the |yield| in the function body is always parsed as a name. // The same goes when parsing |await| in arrow functions. YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind()); AwaitHandling bodyAwaitHandling = GetAwaitHandling(pc->asyncKind()); bool inheritedStrict = pc->sc()->strict(); Node body; { - AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, bodyAwaitHandling); + AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, bodyAwaitHandling); body = functionBody(inHandling, bodyYieldHandling, kind, bodyType); if (!body) return false; } // Revalidate the function name when we transitioned to strict mode. if ((kind == Statement || IsFunctionExpression(kind)) && fun->explicitName() && @@ -3776,33 +3810,33 @@ Parser<ParseHandler, CharT>::functionFor // because of different context. // It should already be checked before this point. nameYieldHandling = YieldIsName; } // We already use the correct await-handling at this point, therefore // we don't need call AutoAwaitIsKeyword here. - uint32_t nameOffset = handler.getFunctionNameOffset(pn, tokenStream); + uint32_t nameOffset = handler.getFunctionNameOffset(pn, anyChars); if (!checkBindingIdentifier(propertyName, nameOffset, nameYieldHandling)) return false; } if (bodyType == StatementListBody) { MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, reportMissingClosing(JSMSG_CURLY_AFTER_BODY, JSMSG_CURLY_OPENED, openedPos)); - funbox->setEnd(tokenStream); + funbox->setEnd(anyChars); } else { #if !JS_HAS_EXPR_CLOSURES MOZ_ASSERT(kind == Arrow); #endif - if (tokenStream.hadError()) + if (anyChars.hadError()) return false; - funbox->setEnd(tokenStream); + funbox->setEnd(anyChars); if (kind == Statement && !matchOrInsertSemicolon()) return false; } if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject()) funbox->setNeedsHomeObject(); if (!finishFunction(isStandaloneFunction)) @@ -3812,21 +3846,22 @@ Parser<ParseHandler, CharT>::functionFor handler.setEndPosition(pn, pos().end); handler.setFunctionBody(pn, body); return true; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart, YieldHandling yieldHandling, - DefaultHandling defaultHandling, - FunctionAsyncKind asyncKind) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); +GeneralParser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart, + YieldHandling yieldHandling, + DefaultHandling defaultHandling, + FunctionAsyncKind asyncKind) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION)); // In sloppy mode, Annex B.3.2 allows labelled function declarations. // Otherwise it's a parse error. ParseContext::Statement* declaredInStmt = pc->innermostStatement(); if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) { MOZ_ASSERT(!pc->sc()->strict(), "labeled functions shouldn't be parsed in strict mode"); @@ -3855,17 +3890,17 @@ Parser<ParseHandler, CharT>::functionStm RootedPropertyName name(context); if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) return null(); } else if (defaultHandling == AllowDefaultName) { name = context->names().default_; - tokenStream.ungetToken(); + anyChars.ungetToken(); } else { /* Unnamed function expressions are forbidden in statement context. */ error(JSMSG_UNNAMED_FUNCTION_STMT); return null(); } // Note the declared name and check for early errors. DeclarationKind kind; @@ -3902,24 +3937,24 @@ Parser<ParseHandler, CharT>::functionStm YieldHandling newYieldHandling = GetYieldHandling(generatorKind); return functionDefinition(pn, toStringStart, InAllowed, newYieldHandling, name, Statement, generatorKind, asyncKind, tryAnnexB); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart, - ExpressionClosure expressionClosureHandling, - InvokedPrediction invoked, - FunctionAsyncKind asyncKind) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); - - AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, GetAwaitHandling(asyncKind)); +GeneralParser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart, + ExpressionClosure expressionClosureHandling, + InvokedPrediction invoked, + FunctionAsyncKind asyncKind) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION)); + + AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, GetAwaitHandling(asyncKind)); GeneratorKind generatorKind = GeneratorKind::NotGenerator; TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_MUL) { generatorKind = GeneratorKind::Generator; if (!tokenStream.getToken(&tt)) @@ -3929,17 +3964,17 @@ Parser<ParseHandler, CharT>::functionExp YieldHandling yieldHandling = GetYieldHandling(generatorKind); RootedPropertyName name(context); if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) return null(); } else { - tokenStream.ungetToken(); + anyChars.ungetToken(); } Node pn = handler.newFunctionExpression(pos()); if (!pn) return null(); if (invoked) pn = handler.setLikelyIIFE(pn); @@ -3965,19 +4000,19 @@ IsEscapeFreeStringLiteral(const TokenPos /* * If the string's length in the source code is its length as a value, * accounting for the quotes, then it must not contain any escape * sequences or line continuations. */ return pos.begin + str->length() + 2 == pos.end; } -template <> +template <typename CharT> bool -Parser<SyntaxParseHandler, char16_t>::asmJS(Node list) +Parser<SyntaxParseHandler, CharT>::asmJS(Node list) { // While asm.js could technically be validated and compiled during syntax // parsing, we have no guarantee that some later JS wouldn't abort the // syntax parse and cause us to re-parse (and re-compile) the asm.js module. // For simplicity, unconditionally abort the syntax parse when "use asm" is // encountered so that asm.js is always validated/compiled exactly once // during a full parse. JS_ALWAYS_FALSE(abortIfSyntaxParser()); @@ -3985,19 +4020,19 @@ Parser<SyntaxParseHandler, char16_t>::as // Record that the current script source constains some AsmJS, to disable // any incremental encoder, as AsmJS cannot be encoded with XDR at the // moment. if (ss) ss->setContainsAsmJS(); return false; } -template <> +template <typename CharT> bool -Parser<FullParseHandler, char16_t>::asmJS(Node list) +Parser<FullParseHandler, CharT>::asmJS(Node list) { // Disable syntax parsing in anything nested inside the asm.js module. disableSyntaxParser(); // We should be encountering the "use asm" directive for the first time; if // the directive is already, we must have failed asm.js validation and we're // reparsing. In that case, don't try to validate again. A non-null // newDirectives means we're not in a normal function. @@ -4023,16 +4058,23 @@ Parser<FullParseHandler, char16_t>::asmJ if (!validated) { pc->newDirectives->setAsmJS(); return false; } return true; } +template <class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::asmJS(Node list) +{ + return asFinalParser()->asmJS(list); +} + /* * Recognize Directive Prologue members and directives. Assuming |pn| is a * candidate for membership in a directive prologue, recognize directives and * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its * |pn_prologue| flag. * * Note that the following is a strict mode function: * @@ -4044,17 +4086,18 @@ Parser<FullParseHandler, char16_t>::asmJ * } * * That is, even though "use\x20loose" can never be a directive, now or in the * future (because of the hex escape), the Directive Prologue extends through it * to the "use strict" statement, which is indeed a directive. */ template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::maybeParseDirective(Node list, Node possibleDirective, bool* cont) +GeneralParser<ParseHandler, CharT>::maybeParseDirective(Node list, Node possibleDirective, + bool* cont) { TokenPos directivePos; JSAtom* directive = handler.isStringExprStatement(possibleDirective, &directivePos); *cont = !!directive; if (!*cont) return true; @@ -4090,52 +4133,52 @@ Parser<ParseHandler, CharT>::maybeParseD // We're going to be in strict mode. Note that this scope explicitly // had "use strict"; pc->sc()->setExplicitUseStrict(); if (!pc->sc()->strict()) { // We keep track of the one possible strict violation that could // occur in the directive prologue -- octal escapes -- and // complain now. - if (tokenStream.sawOctalEscape()) { + if (anyChars.sawOctalEscape()) { error(JSMSG_DEPRECATED_OCTAL); return false; } pc->sc()->strictScript = true; } } else if (directive == context->names().useAsm) { if (pc->isFunctionBox()) return asmJS(list); return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL); } } return true; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::statementList(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::statementList(YieldHandling yieldHandling) { if (!CheckRecursionLimit(context)) return null(); Node pn = handler.newStatementList(pos()); if (!pn) return null(); bool canHaveDirectives = pc->atBodyLevel(); if (canHaveDirectives) - tokenStream.clearSawOctalEscape(); + anyChars.clearSawOctalEscape(); bool afterReturn = false; bool warnedAboutStatementsAfterReturn = false; uint32_t statementBegin = 0; for (;;) { TokenKind tt = TOK_EOF; if (!tokenStream.peekToken(&tt, TokenStream::Operand)) { - if (tokenStream.isEOF()) + if (anyChars.isEOF()) isUnexpectedEOF_ = true; return null(); } if (tt == TOK_EOF || tt == TOK_RC) { TokenPos pos; if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand)) { return null(); } @@ -4143,17 +4186,17 @@ Parser<ParseHandler, CharT>::statementLi break; } if (afterReturn) { if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand)) return null(); } Node next = statementListItem(yieldHandling, canHaveDirectives); if (!next) { - if (tokenStream.isEOF()) + if (anyChars.isEOF()) isUnexpectedEOF_ = true; return null(); } if (!warnedAboutStatementsAfterReturn) { if (afterReturn) { if (!handler.isStatementPermittedAfterReturnStatement(next)) { if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN)) return null(); @@ -4173,17 +4216,17 @@ Parser<ParseHandler, CharT>::statementLi handler.addStatementToList(pn, next); } return pn; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling) { MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); Node pn = exprInParens(inHandling, yieldHandling, TripledotProhibited); if (!pn) return null(); MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_COND); @@ -4193,18 +4236,18 @@ Parser<ParseHandler, CharT>::condition(I if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN)) return null(); } return pn; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::matchLabel(YieldHandling yieldHandling, - MutableHandle<PropertyName*> label) +GeneralParser<ParseHandler, CharT>::matchLabel(YieldHandling yieldHandling, + MutableHandle<PropertyName*> label) { TokenKind tt = TOK_EOF; if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand)) return false; if (TokenKindIsPossibleIdentifier(tt)) { tokenStream.consumeKnownToken(tt, TokenStream::Operand); @@ -4213,175 +4256,176 @@ Parser<ParseHandler, CharT>::matchLabel( return false; } else { label.set(nullptr); } return true; } template <class ParseHandler, typename CharT> -Parser<ParseHandler, CharT>::PossibleError::PossibleError(Parser<ParseHandler, CharT>& parser) +GeneralParser<ParseHandler, CharT>::PossibleError::PossibleError(GeneralParser<ParseHandler, CharT>& parser) : parser_(parser) {} template <class ParseHandler, typename CharT> -typename Parser<ParseHandler, CharT>::PossibleError::Error& -Parser<ParseHandler, CharT>::PossibleError::error(ErrorKind kind) +typename GeneralParser<ParseHandler, CharT>::PossibleError::Error& +GeneralParser<ParseHandler, CharT>::PossibleError::error(ErrorKind kind) { if (kind == ErrorKind::Expression) return exprError_; if (kind == ErrorKind::Destructuring) return destructuringError_; MOZ_ASSERT(kind == ErrorKind::DestructuringWarning); return destructuringWarning_; } template <class ParseHandler, typename CharT> void -Parser<ParseHandler, CharT>::PossibleError::setResolved(ErrorKind kind) +GeneralParser<ParseHandler, CharT>::PossibleError::setResolved(ErrorKind kind) { error(kind).state_ = ErrorState::None; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::PossibleError::hasError(ErrorKind kind) +GeneralParser<ParseHandler, CharT>::PossibleError::hasError(ErrorKind kind) { return error(kind).state_ == ErrorState::Pending; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::PossibleError::hasPendingDestructuringError() +GeneralParser<ParseHandler, CharT>::PossibleError::hasPendingDestructuringError() { return hasError(ErrorKind::Destructuring); } template <class ParseHandler, typename CharT> void -Parser<ParseHandler, CharT>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos, - unsigned errorNumber) +GeneralParser<ParseHandler, CharT>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos, + unsigned errorNumber) { // Don't overwrite a previously recorded error. if (hasError(kind)) return; // If we report an error later, we'll do it from the position where we set // the state to pending. Error& err = error(kind); err.offset_ = pos.begin; err.errorNumber_ = errorNumber; err.state_ = ErrorState::Pending; } template <class ParseHandler, typename CharT> void -Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos, - unsigned errorNumber) +GeneralParser<ParseHandler, CharT>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos, + unsigned errorNumber) { setPending(ErrorKind::Destructuring, pos, errorNumber); } template <class ParseHandler, typename CharT> void -Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos, - unsigned errorNumber) +GeneralParser<ParseHandler, CharT>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos, + unsigned errorNumber) { setPending(ErrorKind::DestructuringWarning, pos, errorNumber); } template <class ParseHandler, typename CharT> void -Parser<ParseHandler, CharT>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos, - unsigned errorNumber) +GeneralParser<ParseHandler, CharT>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos, + unsigned errorNumber) { setPending(ErrorKind::Expression, pos, errorNumber); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::PossibleError::checkForError(ErrorKind kind) +GeneralParser<ParseHandler, CharT>::PossibleError::checkForError(ErrorKind kind) { if (!hasError(kind)) return true; Error& err = error(kind); parser_.errorAt(err.offset_, err.errorNumber_); return false; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::PossibleError::checkForWarning(ErrorKind kind) +GeneralParser<ParseHandler, CharT>::PossibleError::checkForWarning(ErrorKind kind) { if (!hasError(kind)) return true; Error& err = error(kind); return parser_.extraWarningAt(err.offset_, err.errorNumber_); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::PossibleError::checkForDestructuringErrorOrWarning() +GeneralParser<ParseHandler, CharT>::PossibleError::checkForDestructuringErrorOrWarning() { // Clear pending expression error, because we're definitely not in an // expression context. setResolved(ErrorKind::Expression); // Report any pending destructuring error or warning. return checkForError(ErrorKind::Destructuring) && checkForWarning(ErrorKind::DestructuringWarning); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::PossibleError::checkForExpressionError() +GeneralParser<ParseHandler, CharT>::PossibleError::checkForExpressionError() { // Clear pending destructuring error or warning, because we're definitely // not in a destructuring context. setResolved(ErrorKind::Destructuring); setResolved(ErrorKind::DestructuringWarning); // Report any pending expression error. return checkForError(ErrorKind::Expression); } template <class ParseHandler, typename CharT> void -Parser<ParseHandler, CharT>::PossibleError::transferErrorTo(ErrorKind kind, PossibleError* other) +GeneralParser<ParseHandler, CharT>::PossibleError::transferErrorTo(ErrorKind kind, + PossibleError* other) { if (hasError(kind) && !other->hasError(kind)) { Error& err = error(kind); Error& otherErr = other->error(kind); otherErr.offset_ = err.offset_; otherErr.errorNumber_ = err.errorNumber_; otherErr.state_ = err.state_; } } template <class ParseHandler, typename CharT> void -Parser<ParseHandler, CharT>::PossibleError::transferErrorsTo(PossibleError* other) +GeneralParser<ParseHandler, CharT>::PossibleError::transferErrorsTo(PossibleError* other) { MOZ_ASSERT(other); MOZ_ASSERT(this != other); MOZ_ASSERT(&parser_ == &other->parser_, "Can't transfer fields to an instance which belongs to a different parser"); transferErrorTo(ErrorKind::Destructuring, other); transferErrorTo(ErrorKind::Expression, other); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind, - YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN)); +GeneralParser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind, + YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_ASSIGN)); if (kind == DeclarationKind::FormalParameter) pc->functionBox()->hasParameterExprs = true; Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!rhs) return null(); @@ -4394,33 +4438,35 @@ Parser<ParseHandler, CharT>::bindingInit if (foldConstants && !FoldConstants(context, &assign, this)) return null(); return assign; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::bindingIdentifier(DeclarationKind kind, + YieldHandling yieldHandling) { RootedPropertyName name(context, bindingIdentifier(yieldHandling)); if (!name) return null(); Node binding = newName(name); if (!binding || !noteDeclaredName(name, kind, pos())) return null(); return binding; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::bindingIdentifierOrPattern(DeclarationKind kind, - YieldHandling yieldHandling, TokenKind tt) +GeneralParser<ParseHandler, CharT>::bindingIdentifierOrPattern(DeclarationKind kind, + YieldHandling yieldHandling, + TokenKind tt) { if (tt == TOK_LB) return arrayBindingPattern(kind, yieldHandling); if (tt == TOK_LC) return objectBindingPattern(kind, yieldHandling); if (!TokenKindIsPossibleIdentifierName(tt)) { @@ -4428,37 +4474,37 @@ Parser<ParseHandler, CharT>::bindingIden return null(); } return bindingIdentifier(kind, yieldHandling); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind, - YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); +GeneralParser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind, + YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC)); if (!CheckRecursionLimit(context)) return null(); uint32_t begin = pos().begin; Node literal = handler.newObjectLiteral(begin); if (!literal) return null(); Maybe<DeclarationKind> declKind = Some(kind); RootedAtom propAtom(context); for (;;) { TokenKind tt; if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_RC) { - tokenStream.addModifierException(TokenStream::OperandIsNone); + anyChars.addModifierException(TokenStream::OperandIsNone); break; } if (tt == TOK_TRIPLEDOT) { tokenStream.consumeKnownToken(TOK_TRIPLEDOT); uint32_t begin = pos().begin; TokenKind tt; @@ -4472,17 +4518,17 @@ Parser<ParseHandler, CharT>::objectBindi Node inner = bindingIdentifier(kind, yieldHandling); if (!inner) return null(); if (!handler.addSpreadProperty(literal, begin, inner)) return null(); } else { - TokenPos namePos = tokenStream.nextToken().pos; + TokenPos namePos = anyChars.nextToken().pos; PropertyType propType; Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); if (!propName) return null(); if (propType == PropertyType::Normal) { // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|. @@ -4556,19 +4602,20 @@ Parser<ParseHandler, CharT>::objectBindi JSMSG_CURLY_OPENED, begin)); handler.setEndPosition(literal, pos().end); return literal; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB)); +GeneralParser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, + YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB)); if (!CheckRecursionLimit(context)) return null(); uint32_t begin = pos().begin; Node literal = handler.newArrayLiteral(begin); if (!literal) return null(); @@ -4580,18 +4627,18 @@ Parser<ParseHandler, CharT>::arrayBindin return null(); } TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_RB) { - tokenStream.ungetToken(); - tokenStream.addModifierException(TokenStream::OperandIsNone); + anyChars.ungetToken(); + anyChars.addModifierException(TokenStream::OperandIsNone); break; } if (tt == TOK_COMMA) { if (!handler.addElision(literal, pos())) return null(); } else if (tt == TOK_TRIPLEDOT) { uint32_t begin = pos().begin; @@ -4644,33 +4691,33 @@ Parser<ParseHandler, CharT>::arrayBindin JSMSG_BRACKET_OPENED, begin)); handler.setEndPosition(literal, pos().end); return literal; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind, - YieldHandling yieldHandling, - TokenKind tt) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); +GeneralParser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind, + YieldHandling yieldHandling, + TokenKind tt) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(tt)); MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC); return tt == TOK_LB ? arrayBindingPattern(kind, yieldHandling) : objectBindingPattern(kind, yieldHandling); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, - YieldHandling yieldHandling, - TokenKind tt) +GeneralParser<ParseHandler, CharT>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, + YieldHandling yieldHandling, + TokenKind tt) { uint32_t startYieldOffset = pc->lastYieldOffset; uint32_t startAwaitOffset = pc->lastAwaitOffset; Node res = destructuringDeclaration(kind, yieldHandling, tt); if (res) { if (pc->lastYieldOffset != startYieldOffset) { errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT); return null(); @@ -4680,19 +4727,20 @@ Parser<ParseHandler, CharT>::destructuri return null(); } } return res; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); +GeneralParser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling, + unsigned errorNumber) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC)); uint32_t openedPos = pos().begin; ParseContext::Statement stmt(pc, StatementKind::Block); ParseContext::Scope scope(this); if (!scope.init(pc)) return null(); Node list = statementList(yieldHandling); @@ -4703,36 +4751,36 @@ Parser<ParseHandler, CharT>::blockStatem reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED, openedPos)); return finishLexicalScope(scope, list); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::expressionAfterForInOrOf(ParseNodeKind forHeadKind, - YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::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 <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::declarationPattern(DeclarationKind declKind, TokenKind tt, - bool initialDeclaration, - YieldHandling yieldHandling, - ParseNodeKind* forHeadKind, - Node* forInOrOfExpression) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB) || - tokenStream.isCurrentTokenType(TOK_LC)); +GeneralParser<ParseHandler, CharT>::declarationPattern(DeclarationKind declKind, TokenKind tt, + bool initialDeclaration, + YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, + Node* forInOrOfExpression) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB) || + anyChars.isCurrentTokenType(TOK_LC)); Node pattern = destructuringDeclaration(declKind, yieldHandling, tt); if (!pattern) return null(); if (initialDeclaration && forHeadKind) { bool isForIn, isForOf; if (!matchInOrOf(&isForIn, &isForOf)) @@ -4768,24 +4816,24 @@ Parser<ParseHandler, CharT>::declaration handler.checkAndSetIsDirectRHSAnonFunction(init); return handler.newAssignment(PNK_ASSIGN, pattern, init); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::initializerInNameDeclaration(Node binding, - DeclarationKind declKind, - bool initialDeclaration, - YieldHandling yieldHandling, - ParseNodeKind* forHeadKind, - Node* forInOrOfExpression) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN)); +GeneralParser<ParseHandler, CharT>::initializerInNameDeclaration(Node binding, + DeclarationKind declKind, + bool initialDeclaration, + YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, + Node* forInOrOfExpression) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_ASSIGN)); uint32_t initializerOffset; if (!tokenStream.peekOffset(&initializerOffset, TokenStream::Operand)) return false; Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed, yieldHandling, TripledotProhibited); if (!initializer) @@ -4829,19 +4877,21 @@ Parser<ParseHandler, CharT>::initializer } } return handler.finishInitializerAssignment(binding, initializer); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::declarationName(DeclarationKind declKind, TokenKind tt, - bool initialDeclaration, YieldHandling yieldHandling, - ParseNodeKind* forHeadKind, Node* forInOrOfExpression) +GeneralParser<ParseHandler, CharT>::declarationName(DeclarationKind declKind, TokenKind tt, + bool initialDeclaration, + YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, + Node* forInOrOfExpression) { // Anything other than possible identifier is an error. if (!TokenKindIsPossibleIdentifier(tt)) { error(JSMSG_NO_VARIABLE_NAME); return null(); } RootedPropertyName name(context, bindingIdentifier(yieldHandling)); @@ -4909,20 +4959,20 @@ Parser<ParseHandler, CharT>::declaration if (!noteDeclaredName(name, declKind, namePos)) return null(); return binding; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling, - ParseNodeKind kind, - ParseNodeKind* forHeadKind /* = nullptr */, - Node* forInOrOfExpression /* = nullptr */) +GeneralParser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling, + ParseNodeKind kind, + ParseNodeKind* forHeadKind /* = nullptr */, + Node* forInOrOfExpression /* = nullptr */) { MOZ_ASSERT(kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST); DeclarationKind declKind; switch (kind) { case PNK_VAR: declKind = DeclarationKind::Var; break; @@ -4971,17 +5021,18 @@ Parser<ParseHandler, CharT>::declaration return null(); } while (moreDeclarations); return decl; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind) +GeneralParser<ParseHandler, CharT>::lexicalDeclaration(YieldHandling yieldHandling, + DeclarationKind kind) { MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let); /* * Parse body-level lets without a new block object. ES6 specs * that an execution environment's initial lexical environment * is the VariableEnvironment, i.e., body-level lets are in * the same environment record as vars. @@ -4994,19 +5045,19 @@ Parser<ParseHandler, CharT>::lexicalDecl Node decl = declarationList(yieldHandling, kind == DeclarationKind::Const ? PNK_CONST : PNK_LET); if (!decl || !matchOrInsertSemicolon()) return null(); return decl; } -template <> +template <typename CharT> bool -Parser<FullParseHandler, char16_t>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet) +Parser<FullParseHandler, CharT>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet) { if (tt == TOK_LC) { while (true) { // Handle the forms |import {} from 'a'| and // |import { ..., } from 'a'| (where ... is non empty), by // escaping the loop early if the next token is }. if (!tokenStream.getToken(&tt)) return false; @@ -5014,17 +5065,17 @@ Parser<FullParseHandler, char16_t>::name if (tt == TOK_RC) break; if (!TokenKindIsPossibleIdentifierName(tt)) { error(JSMSG_NO_IMPORT_NAME); return false; } - Rooted<PropertyName*> importName(context, tokenStream.currentName()); + Rooted<PropertyName*> importName(context, anyChars.currentName()); TokenPos importNamePos = pos(); bool matched; if (!tokenStream.matchToken(&matched, TOK_AS)) return null(); if (matched) { TokenKind afterAs; @@ -5111,30 +5162,21 @@ Parser<FullParseHandler, char16_t>::name return false; handler.addList(importSpecSet, importSpec); } return true; } -template<> -bool -Parser<SyntaxParseHandler, char16_t>::namedImportsOrNamespaceImport(TokenKind tt, - Node importSpecSet) -{ - MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); - return false; -} - -template<> +template<typename CharT> ParseNode* -Parser<FullParseHandler, char16_t>::importDeclaration() -{ - MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT); +Parser<FullParseHandler, CharT>::importDeclaration() +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_IMPORT)); if (!pc->atModuleLevel()) { error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL); return null(); } uint32_t begin = pos().begin; TokenKind tt; @@ -5215,156 +5257,211 @@ Parser<FullParseHandler, char16_t>::impo ParseNode* node = handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end)); if (!node || !pc->sc()->asModuleContext()->builder.processImport(node)) return null(); return node; } -template<> -SyntaxParseHandler::Node -Parser<SyntaxParseHandler, char16_t>::importDeclaration() +template<typename CharT> +inline SyntaxParseHandler::Node +Parser<SyntaxParseHandler, CharT>::importDeclaration() { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return SyntaxParseHandler::NodeFailure; } -template<> +template <class ParseHandler, typename CharT> +inline typename ParseHandler::Node +GeneralParser<ParseHandler, CharT>::importDeclaration() +{ + return asFinalParser()->importDeclaration(); +} + +template<typename CharT> bool -Parser<FullParseHandler, char16_t>::checkExportedName(JSAtom* exportName) +Parser<FullParseHandler, CharT>::checkExportedName(JSAtom* exportName) { if (!pc->sc()->asModuleContext()->builder.hasExportedName(exportName)) return true; JSAutoByteString str; if (!AtomToPrintableString(context, exportName, &str)) return false; error(JSMSG_DUPLICATE_EXPORT_NAME, str.ptr()); return false; } -template<> -bool -Parser<SyntaxParseHandler, char16_t>::checkExportedName(JSAtom* exportName) +template<typename CharT> +inline bool +Parser<SyntaxParseHandler, CharT>::checkExportedName(JSAtom* exportName) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } -template<> +template<class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::checkExportedName(JSAtom* exportName) +{ + return asFinalParser()->checkExportedName(exportName); +} + +template<typename CharT> bool -Parser<FullParseHandler, char16_t>::checkExportedNamesForDeclaration(ParseNode* node) +Parser<FullParseHandler, CharT>::checkExportedNamesForDeclaration(ParseNode* node) { MOZ_ASSERT(node->isArity(PN_LIST)); for (ParseNode* binding = node->pn_head; binding; binding = binding->pn_next) { if (binding->isKind(PNK_ASSIGN)) binding = binding->pn_left; MOZ_ASSERT(binding->isKind(PNK_NAME)); if (!checkExportedName(binding->pn_atom)) return false; } return true; } -template<> -bool -Parser<SyntaxParseHandler, char16_t>::checkExportedNamesForDeclaration(Node node) +template<typename CharT> +inline bool +Parser<SyntaxParseHandler, CharT>::checkExportedNamesForDeclaration(Node node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } -template<> -bool -Parser<FullParseHandler, char16_t>::checkExportedNameForClause(ParseNode* node) +template<class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::checkExportedNamesForDeclaration(Node node) +{ + return asFinalParser()->checkExportedNamesForDeclaration(node); +} + +template<typename CharT> +inline bool +Parser<FullParseHandler, CharT>::checkExportedNameForClause(ParseNode* node) { return checkExportedName(node->pn_atom); } -template<> -bool -Parser<SyntaxParseHandler, char16_t>::checkExportedNameForClause(Node node) +template<typename CharT> +inline bool +Parser<SyntaxParseHandler, CharT>::checkExportedNameForClause(Node node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } -template<> +template<class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::checkExportedNameForClause(Node node) +{ + return asFinalParser()->checkExportedNameForClause(node); +} + +template<typename CharT> bool -Parser<FullParseHandler, char16_t>::checkExportedNameForFunction(ParseNode* node) +Parser<FullParseHandler, CharT>::checkExportedNameForFunction(ParseNode* node) { return checkExportedName(node->pn_funbox->function()->explicitName()); } -template<> -bool -Parser<SyntaxParseHandler, char16_t>::checkExportedNameForFunction(Node node) +template<typename CharT> +inline bool +Parser<SyntaxParseHandler, CharT>::checkExportedNameForFunction(Node node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } -template<> +template<class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::checkExportedNameForFunction(Node node) +{ + return asFinalParser()->checkExportedNameForFunction(node); +} + +template<typename CharT> bool -Parser<FullParseHandler, char16_t>::checkExportedNameForClass(ParseNode* node) +Parser<FullParseHandler, CharT>::checkExportedNameForClass(ParseNode* node) { const ClassNode& cls = node->as<ClassNode>(); MOZ_ASSERT(cls.names()); return checkExportedName(cls.names()->innerBinding()->pn_atom); } -template<> +template<typename CharT> +inline bool +Parser<SyntaxParseHandler, CharT>::checkExportedNameForClass(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template<class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::checkExportedNameForClass(Node node) +{ + return asFinalParser()->checkExportedNameForClass(node); +} +template<typename CharT> bool -Parser<SyntaxParseHandler, char16_t>::checkExportedNameForClass(Node node) +Parser<FullParseHandler, CharT>::processExport(ParseNode* node) +{ + return pc->sc()->asModuleContext()->builder.processExport(node); +} + +template<typename CharT> +inline bool +Parser<SyntaxParseHandler, CharT>::processExport(Node node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } -template<> +template<class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::processExport(Node node) +{ + return asFinalParser()->processExport(node); +} + +template<typename CharT> bool -Parser<FullParseHandler, char16_t>::processExport(ParseNode* node) -{ - return pc->sc()->asModuleContext()->builder.processExport(node); -} - -template<> -bool -Parser<SyntaxParseHandler, char16_t>::processExport(Node node) +Parser<FullParseHandler, CharT>::processExportFrom(ParseNode* node) +{ + return pc->sc()->asModuleContext()->builder.processExportFrom(node); +} + +template<typename CharT> +inline bool +Parser<SyntaxParseHandler, CharT>::processExportFrom(Node node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } -template<> -bool -Parser<FullParseHandler, char16_t>::processExportFrom(ParseNode* node) -{ - return pc->sc()->asModuleContext()->builder.processExportFrom(node); -} - -template<> -bool -Parser<SyntaxParseHandler, char16_t>::processExportFrom(Node node) -{ - MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); - return false; +template<class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::processExportFrom(Node node) +{ + return asFinalParser()->processExportFrom(node); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList) +GeneralParser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList) { if (!abortIfSyntaxParser()) return null(); - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FROM)); + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FROM)); if (!abortIfSyntaxParser()) return null(); MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); Node moduleSpec = stringLiteral(); if (!moduleSpec) @@ -5380,22 +5477,22 @@ Parser<ParseHandler, CharT>::exportFrom( if (!processExportFrom(node)) return null(); return node; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportBatch(uint32_t begin) +GeneralParser<ParseHandler, CharT>::exportBatch(uint32_t begin) { if (!abortIfSyntaxParser()) return null(); - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_MUL)); + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_MUL)); Node kid = handler.newList(PNK_EXPORT_SPEC_LIST, pos()); if (!kid) return null(); // Handle the form |export *| by adding a special export batch // specifier to the list. Node exportSpec = handler.newExportBatchSpec(pos()); @@ -5404,49 +5501,56 @@ Parser<ParseHandler, CharT>::exportBatch handler.addList(kid, exportSpec); MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR); return exportFrom(begin, kid); } -template<> +template<typename CharT> bool -Parser<FullParseHandler, char16_t>::checkLocalExportNames(ParseNode* node) +Parser<FullParseHandler, CharT>::checkLocalExportNames(ParseNode* node) { // ES 2017 draft 15.2.3.1. for (ParseNode* next = node->pn_head; next; next = next->pn_next) { ParseNode* name = next->pn_left; MOZ_ASSERT(name->isKind(PNK_NAME)); RootedPropertyName ident(context, name->pn_atom->asPropertyName()); if (!checkLocalExportName(ident, name->pn_pos.begin)) return false; } return true; } -template<> +template<typename CharT> bool -Parser<SyntaxParseHandler, char16_t>::checkLocalExportNames(Node node) +Parser<SyntaxParseHandler, CharT>::checkLocalExportNames(Node node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } +template<class ParseHandler, typename CharT> +inline bool +GeneralParser<ParseHandler, CharT>::checkLocalExportNames(Node node) +{ + return asFinalParser()->checkLocalExportNames(node); +} + template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportClause(uint32_t begin) +GeneralParser<ParseHandler, CharT>::exportClause(uint32_t begin) { if (!abortIfSyntaxParser()) return null(); - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC)); Node kid = handler.newList(PNK_EXPORT_SPEC_LIST, pos()); if (!kid) return null(); TokenKind tt; while (true) { // Handle the forms |export {}| and |export { ..., }| (where ... is non @@ -5457,27 +5561,27 @@ Parser<ParseHandler, CharT>::exportClaus if (tt == TOK_RC) break; if (!TokenKindIsPossibleIdentifierName(tt)) { error(JSMSG_NO_BINDING_NAME); return null(); } - Node bindingName = newName(tokenStream.currentName()); + Node bindingName = newName(anyChars.currentName()); if (!bindingName) return null(); bool foundAs; if (!tokenStream.matchToken(&foundAs, TOK_AS)) return null(); if (foundAs) MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME); - Node exportName = newName(tokenStream.currentName()); + Node exportName = newName(anyChars.currentName()); if (!exportName) return null(); if (!checkExportedNameForClause(exportName)) return null(); Node exportSpec = handler.newExportSpec(bindingName, exportName); if (!exportSpec) @@ -5531,22 +5635,22 @@ Parser<ParseHandler, CharT>::exportClaus if (!processExport(node)) return null(); return node; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin) +GeneralParser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin) { if (!abortIfSyntaxParser()) return null(); - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_VAR)); + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_VAR)); Node kid = declarationList(YieldIsName, PNK_VAR); if (!kid) return null(); if (!matchOrInsertSemicolon()) return null(); if (!checkExportedNamesForDeclaration(kid)) return null(); @@ -5558,23 +5662,24 @@ Parser<ParseHandler, CharT>::exportVaria if (!processExport(node)) return null(); return node; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart, - FunctionAsyncKind asyncKind /* = SyncFunction */) +GeneralParser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin, + uint32_t toStringStart, + FunctionAsyncKind asyncKind /* = SyncFunction */) { if (!abortIfSyntaxParser()) return null(); - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION)); Node kid = functionStmt(toStringStart, YieldIsName, NameRequired, asyncKind); if (!kid) return null(); if (!checkExportedNameForFunction(kid)) return null(); @@ -5585,22 +5690,22 @@ Parser<ParseHandler, CharT>::exportFunct if (!processExport(node)) return null(); return node; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin) +GeneralParser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin) { if (!abortIfSyntaxParser()) return null(); - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS)); Node kid = classDefinition(YieldIsName, ClassStatement, NameRequired); if (!kid) return null(); if (!checkExportedNameForClass(kid)) return null(); @@ -5611,24 +5716,24 @@ Parser<ParseHandler, CharT>::exportClass if (!processExport(node)) return null(); return node; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind) +GeneralParser<ParseHandler, CharT>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind) { if (!abortIfSyntaxParser()) return null(); MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let); - MOZ_ASSERT_IF(kind == DeclarationKind::Const, tokenStream.isCurrentTokenType(TOK_CONST)); - MOZ_ASSERT_IF(kind == DeclarationKind::Let, tokenStream.isCurrentTokenType(TOK_LET)); + MOZ_ASSERT_IF(kind == DeclarationKind::Const, anyChars.isCurrentTokenType(TOK_CONST)); + MOZ_ASSERT_IF(kind == DeclarationKind::Let, anyChars.isCurrentTokenType(TOK_LET)); Node kid = lexicalDeclaration(YieldIsName, kind); if (!kid) return null(); if (!checkExportedNamesForDeclaration(kid)) return null(); Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); @@ -5638,24 +5743,24 @@ Parser<ParseHandler, CharT>::exportLexic if (!processExport(node)) return null(); return node; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin, - uint32_t toStringStart, - FunctionAsyncKind asyncKind /* = SyncFunction */) +GeneralParser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin, + uint32_t toStringStart, + FunctionAsyncKind asyncKind /* = SyncFunction */) { if (!abortIfSyntaxParser()) return null(); - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION)); Node kid = functionStmt(toStringStart, YieldIsName, AllowDefaultName, asyncKind); if (!kid) return null(); Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end)); if (!node) return null(); @@ -5663,22 +5768,22 @@ Parser<ParseHandler, CharT>::exportDefau if (!processExport(node)) return null(); return node; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin) +GeneralParser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin) { if (!abortIfSyntaxParser()) return null(); - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS)); Node kid = classDefinition(YieldIsName, ClassStatement, AllowDefaultName); if (!kid) return null(); Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end)); if (!node) return null(); @@ -5686,17 +5791,17 @@ Parser<ParseHandler, CharT>::exportDefau if (!processExport(node)) return null(); return node; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin) +GeneralParser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin) { if (!abortIfSyntaxParser()) return null(); HandlePropertyName name = context->names().default_; Node nameNode = newName(name); if (!nameNode) return null(); @@ -5719,22 +5824,22 @@ Parser<ParseHandler, CharT>::exportDefau if (!processExport(node)) return null(); return node; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportDefault(uint32_t begin) +GeneralParser<ParseHandler, CharT>::exportDefault(uint32_t begin) { if (!abortIfSyntaxParser()) return null(); - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_DEFAULT)); + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_DEFAULT)); TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); if (!checkExportedName(context->names().default_)) return null(); @@ -5749,37 +5854,37 @@ Parser<ParseHandler, CharT>::exportDefau if (nextSameLine == TOK_FUNCTION) { uint32_t toStringStart = pos().begin; tokenStream.consumeKnownToken(TOK_FUNCTION); return exportDefaultFunctionDeclaration(begin, toStringStart, FunctionAsyncKind::AsyncFunction); } - tokenStream.ungetToken(); + anyChars.ungetToken(); return exportDefaultAssignExpr(begin); } case TOK_CLASS: return exportDefaultClassDeclaration(begin); default: - tokenStream.ungetToken(); + anyChars.ungetToken(); return exportDefaultAssignExpr(begin); } } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::exportDeclaration() +GeneralParser<ParseHandler, CharT>::exportDeclaration() { if (!abortIfSyntaxParser()) return null(); - MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT); + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_EXPORT)); if (!pc->atModuleLevel()) { error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL); return null(); } uint32_t begin = pos().begin; @@ -5830,32 +5935,32 @@ Parser<ParseHandler, CharT>::exportDecla default: error(JSMSG_DECLARATION_AFTER_EXPORT); return null(); } } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::expressionStatement(YieldHandling yieldHandling, - InvokedPrediction invoked) -{ - tokenStream.ungetToken(); +GeneralParser<ParseHandler, CharT>::expressionStatement(YieldHandling yieldHandling, + InvokedPrediction invoked) +{ + anyChars.ungetToken(); Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited, /* possibleError = */ nullptr, invoked); if (!pnexpr) return null(); if (!matchOrInsertSemicolon()) return null(); return handler.newExprStatement(pnexpr, pos().end); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::consequentOrAlternative(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::consequentOrAlternative(YieldHandling yieldHandling) { TokenKind next; if (!tokenStream.peekToken(&next, TokenStream::Operand)) return null(); // Annex B.3.4 says that unbraced FunctionDeclarations under if/else in // non-strict code act as if they were braced: |if (x) function f() {}| // parses as |if (x) { function f() {} }|. @@ -5899,17 +6004,17 @@ Parser<ParseHandler, CharT>::consequentO return finishLexicalScope(scope, block); } return statement(yieldHandling); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::ifStatement(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::ifStatement(YieldHandling yieldHandling) { Vector<Node, 4> condList(context), thenList(context); Vector<uint32_t, 4> posList(context); Node elseBranch; ParseContext::Statement stmt(pc, StatementKind::If); while (true) { @@ -5958,17 +6063,17 @@ Parser<ParseHandler, CharT>::ifStatement return null(); } return elseBranch; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::doWhileStatement(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::doWhileStatement(YieldHandling yieldHandling) { uint32_t begin = pos().begin; ParseContext::Statement stmt(pc, StatementKind::DoLoop); Node body = statement(yieldHandling); if (!body) return null(); MUST_MATCH_TOKEN_MOD(TOK_WHILE, TokenStream::Operand, JSMSG_WHILE_AFTER_DO); Node cond = condition(InAllowed, yieldHandling); @@ -5984,55 +6089,54 @@ Parser<ParseHandler, CharT>::doWhileStat bool ignored; if (!tokenStream.matchToken(&ignored, TOK_SEMI, TokenStream::Operand)) return null(); return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end)); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::whileStatement(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::whileStatement(YieldHandling yieldHandling) { uint32_t begin = pos().begin; ParseContext::Statement stmt(pc, StatementKind::WhileLoop); Node cond = condition(InAllowed, yieldHandling); if (!cond) return null(); Node body = statement(yieldHandling); if (!body) return null(); return handler.newWhileStatement(begin, cond, body); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::matchInOrOf(bool* isForInp, bool* isForOfp) +GeneralParser<ParseHandler, CharT>::matchInOrOf(bool* isForInp, bool* isForOfp) { TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) return false; *isForInp = tt == TOK_IN; *isForOfp = tt == TOK_OF; if (!*isForInp && !*isForOfp) - tokenStream.ungetToken(); + anyChars.ungetToken(); MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp); return true; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling, - ParseNodeKind* forHeadKind, - Node* forInitialPart, - Maybe<ParseContext::Scope>& forLoopLexicalScope, - Node* forInOrOfExpression) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP)); +GeneralParser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, Node* forInitialPart, + Maybe<ParseContext::Scope>& forLoopLexicalScope, + Node* forInOrOfExpression) +{ + MOZ_ASSERT(anyChars.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) { @@ -6071,17 +6175,17 @@ Parser<ParseHandler, CharT>::forHeadStar tokenStream.consumeKnownToken(TOK_LET, TokenStream::Operand); TokenKind next; if (!tokenStream.peekToken(&next)) return false; parsingLexicalDeclaration = nextTokenContinuesLetDeclaration(next); if (!parsingLexicalDeclaration) { - tokenStream.ungetToken(); + anyChars.ungetToken(); letIsIdentifier = true; } } if (parsingLexicalDeclaration) { forLoopLexicalScope.emplace(this); if (!forLoopLexicalScope->init(pc)) return null(); @@ -6169,19 +6273,20 @@ Parser<ParseHandler, CharT>::forHeadStar // Finally, parse the iterated expression, making the for-loop's closing // ')' the next token. *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling); return *forInOrOfExpression != null(); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); +GeneralParser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FOR)); + uint32_t begin = pos().begin; ParseContext::Statement stmt(pc, StatementKind::ForLoop); IteratorKind iterKind = IteratorKind::Sync; unsigned iflags = 0; if (pc->isAsync()) { @@ -6322,19 +6427,19 @@ Parser<ParseHandler, CharT>::forStatemen if (forLoopLexicalScope) return finishLexicalScope(*forLoopLexicalScope, forLoop); return forLoop; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH)); +GeneralParser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_SWITCH)); uint32_t begin = pos().begin; MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited); if (!discriminant) return null(); @@ -6429,19 +6534,19 @@ Parser<ParseHandler, CharT>::switchState handler.setEndPosition(caseList, pos().end); return handler.newSwitchStatement(begin, discriminant, caseList); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE)); +GeneralParser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CONTINUE)); uint32_t begin = pos().begin; RootedPropertyName label(context); if (!matchLabel(yieldHandling, &label)) return null(); // Labeled 'continue' statements target the nearest labeled loop // statements with the same label. Unlabeled 'continue' statements target @@ -6487,19 +6592,19 @@ Parser<ParseHandler, CharT>::continueSta if (!matchOrInsertSemicolon()) return null(); return handler.newContinueStatement(label, TokenPos(begin, pos().end)); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK)); +GeneralParser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_BREAK)); uint32_t begin = pos().begin; RootedPropertyName label(context); if (!matchLabel(yieldHandling, &label)) return null(); // Labeled 'break' statements target the nearest labeled statements (could // be any kind) with the same label. Unlabeled 'break' statements target @@ -6527,19 +6632,19 @@ Parser<ParseHandler, CharT>::breakStatem if (!matchOrInsertSemicolon()) return null(); return handler.newBreakStatement(label, TokenPos(begin, pos().end)); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN)); +GeneralParser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_RETURN)); uint32_t begin = pos().begin; MOZ_ASSERT(pc->isFunctionBox()); pc->functionBox()->usesReturn = true; // Parse an optional operand. // // This is ugly, but we don't want to require a semicolon. @@ -6564,19 +6669,19 @@ Parser<ParseHandler, CharT>::returnState if (!matchOrInsertSemicolon()) return null(); return handler.newReturnStatement(exprNode, TokenPos(begin, pos().end)); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::yieldExpression(InHandling inHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD)); +GeneralParser<ParseHandler, CharT>::yieldExpression(InHandling inHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_YIELD)); uint32_t begin = pos().begin; MOZ_ASSERT(pc->isGenerator()); MOZ_ASSERT(pc->isFunctionBox()); pc->lastYieldOffset = begin; Node exprNode; @@ -6597,17 +6702,17 @@ Parser<ParseHandler, CharT>::yieldExpres case TOK_RC: case TOK_RB: case TOK_RP: case TOK_COLON: case TOK_COMMA: case TOK_IN: // No value. exprNode = null(); - tokenStream.addModifierException(TokenStream::NoneIsOperand); + anyChars.addModifierException(TokenStream::NoneIsOperand); break; case TOK_MUL: kind = PNK_YIELD_STAR; tokenStream.consumeKnownToken(TOK_MUL, TokenStream::Operand); MOZ_FALLTHROUGH; default: exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited); if (!exprNode) @@ -6615,19 +6720,19 @@ Parser<ParseHandler, CharT>::yieldExpres } if (kind == PNK_YIELD_STAR) return handler.newYieldStarExpression(begin, exprNode); return handler.newYieldExpression(begin, exprNode); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH)); +GeneralParser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_WITH)); uint32_t begin = pos().begin; // Usually we want the constructs forbidden in strict mode code to be a // subset of those that ContextOptions::extraWarnings() warns about, and we // use strictModeError directly. But while 'with' is forbidden in strict // mode code, it doesn't even merit a warning in non-strict code. See // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1. if (pc->sc()->strict()) { @@ -6653,17 +6758,17 @@ Parser<ParseHandler, CharT>::withStateme pc->sc()->setBindingsAccessedDynamically(); return handler.newWithStatement(begin, objectExpr, innerBlock); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::labeledItem(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::labeledItem(YieldHandling yieldHandling) { TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); if (tt == TOK_FUNCTION) { TokenKind next; if (!tokenStream.peekToken(&next)) @@ -6682,23 +6787,23 @@ Parser<ParseHandler, CharT>::labeledItem if (pc->sc()->strict()) { error(JSMSG_FUNCTION_LABEL); return null(); } return functionStmt(pos().begin, yieldHandling, NameRequired); } - tokenStream.ungetToken(); + anyChars.ungetToken(); return statement(yieldHandling); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::labeledStatement(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::labeledStatement(YieldHandling yieldHandling) { RootedPropertyName label(context, labelIdentifier(yieldHandling)); if (!label) return null(); auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) { return stmt->label() == label; }; @@ -6718,19 +6823,19 @@ Parser<ParseHandler, CharT>::labeledStat if (!pn) return null(); return handler.newLabeledStatement(label, pn, begin); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW)); +GeneralParser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_THROW)); uint32_t begin = pos().begin; /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */ TokenKind tt = TOK_EOF; if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand)) return null(); if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) { error(JSMSG_MISSING_EXPR_AFTER_THROW); @@ -6748,19 +6853,19 @@ Parser<ParseHandler, CharT>::throwStatem if (!matchOrInsertSemicolon()) return null(); return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end)); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY)); +GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_TRY)); uint32_t begin = pos().begin; /* * try nodes are ternary. * kid1 is the try statement * kid2 is the catch node list or null * kid3 is the finally statement * @@ -6894,30 +6999,30 @@ Parser<ParseHandler, CharT>::tryStatemen finallyBlock = finishLexicalScope(scope, finallyBlock); if (!finallyBlock) return null(); MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY, JSMSG_CURLY_OPENED, openedPos)); } else { - tokenStream.ungetToken(); + anyChars.ungetToken(); } if (!catchScope && !finallyBlock) { error(JSMSG_CATCH_OR_FINALLY); return null(); } return handler.newTryStatement(begin, innerBlock, catchScope, finallyBlock); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::catchBlockStatement(YieldHandling yieldHandling, - ParseContext::Scope& catchParamScope) +GeneralParser<ParseHandler, CharT>::catchBlockStatement(YieldHandling yieldHandling, + ParseContext::Scope& catchParamScope) { uint32_t openedPos = pos().begin; ParseContext::Statement stmt(pc, StatementKind::Block); // ES 13.15.7 CatchClauseEvaluation // // Step 8 means that the body of a catch block always has an additional @@ -6942,17 +7047,17 @@ Parser<ParseHandler, CharT>::catchBlockS // The catch parameter names are not bound in the body scope, so remove // them before generating bindings. scope.removeCatchParameters(pc, catchParamScope); return finishLexicalScope(scope, list); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::debuggerStatement() +GeneralParser<ParseHandler, CharT>::debuggerStatement() { TokenPos p; p.begin = pos().begin; if (!matchOrInsertSemicolon()) return null(); p.end = pos().end; pc->sc()->setBindingsAccessedDynamically(); @@ -6981,46 +7086,46 @@ ToAccessorType(PropertyType propType) return AccessorType::None; default: MOZ_CRASH("unexpected property type"); } } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling, - ClassContext classContext, - DefaultHandling defaultHandling) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); +GeneralParser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling, + ClassContext classContext, + DefaultHandling defaultHandling) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS)); uint32_t classStartOffset = pos().begin; bool savedStrictness = setLocalStrictMode(true); TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); RootedPropertyName name(context); if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) return null(); } else if (classContext == ClassStatement) { if (defaultHandling == AllowDefaultName) { name = context->names().default_; - tokenStream.ungetToken(); + anyChars.ungetToken(); } else { // Class statements must have a bound name error(JSMSG_UNNAMED_CLASS_STMT); return null(); } } else { // Make sure to put it back, whatever it was - tokenStream.ungetToken(); + anyChars.ungetToken(); } // Push a ParseContext::ClassStatement to keep track of the constructor // funbox. ParseContext::ClassStatement classStmt(pc); RootedAtom propAtom(context); @@ -7078,19 +7183,19 @@ Parser<ParseHandler, CharT>::classDefini tokenStream.consumeKnownToken(tt); error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); return null(); } if (tt != TOK_LP) isStatic = true; else - tokenStream.ungetToken(); + anyChars.ungetToken(); } else { - tokenStream.ungetToken(); + anyChars.ungetToken(); } uint32_t nameOffset; if (!tokenStream.peekOffset(&nameOffset)) return null(); PropertyType propType; Node propName = propertyName(yieldHandling, declKind, classMethods, &propType, &propAtom); @@ -7126,28 +7231,28 @@ Parser<ParseHandler, CharT>::classDefini errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); return null(); } RootedAtom funName(context); switch (propType) { case PropertyType::GetterNoExpressionClosure: case PropertyType::SetterNoExpressionClosure: - if (!tokenStream.isCurrentTokenType(TOK_RB)) { + if (!anyChars.isCurrentTokenType(TOK_RB)) { funName = prefixAccessorName(propType, propAtom); if (!funName) return null(); } break; case PropertyType::Constructor: case PropertyType::DerivedConstructor: funName = name; break; default: - if (!tokenStream.isCurrentTokenType(TOK_RB)) + if (!anyChars.isCurrentTokenType(TOK_RB)) funName = propAtom; } // Calling toString on constructors need to return the source text for // the entire class. The end offset is unknown at this point in // parsing and will be amended when class parsing finishes below. Node fn = methodDefinition(isConstructor ? classStartOffset : nameOffset, propType, funName); @@ -7210,19 +7315,19 @@ Parser<ParseHandler, CharT>::classDefini MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness)); return handler.newClass(nameNode, classHeritage, methodsOrBlock, TokenPos(classStartOffset, classEndOffset)); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::nextTokenContinuesLetDeclaration(TokenKind next) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET)); +GeneralParser<ParseHandler, CharT>::nextTokenContinuesLetDeclaration(TokenKind next) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LET)); #ifdef DEBUG TokenKind verify; MOZ_ALWAYS_TRUE(tokenStream.peekToken(&verify)); MOZ_ASSERT(next == verify); #endif // Destructuring continues a let declaration. @@ -7241,29 +7346,29 @@ Parser<ParseHandler, CharT>::nextTokenCo // parse tree with ASI applied. No backsies! // Otherwise a let declaration must have a name. return TokenKindIsPossibleIdentifier(next); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::variableStatement(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::variableStatement(YieldHandling yieldHandling) { Node vars = declarationList(yieldHandling, PNK_VAR); if (!vars) return null(); if (!matchOrInsertSemicolon()) return null(); return vars; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::statement(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::statement(YieldHandling yieldHandling) { MOZ_ASSERT(checkOptionsCalled); if (!CheckRecursionLimit(context)) return null(); TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) @@ -7282,17 +7387,17 @@ Parser<ParseHandler, CharT>::statement(Y case TOK_SEMI: return handler.newEmptyStatement(pos()); // ExpressionStatement[?Yield]. case TOK_YIELD: { // Don't use a ternary operator here due to obscure linker issues // around using static consts in the arms of a ternary. - TokenStream::Modifier modifier; + Modifier modifier; if (yieldExpressionsSupported()) modifier = TokenStream::Operand; else modifier = TokenStream::None; TokenKind next; if (!tokenStream.peekToken(&next, modifier)) return null(); @@ -7470,18 +7575,18 @@ Parser<ParseHandler, CharT>::statement(Y return null(); // NOTE: default case handled in the ExpressionStatement section. } } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::statementListItem(YieldHandling yieldHandling, - bool canHaveDirectives /* = false */) +GeneralParser<ParseHandler, CharT>::statementListItem(YieldHandling yieldHandling, + bool canHaveDirectives /* = false */) { MOZ_ASSERT(checkOptionsCalled); if (!CheckRecursionLimit(context)) return null(); TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) @@ -7500,28 +7605,28 @@ Parser<ParseHandler, CharT>::statementLi case TOK_SEMI: return handler.newEmptyStatement(pos()); // ExpressionStatement[?Yield]. // // These should probably be handled by a single ExpressionStatement // function in a default, not split up this way. case TOK_STRING: - if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) { + if (!canHaveDirectives && anyChars.currentToken().atom() == context->names().useAsm) { if (!abortIfSyntaxParser()) return null(); if (!warning(JSMSG_USE_ASM_DIRECTIVE_FAIL)) return null(); } return expressionStatement(yieldHandling); case TOK_YIELD: { // Don't use a ternary operator here due to obscure linker issues // around using static consts in the arms of a ternary. - TokenStream::Modifier modifier; + Modifier modifier; if (yieldExpressionsSupported()) modifier = TokenStream::Operand; else modifier = TokenStream::None; TokenKind next; if (!tokenStream.peekToken(&next, modifier)) return null(); @@ -7663,20 +7768,20 @@ Parser<ParseHandler, CharT>::statementLi return null(); // NOTE: default case handled in the ExpressionStatement section. } } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::expr(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, - PossibleError* possibleError /* = nullptr */, - InvokedPrediction invoked /* = PredictUninvoked */) +GeneralParser<ParseHandler, CharT>::expr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError /* = nullptr */, + InvokedPrediction invoked /* = PredictUninvoked */) { Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling, possibleError, invoked); if (!pn) return null(); bool matched; if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand)) @@ -7703,17 +7808,17 @@ Parser<ParseHandler, CharT>::expr(InHand if (!tokenStream.peekToken(&tt)) return null(); if (tt != TOK_ARROW) { error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP)); return null(); } - tokenStream.ungetToken(); // put back right paren + anyChars.ungetToken(); // put back right paren break; } } // 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. @@ -7788,21 +7893,21 @@ Precedence(ParseNodeKind pnk) { MOZ_ASSERT(pnk >= PNK_BINOP_FIRST); MOZ_ASSERT(pnk <= PNK_BINOP_LAST); return PrecedenceTable[pnk - PNK_BINOP_FIRST]; } template <class ParseHandler, typename CharT> MOZ_ALWAYS_INLINE typename ParseHandler::Node -Parser<ParseHandler, CharT>::orExpr(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, - ExpressionClosure expressionClosureHandling, - PossibleError* possibleError, - InvokedPrediction invoked /* = PredictUninvoked */) +GeneralParser<ParseHandler, CharT>::orExpr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + ExpressionClosure expressionClosureHandling, + PossibleError* possibleError, + InvokedPrediction invoked /* = PredictUninvoked */) { // Shift-reduce parser for the binary operator part of the JS expression // syntax. // Conceptually there's just one stack, a stack of pairs (lhs, op). // It's implemented using two separate arrays, though. Node nodeStack[PRECEDENCE_CLASSES]; ParseNodeKind kindStack[PRECEDENCE_CLASSES]; @@ -7867,30 +7972,30 @@ Parser<ParseHandler, CharT>::orExpr(InHa kindStack[depth] = pnk; depth++; MOZ_ASSERT(depth <= PRECEDENCE_CLASSES); } // When the next token is no longer a binary operator, it's potentially the // start of an expression. Add a modifier exception so that the next token // modifier can be Operand. - tokenStream.ungetToken(); - tokenStream.addModifierException(TokenStream::OperandIsNone); + anyChars.ungetToken(); + anyChars.addModifierException(TokenStream::OperandIsNone); MOZ_ASSERT(depth == 0); return pn; } template <class ParseHandler, typename CharT> MOZ_ALWAYS_INLINE typename ParseHandler::Node -Parser<ParseHandler, CharT>::condExpr(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, - ExpressionClosure expressionClosureHandling, - PossibleError* possibleError, - InvokedPrediction invoked /* = PredictUninvoked */) +GeneralParser<ParseHandler, CharT>::condExpr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + ExpressionClosure expressionClosureHandling, + PossibleError* possibleError, + InvokedPrediction invoked /* = PredictUninvoked */) { Node condition = orExpr(inHandling, yieldHandling, tripledotHandling, expressionClosureHandling, possibleError, invoked); if (!condition) return null(); if (handler.isExpressionClosure(condition)) return condition; @@ -7911,20 +8016,20 @@ Parser<ParseHandler, CharT>::condExpr(In if (!elseExpr) return null(); return handler.newConditional(condition, thenExpr, elseExpr); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yieldHandling, - TripledotHandling tripledotHandling, - PossibleError* possibleError /* = nullptr */, - InvokedPrediction invoked /* = PredictUninvoked */) +GeneralParser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError /* = nullptr */, + InvokedPrediction invoked /* = PredictUninvoked */) { if (!CheckRecursionLimit(context)) return null(); // 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 // that obviously isn't part of an expression: , ; : ) ] } // @@ -7957,17 +8062,17 @@ Parser<ParseHandler, CharT>::assignExpr( return identifierReference(name); } } if (firstToken == TOK_NUMBER) { if (!tokenStream.nextTokenEndsExpr(&endsExpr)) return null(); if (endsExpr) - return newNumber(tokenStream.currentToken()); + return newNumber(anyChars.currentToken()); } if (firstToken == TOK_STRING) { if (!tokenStream.nextTokenEndsExpr(&endsExpr)) return null(); if (endsExpr) return stringLiteral(); } @@ -7980,21 +8085,21 @@ Parser<ParseHandler, CharT>::assignExpr( TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); if (TokenKindIsPossibleIdentifier(nextSameLine)) maybeAsyncArrow = true; } - tokenStream.ungetToken(); + anyChars.ungetToken(); // Save the tokenizer state in case we find an arrow function and have to // rewind. - TokenStream::Position start(keepAtoms); + typename TokenStream::Position start(keepAtoms); tokenStream.tell(&start); PossibleError possibleErrorInner(*this); Node lhs; TokenKind tokenAfterLHS; bool isArrow; if (maybeAsyncArrow) { tokenStream.consumeKnownToken(TOK_ASYNC, TokenStream::Operand); @@ -8035,34 +8140,34 @@ Parser<ParseHandler, CharT>::assignExpr( if (isArrow) { tokenStream.seek(start); TokenKind next; if (!tokenStream.getToken(&next, TokenStream::Operand)) return null(); uint32_t toStringStart = pos().begin; - tokenStream.ungetToken(); + anyChars.ungetToken(); FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction; if (next == TOK_ASYNC) { tokenStream.consumeKnownToken(next, TokenStream::Operand); TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); // The AsyncArrowFunction production are // async [no LineTerminator here] AsyncArrowBindingIdentifier ... // async [no LineTerminator here] ArrowFormalParameters ... if (TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TOK_LP) asyncKind = FunctionAsyncKind::AsyncFunction; else - tokenStream.ungetToken(); + anyChars.ungetToken(); } Node pn = handler.newArrowFunction(pos()); if (!pn) return null(); return functionDefinition(pn, toStringStart, inHandling, yieldHandling, nullptr, Arrow, GeneratorKind::NotGenerator, asyncKind); @@ -8082,25 +8187,25 @@ Parser<ParseHandler, CharT>::assignExpr( case TOK_RSHASSIGN: kind = PNK_RSHASSIGN; break; case TOK_URSHASSIGN: kind = PNK_URSHASSIGN; break; case TOK_MULASSIGN: kind = PNK_MULASSIGN; break; case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; break; case TOK_MODASSIGN: kind = PNK_MODASSIGN; break; case TOK_POWASSIGN: kind = PNK_POWASSIGN; break; default: - MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment()); + MOZ_ASSERT(!anyChars.isCurrentTokenAssignment()); if (!possibleError) { if (!possibleErrorInner.checkForExpressionError()) return null(); } else { possibleErrorInner.transferErrorsTo(possibleError); } - tokenStream.ungetToken(); + anyChars.ungetToken(); return lhs; } // Verify the left-hand side expression doesn't have a forbidden form. if (handler.isUnparenthesizedDestructuringPattern(lhs)) { if (kind != PNK_ASSIGN) { error(JSMSG_BAD_DESTRUCT_ASS); return null(); @@ -8139,18 +8244,18 @@ Parser<ParseHandler, CharT>::assignExpr( if (kind == PNK_ASSIGN) handler.checkAndSetIsDirectRHSAnonFunction(rhs); return handler.newAssignment(kind, lhs, rhs); } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::isValidSimpleAssignmentTarget(Node node, - FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */) +GeneralParser<ParseHandler, CharT>::isValidSimpleAssignmentTarget(Node node, + FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */) { // Note that this method implements *only* a boolean test. Reporting an // error for the various syntaxes that fail this, and warning for the // various syntaxes that "pass" this but should not, occurs elsewhere. if (handler.isName(node)) { if (!pc->sc()->strict()) return true; @@ -8166,30 +8271,30 @@ Parser<ParseHandler, CharT>::isValidSimp return true; } return false; } template <class ParseHandler, typename CharT> const char* -Parser<ParseHandler, CharT>::nameIsArgumentsOrEval(Node node) +GeneralParser<ParseHandler, CharT>::nameIsArgumentsOrEval(Node node) { MOZ_ASSERT(handler.isName(node), "must only call this function on known names"); if (handler.isEvalName(node, context)) return js_eval_str; if (handler.isArgumentsName(node, context)) return js_arguments_str; return nullptr; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::checkIncDecOperand(Node operand, uint32_t operandOffset) +GeneralParser<ParseHandler, CharT>::checkIncDecOperand(Node operand, uint32_t operandOffset) { if (handler.isName(operand)) { if (const char* chars = nameIsArgumentsOrEval(operand)) { if (!strictModeErrorAt(operandOffset, JSMSG_BAD_STRICT_ASSIGN, chars)) return false; } } else if (handler.isPropertyAccess(operand)) { // Permitted: no additional testing/fixup needed. @@ -8207,32 +8312,32 @@ Parser<ParseHandler, CharT>::checkIncDec MOZ_ASSERT(isValidSimpleAssignmentTarget(operand, PermitAssignmentToFunctionCalls), "inconsistent increment/decrement operand validation"); return true; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, - uint32_t begin) +GeneralParser<ParseHandler, CharT>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, + uint32_t begin) { Node kid = unaryExpr(yieldHandling, TripledotProhibited, ExpressionClosure::Forbidden); if (!kid) return null(); return handler.newUnary(kind, begin, kid); } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::unaryExpr(YieldHandling yieldHandling, - TripledotHandling tripledotHandling, - ExpressionClosure expressionClosureHandling, - PossibleError* possibleError /* = nullptr */, - InvokedPrediction invoked /* = PredictUninvoked */) +GeneralParser<ParseHandler, CharT>::unaryExpr(YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + ExpressionClosure expressionClosureHandling, + PossibleError* possibleError /* = nullptr */, + InvokedPrediction invoked /* = PredictUninvoked */) { if (!CheckRecursionLimit(context)) return null(); TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); uint32_t begin = pos().begin; @@ -8341,17 +8446,17 @@ Parser<ParseHandler, CharT>::unaryExpr(Y begin, expr); } } } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling) +GeneralParser<ParseHandler, CharT>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling) { uint32_t startYieldOffset = pc->lastYieldOffset; uint32_t startAwaitOffset = pc->lastAwaitOffset; Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (res) { if (pc->lastYieldOffset != startYieldOffset) { errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT); return null(); @@ -8361,19 +8466,19 @@ Parser<ParseHandler, CharT>::assignExprW return null(); } } return res; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode, - bool* isSpread, - PossibleError* possibleError /* = nullptr */) +GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode, + bool* isSpread, + PossibleError* possibleError /* = nullptr */) { bool matched; if (!tokenStream.matchToken(&matched, TOK_RP, TokenStream::Operand)) return false; if (matched) { handler.setEndPosition(listNode, pos().end); return true; } @@ -8416,34 +8521,34 @@ Parser<ParseHandler, CharT>::argumentLis MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS); handler.setEndPosition(listNode, pos().end); return true; } template <class ParseHandler, typename CharT> bool -Parser<ParseHandler, CharT>::checkAndMarkSuperScope() +GeneralParser<ParseHandler, CharT>::checkAndMarkSuperScope() { if (!pc->sc()->allowSuperProperty()) return false; pc->setSuperScopeNeedsHomeObject(); return true; } template <class ParseHandler, typename CharT> typename ParseHandler::Node -Parser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling, - TripledotHandling tripledotHandling, - ExpressionClosure expressionClosureHandling, - TokenKind tt, bool allowCallSyntax /* = true */, - PossibleError* possibleError /* = nullptr */, - InvokedPrediction invoked /* = PredictUninvoked */) -{ - MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); +GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + ExpressionClosure expressionClosureHandling, + TokenKind tt, bool allowCallSyntax /* = true */, + PossibleError* possibleError /* = nullptr */, + InvokedPrediction invoked /* = PredictUninvoked */) +{ + MOZ_ASSERT(anyChars.isCurrentTokenType(tt)); Node lhs; if (!CheckRecursionLimit(context)) return null(); /* Check for new expression first. */ if (tt == TOK_NEW) { @@ -8451,17 +8556,17 @@ Parser<ParseHandler, CharT>::memberExpr( // Make sure this wasn't a |new.target| in disguise. Node newTarget; if (!tryNewTarget(newTarget)) return null(); if (newTarget) { lhs = newTarget; } else { // Gotten by tryNewTarget - tt = tokenStream.currentToken().type; + tt = anyChars.currentToken().type; Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, ExpressionClosure::Forbidden, tt, /* allowCallSyntax = */ false, /* possibleError = */ nullptr, PredictInvoked); if (!ctorExpr) return null(); lhs = handler.newNewExpression(newBegin, ctorExpr); @@ -8491,30 +8596,30 @@ Parser<ParseHandler, CharT>::memberExpr( possibleError, invoked); if (!lhs) return null(); if (handler.isExpressionClosure(lhs)) return lhs; } - MOZ_ASSERT_IF(handler.isSuperBase(lhs), tokenStream.isCurrentTokenType(TOK_SUPER)); + MOZ_ASSERT_IF(handler.isSuperBase(lhs), anyChars.isCurrentTokenType(TOK_SUPER)); while (true) { if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_EOF) break; Node nextMember; if (tt == TOK_DOT) { if (!tokenStream.getToken(&tt)) return null(); if (TokenKindIsPossibleIdentifierName(tt)) { - PropertyName* field = tokenStream.currentName(); + PropertyName* field = anyChars.currentName(); if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) { error(JSMSG_BAD_SUPERPROP, "property"); return null(); } nextMember = handler.newPropertyAccess(lhs, field, pos().end); if (!nextMember) return null(); } else { @@ -8643,17 +8748,17 @@ Parser<ParseHandler, CharT>::memberExpr( } } else { if (!taggedTemplate(yieldHandling, nextMember, tt)) return null(); } handler.setOp(nextMember, op); }