From ac0651448ae463d89a071ae8812e856d9a23d1cd Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Tue, 29 May 2018 21:28:41 +0800 Subject: [PATCH] import changes from tenfourfox: https://github.com/classilla/tenfourfox/compare/966d8cab820858bcda2fa8e945ed14b66d9e83f9...e7767cdaa56ee060b14702ed64ae594958fcb1dd - issue #385: unconditionally disable JS source compression (6a91f9666) - #359: security.pki.sha1_enforcement_level -> 1 (966d8cab8) - for #375: M1349940 M1355595 M1334635 M1353708 M1354782 M1354781 M1349742 M1345315 M1356655 M1356663 M1357638 M1358809 M1307134 M1331342 M1220466 (964aa7e7c) - #378: ensure remaining users of faulty LZ4 module never execute (922a32738) - #387: pulled nsGenConList.* with relevant deps up to mozilla-central (b1831fc75) - prerequisite for #375: M1347759 (8e58b5c8f) - #375: M1348278 (8d665811d) - #368: pref on Brotli (7da09aa4e) - #375: M1330912 (sync patch only) (bea84e507) - #375: M1342719 M1342720 (f4015e0c1) - Bug 1071646 - Light refactoring of lexical binding helpers in Parser. (r=jorendorff) (167ad8564) - Bug 1071646 - Make functions block-scoped in JS and implement Annex B semantics for compatibility. (r=jorendorff) (25844de1b) - Bug 1071646 - Support labelled function declarations in sloppy mode per Annex B.3.2. (r=jorendorff) (37bf861a9) - Bug 1071646 - Introduce JSOP_BINDVAR to support Annex B.3.3.3. (r=jorendorff) (884bd2ab5) - Bug 1071646 - Cast ParseNode to Definition using as. (r=jorendorff) (feba46e3f) - Bug 1071646 - Support JSOP_BINDVAR in Baseline. (r=jandem) (92b6a3b48) - #372: merge changeset 299512 (c28302281) - Bug 1071646 - Support JSOP_BINDVAR in Ion. (r=jandem) (d05f77998) - Bug 1231758 - Fix bogus assertion in BCE for Annex B function assignment. (r=jorendorff) (a11dcf0cc) - Bug 1234717 - Fix upvar TDZ for block-scoped functions. (r=jorendorff) (2ec542b5a) - Bug 1071646 - Forgot to commit tests on backout and relanding cycle. (r=me) (069a26bf9) - #372: merge changeset 303861 (745f7e7a2) - Bug 1235590 - Allow redeclaring block-scoped functions and warn about deprecation for now. (r=jorendorff) (8a51cc1f9) - Bug 1243793 - Fix handling of labels when emitting hoisted function definitions. (r=jorendorff) (c0ffd70f9) - Bug 1236875 - Fix BytecodeEmitter::atBodyLevel() for modules r=efaust (5b2c22f62) - Bug 1304641 - TraceLogger: Also throw compartment mismatch error when running in the jits, r=bbouvier (717134569) - Bug 1198833 - Variable redeclaration should be a syntax error r=shu (ac6710b0f) - #393, Bug 1135377 - Part 1: Implement RegExp unicode flag. r=till, f=anba (d00063089) - #393, Bug 1135377 - Part 2: Parse RegExp unicode character in non-CharacterClass. r=till, f=anba (c85a176bd) - #393, Bug 1135377 - Part 3: Parse RegExp unicode character in CharacterClass. r=till, f=anba (68f44ec41) - #393, Bug 1135377 - Part 4: Support everything Atom in RegExp with unicode flag. r=till, f=anba (4e924a688) - #393, Bug 1135377 - Part 5: Support CharacterClassEscape in RegExp with unicode flag. r=till, f=anba (45a4712b0) - #393, Bug 1135377 - Part 6: Support ignoreCase for BMP in RegExp with unicode flag. r=till, f=anba (cf744e9d3) - #393, Bug 1135377 - Part 7: Support ignoreCase for non-BMP in RegExp with unicode flag. r=till, f=anba (f31a9f9e8) - #393, Bug 1135377 - Part 8: Disallow extended pattern in RegExp with unicode flag. r=till, f=anba (c05db4075) - #393, Bug 1135377 - Part 9: Use RegExp unicode flag in String.prototype.{match,replace,split}. r=till, f=anba (8636a96b7) - #393, Bug 1135377 - Part 10: Decrement index when it points trail surrogate that has corresponding lead surrogate. r=till, f=anba (522d06ab3) - #393, Bug 1135377 - Part 11: Support back reference with unicode flag. r=till, f=anba (122e41a28) - #393, Bug 1279467 - Fix null handling in RegExp character class with unicode flag. r=till (741f170e6) - #393: bustage fix (3e39760c8) - #393, Bug 1281739 - Do not match K, S, k, s, KELVIN SIGN, LATIN SMALL LETTER LONG S with \W in unicode ignoreCase RegExp. r=till (3c5d4bab8) - #375: M1233101 (a6a0b895f) - #375, #391: branch hint malloc and recursion checks, clean up MAsm type barrier spooge (a008a1978) - closes #380: fix toSource() tests (f9de6783b) - #394: fix and enable ES7 exponentiation operator (6dc5f54dc) - #396: M1342009, plus refactor to PLDHashTable a la M1352888 (06fcc091f) - #375: M1347634 1352235 (aa6b13c59) - #375: M1351303 (f7badd24e) - #375: M1287277 M1259677; temporarily disable M1351303 due to crashes (75d95f15b) - #375: M1363423 M1273828 M1364661 (e7767cdaa) --- browser/app/profile/firefox.js | 6 +- browser/base/content/tabbrowser.xml | 138 +- browser/components/uitour/UITour.jsm | 7 +- dom/base/Element.cpp | 30 +- dom/base/Element.h | 23 + dom/base/FragmentOrElement.cpp | 18 - dom/base/FragmentOrElement.h | 4 +- dom/base/nsAttrValue.cpp | 10 +- dom/base/nsContentUtils.cpp | 12 +- dom/base/nsGenericDOMDataNode.cpp | 11 - dom/base/nsGenericDOMDataNode.h | 3 - dom/base/nsGlobalWindow.cpp | 12 +- dom/base/nsIContent.h | 17 - dom/base/nsNodeInfoManager.cpp | 36 +- dom/base/nsNodeInfoManager.h | 3 + dom/canvas/CanvasRenderingContext2D.cpp | 5 +- dom/canvas/CanvasRenderingContext2D.h | 3 + dom/events/IMEStateManager.cpp | 8 +- dom/events/IMEStateManager.h | 1 + editor/libeditor/DeleteRangeTxn.cpp | 26 +- editor/libeditor/DeleteRangeTxn.h | 4 +- editor/libeditor/nsEditor.cpp | 10 +- editor/libeditor/nsEditor.h | 1 + js/public/Utility.h | 2 +- js/src/builtin/ReflectParse.cpp | 16 +- js/src/builtin/RegExp.cpp | 76 +- js/src/builtin/RegExp.js | 5 +- js/src/frontend/BytecodeEmitter.cpp | 209 +- js/src/frontend/BytecodeEmitter.h | 14 +- js/src/frontend/FoldConstants.cpp | 16 + js/src/frontend/FullParseHandler.h | 75 +- js/src/frontend/NameFunctions.cpp | 6 + js/src/frontend/ParseMaps.h | 12 + js/src/frontend/ParseNode.cpp | 3 +- js/src/frontend/ParseNode.h | 18 +- js/src/frontend/Parser.cpp | 683 +++-- js/src/frontend/Parser.h | 47 +- js/src/frontend/SharedContext.h | 6 + js/src/frontend/SyntaxParseHandler.h | 32 +- js/src/frontend/TokenStream.cpp | 4 +- .../irregexp/NativeRegExpMacroAssembler.cpp | 14 +- js/src/irregexp/NativeRegExpMacroAssembler.h | 2 +- js/src/irregexp/RegExpAST.h | 4 +- js/src/irregexp/RegExpBytecode.h | 3 +- js/src/irregexp/RegExpEngine.cpp | 296 +- js/src/irregexp/RegExpEngine.h | 39 +- js/src/irregexp/RegExpInterpreter.cpp | 21 + js/src/irregexp/RegExpMacroAssembler.cpp | 41 +- js/src/irregexp/RegExpMacroAssembler.h | 10 +- js/src/irregexp/RegExpParser.cpp | 948 ++++++- js/src/irregexp/RegExpParser.h | 24 +- js/src/jit-test/jit_test.py | 2 +- .../jit-test/tests/auto-regress/bug771027.js | 2 +- js/src/jit-test/tests/baseline/bug1081850.js | 18 - .../jit-test/tests/basic/bug667504-syntax.js | 3 +- .../tests/basic/functionRedeclConst.js | 2 +- .../tests/basic/functionRedeclGlobalConst.js | 2 +- .../jit-test/tests/basic/functionRedeclLet.js | 2 +- js/src/jit-test/tests/ion/bug1148973-1.js | 6 +- js/src/jit-test/tests/ion/bug1212605.js | 2 +- js/src/jit-test/tests/ion/bug1214013.js | 2 +- js/src/jit-test/tests/modules/bug-1236875.js | 2 + .../tests/modules/duplicate-imports.js | 33 +- .../jit-test/tests/tracelogger/bug1304641.js | 13 + js/src/jit/BaselineCompiler.cpp | 20 + js/src/jit/BaselineCompiler.h | 1 + js/src/jit/BytecodeAnalysis.cpp | 1 + js/src/jit/CodeGenerator.cpp | 10 + js/src/jit/CodeGenerator.h | 1 + js/src/jit/IonBuilder.cpp | 13 + js/src/jit/IonBuilder.h | 1 + js/src/jit/Lowering.cpp | 10 + js/src/jit/Lowering.h | 1 + js/src/jit/MIR.h | 33 + js/src/jit/MOpcodes.h | 1 + js/src/jit/MacroAssembler.h | 9 +- js/src/jit/VMFunctions.cpp | 28 +- js/src/jit/VMFunctions.h | 1 + js/src/jit/shared/LIR-shared.h | 16 + js/src/jit/shared/LOpcodes-shared.h | 1 + js/src/js.msg | 17 +- js/src/jsapi.cpp | 58 +- js/src/jsapi.h | 1 + js/src/jscompartment.h | 1 + js/src/jsfriendapi.h | 12 +- js/src/jsstr.cpp | 60 +- js/src/jsversion.h | 7 - .../extensions/function-definition-with.js | 9 +- .../extensions/strict-function-statements.js | 30 +- js/src/tests/ecma_6/Class/outerBinding.js | 2 +- .../block-scoped-functions-annex-b-eval.js | 38 + .../block-scoped-functions-annex-b-if.js | 42 + .../block-scoped-functions-annex-b-label.js | 43 + ...block-scoped-functions-annex-b-property.js | 18 + ...lock-scoped-functions-annex-b-same-name.js | 7 + .../block-scoped-functions-annex-b-with.js | 18 + .../block-scoped-functions-annex-b.js | 31 + ...lock-scoped-functions-deprecated-redecl.js | 78 + .../block-scoped-functions-hoisted-tdz.js | 30 + .../block-scoped-functions-strict.js | 45 + .../ecma_6/LexicalEnvironment/for-loop.js | 5 +- js/src/tests/ecma_6/RegExp/class-null.js | 15 + js/src/tests/ecma_6/RegExp/descriptor.js | 6 +- js/src/tests/ecma_6/RegExp/flag-accessors.js | 7 +- js/src/tests/ecma_6/RegExp/flags.js | 10 +- .../ecma_6/RegExp/unicode-back-reference.js | 39 + js/src/tests/ecma_6/RegExp/unicode-braced.js | 166 ++ .../RegExp/unicode-character-class-escape.js | 75 + .../ecma_6/RegExp/unicode-class-braced.js | 236 ++ .../ecma_6/RegExp/unicode-class-empty.js | 25 + .../ecma_6/RegExp/unicode-class-ignoreCase.js | 28 + .../ecma_6/RegExp/unicode-class-lead-trail.js | 142 + .../ecma_6/RegExp/unicode-class-negated.js | 64 + .../ecma_6/RegExp/unicode-class-range.js | 28 + .../tests/ecma_6/RegExp/unicode-class-raw.js | 65 + .../RegExp/unicode-disallow-extended.js | 117 + .../tests/ecma_6/RegExp/unicode-everything.js | 59 + .../ecma_6/RegExp/unicode-ignoreCase-ascii.js | 45 + .../RegExp/unicode-ignoreCase-escape.js | 71 + .../RegExp/unicode-ignoreCase-negated.js | 19 + .../tests/ecma_6/RegExp/unicode-ignoreCase.js | 2491 +++++++++++++++++ js/src/tests/ecma_6/RegExp/unicode-index.js | 17 + .../tests/ecma_6/RegExp/unicode-lead-trail.js | 218 ++ js/src/tests/ecma_6/RegExp/unicode-raw.js | 139 + .../tests/ecma_6/String/AdvanceStringIndex.js | 43 + ...claration-and-nested-function-statement.js | 12 +- js/src/tests/ecma_7/Math/Pow.js | 45 +- js/src/tests/js1_5/Regress/regress-326453.js | 21 - .../tests/js1_5/extensions/regress-245795.js | 33 +- .../js1_5/extensions/regress-336409-1.js | 5 +- .../js1_5/extensions/regress-336410-1.js | 5 +- .../tests/js1_5/extensions/regress-406572.js | 8 +- .../js1_8_1/regress/regress-452498-185.js | 2 +- .../js1_8_1/regress/regress-452498-191.js | 2 +- js/src/tests/js1_8_5/reflect-parse/classes.js | 6 +- .../js1_8_5/reflect-parse/declarations.js | 10 +- js/src/vm/CaseFolding.txt | 1414 ++++++++++ js/src/vm/Interpreter.cpp | 7 +- js/src/vm/Opcodes.h | 10 +- js/src/vm/RegExpObject.cpp | 14 +- js/src/vm/RegExpObject.h | 13 +- js/src/vm/ScopeObject.h | 14 + js/src/vm/TraceLogging.cpp | 4 +- js/src/vm/Unicode.cpp | 435 +++ js/src/vm/Unicode.h | 95 + js/src/vm/Xdr.h | 4 +- js/src/vm/make_unicode.py | 148 +- js/xpconnect/tests/chrome/test_xrayToJS.xul | 4 +- js/xpconnect/wrappers/AccessCheck.cpp | 3 +- layout/base/FrameLayerBuilder.cpp | 62 +- layout/base/nsCounterManager.cpp | 77 +- layout/base/nsCounterManager.h | 2 +- layout/base/nsDisplayList.cpp | 67 +- layout/base/nsDisplayList.h | 21 +- layout/base/nsGenConList.cpp | 181 +- layout/base/nsGenConList.h | 51 +- layout/base/nsQuoteList.cpp | 20 +- layout/base/nsQuoteList.h | 2 +- layout/generic/nsAbsoluteContainingBlock.cpp | 50 +- layout/generic/nsFrame.cpp | 201 +- layout/generic/nsFrameStateBits.h | 8 + layout/generic/nsTextFrame.cpp | 10 +- layout/style/nsRuleNode.cpp | 3 +- layout/xul/tree/nsTreeBodyFrame.cpp | 3 + modules/libpref/init/all.js | 4 +- netwerk/base/nsIOService.cpp | 7 + parser/html/nsHtml5TreeOperation.h | 2 +- toolkit/components/places/SQLFunctions.cpp | 84 +- toolkit/components/places/SQLFunctions.h | 10 +- toolkit/components/places/UnifiedComplete.js | 9 +- toolkit/components/telemetry/Histograms.json | 4 +- toolkit/content/widgets/autocomplete.xml | 17 +- xpcom/ds/nsAtomTable.cpp | 46 + xpcom/ds/nsIAtom.idl | 5 + xpcom/glue/PLDHashTable.cpp | 44 + xpcom/glue/nsDataHashtable.h | 27 +- xpcom/io/nsEscape.cpp | 12 +- 177 files changed, 10070 insertions(+), 1200 deletions(-) delete mode 100644 js/src/jit-test/tests/baseline/bug1081850.js create mode 100644 js/src/jit-test/tests/modules/bug-1236875.js create mode 100644 js/src/jit-test/tests/tracelogger/bug1304641.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-eval.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-if.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-label.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-property.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-same-name.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-with.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-deprecated-redecl.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-hoisted-tdz.js create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-strict.js create mode 100644 js/src/tests/ecma_6/RegExp/class-null.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-back-reference.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-braced.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-character-class-escape.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-class-braced.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-class-empty.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-class-ignoreCase.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-class-lead-trail.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-class-negated.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-class-range.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-class-raw.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-disallow-extended.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-everything.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-ignoreCase-ascii.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-ignoreCase-escape.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-ignoreCase-negated.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-ignoreCase.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-index.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-lead-trail.js create mode 100644 js/src/tests/ecma_6/RegExp/unicode-raw.js create mode 100644 js/src/tests/ecma_6/String/AdvanceStringIndex.js delete mode 100644 js/src/tests/js1_5/Regress/regress-326453.js create mode 100644 js/src/vm/CaseFolding.txt diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 4bce03c10..0fc8fc39f 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1539,16 +1539,16 @@ pref("browser.translation.engine", "bing"); // Telemetry settings. // Determines if Telemetry pings can be archived locally. -pref("toolkit.telemetry.archive.enabled", true); +pref("toolkit.telemetry.archive.enabled", false); // Whether we enable opt-out Telemetry for a sample of the release population. pref("toolkit.telemetry.optoutSample", true); // Telemetry experiments settings. -pref("experiments.enabled", true); +pref("experiments.enabled", false); pref("experiments.manifest.fetchIntervalSeconds", 86400); pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/manifest/v1/firefox/%VERSION%/%CHANNEL%"); // Whether experiments are supported by the current application profile. -pref("experiments.supported", true); +pref("experiments.supported", false); // Enable GMP support in the addon manager. pref("media.gmp-provider.enabled", true); diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index d82a3a987..d36f51e85 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1947,11 +1947,9 @@ if (animate) { requestAnimationFrame(function () { - //this.tabContainer._handleTabTelemetryStart(t, aURI); - // kick the animation off t.setAttribute("fadein", "true"); - }.bind(this)); + }); } return t; @@ -2132,8 +2130,6 @@ return; } - //this.tabContainer._handleTabTelemetryStart(aTab); - this._blurTab(aTab); aTab.style.maxWidth = ""; // ensure that fade-out transition happens aTab.removeAttribute("fadein"); @@ -2285,7 +2281,7 @@ // update the UI early for responsiveness aTab.collapsed = true; - this.tabContainer._fillTrailingGap(); + //this.tabContainer._fillTrailingGap(); this._blurTab(aTab); this._removingTabs.splice(this._removingTabs.indexOf(aTab), 1); @@ -4529,8 +4525,12 @@ @@ -4757,6 +4754,12 @@ 2) { @@ -4913,16 +4916,19 @@ let scrollButtonWidth = this.mTabstrip._scrollButtonDown.getBoundingClientRect().width; let paddingStart = this.mTabstrip.scrollboxPaddingStart; + let pinnedTabWidth; let width = 0; for (let i = numPinned - 1; i >= 0; i--) { let tab = this.childNodes[i]; - width += tab.getBoundingClientRect().width; + if (!pinnedTabWidth) { + pinnedTabWidth = tab.getBoundingClientRect().width; + } + width += pinnedTabWidth; tab.style.MozMarginStart = - (width + scrollButtonWidth + paddingStart) + "px"; } this.style.MozPaddingStart = width + paddingStart + "px"; - } else { this.removeAttribute("positionpinnedtabs"); @@ -5071,7 +5077,7 @@ var width = this.mTabstrip.boxObject.width; if (width != this.mTabstripWidth) { this.adjustTabstrip(); - this._fillTrailingGap(); + //this._fillTrailingGap(); this._handleTabSelect(); this.mTabstripWidth = width; } @@ -5231,7 +5237,7 @@ this.adjustTabstrip(); if (tab.getAttribute("selected") == "true") { - this._fillTrailingGap(); + //this._fillTrailingGap(); this._handleTabSelect(); } else { this._notifyBackgroundTab(tab); @@ -5258,89 +5264,6 @@ - - - - - - - - - - - - 1) { - let averageInterval = 0; - for (let i = 1; i < frameCount; i++) { - averageInterval += intervals[i]; - }; - averageInterval = averageInterval / (frameCount - 1); - - Services.telemetry.getHistogramById("FX_TAB_ANIM_ANY_FRAME_INTERVAL_MS").add(averageInterval); - - if (aTab._recordingTabOpenPlain) { - delete aTab._recordingTabOpenPlain; - // While we do have a telemetry probe NEWTAB_PAGE_ENABLED to monitor newtab preview, it'll be - // easier to overview the data without slicing by it. Hence the additional histograms with _PREVIEW. - let preview = this._browserNewtabpageEnabled ? "_PREVIEW" : ""; - Services.telemetry.getHistogramById("FX_TAB_ANIM_OPEN" + preview + "_FRAME_INTERVAL_MS").add(averageInterval); - } - } -*/ - ]]> - - - @@ -5355,8 +5278,6 @@ var tab = event.target; - //this._handleTabTelemetryEnd(tab); - if (tab.getAttribute("fadein") == "true") { if (tab._fullyOpen) this.adjustTabstrip(); @@ -6389,7 +6310,7 @@ if (val) { this.setAttribute("label", val); this.removeAttribute("inactive"); - this._calcMouseTargetRect(); + this._mouseTargetRect = null; MousePosTracker.addListener(this); } else { this.setAttribute("inactive", "true"); @@ -6405,6 +6326,9 @@ @@ -6429,7 +6353,7 @@ switch (event.type) { case "resize": - this._calcMouseTargetRect(); + this._mouseTargetRect = null; break; } ]]> @@ -6460,7 +6384,7 @@ if (!this.hasAttribute("sizelimit")) { this.setAttribute("sizelimit", "true"); - this._calcMouseTargetRect(); + this._mouseTargetRect = null; } diff --git a/browser/components/uitour/UITour.jsm b/browser/components/uitour/UITour.jsm index 137a81195..3c835bdb0 100644 --- a/browser/components/uitour/UITour.jsm +++ b/browser/components/uitour/UITour.jsm @@ -735,7 +735,12 @@ this.UITour = { } } - this.initForBrowser(browser, window); + // For performance reasons, only call initForBrowser if we did something + // that will require a teardownTourForBrowser call later. + // getConfiguration (called from about:home) doesn't require any future + // uninitialization. + if (action != "getConfiguration") + this.initForBrowser(browser, window); return true; }, diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 7b8d15214..2f482091f 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -144,6 +144,9 @@ #include "nsComputedDOMStyle.h" #include "mozilla/Preferences.h" +#include "nsIIOService.h" +#include "nsISpeculativeConnect.h" + using namespace mozilla; using namespace mozilla::dom; @@ -188,6 +191,14 @@ Element::QueryInterface(REFNSIID aIID, void** aInstancePtr) aInstancePtr); } +void +Element::SetCustomElementData(CustomElementData* aData) +{ + nsDOMSlots *slots = DOMSlots(); + MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set."); + slots->mCustomElementData = aData; +} + EventStates Element::IntrinsicState() const { @@ -1186,10 +1197,10 @@ Element::SetAttribute(const nsAString& aName, if (IsHTMLElement() && IsInHTMLDocument()) { nsAutoString lower; nsContentUtils::ASCIIToLower(aName, lower); - nameAtom = do_GetAtom(lower); + nameAtom = NS_AtomizeMainThread(lower); } else { - nameAtom = do_GetAtom(aName); + nameAtom = NS_AtomizeMainThread(aName); } if (!nameAtom) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); @@ -1271,7 +1282,7 @@ Element::GetAttributeNS(const nsAString& aNamespaceURI, return; } - nsCOMPtr name = do_GetAtom(aLocalName); + nsCOMPtr name = NS_AtomizeMainThread(aLocalName); bool hasAttr = GetAttr(nsid, name, aReturn); if (!hasAttr) { SetDOMStringToNull(aReturn); @@ -1303,7 +1314,7 @@ Element::RemoveAttributeNS(const nsAString& aNamespaceURI, const nsAString& aLocalName, ErrorResult& aError) { - nsCOMPtr name = do_GetAtom(aLocalName); + nsCOMPtr name = NS_AtomizeMainThread(aLocalName); int32_t nsid = nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI); @@ -1389,7 +1400,7 @@ Element::HasAttributeNS(const nsAString& aNamespaceURI, return false; } - nsCOMPtr name = do_GetAtom(aLocalName); + nsCOMPtr name = NS_AtomizeMainThread(aLocalName); return HasAttr(nsid, name); } @@ -2991,6 +3002,15 @@ Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) EventStateManager::SetActiveManager( aVisitor.mPresContext->EventStateManager(), this); + + // OK, we're pretty sure we're going to load, so warm up a speculative + // connection to be sure we have one ready when we open the channel. + nsCOMPtr + speculator(do_QueryInterface(nsContentUtils::GetIOService())); + nsCOMPtr ir = do_QueryInterface(handler); + // We need bug 1304219 for this part, but this will suffice for now. + //speculator->SpeculativeConnect2(absURI, NodePrincipal(), ir); + speculator->SpeculativeConnect(absURI, ir); } } } diff --git a/dom/base/Element.h b/dom/base/Element.h index a94389621..2665686b4 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -361,6 +361,29 @@ public: Directionality GetComputedDirectionality() const; + /** + * Gets the custom element data used by web components' custom element. + * Custom element data is created at the first attempt to enqueue a callback. + * + * @return The custom element data or null if none. + */ + inline CustomElementData* GetCustomElementData() const + { + nsDOMSlots *slots = GetExistingDOMSlots(); + if (slots) { + return slots->mCustomElementData; + } + return nullptr; + } + + /** + * Sets the custom element data, ownership of the + * callback data is taken by this element. + * + * @param aData The custom element data. + */ + void SetCustomElementData(CustomElementData* aData); + protected: /** * Method to get the _intrinsic_ content state of this element. This is the diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 84b036482..e43ca48b2 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1116,24 +1116,6 @@ FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent) } } -CustomElementData* -FragmentOrElement::GetCustomElementData() const -{ - nsDOMSlots *slots = GetExistingDOMSlots(); - if (slots) { - return slots->mCustomElementData; - } - return nullptr; -} - -void -FragmentOrElement::SetCustomElementData(CustomElementData* aData) -{ - nsDOMSlots *slots = DOMSlots(); - MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set."); - slots->mCustomElementData = aData; -} - nsresult FragmentOrElement::InsertChildAt(nsIContent* aKid, uint32_t aIndex, diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index dc29fc63f..b45f60517 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -34,6 +34,7 @@ class nsIURI; namespace mozilla { namespace dom { +struct CustomElementData; class Element; } // namespace dom } // namespace mozilla @@ -157,9 +158,6 @@ public: virtual void SetXBLInsertionParent(nsIContent* aContent) override; virtual bool IsLink(nsIURI** aURI) const override; - virtual CustomElementData *GetCustomElementData() const override; - virtual void SetCustomElementData(CustomElementData* aData) override; - virtual void DestroyContent() override; virtual void SaveSubtreeState() override; diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp index 976d3f2fd..3ee8bd348 100644 --- a/dom/base/nsAttrValue.cpp +++ b/dom/base/nsAttrValue.cpp @@ -735,7 +735,7 @@ nsAttrValue::GetAsAtom() const { switch (Type()) { case eString: - return do_GetAtom(GetStringValue()); + return NS_AtomizeMainThread(GetStringValue()); case eAtom: { @@ -747,7 +747,7 @@ nsAttrValue::GetAsAtom() const { nsAutoString val; ToString(val); - return do_GetAtom(val); + return NS_AtomizeMainThread(val); } } } @@ -1260,7 +1260,7 @@ nsAttrValue::ParseAtomArray(const nsAString& aValue) ++iter; } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter)); - nsCOMPtr classAtom = do_GetAtom(Substring(start, iter)); + nsCOMPtr classAtom = NS_AtomizeMainThread(Substring(start, iter)); if (!classAtom) { Reset(); return; @@ -1301,7 +1301,7 @@ nsAttrValue::ParseAtomArray(const nsAString& aValue) ++iter; } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter)); - classAtom = do_GetAtom(Substring(start, iter)); + classAtom = NS_AtomizeMainThread(Substring(start, iter)); if (!array->AppendElement(classAtom)) { Reset(); @@ -1718,7 +1718,7 @@ nsAttrValue::SetMiscAtomOrString(const nsAString* aValue) "Empty string?"); MiscContainer* cont = GetMiscContainer(); if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) { - nsCOMPtr atom = NS_NewAtom(*aValue); + nsCOMPtr atom = NS_AtomizeMainThread(*aValue); if (atom) { cont->mStringBits = reinterpret_cast(atom.forget().take()) | eAtomBase; diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index c73709865..42fe7d5ae 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -2850,11 +2850,11 @@ nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver, if (*aNamespace == kNameSpaceID_Unknown) return NS_ERROR_FAILURE; - *aLocalName = NS_NewAtom(Substring(colon + 1, end)).take(); + *aLocalName = NS_AtomizeMainThread(Substring(colon + 1, end)).take(); } else { *aNamespace = kNameSpaceID_None; - *aLocalName = NS_NewAtom(aQName).take(); + *aLocalName = NS_AtomizeMainThread(aQName).take(); } NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY); return NS_OK; @@ -2879,7 +2879,7 @@ nsContentUtils::GetNodeInfoFromQName(const nsAString& aNamespaceURI, const char16_t* end; qName.EndReading(end); - nsCOMPtr prefix = do_GetAtom(Substring(qName.get(), colon)); + nsCOMPtr prefix = NS_AtomizeMainThread(Substring(qName.get(), colon)); rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix, nsID, aNodeType, aNodeInfo); @@ -2939,7 +2939,7 @@ nsContentUtils::SplitExpatName(const char16_t *aExpatName, nsIAtom **aPrefix, nameStart = (uriEnd + 1); if (nameEnd) { const char16_t *prefixStart = nameEnd + 1; - *aPrefix = NS_NewAtom(Substring(prefixStart, pos)).take(); + *aPrefix = NS_AtomizeMainThread(Substring(prefixStart, pos)).take(); } else { nameEnd = pos; @@ -2952,7 +2952,7 @@ nsContentUtils::SplitExpatName(const char16_t *aExpatName, nsIAtom **aPrefix, nameEnd = pos; *aPrefix = nullptr; } - *aLocalName = NS_NewAtom(Substring(nameStart, nameEnd)).take(); + *aLocalName = NS_AtomizeMainThread(Substring(nameStart, nameEnd)).take(); } // static @@ -3708,7 +3708,7 @@ nsContentUtils::GetEventMessageAndAtom(const nsAString& aName, } *aEventMessage = eUnidentifiedEvent; - nsCOMPtr atom = do_GetAtom(NS_LITERAL_STRING("on") + aName); + nsCOMPtr atom = NS_AtomizeMainThread(NS_LITERAL_STRING("on") + aName); sUserDefinedEvents->AppendObject(atom); mapping.mAtom = atom; mapping.mMessage = eUnidentifiedEvent; diff --git a/dom/base/nsGenericDOMDataNode.cpp b/dom/base/nsGenericDOMDataNode.cpp index 40403a775..938bf613e 100644 --- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -771,17 +771,6 @@ nsGenericDOMDataNode::SetXBLInsertionParent(nsIContent* aContent) } } -CustomElementData * -nsGenericDOMDataNode::GetCustomElementData() const -{ - return nullptr; -} - -void -nsGenericDOMDataNode::SetCustomElementData(CustomElementData* aData) -{ -} - bool nsGenericDOMDataNode::IsNodeOfType(uint32_t aFlags) const { diff --git a/dom/base/nsGenericDOMDataNode.h b/dom/base/nsGenericDOMDataNode.h index e8620301c..3eae37347 100644 --- a/dom/base/nsGenericDOMDataNode.h +++ b/dom/base/nsGenericDOMDataNode.h @@ -163,9 +163,6 @@ public: virtual bool IsNodeOfType(uint32_t aFlags) const override; virtual bool IsLink(nsIURI** aURI) const override; - virtual mozilla::dom::CustomElementData* GetCustomElementData() const override; - virtual void SetCustomElementData(mozilla::dom::CustomElementData* aData) override; - NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override; NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const; virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 080b0302f..e824c9541 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -4890,12 +4890,6 @@ nsGlobalWindow::GetOuterSize(ErrorResult& aError) return nsIntSize(0, 0); } - nsGlobalWindow* rootWindow = - static_cast(GetPrivateRoot()); - if (rootWindow) { - rootWindow->FlushPendingNotifications(Flush_Layout); - } - nsIntSize sizeDevPixels; aError = treeOwnerAsWin->GetSize(&sizeDevPixels.width, &sizeDevPixels.height); if (aError.Failed()) { @@ -5077,11 +5071,7 @@ nsGlobalWindow::GetInnerScreenRect() return nsRect(); } - nsGlobalWindow* rootWindow = - static_cast(GetPrivateRoot()); - if (rootWindow) { - rootWindow->FlushPendingNotifications(Flush_Layout); - } + EnsureSizeUpToDate(); if (!mDocShell) { return nsRect(); diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index f30de1f18..78711472f 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -25,7 +25,6 @@ namespace mozilla { class EventChainPreVisitor; namespace dom { class ShadowRoot; -struct CustomElementData; } // namespace dom namespace widget { struct IMEState; @@ -703,22 +702,6 @@ public: */ nsIContent *GetFlattenedTreeParent() const; - /** - * Gets the custom element data used by web components custom element. - * Custom element data is created at the first attempt to enqueue a callback. - * - * @return The custom element data or null if none. - */ - virtual mozilla::dom::CustomElementData *GetCustomElementData() const = 0; - - /** - * Sets the custom element data, ownership of the - * callback data is taken by this content. - * - * @param aCallbackData The custom element data. - */ - virtual void SetCustomElementData(mozilla::dom::CustomElementData* aData) = 0; - /** * API to check if this is a link that's traversed in response to user input * (e.g. a click event). Specializations for HTML/SVG/generic XML allow for diff --git a/dom/base/nsNodeInfoManager.cpp b/dom/base/nsNodeInfoManager.cpp index 0eacd921e..57ef02b8a 100644 --- a/dom/base/nsNodeInfoManager.cpp +++ b/dom/base/nsNodeInfoManager.cpp @@ -111,7 +111,8 @@ nsNodeInfoManager::nsNodeInfoManager() mNonDocumentNodeInfos(0), mTextNodeInfo(nullptr), mCommentNodeInfo(nullptr), - mDocumentNodeInfo(nullptr) + mDocumentNodeInfo(nullptr), + mRecentlyUsedNodeInfos{} { nsLayoutStatics::AddRef(); @@ -232,11 +233,19 @@ nsNodeInfoManager::GetNodeInfo(nsIAtom *aName, nsIAtom *aPrefix, NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType, aExtraName); + uint32_t index = + GetNodeInfoInnerHashValue(&tmpKey) % RECENTLY_USED_NODEINFOS_SIZE; + NodeInfo* ni = mRecentlyUsedNodeInfos[index]; + if (ni && NodeInfoInnerKeyCompare(&(ni->mInner), &tmpKey)) { + RefPtr nodeInfo = ni; + return nodeInfo.forget(); + } + void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey); if (node) { RefPtr nodeInfo = static_cast(node); - + mRecentlyUsedNodeInfos[index] = nodeInfo; return nodeInfo.forget(); } @@ -254,6 +263,7 @@ nsNodeInfoManager::GetNodeInfo(nsIAtom *aName, nsIAtom *aPrefix, NS_IF_ADDREF(mDocument); } + mRecentlyUsedNodeInfos[index] = newNodeInfo; return newNodeInfo.forget(); } @@ -272,12 +282,21 @@ nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix, NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType); + uint32_t index = + GetNodeInfoInnerHashValue(&tmpKey) % RECENTLY_USED_NODEINFOS_SIZE; + NodeInfo* ni = mRecentlyUsedNodeInfos[index]; + if (ni && NodeInfoInnerKeyCompare(&(ni->mInner), &tmpKey)) { + RefPtr nodeInfo = ni; + nodeInfo.forget(aNodeInfo); + return NS_OK; + } + void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey); if (node) { - NodeInfo* nodeInfo = static_cast(node); - - NS_ADDREF(*aNodeInfo = nodeInfo); + RefPtr nodeInfo = static_cast(node); + mRecentlyUsedNodeInfos[index] = nodeInfo; + nodeInfo.forget(aNodeInfo); return NS_OK; } @@ -298,6 +317,7 @@ nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix, NS_IF_ADDREF(mDocument); } + mRecentlyUsedNodeInfos[index] = newNodeInfo; newNodeInfo.forget(aNodeInfo); return NS_OK; @@ -420,6 +440,12 @@ nsNodeInfoManager::RemoveNodeInfo(NodeInfo *aNodeInfo) } } + uint32_t index = + GetNodeInfoInnerHashValue(&aNodeInfo->mInner) % RECENTLY_USED_NODEINFOS_SIZE; + if (mRecentlyUsedNodeInfos[index] == aNodeInfo) { + mRecentlyUsedNodeInfos[index] = nullptr; + } + #ifdef DEBUG bool ret = #endif diff --git a/dom/base/nsNodeInfoManager.h b/dom/base/nsNodeInfoManager.h index d51e25cfa..dee82c92d 100644 --- a/dom/base/nsNodeInfoManager.h +++ b/dom/base/nsNodeInfoManager.h @@ -33,6 +33,8 @@ class NodeInfo; } // namespace dom } // namespace mozilla +#define RECENTLY_USED_NODEINFOS_SIZE 31 + class nsNodeInfoManager final { private: @@ -138,6 +140,7 @@ private: mozilla::dom::NodeInfo * MOZ_NON_OWNING_REF mCommentNodeInfo; // WEAK to avoid circular ownership mozilla::dom::NodeInfo * MOZ_NON_OWNING_REF mDocumentNodeInfo; // WEAK to avoid circular ownership RefPtr mBindingManager; + mozilla::dom::NodeInfo* mRecentlyUsedNodeInfos[RECENTLY_USED_NODEINFOS_SIZE]; }; #endif /* nsNodeInfoManager_h___ */ diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 1536af1be..d7cad7542 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -3849,7 +3849,6 @@ CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText, // calls bidi algo twice since it needs the full text width and the // bounding boxes before rendering anything - nsBidi bidiEngine; rv = nsBidiPresUtils::ProcessText(textToDraw.get(), textToDraw.Length(), isRTL ? NSBIDI_RTL : NSBIDI_LTR, @@ -3859,7 +3858,7 @@ CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText, nullptr, 0, &totalWidthCoord, - &bidiEngine); + &mBidiEngine); if (NS_FAILED(rv)) { return rv; } @@ -3974,7 +3973,7 @@ CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText, nullptr, 0, nullptr, - &bidiEngine); + &mBidiEngine); mTarget->SetTransform(oldTransform); diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index d2782b594..ece78e589 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -28,6 +28,7 @@ #include "FilterSupport.h" #include "nsSVGEffects.h" #include "Layers.h" +#include "nsBidi.h" class nsGlobalWindow; class nsXULElement; @@ -821,6 +822,8 @@ protected: nsTArray mHitRegionsOptions; + nsBidi mBidiEngine; + /** * Returns true if a shadow should be drawn along with a * drawing operation. diff --git a/dom/events/IMEStateManager.cpp b/dom/events/IMEStateManager.cpp index 96fcf16b3..16b4cddc7 100644 --- a/dom/events/IMEStateManager.cpp +++ b/dom/events/IMEStateManager.cpp @@ -171,6 +171,7 @@ TextCompositionArray* IMEStateManager::sTextCompositions = nullptr; bool IMEStateManager::sInstalledMenuKeyboardListener = false; bool IMEStateManager::sIsGettingNewIMEState = false; bool IMEStateManager::sCheckForIMEUnawareWebApps = false; +bool IMEStateManager::sInputModeSupported = false; bool IMEStateManager::sRemoteHasFocus = false; // static @@ -181,6 +182,11 @@ IMEStateManager::Init() &sCheckForIMEUnawareWebApps, "intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition", false); + + Preferences::AddBoolVarCache( + &sInputModeSupported, + "dom.forms.inputmode", + false); } // static @@ -1036,7 +1042,7 @@ IMEStateManager::SetIMEState(const IMEState& aState, context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String()); } - if (Preferences::GetBool("dom.forms.inputmode", false) || + if (sInputModeSupported || nsContentUtils::IsChromeDoc(aContent->OwnerDoc())) { aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode, context.mHTMLInputInputmode); diff --git a/dom/events/IMEStateManager.h b/dom/events/IMEStateManager.h index 1ff31743c..9c2df949e 100644 --- a/dom/events/IMEStateManager.h +++ b/dom/events/IMEStateManager.h @@ -251,6 +251,7 @@ protected: static bool sInstalledMenuKeyboardListener; static bool sIsGettingNewIMEState; static bool sCheckForIMEUnawareWebApps; + static bool sInputModeSupported; static bool sRemoteHasFocus; class MOZ_STACK_CLASS GettingNewIMEStateBlocker final diff --git a/editor/libeditor/DeleteRangeTxn.cpp b/editor/libeditor/DeleteRangeTxn.cpp index 58ce18a97..1b14c5629 100644 --- a/editor/libeditor/DeleteRangeTxn.cpp +++ b/editor/libeditor/DeleteRangeTxn.cpp @@ -65,11 +65,19 @@ DeleteRangeTxn::DoTransaction() MOZ_ASSERT(mRange && mEditor); nsresult res; + // Swap mRange out into a stack variable, so we make sure to null it + // out on return from this function. Once this function returns, we no longer + // need mRange, and keeping it alive in the long term slows down all + // DOM mutations because it's observing them. + // (Modified from bug 1349940.) + RefPtr rangeToDelete; + rangeToDelete.swap(mRange); + // build the child transactions - nsCOMPtr startParent = mRange->GetStartParent(); - int32_t startOffset = mRange->StartOffset(); - nsCOMPtr endParent = mRange->GetEndParent(); - int32_t endOffset = mRange->EndOffset(); + nsCOMPtr startParent = rangeToDelete->GetStartParent(); + int32_t startOffset = rangeToDelete->StartOffset(); + nsCOMPtr endParent = rangeToDelete->GetEndParent(); + int32_t endOffset = rangeToDelete->EndOffset(); MOZ_ASSERT(startParent && endParent); if (startParent == endParent) { @@ -82,7 +90,7 @@ DeleteRangeTxn::DoTransaction() res = CreateTxnsToDeleteContent(startParent, startOffset, nsIEditor::eNext); NS_ENSURE_SUCCESS(res, res); // delete the intervening nodes - res = CreateTxnsToDeleteNodesBetween(); + res = CreateTxnsToDeleteNodesBetween(rangeToDelete); NS_ENSURE_SUCCESS(res, res); // delete the relevant content in the end node res = CreateTxnsToDeleteContent(endParent, endOffset, nsIEditor::ePrevious); @@ -110,16 +118,12 @@ DeleteRangeTxn::DoTransaction() NS_IMETHODIMP DeleteRangeTxn::UndoTransaction() { - MOZ_ASSERT(mRange && mEditor); - return EditAggregateTxn::UndoTransaction(); } NS_IMETHODIMP DeleteRangeTxn::RedoTransaction() { - MOZ_ASSERT(mRange && mEditor); - return EditAggregateTxn::RedoTransaction(); } @@ -211,11 +215,11 @@ DeleteRangeTxn::CreateTxnsToDeleteContent(nsINode* aNode, } nsresult -DeleteRangeTxn::CreateTxnsToDeleteNodesBetween() +DeleteRangeTxn::CreateTxnsToDeleteNodesBetween(nsRange *aRangeToDelete) { nsCOMPtr iter = NS_NewContentSubtreeIterator(); - nsresult res = iter->Init(mRange); + nsresult res = iter->Init(aRangeToDelete); NS_ENSURE_SUCCESS(res, res); while (!iter->IsDone()) { diff --git a/editor/libeditor/DeleteRangeTxn.h b/editor/libeditor/DeleteRangeTxn.h index 680762e21..be713cd44 100644 --- a/editor/libeditor/DeleteRangeTxn.h +++ b/editor/libeditor/DeleteRangeTxn.h @@ -54,7 +54,7 @@ protected: int32_t aStartOffset, int32_t aEndOffset); - nsresult CreateTxnsToDeleteNodesBetween(); + nsresult CreateTxnsToDeleteNodesBetween(nsRange *aRangeToDelete); nsresult CreateTxnsToDeleteContent(nsINode* aParent, int32_t aOffset, @@ -63,6 +63,8 @@ protected: protected: /** p1 in the range */ + /* After bug 1349940, this is only non-null until DoTransaction is called + and we convert it into child transactions. */ RefPtr mRange; /** the editor for this transaction */ diff --git a/editor/libeditor/nsEditor.cpp b/editor/libeditor/nsEditor.cpp index 90bd22057..4656d1496 100644 --- a/editor/libeditor/nsEditor.cpp +++ b/editor/libeditor/nsEditor.cpp @@ -146,6 +146,7 @@ nsEditor::nsEditor() , mDispatchInputEvent(true) , mIsInEditAction(false) , mHidingCaret(false) +, mSpellCheckerDictionaryUpdated(true) { } @@ -1309,6 +1310,11 @@ NS_IMETHODIMP nsEditor::SyncRealTimeSpell() GetInlineSpellChecker(enable, getter_AddRefs(spellChecker)); if (mInlineSpellChecker) { + if (!mSpellCheckerDictionaryUpdated && enable) { + mInlineSpellChecker->UpdateCurrentDictionary(); + mSpellCheckerDictionaryUpdated = true; + } + // We might have a mInlineSpellChecker even if there are no dictionaries // available since we don't destroy the mInlineSpellChecker when the last // dictionariy is removed, but in that case spellChecker is null @@ -5122,8 +5128,10 @@ void nsEditor::OnFocus(nsIDOMEventTarget* aFocusEventTarget) { InitializeSelection(aFocusEventTarget); - if (mInlineSpellChecker) { + mSpellCheckerDictionaryUpdated = false; + if (mInlineSpellChecker && CanEnableSpellCheck()) { mInlineSpellChecker->UpdateCurrentDictionary(); + mSpellCheckerDictionaryUpdated = true; } } diff --git a/editor/libeditor/nsEditor.h b/editor/libeditor/nsEditor.h index 725898ece..feb3ed21a 100644 --- a/editor/libeditor/nsEditor.h +++ b/editor/libeditor/nsEditor.h @@ -883,6 +883,7 @@ protected: bool mDispatchInputEvent; bool mIsInEditAction; // true while the instance is handling an edit action bool mHidingCaret; // whether caret is hidden forcibly. + bool mSpellCheckerDictionaryUpdated; // Whether spellchecker dictionary is initialized after focused. friend bool NSCanUnload(nsISupports* serviceMgr); friend class nsAutoTxnsConserveSelection; diff --git a/js/public/Utility.h b/js/public/Utility.h index 318b80298..4ee6cce65 100644 --- a/js/public/Utility.h +++ b/js/public/Utility.h @@ -291,7 +291,7 @@ static inline char* js_strdup(const char* s) QUALIFIERS T * \ NEWNAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \ void* memory = ALLOCATOR(sizeof(T)); \ - return memory \ + return MOZ_LIKELY(memory) \ ? new(memory) T(mozilla::Forward(args)...) \ : nullptr; \ } diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index abe869be4..e95c37df1 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -1890,7 +1890,7 @@ ASTSerializer::unop(ParseNodeKind kind, JSOp op) if (IsDeleteKind(kind)) return UNOP_DELETE; - if (kind == PNK_TYPEOFNAME || kind == PNK_TYPEOFEXPR) + if (IsTypeofKind(kind)) return UNOP_TYPEOF; switch (op) { @@ -2040,6 +2040,9 @@ ASTSerializer::declaration(ParseNode* pn, MutableHandleValue dst) case PNK_FUNCTION: return function(pn, AST_FUNC_DECL, dst); + case PNK_ANNEXB_FUNCTION: + return function(pn->pn_left, AST_FUNC_DECL, dst); + case PNK_VAR: return variableDeclaration(pn, false, dst); @@ -2411,6 +2414,17 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) case PNK_VAR: return declaration(pn, dst); + case PNK_ANNEXB_FUNCTION: + // XXXshu NOP check used only for phasing in block-scope function + // XXXshu early errors. + // XXXshu + // XXXshu Back out when major version >= 50. See [1]. + // XXXshu + // XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10 + if (pn->pn_left->isKind(PNK_NOP)) + return builder.emptyStatement(&pn->pn_pos, dst); + return declaration(pn->pn_left, dst); + case PNK_LETBLOCK: return letBlock(pn, dst); diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 2991118eb..ea9aa17de 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -14,12 +14,14 @@ #include "jit/InlinableNatives.h" #include "vm/RegExpStatics.h" #include "vm/StringBuffer.h" +#include "vm/Unicode.h" #include "jsobjinlines.h" #include "vm/NativeObject-inl.h" using namespace js; +using namespace js::unicode; using mozilla::ArrayLength; using mozilla::Maybe; @@ -176,8 +178,11 @@ RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle obj, /* Steps 8-10. */ CompileOptions options(cx); frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr); - if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern)) + if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern, + flags & UnicodeFlag)) + { return false; + } if (staticsUse == UseRegExpStatics) { RegExpStatics* res = cx->global()->getRegExpStatics(cx); @@ -561,6 +566,24 @@ regexp_sticky(JSContext* cx, unsigned argc, JS::Value* vp) return CallNonGenericMethod(cx, args); } +/* ES6 21.2.5.15. */ +MOZ_ALWAYS_INLINE bool +regexp_unicode_impl(JSContext* cx, const CallArgs& args) +{ + MOZ_ASSERT(IsRegExpObject(args.thisv())); + /* Steps 4-6. */ + args.rval().setBoolean(args.thisv().toObject().as().unicode()); + return true; +} + +static bool +regexp_unicode(JSContext* cx, unsigned argc, JS::Value* vp) +{ + /* Steps 1-3. */ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(cx, args); +} + const JSPropertySpec js::regexp_properties[] = { JS_SELF_HOSTED_GET("flags", "RegExpFlagsGetter", 0), JS_PSG("global", regexp_global, 0), @@ -568,6 +591,7 @@ const JSPropertySpec js::regexp_properties[] = { JS_PSG("multiline", regexp_multiline, 0), JS_PSG("source", regexp_source, 0), JS_PSG("sticky", regexp_sticky, 0), + JS_PSG("unicode", regexp_unicode, 0), JS_PS_END }; @@ -736,6 +760,29 @@ SetLastIndex(JSContext* cx, Handle reobj, double lastIndex) return true; } +template +static bool +IsTrailSurrogateWithLeadSurrogateImpl(JSContext* cx, HandleLinearString input, size_t index) +{ + JS::AutoCheckCannotGC nogc; + MOZ_ASSERT(index > 0 && index < input->length()); + const CharT* inputChars = input->chars(nogc); + + return unicode::IsTrailSurrogate(inputChars[index]) && + unicode::IsLeadSurrogate(inputChars[index - 1]); +} + +static bool +IsTrailSurrogateWithLeadSurrogate(JSContext* cx, HandleLinearString input, int32_t index) +{ + if (index <= 0 || size_t(index) >= input->length()) + return false; + + return input->hasLatin1Chars() + ? IsTrailSurrogateWithLeadSurrogateImpl(cx, input, index) + : IsTrailSurrogateWithLeadSurrogateImpl(cx, input, index); +} + /* ES6 final draft 21.2.5.2.2. */ RegExpRunStatus js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string, @@ -818,6 +865,33 @@ js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string, return RegExpRunStatus_Success_NotFound; } + /* Steps 12-13. */ + if (reobj->unicode()) { + /* + * ES6 21.2.2.2 step 2. + * Let listIndex be the index into Input of the character that was + * obtained from element index of str. + * + * In the spec, pattern match is performed with decoded Unicode code + * points, but our implementation performs it with UTF-16 encoded + * string. In step 2, we should decrement searchIndex (index) if it + * points the trail surrogate that has corresponding lead surrogate. + * + * var r = /\uD83D\uDC38/ug; + * r.lastIndex = 1; + * var str = "\uD83D\uDC38"; + * var result = r.exec(str); // pattern match starts from index 0 + * print(result.index); // prints 0 + * + * Note: this doesn't match the current spec text and result in + * different values for `result.index` under certain conditions. + * However, the spec will change to match our implementation's + * behavior. See https://github.com/tc39/ecma262/issues/128. + */ + if (IsTrailSurrogateWithLeadSurrogate(cx, input, searchIndex)) + searchIndex--; + } + /* Step 14-29. */ RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, searchIndex, matches); if (status == RegExpRunStatus_Error) diff --git a/js/src/builtin/RegExp.js b/js/src/builtin/RegExp.js index 13cc5ba89..7b002dd47 100644 --- a/js/src/builtin/RegExp.js +++ b/js/src/builtin/RegExp.js @@ -25,9 +25,8 @@ function RegExpFlagsGetter() { result += "m"; // Steps 13-15. - // TODO: Uncomment these steps when bug 1135377 is fixed. - // if (R.unicode) - // result += "u"; + if (R.unicode) + result += "u"; // Steps 16-18. if (R.sticky) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 0a6cc35ad..0d232a324 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1372,20 +1372,20 @@ BytecodeEmitter::emitVarIncDec(ParseNode* pn) } bool -BytecodeEmitter::atBodyLevel() const +BytecodeEmitter::atBodyLevel(StmtInfoBCE* stmt) const { // 'eval' and non-syntactic scripts are always under an invisible lexical // scope, but since it is not syntactic, it should still be considered at // body level. if (sc->staticScope()->is()) { - bool bl = !innermostStmt()->enclosing; - MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK); - MOZ_ASSERT_IF(bl, innermostStmt()->staticScope - ->as() - .enclosingStaticScope() == sc->staticScope()); + bool bl = !stmt->enclosing; + MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK); + MOZ_ASSERT_IF(bl, stmt->staticScope + ->as() + .enclosingStaticScope() == sc->staticScope()); return bl; } - return !innermostStmt() || sc->isModuleBox(); + return !stmt; } uint32_t @@ -1738,7 +1738,7 @@ BytecodeEmitter::bindNameToSlotHelper(ParseNode* pn) MOZ_ASSERT(dn->isDefn()); pn->pn_dflags |= (dn->pn_dflags & PND_CONST); } else if (pn->isDefn()) { - dn = (Definition*) pn; + dn = &pn->as(); } else { return true; } @@ -2344,6 +2344,22 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) MOZ_ASSERT(pn->pn_count == 1); return checkSideEffects(pn->pn_head, answer); + case PNK_ANNEXB_FUNCTION: + MOZ_ASSERT(pn->isArity(PN_BINARY)); + + // XXXshu NOP check used only for phasing in block-scope function + // XXXshu early errors. + // XXXshu + // XXXshu Back out when major version >= 50. See [1]. + // XXXshu + // XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10 + if (pn->pn_left->isKind(PNK_NOP)) { + *answer = false; + return true; + } + + return checkSideEffects(pn->pn_left, answer); + case PNK_ARGSBODY: *answer = true; return true; @@ -3094,11 +3110,23 @@ BytecodeEmitter::emitSwitch(ParseNode* pn) if (!enterBlockScope(&stmtInfo, cases->pn_objbox, JSOP_UNINITIALIZED, 0)) return false; - stmtInfo.type = StmtType::SWITCH; - stmtInfo.update = top = offset(); - // Advance |cases| to refer to the switch case list. cases = cases->expr(); + + // A switch statement may contain hoisted functions inside its + // cases. The PNX_FUNCDEFS flag is propagated from the STATEMENTLIST + // bodies of the cases to the case list. + if (cases->pn_xflags & PNX_FUNCDEFS) { + for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) { + if (caseNode->pn_right->pn_xflags & PNX_FUNCDEFS) { + if (!emitHoistedFunctionsInList(caseNode->pn_right)) + return false; + } + } + } + + stmtInfo.type = StmtType::SWITCH; + stmtInfo.update = top = offset(); } else { MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST)); top = offset(); @@ -4398,12 +4426,24 @@ BytecodeEmitter::emitVariables(ParseNode* pn, VarEmitOption emitOption) */ MOZ_ASSERT(binding->isOp(JSOP_NOP)); MOZ_ASSERT(emitOption != DefineVars); + MOZ_ASSERT_IF(emitOption == AnnexB, binding->pn_left->isKind(PNK_NAME)); - /* - * To allow the front end to rewrite var f = x; as f = x; when a - * function f(){} precedes the var, detect simple name assignment - * here and initialize the name. - */ + // To allow the front end to rewrite |var f = x;| as |f = x;| when a + // |function f(){}| precedes the var, detect simple name assignment + // here and initialize the name. + // + // There is a corner case where a function declaration synthesizes + // an Annex B declaration, which in turn gets rewritten later as a + // simple assignment due to hoisted function declaration of the + // same name. For example, + // + // { + // // Synthesizes an Annex B declaration because no 'f' binding + // // yet exists. This later gets rewritten as an assignment when + // // the outer function 'f' gets hoisted. + // function f() {} + // } + // function f() {} if (binding->pn_left->isKind(PNK_NAME)) { if (!emitSingleVariable(pn, binding->pn_left, binding->pn_right, emitOption)) return false; @@ -4458,13 +4498,18 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode op == JSOP_STRICTSETGNAME) { MOZ_ASSERT(emitOption != PushInitialValues); - JSOp bindOp; - if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME) - bindOp = JSOP_BINDNAME; - else - bindOp = JSOP_BINDGNAME; - if (!emitIndex32(bindOp, atomIndex)) - return false; + if (op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME) { + if (!emitIndex32(JSOP_BINDGNAME, atomIndex)) + return false; + } else if (emitOption == AnnexB) { + // Annex B vars always go on the nearest variable environment, + // even if scopes on the chain contain same-named bindings. + if (!emit1(JSOP_BINDVAR)) + return false; + } else { + if (!emitIndex32(JSOP_BINDNAME, atomIndex)) + return false; + } } bool oldEmittingForInit = emittingForInit; @@ -4488,7 +4533,7 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode // If we are not initializing, nothing to pop. If we are initializing // lets, we must emit the pops. - if (emitOption == InitializeVars) { + if (emitOption == InitializeVars || emitOption == AnnexB) { MOZ_ASSERT_IF(binding->isDefn(), initializer == binding->pn_expr); if (!binding->pn_scopecoord.isFree()) { if (!emitVarOp(binding, op)) @@ -5326,6 +5371,30 @@ BytecodeEmitter::emitLetBlock(ParseNode* pnLet) return true; } +bool +BytecodeEmitter::emitHoistedFunctionsInList(ParseNode* list) +{ + MOZ_ASSERT(list->pn_xflags & PNX_FUNCDEFS); + + for (ParseNode* pn = list->pn_head; pn; pn = pn->pn_next) { + ParseNode* maybeFun = pn; + + if (!sc->strict()) { + while (maybeFun->isKind(PNK_LABEL)) + maybeFun = maybeFun->as().statement(); + } + + if (maybeFun->isKind(PNK_ANNEXB_FUNCTION) || + (maybeFun->isKind(PNK_FUNCTION) && maybeFun->functionIsHoisted())) + { + if (!emitTree(maybeFun)) + return false; + } + } + + return true; +} + // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See // the comment on emitSwitch. MOZ_NEVER_INLINE bool @@ -5337,7 +5406,17 @@ BytecodeEmitter::emitLexicalScope(ParseNode* pn) if (!enterBlockScope(&stmtInfo, pn->pn_objbox, JSOP_UNINITIALIZED, 0)) return false; - if (!emitTree(pn->pn_expr)) + ParseNode* body = pn->pn_expr; + + if (body->isKind(PNK_STATEMENTLIST) && body->pn_xflags & PNX_FUNCDEFS) { + // This block contains function statements whose definitions are + // hoisted to the top of the block. Emit these as a separate pass + // before the rest of the block. + if (!emitHoistedFunctionsInList(body)) + return false; + } + + if (!emitTree(body)) return false; if (!leaveNestedScope(&stmtInfo)) @@ -6198,6 +6277,12 @@ BytecodeEmitter::emitComprehensionFor(ParseNode* compFor) MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) { + ParseNode* assignmentForAnnexB = nullptr; + if (pn->isKind(PNK_ANNEXB_FUNCTION)) { + assignmentForAnnexB = pn->pn_right; + pn = pn->pn_left; + } + FunctionBox* funbox = pn->pn_funbox; RootedFunction fun(cx, funbox->function()); MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript()); @@ -6208,9 +6293,25 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) * function will be seen by emitFunction in two places. */ if (funbox->wasEmitted) { + // Annex B block-scoped functions are hoisted like any other + // block-scoped function to the top of their scope. When their + // definitions are seen for the second time, we need to emit the + // assignment that assigns the function to the outer 'var' binding. + if (assignmentForAnnexB) { + if (assignmentForAnnexB->isKind(PNK_VAR)) { + if (!emitVariables(assignmentForAnnexB, AnnexB)) + return false; + } else { + MOZ_ASSERT(assignmentForAnnexB->isKind(PNK_ASSIGN)); + if (!emitTree(assignmentForAnnexB)) + return false; + if (!emit1(JSOP_POP)) + return false; + } + } + MOZ_ASSERT_IF(fun->hasScript(), fun->nonLazyScript()); MOZ_ASSERT(pn->functionIsHoisted()); - MOZ_ASSERT(sc->isFunctionBox()); return true; } @@ -6318,10 +6419,28 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) * For modules, we record the function and instantiate the binding during * ModuleDeclarationInstantiation(), before the script is run. */ - if (sc->isGlobalContext()) { + + // Check for functions that were parsed under labeled statements per ES6 + // Annex B.3.2. + bool blockScopedFunction = !atBodyLevel(); + if (!sc->strict() && blockScopedFunction) { + StmtInfoBCE* stmt = innermostStmt(); + while (stmt && stmt->type == StmtType::LABEL) + stmt = stmt->enclosing; + blockScopedFunction = !atBodyLevel(stmt); + } + + if (blockScopedFunction) { + if (!emitIndexOp(JSOP_LAMBDA, index)) + return false; + MOZ_ASSERT(pn->getOp() == JSOP_INITLEXICAL); + if (!emitVarOp(pn, pn->getOp())) + return false; + if (!emit1(JSOP_POP)) + return false; + } else if (sc->isGlobalContext()) { MOZ_ASSERT(pn->pn_scopecoord.isFree()); MOZ_ASSERT(pn->getOp() == JSOP_NOP); - MOZ_ASSERT(atBodyLevel()); switchToPrologue(); if (!emitIndex32(JSOP_DEFFUN, index)) return false; @@ -7958,7 +8077,6 @@ BytecodeEmitter::emitArgsBody(ParseNode *pn) // Carefully emit everything in the right order: // 1. Defaults and Destructuring for each argument // 2. Functions - ParseNode* pnchild = pnlast->pn_head; bool hasDefaults = sc->asFunctionBox()->hasDefaults(); ParseNode* rest = nullptr; bool restIsDefn = false; @@ -8019,21 +8137,11 @@ BytecodeEmitter::emitArgsBody(ParseNode *pn) } } if (pnlast->pn_xflags & PNX_FUNCDEFS) { - // This block contains top-level function definitions. To ensure - // that we emit the bytecode defining them before the rest of code - // in the block we use a separate pass over functions. During the - // main pass later the emitter will add JSOP_NOP with source notes - // for the function to preserve the original functions position - // when decompiling. - // - // Currently this is used only for functions, as compile-as-we go - // mode for scripts does not allow separate emitter passes. - for (ParseNode* pn2 = pnchild; pn2; pn2 = pn2->pn_next) { - if (pn2->isKind(PNK_FUNCTION) && pn2->functionIsHoisted()) { - if (!emitTree(pn2)) - return false; - } - } + // This function contains top-level inner function definitions. To + // ensure that we emit the bytecode defining them before the rest + // of code in the block we use a separate pass over functions. + if (!emitHoistedFunctionsInList(pnlast)) + return false; } return emitTree(pnlast); } @@ -8256,6 +8364,19 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) return false; break; + case PNK_ANNEXB_FUNCTION: + // XXXshu NOP check used only for phasing in block-scope function + // XXXshu early errors. + // XXXshu + // XXXshu Back out when major version >= 50. See [1]. + // XXXshu + // XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10 + if (pn->pn_left->isKind(PNK_NOP)) + break; + if (!emitFunction(pn)) + return false; + break; + case PNK_ARGSBODY: if (!emitArgsBody(pn)) return false; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 57837e275..7d4b31d1c 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -121,7 +121,12 @@ enum VarEmitOption { // Emit code to evaluate initializer expressions and leave those values on // the stack. This is used to implement `for (let/const ...;;)` and // deprecated `let` blocks. - PushInitialValues + PushInitialValues, + + // Like InitializeVars, but bind using BINDVAR instead of + // BINDNAME/BINDGNAME. Only used for emitting declarations synthesized for + // Annex B block-scoped function semantics. + AnnexB, }; struct BytecodeEmitter @@ -249,7 +254,10 @@ struct BytecodeEmitter return parser->blockScopes[dn->pn_blockid]; } - bool atBodyLevel() const; + bool atBodyLevel(StmtInfoBCE* stmt) const; + bool atBodyLevel() const { + return atBodyLevel(innermostStmt()); + } uint32_t computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut); bool isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn); bool computeDefinitionIsAliased(BytecodeEmitter* bceOfDef, Definition* dn, JSOp* op); @@ -464,6 +472,8 @@ struct BytecodeEmitter MOZ_NEVER_INLINE bool emitFunction(ParseNode* pn, bool needsProto = false); MOZ_NEVER_INLINE bool emitObject(ParseNode* pn); + bool emitHoistedFunctionsInList(ParseNode* pn); + bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, PropListType type); // To catch accidental misuse, emitUint16Operand/emit3 assert that they are diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index f226310c2..bd2ffd145 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -100,6 +100,11 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) *result = false; return true; + case PNK_ANNEXB_FUNCTION: + MOZ_ASSERT(node->isArity(PN_BINARY)); + *result = false; + return true; + case PNK_MODULE: *result = false; return true; @@ -1783,6 +1788,17 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo case PNK_FUNCTION: return FoldFunction(cx, pn, parser, inGenexpLambda); + case PNK_ANNEXB_FUNCTION: + // XXXshu NOP check used only for phasing in block-scope function + // XXXshu early errors. + // XXXshu + // XXXshu Back out when major version >= 50. See [1]. + // XXXshu + // XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10 + if (pn->pn_left->isKind(PNK_NOP)) + return true; + return FoldFunction(cx, pn->pn_left, parser, inGenexpLambda); + case PNK_MODULE: return FoldModule(cx, pn, parser); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 031f78ec7..23e2110db 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -245,6 +245,21 @@ class FullParseHandler return new_(kind, op, pos, kid); } + ParseNode* newUpdate(ParseNodeKind kind, uint32_t begin, ParseNode* kid) { + TokenPos pos(begin, kid->pn_pos.end); + return new_(kind, JSOP_NOP, pos, kid); + } + + ParseNode* newSpread(uint32_t begin, ParseNode* kid) { + TokenPos pos(begin, kid->pn_pos.end); + return new_(PNK_SPREAD, JSOP_NOP, pos, kid); + } + + ParseNode* newArrayPush(uint32_t begin, ParseNode* kid) { + TokenPos pos(begin, kid->pn_pos.end); + return new_(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, pos, kid); + } + ParseNode* newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) { return new_(kind, op, pos(), (ParseNode*) nullptr, (ParseNode*) nullptr); } @@ -444,24 +459,40 @@ class FullParseHandler return pn; } + template + bool isFunctionStmt(ParseNode* stmt, PC* pc) { + if (!pc->sc->strict()) { + while (stmt->isKind(PNK_LABEL)) + stmt = stmt->as().statement(); + } + + return stmt->isKind(PNK_FUNCTION) || stmt->isKind(PNK_ANNEXB_FUNCTION); + } + template void addStatementToList(ParseNode* list, ParseNode* stmt, PC* pc) { MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST)); - if (stmt->isKind(PNK_FUNCTION)) { - if (pc->atBodyLevel()) { - // PNX_FUNCDEFS notifies the emitter that the block contains - // body-level function definitions that should be processed - // before the rest of nodes. - list->pn_xflags |= PNX_FUNCDEFS; - } else { - // General deoptimization was done in Parser::functionDef. - MOZ_ASSERT_IF(pc->sc->isFunctionBox(), - pc->sc->asFunctionBox()->hasExtensibleScope()); - } - } - list->append(stmt); + + if (isFunctionStmt(stmt, pc)) { + // PNX_FUNCDEFS notifies the emitter that the block contains + // body-level function definitions that should be processed + // before the rest of nodes. + list->pn_xflags |= PNX_FUNCDEFS; + } + } + + template + void addCaseStatementToList(ParseNode* list, ParseNode* casepn, PC* pc) { + MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST)); + MOZ_ASSERT(casepn->isKind(PNK_CASE)); + MOZ_ASSERT(casepn->pn_right->isKind(PNK_STATEMENTLIST)); + + list->append(casepn); + + if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS) + list->pn_xflags |= PNX_FUNCDEFS; } bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) { @@ -656,6 +687,11 @@ class FullParseHandler MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); pn->pn_funbox = funbox; } + ParseNode* newFunctionDefinitionForAnnexB(ParseNode* pn, ParseNode* assignment) { + MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); + MOZ_ASSERT(assignment->isKind(PNK_ASSIGN) || assignment->isKind(PNK_VAR)); + return new_(PNK_ANNEXB_FUNCTION, JSOP_NOP, pos(), pn, assignment); + } void addFunctionArgument(ParseNode* pn, ParseNode* argpn) { pn->pn_body->append(argpn); } @@ -713,6 +749,15 @@ class FullParseHandler return false; } + bool isUnparenthesizedUnaryExpression(ParseNode* node) { + if (!node->isInParens()) { + ParseNodeKind kind = node->getKind(); + return kind == PNK_VOID || kind == PNK_NOT || kind == PNK_BITNOT || kind == PNK_POS || + kind == PNK_NEG || IsTypeofKind(kind) || IsDeleteKind(kind); + } + return false; + } + bool isReturnStatement(ParseNode* node) { return node->isKind(PNK_RETURN); } @@ -727,7 +772,7 @@ class FullParseHandler return node->isKind(PNK_SUPERBASE); } - inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op); + inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init); inline void setLexicalDeclarationOp(ParseNode* pn, JSOp op); void setBeginPosition(ParseNode* pn, ParseNode* oth) { @@ -990,7 +1035,7 @@ FullParseHandler::setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseN } inline bool -FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op) +FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init) { if (pn->isUsed()) { pn = makeAssignment(pn, init); diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index cbc6578b1..d9f97c1c5 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -791,6 +791,12 @@ class NameResolver return false; break; + case PNK_ANNEXB_FUNCTION: + MOZ_ASSERT(cur->isArity(PN_BINARY)); + if (!resolve(cur->pn_left, prefix)) + return false; + break; + // Kinds that should be handled by parent node resolution. case PNK_IMPORT_SPEC: // by PNK_IMPORT_SPEC_LIST diff --git a/js/src/frontend/ParseMaps.h b/js/src/frontend/ParseMaps.h index 899a78e0e..6d10fa631 100644 --- a/js/src/frontend/ParseMaps.h +++ b/js/src/frontend/ParseMaps.h @@ -441,6 +441,18 @@ class AtomDecls return p.value().front(); } + /* Return the definition at the tail of the chain for |atom|. */ + DefinitionNode lookupLast(JSAtom* atom) const { + MOZ_ASSERT(map); + DefinitionList::Range range = lookupMulti(atom); + DefinitionNode dn = ParseHandler::nullDefinition(); + while (!range.empty()) { + dn = range.front(); + range.popFront(); + } + return dn; + } + /* Perform a lookup that can iterate over the definitions associated with |atom|. */ DefinitionList::Range lookupMulti(JSAtom* atom) const { MOZ_ASSERT(map); diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 245831c07..0dfeb0cc4 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -283,7 +283,8 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_NEWTARGET: case PNK_SETTHIS: case PNK_FOR: - case PNK_COMPREHENSIONFOR: { + case PNK_COMPREHENSIONFOR: + case PNK_ANNEXB_FUNCTION: { MOZ_ASSERT(pn->isArity(PN_BINARY)); stack->push(pn->pn_left); stack->push(pn->pn_right); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index d70a06892..67de5661d 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -165,6 +165,7 @@ class PackedScopeCoordinate F(FORIN) \ F(FOROF) \ F(FORHEAD) \ + F(ANNEXB_FUNCTION) \ F(ARGSBODY) \ F(SPREAD) \ F(MUTATEPROTO) \ @@ -258,6 +259,12 @@ IsDeleteKind(ParseNodeKind kind) return PNK_DELETENAME <= kind && kind <= PNK_DELETEEXPR; } +inline bool +IsTypeofKind(ParseNodeKind kind) +{ + return PNK_TYPEOFNAME <= kind && kind <= PNK_TYPEOFEXPR; +} + /* * Label Variant Members * ----- ------- ------- @@ -271,6 +278,9 @@ IsDeleteKind(ParseNodeKind kind) * pn_scopecoord: hops and var index for function * pn_dflags: PND_* definition/use flags (see below) * pn_blockid: block id number + * PNK_ANNEXB_FUNCTION binary pn_left: PNK_FUNCTION + * pn_right: assignment for annex B semantics for + * block-scoped function * PNK_ARGSBODY list list of formal parameters with * PNK_NAME node with non-empty name for * SingleNameBinding without Initializer @@ -428,7 +438,6 @@ IsDeleteKind(ParseNodeKind kind) * PNK_DELETENAME unary pn_kid: PNK_NAME expr * PNK_DELETEPROP unary pn_kid: PNK_DOT expr * PNK_DELETEELEM unary pn_kid: PNK_ELEM expr - * PNK_DELETESUPERELEM unary pn_kid: PNK_SUPERELEM expr * PNK_DELETEEXPR unary pn_kid: MEMBER expr that's evaluated, then the * overall delete evaluates to true; can't be a kind * for a more-specific PNK_DELETE* unless constant @@ -780,7 +789,8 @@ class ParseNode isOp(JSOP_DEFFUN) || // non-body-level function statement isOp(JSOP_NOP) || // body-level function stmt in global code isOp(JSOP_GETLOCAL) || // body-level function stmt in function code - isOp(JSOP_GETARG)); // body-level function redeclaring formal + isOp(JSOP_GETARG) || // body-level function redeclaring formal + isOp(JSOP_INITLEXICAL)); // block-level function stmt return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN); } @@ -1607,6 +1617,8 @@ struct Definition : public ParseNode IMPORT }; + static bool test(const ParseNode& pn) { return pn.isDefn(); } + bool canHaveInitializer() { return int(kind()) <= int(ARG); } static const char* kindString(Kind kind); @@ -1615,6 +1627,8 @@ struct Definition : public ParseNode if (getKind() == PNK_FUNCTION) { if (isOp(JSOP_GETARG)) return ARG; + if (isOp(JSOP_INITLEXICAL)) + return LET; return VAR; } MOZ_ASSERT(getKind() == PNK_NAME); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index e5d20dbfb..764bf48b2 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -103,9 +103,7 @@ ParseContext::checkLocalsOverflow(TokenStream& ts) static void MarkUsesAsHoistedLexical(ParseNode* pn) { - MOZ_ASSERT(pn->isDefn()); - - Definition* dn = (Definition*)pn; + Definition* dn = &pn->as(); ParseNode** pnup = &dn->dn_uses; ParseNode* pnu; unsigned start = pn->pn_blockid; @@ -222,6 +220,8 @@ ParseContext::define(TokenStream& ts, MOZ_ASSERT(!pn->isUsed()); MOZ_ASSERT_IF(pn->isDefn(), pn->isPlaceholder()); + pn->setDefn(true); + Definition* prevDef = nullptr; if (kind == Definition::LET || kind == Definition::CONSTANT) prevDef = decls_.lookupFirst(name); @@ -240,7 +240,7 @@ ParseContext::define(TokenStream& ts, while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) { MOZ_ASSERT(pnu->pn_blockid >= bodyid); MOZ_ASSERT(pnu->isUsed()); - pnu->pn_lexdef = (Definition*) pn; + pnu->pn_lexdef = &pn->as(); pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; pnup = &pnu->pn_link; } @@ -258,12 +258,11 @@ ParseContext::define(TokenStream& ts, } MOZ_ASSERT_IF(kind != Definition::LET && kind != Definition::CONSTANT, !lexdeps->lookup(name)); - pn->setDefn(true); pn->pn_dflags &= ~PND_PLACEHOLDER; if (kind == Definition::CONSTANT) pn->pn_dflags |= PND_CONST; - Definition* dn = (Definition*)pn; + Definition* dn = &pn->as(); switch (kind) { case Definition::ARG: MOZ_ASSERT(sc->isFunctionBox()); @@ -393,9 +392,27 @@ ParseContext::updateDecl(TokenStream& ts, JSAtom* atom, Node pn) Definition* oldDecl = decls_.lookupFirst(atom); pn->setDefn(true); - Definition* newDecl = (Definition*)pn; + Definition* newDecl = &pn->template as(); decls_.updateFirst(atom, newDecl); + if (oldDecl->isOp(JSOP_INITLEXICAL)) { + // XXXshu Special case used only for phasing in block-scope function + // XXXshu early errors. + // XXXshu + // XXXshu Back out when major version >= 50. See [1]. + // XXXshu + // XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10 + MOZ_ASSERT(oldDecl->getKind() == PNK_FUNCTION); + MOZ_ASSERT(newDecl->getKind() == PNK_FUNCTION); + MOZ_ASSERT(!sc->strict()); + MOZ_ASSERT(oldDecl->isBound()); + MOZ_ASSERT(!oldDecl->pn_scopecoord.isFree()); + newDecl->pn_scopecoord = oldDecl->pn_scopecoord; + newDecl->pn_dflags |= PND_BOUND; + newDecl->setOp(JSOP_INITLEXICAL); + return; + } + if (sc->isGlobalContext() || oldDecl->isDeoptimized()) { MOZ_ASSERT(newDecl->isFreeVar()); // Global 'var' bindings have no slots, but are still tracked for @@ -1203,18 +1220,6 @@ template <> bool Parser::checkFunctionArguments() { - /* - * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope - * operation which means it aliases any bindings with the same name. - */ - if (FuncStmtSet* set = pc->funcStmts) { - for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) { - PropertyName* name = r.front()->asPropertyName(); - if (Definition* dn = pc->decls().lookupFirst(name)) - dn->pn_dflags |= PND_CLOSED; - } - } - /* Time to implement the odd semantics of 'arguments'. */ HandlePropertyName arguments = context->names().arguments; @@ -1409,7 +1414,7 @@ Parser::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt for (ParseNode* pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { MOZ_ASSERT(pnu->isUsed()); MOZ_ASSERT(!pnu->isDefn()); - pnu->pn_lexdef = (Definition*) pn; + pnu->pn_lexdef = &pn->as(); pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; } pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS; @@ -1452,7 +1457,7 @@ Parser::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt return false; pn->dn_uses = lhs; dn->pn_link = nullptr; - dn = (Definition*) lhs; + dn = &lhs->as(); } } @@ -1463,7 +1468,7 @@ Parser::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt dn->setOp((CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_GETNAME); dn->setDefn(false); dn->setUsed(true); - dn->pn_lexdef = (Definition*) pn; + dn->pn_lexdef = &pn->as(); dn->pn_scopecoord.makeFree(); dn->pn_dflags &= ~PND_BOUND; return true; @@ -1495,18 +1500,22 @@ struct BindData void initLexical(VarContext varContext, JSOp op, StaticBlockObject* blockObj, unsigned overflow) { - init(LexicalBinding, op, op == JSOP_DEFCONST); + init(LexicalBinding, op, op == JSOP_DEFCONST, false); letData_.varContext = varContext; letData_.blockObj = blockObj; letData_.overflow = overflow; } void initVar(JSOp op) { - init(VarBinding, op, false); + init(VarBinding, op, false, false); + } + + void initAnnexBVar() { + init(VarBinding, JSOP_DEFVAR, false, true); } void initDestructuring(JSOp op) { - init(DestructuringBinding, op, false); + init(DestructuringBinding, op, false, false); } void setNameNode(typename ParseHandler::Node pn) { @@ -1529,6 +1538,11 @@ struct BindData return isConst_; } + bool isAnnexB() { + MOZ_ASSERT(isInitialized()); + return isAnnexB_; + } + const LetData& letData() { MOZ_ASSERT(kind_ == LexicalBinding); return letData_; @@ -1565,17 +1579,19 @@ struct BindData JSOp op_; // Prologue bytecode or nop. bool isConst_; // Whether this is a const binding. + bool isAnnexB_; // Whether this is a synthesized 'var' binding for Annex B.3. LetData letData_; bool isInitialized() { return kind_ != Uninitialized; } - void init(BindingKind kind, JSOp op, bool isConst) { + void init(BindingKind kind, JSOp op, bool isConst, bool isAnnexB) { MOZ_ASSERT(!isInitialized()); kind_ = kind; op_ = op; isConst_ = isConst; + isAnnexB_ = isAnnexB; } }; @@ -1795,7 +1811,6 @@ bool Parser::leaveFunction(ParseNode* fn, ParseContext* outerpc, FunctionSyntaxKind kind) { - bool bodyLevel = outerpc->atBodyLevel(); FunctionBox* funbox = fn->pn_funbox; MOZ_ASSERT(funbox == pc->sc->asFunctionBox()); @@ -1865,10 +1880,10 @@ Parser::leaveFunction(ParseNode* fn, ParseContextdn_uses) { // In ES6, lexical bindings cannot be accessed until - // initialized. If we are parsing a body-level function, - // it is hoisted to the top, so we conservatively mark all - // uses linked to an outer lexical binding as needing TDZ - // checks. e.g., + // initialized. If we are parsing a function statement it + // is hoisted to the top of its lexical scope, so we + // conservatively mark all uses linked to an outer lexical + // binding as needing TDZ checks. e.g., // // function outer() { // inner2(); @@ -1891,7 +1906,7 @@ Parser::leaveFunction(ParseNode* fn, ParseContextisLexical() && - (bodyLevel || + (kind == Statement || IsNonDominatingInScopedSwitch(outerpc, name, outer_dn)); AssociateUsesWithOuterDefinition(pnu, dn, outer_dn, markUsesAsLexical); } @@ -2239,138 +2254,237 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn return true; } +template <> +bool +Parser::bindBodyLevelFunctionName(HandlePropertyName funName, + ParseNode** pn_) +{ + MOZ_ASSERT(pc->atBodyLevel() || !pc->sc->strict()); + + ParseNode*& pn = *pn_; + + /* + * Handle redeclaration and optimize cases where we can statically bind the + * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). + */ + if (Definition* dn = pc->decls().lookupFirst(funName)) { + MOZ_ASSERT(!dn->isUsed()); + MOZ_ASSERT(dn->isDefn()); + + if (dn->kind() == Definition::CONSTANT || dn->kind() == Definition::LET) + return reportRedeclaration(nullptr, Definition::VAR, funName); + + /* + * Body-level function statements are effectively variable + * declarations where the initialization is hoisted to the + * beginning of the block. This means that any other variable + * declaration with the same name is really just an assignment to + * the function's binding (which is mutable), so turn any existing + * declaration into a use. + */ + if (dn->kind() == Definition::ARG) { + // The exception to the above comment is when the function + // has the same name as an argument. Then the argument node + // remains a definition. But change the function node pn so + // that it knows where the argument is located. + pn->setOp(JSOP_GETARG); + pn->setDefn(true); + pn->pn_scopecoord = dn->pn_scopecoord; + pn->pn_blockid = dn->pn_blockid; + pn->pn_dflags |= PND_BOUND; + dn->markAsAssigned(); + } else { + if (!makeDefIntoUse(dn, pn, funName)) + return false; + } + } else { + /* + * If this function was used before it was defined, claim the + * pre-created definition node for this function that primaryExpr + * put in pc->lexdeps on first forward reference, and recycle pn. + */ + if (Definition* fn = pc->lexdeps.lookupDefn(funName)) { + MOZ_ASSERT(fn->isDefn()); + fn->setKind(PNK_FUNCTION); + fn->setArity(PN_CODE); + fn->pn_pos.begin = pn->pn_pos.begin; + fn->pn_pos.end = pn->pn_pos.end; + + fn->pn_body = nullptr; + fn->pn_scopecoord.makeFree(); + + pc->lexdeps->remove(funName); + handler.freeTree(pn); + pn = fn; + } + + if (!pc->define(tokenStream, funName, pn, Definition::VAR)) + return false; + } + + /* No further binding (in BindNameToSlot) is needed for functions. */ + pn->pn_dflags |= PND_BOUND; + + MOZ_ASSERT(pn->functionIsHoisted()); + MOZ_ASSERT(pc->sc->isGlobalContext() == pn->pn_scopecoord.isFree()); + + return true; +} + +template <> +bool +Parser::bindLexicalFunctionName(HandlePropertyName funName, + ParseNode* pn); + template <> bool Parser::checkFunctionDefinition(HandlePropertyName funName, ParseNode** pn_, FunctionSyntaxKind kind, - bool* pbodyProcessed) + bool* pbodyProcessed, + ParseNode** assignmentForAnnexBOut) { ParseNode*& pn = *pn_; *pbodyProcessed = false; - /* Function statements add a binding to the enclosing scope. */ - bool bodyLevel = pc->atBodyLevel(); - if (kind == Statement) { - /* - * Handle redeclaration and optimize cases where we can statically bind the - * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). - */ - if (Definition* dn = pc->decls().lookupFirst(funName)) { - MOZ_ASSERT(!dn->isUsed()); - MOZ_ASSERT(dn->isDefn()); + MOZ_ASSERT(assignmentForAnnexBOut); + *assignmentForAnnexBOut = nullptr; - bool throwRedeclarationError = dn->kind() == Definition::CONSTANT || - dn->kind() == Definition::LET; - if (options().extraWarningsOption || throwRedeclarationError) { - JSAutoByteString name; - ParseReportKind reporter = throwRedeclarationError - ? ParseError - : ParseExtraWarning; - if (!AtomToPrintableString(context, funName, &name) || - !report(reporter, false, nullptr, JSMSG_REDECLARED_VAR, - Definition::kindString(dn->kind()), name.ptr())) - { + // In sloppy mode, ES6 Annex B.3.2 allows labelled function + // declarations. Otherwise it is a parse error. + bool bodyLevelFunction = pc->atBodyLevel(); + if (!bodyLevelFunction) { + StmtInfoPC* stmt = pc->innermostStmt(); + if (stmt->type == StmtType::LABEL) { + if (pc->sc->strict()) { + report(ParseError, false, null(), JSMSG_FUNCTION_LABEL); return false; } - } - /* - * Body-level function statements are effectively variable - * declarations where the initialization is hoisted to the - * beginning of the block. This means that any other variable - * declaration with the same name is really just an assignment to - * the function's binding (which is mutable), so turn any existing - * declaration into a use. - */ - if (bodyLevel) { - if (dn->kind() == Definition::ARG) { - // The exception to the above comment is when the function - // has the same name as an argument. Then the argument node - // remains a definition. But change the function node pn so - // that it knows where the argument is located. - pn->setOp(JSOP_GETARG); - pn->setDefn(true); - pn->pn_scopecoord = dn->pn_scopecoord; - pn->pn_blockid = dn->pn_blockid; - pn->pn_dflags |= PND_BOUND; - dn->markAsAssigned(); - } else { - if (!makeDefIntoUse(dn, pn, funName)) - return false; + stmt = pc->innermostNonLabelStmt(); + // A switch statement is always braced, so it's okay to label + // functions in sloppy mode under switch. + if (stmt && stmt->type != StmtType::BLOCK && stmt->type != StmtType::SWITCH) { + report(ParseError, false, null(), JSMSG_SLOPPY_FUNCTION_LABEL); + return false; } + + bodyLevelFunction = pc->atBodyLevel(stmt); } - } else if (bodyLevel) { - /* - * If this function was used before it was defined, claim the - * pre-created definition node for this function that primaryExpr - * put in pc->lexdeps on first forward reference, and recycle pn. - */ - if (Definition* fn = pc->lexdeps.lookupDefn(funName)) { - MOZ_ASSERT(fn->isDefn()); - fn->setKind(PNK_FUNCTION); - fn->setArity(PN_CODE); - fn->pn_pos.begin = pn->pn_pos.begin; - fn->pn_pos.end = pn->pn_pos.end; - - fn->pn_body = nullptr; - fn->pn_scopecoord.makeFree(); - - pc->lexdeps->remove(funName); - handler.freeTree(pn); - pn = fn; - } - - if (!pc->define(tokenStream, funName, pn, Definition::VAR)) - return false; } - if (bodyLevel) { - MOZ_ASSERT(pn->functionIsHoisted()); - MOZ_ASSERT(pc->sc->isGlobalContext() == pn->pn_scopecoord.isFree()); + if (bodyLevelFunction) { + if (!bindBodyLevelFunctionName(funName, pn_)) + return false; } else { - /* - * As a SpiderMonkey-specific extension, non-body-level function - * statements (e.g., functions in an "if" or "while" block) are - * dynamically bound when control flow reaches the statement. - */ - MOZ_ASSERT(!pc->sc->strict()); - MOZ_ASSERT(pn->pn_scopecoord.isFree()); - if (pc->sc->isFunctionBox()) { - FunctionBox* funbox = pc->sc->asFunctionBox(); - funbox->setMightAliasLocals(); - funbox->setHasExtensibleScope(); - } - pn->setOp(JSOP_DEFFUN); + Definition* annexDef = nullptr; + Node synthesizedDeclarationList = null(); - /* - * Instead of setting bindingsAccessedDynamically, which would be - * overly conservative, remember the names of all function - * statements and mark any bindings with the same as aliased at the - * end of functionBody. - */ - if (!pc->funcStmts) { - pc->funcStmts = alloc.new_(alloc); - if (!pc->funcStmts || !pc->funcStmts->init()) { - ReportOutOfMemory(context); - return false; + if (!pc->sc->strict()) { + // Under non-strict mode, try ES6 Annex B.3.3 semantics. If + // making an additional 'var' binding of the same name does + // not throw an early error, do so. This 'var' binding would + // be assigned the function object when its declaration is + // reached, not at the start of the block. + + annexDef = pc->decls().lookupFirst(funName); + if (annexDef) { + if (annexDef->kind() == Definition::CONSTANT || + annexDef->kind() == Definition::LET) + { + if (annexDef->isKind(PNK_FUNCTION)) { + // XXXshu Code used only for phasing in block-scope + // XXXshu function early errors. Ignore redeclarations + // XXXshu here and generate Annex B assignments for + // XXXshu block-scoped functions that redeclare other + // XXXshu block-scoped functions. + // XXXshu + // XXXshu Get the possibly-synthesized var that was + // XXXshu already made for the first of the block-scoped + // XXXshu functions. + // XXXshu + // XXXshu Back out when major version >= 50. See [1]. + // XXXshu + // XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10 + annexDef = pc->decls().lookupLast(funName); + if (annexDef->kind() == Definition::CONSTANT || + annexDef->kind() == Definition::LET) + { + annexDef = nullptr; + } + } else { + // Do not emit Annex B assignment if we would've + // thrown a redeclaration error. + annexDef = nullptr; + } + } + } else { + // Synthesize a new 'var' binding if one does not exist. + ParseNode* varNode = newBindingNode(funName, /* functionScope = */ true); + if (!varNode) + return false; + + // Treat the 'var' binding as body level. Otherwise the + // lexical binding of the function name below would result + // in a redeclaration. That is, + // { var x; let x; } is an early error. + // var x; { let x; } is not. + varNode->pn_blockid = pc->bodyid; + + BindData data(context); + data.initAnnexBVar(); + data.setNameNode(varNode); + if (!data.bind(funName, this)) + return false; + + annexDef = &varNode->as(); + + synthesizedDeclarationList = handler.newDeclarationList(PNK_VAR, JSOP_DEFVAR); + if (!synthesizedDeclarationList) + return false; + handler.addList(synthesizedDeclarationList, annexDef); } } - if (!pc->funcStmts->put(funName)) + + if (!bindLexicalFunctionName(funName, pn)) return false; - /* - * Due to the implicit declaration mechanism, 'arguments' will not - * have decls and, even if it did, they will not be noted as closed - * in the emitter. Thus, in the corner case of function statements - * overridding arguments, flag the whole scope as dynamic. - */ - if (funName == context->names().arguments) - pc->sc->setBindingsAccessedDynamically(); - } + if (annexDef) { + MOZ_ASSERT(!pc->sc->strict()); - /* No further binding (in BindNameToSlot) is needed for functions. */ - pn->pn_dflags |= PND_BOUND; + // Synthesize an assignment assigning the lexical name to the + // 'var' name for Annex B. + + ParseNode* rhs = newName(funName); + if (!rhs) + return false; + if (!noteNameUse(funName, rhs)) + return false; + + // If we synthesized a new definition, emit the declaration to + // ensure DEFVAR is correctly emitted in global scripts. + // Otherwise, synthesize a simple assignment and emit that. + if (synthesizedDeclarationList) { + if (!handler.finishInitializerAssignment(annexDef, rhs)) + return false; + *assignmentForAnnexBOut = synthesizedDeclarationList; + } else { + ParseNode* lhs = newName(funName); + if (!lhs) + return false; + lhs->setOp(JSOP_SETNAME); + + // Manually link up the LHS with the non-lexical definition. + handler.linkUseToDef(lhs, annexDef); + + ParseNode* assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs, pc, JSOP_NOP); + if (!assign) + return false; + + *assignmentForAnnexBOut = assign; + } + } + } } else { /* A function expression does not introduce any binding. */ pn->setOp(kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA); @@ -2479,7 +2593,8 @@ template <> bool Parser::checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind, - bool* pbodyProcessed) + bool* pbodyProcessed, + Node* assignmentForAnnexBOut) { *pbodyProcessed = false; @@ -2487,10 +2602,18 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, bool bodyLevel = pc->atBodyLevel(); if (kind == Statement) { + *assignmentForAnnexBOut = null(); + + if (!bodyLevel) { + // Block-scoped functions cannot yet be parsed lazily. + return abortIfSyntaxParser(); + } + /* * Handle redeclaration and optimize cases where we can statically bind the * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). */ + if (DefinitionNode dn = pc->decls().lookupFirst(funName)) { if (dn == Definition::CONSTANT || dn == Definition::LET) { JSAutoByteString name; @@ -2501,16 +2624,13 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, return false; } } - } else if (bodyLevel) { + } else { if (pc->lexdeps.lookupDefn(funName)) pc->lexdeps->remove(funName); if (!pc->define(tokenStream, funName, *pn, Definition::VAR)) return false; } - - if (!bodyLevel && funName == context->names().arguments) - pc->sc->setBindingsAccessedDynamically(); } if (kind == Arrow) { @@ -2591,7 +2711,8 @@ template typename ParseHandler::Node Parser::functionDef(InHandling inHandling, YieldHandling yieldHandling, HandlePropertyName funName, FunctionSyntaxKind kind, - GeneratorKind generatorKind, InvokedPrediction invoked) + GeneratorKind generatorKind, InvokedPrediction invoked, + Node* assignmentForAnnexBOut) { MOZ_ASSERT_IF(kind == Statement, funName); @@ -2599,12 +2720,13 @@ Parser::functionDef(InHandling inHandling, YieldHandling yieldHand Node pn = handler.newFunctionDefinition(); if (!pn) return null(); + handler.setBlockId(pn, pc->blockid()); if (invoked) pn = handler.setLikelyIIFE(pn); bool bodyProcessed; - if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed)) + if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed, assignmentForAnnexBOut)) return null(); if (bodyProcessed) @@ -2791,7 +2913,6 @@ Parser::functionArgsAndBody(InHandling inHandling, ParseNode* if (!addFreeVariablesFromLazyFunction(fun, pc)) return false; - pn->pn_blockid = outerpc->blockid(); PropagateTransitiveParseFlags(funbox, outerpc->sc); return true; } while (false); @@ -2809,8 +2930,6 @@ Parser::functionArgsAndBody(InHandling inHandling, ParseNode* if (!leaveFunction(pn, outerpc, kind)) return false; - pn->pn_blockid = outerpc->blockid(); - /* * Fruit of the poisonous tree: if a closure contains a dynamic name access * (eval, with, etc), we consider the parent to do the same. The reason is @@ -3052,6 +3171,24 @@ Parser::functionStmt(YieldHandling yieldHandling, DefaultHandling { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); + // ES6 Annex B.3.4 says we can parse function declarations unbraced under if or + // else as if it were braced. That is, |if (x) function f() {}| is parsed as + // |if (x) { function f() {} }|. + Maybe synthesizedStmtInfoForAnnexB; + Node synthesizedBlockForAnnexB = null(); + StmtInfoPC *stmt = pc->innermostStmt(); + if (!pc->sc->strict() && stmt) { + if (stmt->type == StmtType::IF || stmt->type == StmtType::ELSE) { + if (!abortIfSyntaxParser()) + return null(); + + synthesizedStmtInfoForAnnexB.emplace(*this, StmtType::BLOCK); + synthesizedBlockForAnnexB = pushLexicalScope(*synthesizedStmtInfoForAnnexB); + if (!synthesizedBlockForAnnexB) + return null(); + } + } + RootedPropertyName name(context); GeneratorKind generatorKind = NotGenerator; TokenKind tt; @@ -3079,12 +3216,35 @@ Parser::functionStmt(YieldHandling yieldHandling, DefaultHandling return null(); } - /* We forbid function statements in strict mode code. */ - if (!pc->atBodyLevel() && pc->sc->needStrictChecks() && - !report(ParseStrictError, pc->sc->strict(), null(), JSMSG_STRICT_FUNCTION_STATEMENT)) + Node assignmentForAnnexB; + Node fun = functionDef(InAllowed, yieldHandling, name, Statement, generatorKind, + PredictUninvoked, &assignmentForAnnexB); + if (!fun) return null(); - return functionDef(InAllowed, yieldHandling, name, Statement, generatorKind); + if (assignmentForAnnexB) { + fun = handler.newFunctionDefinitionForAnnexB(fun, assignmentForAnnexB); + if (!fun) + return null(); + } + + // Note that we may have synthesized a block for Annex B.3.4 without + // having synthesized an assignment for Annex B.3.3, e.g., + // + // let f = 1; + // { + // if (1) function f() {} + // } + if (synthesizedBlockForAnnexB) { + Node body = handler.newStatementList(pc->blockid(), handler.getPosition(fun)); + if (!body) + return null(); + handler.addStatementToList(body, fun, pc); + handler.setLexicalScopeBody(synthesizedBlockForAnnexB, body); + return synthesizedBlockForAnnexB; + } + + return fun; } template @@ -3487,8 +3647,41 @@ Parser::bindLexical(BindData* data, // The reason we compare using >= instead of == on the block id is to // detect redeclarations where a 'var' binding first appeared in a // nested block: |{ var x; } let x;| - if (dn && dn->pn_blockid >= pc->blockid()) + if (dn && dn->pn_blockid >= pc->blockid()) { + // XXXshu Used only for phasing in block-scope function early + // XXXshu errors. + // XXXshu + // XXXshu Back out when major version >= 50. See [1]. + // XXXshu + // XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10 + if (pn->isKind(PNK_FUNCTION) && dn->isKind(PNK_FUNCTION) && !pc->sc->strict()) { + if (!parser->makeDefIntoUse(dn, pn, name)) + return false; + + MOZ_ASSERT(blockObj); + Shape* shape = blockObj->lastProperty()->search(cx, NameToId(name)); + MOZ_ASSERT(shape); + uint32_t oldDefIndex = blockObj->shapeToIndex(*shape); + blockObj->updateDefinitionParseNode(oldDefIndex, dn, + reinterpret_cast(pn)); + + //parser->addTelemetry(JSCompartment::DeprecatedBlockScopeFunRedecl); + JSAutoByteString bytes; + if (!AtomToPrintableString(cx, name, &bytes)) + return false; + if (!parser->report(ParseWarning, false, null(), + JSMSG_DEPRECATED_BLOCK_SCOPE_FUN_REDECL, + bytes.ptr())) + { + return false; + } + + return true; + } + return parser->reportRedeclaration(pn, dn->kind(), name); + } + if (!pc->define(parser->tokenStream, name, pn, bindingKind)) return false; } @@ -3688,19 +3881,24 @@ Parser::bindVar(BindData* data, StmtInfoPC* stmt = LexicalLookup(pc, name); if (stmt && stmt->type == StmtType::WITH) { - parser->handler.setFlag(pn, PND_DEOPTIMIZED); - if (pc->sc->isFunctionBox()) { - FunctionBox* funbox = pc->sc->asFunctionBox(); - funbox->setMightAliasLocals(); - } + // Do not deoptimize if we are binding a synthesized 'var' binding for + // Annex B.3.3, which states that the synthesized binding is to go on + // the nearest VariableEnvironment. Deoptimizing here would + // erroneously emit NAME ops when assigning to the Annex B 'var'. + if (!data->isAnnexB()) { + parser->handler.setFlag(pn, PND_DEOPTIMIZED); + if (pc->sc->isFunctionBox()) { + FunctionBox* funbox = pc->sc->asFunctionBox(); + funbox->setMightAliasLocals(); + } - /* - * Make sure to indicate the need to deoptimize the script's arguments - * object. Mark the function as if it contained a debugger statement, - * which will deoptimize arguments as much as possible. - */ - if (name == cx->names().arguments) - pc->sc->setHasDebuggerStatement(); + // Make sure to indicate the need to deoptimize the script's + // arguments object. Mark the function as if it contained a + // debugger statement, which will deoptimize arguments as much as + // possible. + if (name == cx->names().arguments) + pc->sc->setHasDebuggerStatement(); + } // Find the nearest enclosing non-with scope that defined name, if // any, for redeclaration checks below. @@ -3835,23 +4033,27 @@ Parser::noteNameUse(HandlePropertyName name, Node pn) template <> bool -Parser::bindUninitialized(BindData* data, ParseNode* pn) +Parser::bindUninitialized(BindData* data, HandlePropertyName name, + ParseNode* pn) { - MOZ_ASSERT(pn->isKind(PNK_NAME)); - - RootedPropertyName name(context, pn->pn_atom->asPropertyName()); - data->setNameNode(pn); - if (!data->bind(name, this)) - return false; - return true; + return data->bind(name, this); } template <> bool -Parser::bindInitialized(BindData* data, ParseNode* pn) +Parser::bindUninitialized(BindData* data, ParseNode* pn) { - if (!bindUninitialized(data, pn)) + RootedPropertyName name(context, pn->name()); + return bindUninitialized(data, name, pn); +} + +template <> +bool +Parser::bindInitialized(BindData* data, HandlePropertyName name, + ParseNode* pn) +{ + if (!bindUninitialized(data, name, pn)) return false; /* @@ -3872,6 +4074,14 @@ Parser::bindInitialized(BindData* data, Pars return true; } +template <> +bool +Parser::bindInitialized(BindData* data, ParseNode* pn) +{ + RootedPropertyName name(context, pn->name()); + return bindInitialized(data, name, pn); +} + template <> bool Parser::checkDestructuringName(BindData* data, ParseNode* expr) @@ -4420,7 +4630,7 @@ Parser::variables(YieldHandling yieldHandling, if (!bindBeforeInitializer && !data.bind(name, this)) return null(); - if (!handler.finishInitializerAssignment(pn2, init, data.op())) + if (!handler.finishInitializerAssignment(pn2, init)) return null(); } } @@ -4441,28 +4651,45 @@ Parser::variables(YieldHandling yieldHandling, template <> bool -Parser::checkAndPrepareLexical(bool isConst, const TokenPos& errorPos) +Parser::checkAndPrepareLexical(PrepareLexicalKind prepareWhat, + const TokenPos& errorPos) { /* - * This is a lexical declaration. We must be directly under a block per the - * proposed ES4 specs, but not an implicit block created due to - * 'for (let ...)'. If we pass this error test, make the enclosing - * StmtInfoPC be our scope. Further let declarations in this block will - * find this scope statement and use the same block object. + * This is a lexical declaration. We must be directly under a block for + * 'let' and 'const' declarations. If we pass this error test, make the + * enclosing StmtInfoPC be our scope. Further let declarations in this + * block will find this scope statement and use the same block object. + * + * Function declarations behave like 'let', except that they are allowed + * per ES6 Annex B.3.2 to be labeled, unlike plain 'let' and 'const' + * declarations. * * If we are the first let declaration in this block (i.e., when the * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then * we also need to set pc->blockNode to be our PNK_LEXICALSCOPE. */ - StmtInfoPC* stmt = pc->innermostStmt(); + + // ES6 Annex B.3.2 does not apply in strict mode, and labeled functions in + // strict mode should have been rejected by checkFunctionDefinition. + MOZ_ASSERT_IF(pc->innermostStmt() && + pc->innermostStmt()->type == StmtType::LABEL && + prepareWhat == PrepareFunction, + !pc->sc->strict()); + + StmtInfoPC* stmt = prepareWhat == PrepareFunction + ? pc->innermostNonLabelStmt() + : pc->innermostStmt(); if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) { - reportWithOffset(ParseError, false, errorPos.begin, JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, - isConst ? "const" : "lexical"); + reportWithOffset(ParseError, false, errorPos.begin, + stmt->type == StmtType::LABEL + ? JSMSG_LEXICAL_DECL_LABEL + : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, + prepareWhat == PrepareConst ? "const" : "lexical"); return false; } if (!stmt) { - MOZ_ASSERT(pc->atBodyLevel()); + MOZ_ASSERT_IF(prepareWhat != PrepareFunction, pc->atBodyLevel()); /* * Self-hosted code must be usable against *any* global object, @@ -4473,7 +4700,7 @@ Parser::checkAndPrepareLexical(bool isConst, const TokenPos& e bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->innermostScopeStmt(); if (options().selfHostingMode && isGlobal) { report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, - isConst ? "'const'" : "'let'"); + prepareWhat == PrepareConst ? "'const'" : "'let'"); return false; } return true; @@ -4499,8 +4726,12 @@ Parser::checkAndPrepareLexical(bool isConst, const TokenPos& e * catch block (catch is a lexical scope by definition). */ MOZ_ASSERT(stmt->canBeBlockScope() && stmt->type != StmtType::CATCH); - - pc->stmtStack.makeInnermostLexicalScope(*blockObj); + if (prepareWhat == PrepareFunction) { + stmt->isBlockScope = true; + pc->stmtStack.linkAsInnermostScopeStmt(stmt, *blockObj); + } else { + pc->stmtStack.makeInnermostLexicalScope(*blockObj); + } MOZ_ASSERT(!blockScopes[stmt->blockid]); blockScopes[stmt->blockid].set(blockObj); @@ -4530,33 +4761,54 @@ CurrentLexicalStaticBlock(ParseContext* pc) } template <> -ParseNode* -Parser::makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, - const TokenPos& pos) +bool +Parser::prepareAndBindInitializedLexicalWithNode(HandlePropertyName name, + PrepareLexicalKind prepareWhat, + ParseNode* pn, + const TokenPos& pos) { BindData data(context); - if (!checkAndPrepareLexical(isConst, pos)) - return null(); - data.initLexical(HoistVars, isConst ? JSOP_DEFCONST : JSOP_DEFLET, + if (!checkAndPrepareLexical(prepareWhat, pos)) + return false; + data.initLexical(HoistVars, prepareWhat == PrepareConst ? JSOP_DEFCONST : JSOP_DEFLET, CurrentLexicalStaticBlock(pc), JSMSG_TOO_MANY_LOCALS); + return bindInitialized(&data, name, pn); +} + +template <> +ParseNode* +Parser::makeInitializedLexicalBinding(HandlePropertyName name, + PrepareLexicalKind prepareWhat, + const TokenPos& pos) +{ ParseNode* dn = newBindingNode(name, false); if (!dn) return null(); handler.setPosition(dn, pos); - if (!bindInitialized(&data, dn)) + if (!prepareAndBindInitializedLexicalWithNode(name, prepareWhat, dn, pos)) return null(); return dn; } +template <> +bool +Parser::bindLexicalFunctionName(HandlePropertyName funName, + ParseNode* pn) +{ + MOZ_ASSERT(!pc->atBodyLevel()); + pn->pn_blockid = pc->blockid(); + return prepareAndBindInitializedLexicalWithNode(funName, PrepareFunction, pn, pos()); +} + template <> ParseNode* Parser::lexicalDeclaration(YieldHandling yieldHandling, bool isConst) { handler.disableSyntaxParser(); - if (!checkAndPrepareLexical(isConst, pos())) + if (!checkAndPrepareLexical(isConst ? PrepareConst : PrepareLet, pos())) return null(); /* @@ -5068,7 +5320,7 @@ Parser::exportDeclaration() default: tokenStream.ungetToken(); RootedPropertyName name(context, context->names().starDefaultStar); - binding = makeInitializedLexicalBinding(name, true, pos()); + binding = makeInitializedLexicalBinding(name, PrepareConst, pos()); if (!binding) return null(); kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited); @@ -5815,7 +6067,7 @@ Parser::switchStatement(YieldHandling yieldHandling) afterReturn = true; } } - handler.addList(body, stmt); + handler.addStatementToList(body, stmt, pc); } // In ES6, lexical bindings cannot be accessed until initialized. If @@ -5834,7 +6086,7 @@ Parser::switchStatement(YieldHandling yieldHandling) Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body); if (!casepn) return null(); - handler.addList(caseList, casepn); + handler.addCaseStatementToList(caseList, casepn, pc); } /* @@ -6662,7 +6914,7 @@ Parser::classDefinition(YieldHandling yieldHandling, ParseNode* nameNode = null(); ParseNode* methodsOrBlock = classMethods; if (name) { - ParseNode* innerBinding = makeInitializedLexicalBinding(name, true, namePos); + ParseNode* innerBinding = makeInitializedLexicalBinding(name, PrepareConst, namePos); if (!innerBinding) return null(); @@ -6673,7 +6925,7 @@ Parser::classDefinition(YieldHandling yieldHandling, ParseNode* outerBinding = null(); if (classContext == ClassStatement) { - outerBinding = makeInitializedLexicalBinding(name, false, namePos); + outerBinding = makeInitializedLexicalBinding(name, PrepareLet, namePos); if (!outerBinding) return null(); } @@ -7098,6 +7350,11 @@ Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling ParseNodeKind pnk; if (tok == TOK_IN ? inHandling == InAllowed : TokenKindIsBinaryOp(tok)) { + // Report an error for unary expressions on the LHS of **. + if (tok == TOK_POW && handler.isUnparenthesizedUnaryExpression(pn)) { + report(ParseError, false, null(), JSMSG_BAD_POW_LEFTSIDE); + return null(); + } pnk = BinaryOpTokenKindToParseNodeKind(tok); } else { tok = TOK_EOF; @@ -7540,10 +7797,9 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment; if (!checkAndMarkAsIncOperand(pn2, flavor)) return null(); - return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, - JSOP_NOP, - begin, - pn2); + return handler.newUpdate((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, + begin, + pn2); } case TOK_DELETE: { @@ -7576,10 +7832,9 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment; if (!checkAndMarkAsIncOperand(pn, flavor)) return null(); - return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, - JSOP_NOP, - begin, - pn); + return handler.newUpdate((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, + begin, + pn); } return pn; } @@ -8415,7 +8670,7 @@ Parser::comprehensionTail(GeneratorKind comprehensionKind) return null(); if (comprehensionKind == NotGenerator) - return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr); + return handler.newArrayPush(begin, bodyExpr); MOZ_ASSERT(comprehensionKind == StarGenerator); Node yieldExpr = newYieldExpression(begin, bodyExpr); @@ -8542,7 +8797,7 @@ Parser::argumentList(YieldHandling yieldHandling, Node listNode, b if (!argNode) return false; if (spread) { - argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode); + argNode = handler.newSpread(begin, argNode); if (!argNode) return false; } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index aa51d6343..a9cbcf167 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -53,7 +53,6 @@ struct StmtInfoPC : public StmtInfoBase {} }; -typedef HashSet, LifoAllocPolicy> FuncStmtSet; class SharedContext; typedef Vector DeclVector; @@ -236,10 +235,6 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext public: OwnedAtomDefnMapPtr lexdeps; /* unresolved lexical name dependencies */ - FuncStmtSet* funcStmts; /* Set of (non-top-level) function statements - that will alias any top-level bindings with - the same name. */ - // All inner functions in this context. Only filled in when parsing syntax. Rooted> innerFunctions; @@ -277,7 +272,6 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext parserPC(&prs->pc), oldpc(prs->pc), lexdeps(prs->context), - funcStmts(nullptr), innerFunctions(prs->context, TraceableVector(prs->context)), newDirectives(newDirectives), inDeclDestructuring(false) @@ -295,6 +289,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext StmtInfoPC* innermostStmt() const { return stmtStack.innermost(); } StmtInfoPC* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); } + StmtInfoPC* innermostNonLabelStmt() const { return stmtStack.innermostNonLabel(); } JSObject* innermostStaticScope() const { if (StmtInfoPC* stmt = innermostScopeStmt()) return stmt->staticScope; @@ -308,19 +303,23 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext // function f1() { function f2() { } } // if (cond) { function f3() { if (cond) { function f4() { } } } } // - bool atBodyLevel() { + bool atBodyLevel(StmtInfoPC* stmt) { // 'eval' and non-syntactic scripts are always under an invisible // lexical scope, but since it is not syntactic, it should still be // considered at body level. if (sc->staticScope()->is()) { - bool bl = !innermostStmt()->enclosing; - MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK); - MOZ_ASSERT_IF(bl, innermostStmt()->staticScope - ->template as() - .enclosingStaticScope() == sc->staticScope()); + bool bl = !stmt->enclosing; + MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK); + MOZ_ASSERT_IF(bl, stmt->staticScope + ->template as() + .enclosingStaticScope() == sc->staticScope()); return bl; } - return !innermostStmt(); + return !stmt; + } + + bool atBodyLevel() { + return atBodyLevel(innermostStmt()); } bool atGlobalLevel() { @@ -728,7 +727,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node functionDef(InHandling inHandling, YieldHandling uieldHandling, HandlePropertyName name, FunctionSyntaxKind kind, GeneratorKind generatorKind, - InvokedPrediction invoked = PredictUninvoked); + InvokedPrediction invoked = PredictUninvoked, + Node* assignmentForAnnexBOut = nullptr); bool functionArgsAndBody(InHandling inHandling, Node pn, HandleFunction fun, FunctionSyntaxKind kind, GeneratorKind generatorKind, Directives inheritedDirectives, Directives* newDirectives); @@ -794,8 +794,10 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node newThisName(); bool makeDefIntoUse(Definition* dn, Node pn, HandleAtom atom); + bool bindLexicalFunctionName(HandlePropertyName funName, ParseNode* pn); + bool bindBodyLevelFunctionName(HandlePropertyName funName, ParseNode** pn); bool checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind, - bool* pbodyProcessed); + bool* pbodyProcessed, Node* assignmentForAnnexBOut); bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body); bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext* pc); @@ -844,8 +846,17 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node objectLiteral(YieldHandling yieldHandling); - bool checkAndPrepareLexical(bool isConst, const TokenPos& errorPos); - Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos& pos); + enum PrepareLexicalKind { + PrepareLet, + PrepareConst, + PrepareFunction + }; + bool checkAndPrepareLexical(PrepareLexicalKind prepareWhat, const TokenPos& errorPos); + bool prepareAndBindInitializedLexicalWithNode(HandlePropertyName name, + PrepareLexicalKind prepareWhat, + ParseNode* pn, const TokenPos& pos); + Node makeInitializedLexicalBinding(HandlePropertyName name, PrepareLexicalKind prepareWhat, + const TokenPos& pos); Node newBindingNode(PropertyName* name, bool functionScope, VarContext varContext = HoistVars); @@ -860,7 +871,9 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter bool checkDestructuringObject(BindData* data, Node objectPattern); bool checkDestructuringName(BindData* data, Node expr); + bool bindInitialized(BindData* data, HandlePropertyName name, Node pn); bool bindInitialized(BindData* data, Node pn); + bool bindUninitialized(BindData* data, HandlePropertyName name, Node pn); bool bindUninitialized(BindData* data, Node pn); bool makeSetCall(Node node, unsigned errnum); Node cloneDestructuringDefault(Node opn); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 80c533f2a..c07595112 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -605,6 +605,12 @@ class MOZ_STACK_CLASS StmtInfoStack StmtInfo* innermost() const { return innermostStmt_; } StmtInfo* innermostScopeStmt() const { return innermostScopeStmt_; } + StmtInfo* innermostNonLabel() const { + StmtInfo* stmt = innermost(); + while (stmt && stmt->type == StmtType::LABEL) + stmt = stmt->enclosing; + return stmt; + } void push(StmtInfo* stmt, StmtType type) { stmt->type = type; diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 3785ffd18..687ec59e2 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -131,6 +131,11 @@ class SyntaxParseHandler // eventually enforce extraWarnings and will require this then.) NodeUnparenthesizedAssignment, + // This node is necessary to determine if the base operand in an + // exponentiation operation is an unparenthesized unary expression. + // We want to reject |-2 ** 3|, but still need to allow |(-2) ** 3|. + NodeUnparenthesizedUnary, + // This node is necessary to determine if the LHS of a property access is // super related. NodeSuperBase @@ -235,14 +240,26 @@ class SyntaxParseHandler } Node newDelete(uint32_t begin, Node expr) { - return NodeGeneric; + return NodeUnparenthesizedUnary; } Node newTypeof(uint32_t begin, Node kid) { - return NodeGeneric; + return NodeUnparenthesizedUnary; } Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) { + return NodeUnparenthesizedUnary; + } + + Node newUpdate(ParseNodeKind kind, uint32_t begin, Node kid) { + return NodeGeneric; + } + + Node newSpread(uint32_t begin, Node kid) { + return NodeGeneric; + } + + Node newArrayPush(uint32_t begin, Node kid) { return NodeGeneric; } @@ -290,6 +307,7 @@ class SyntaxParseHandler Node newStatementList(unsigned blockid, const TokenPos& pos) { return NodeGeneric; } void addStatementToList(Node list, Node stmt, ParseContext* pc) {} + void addCaseStatementToList(Node list, Node stmt, ParseContext* pc) {} bool prependInitialYield(Node stmtList, Node gen) { return true; } Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; } @@ -333,6 +351,7 @@ class SyntaxParseHandler Node newFunctionDefinition() { return NodeHoistableDeclaration; } void setFunctionBody(Node pn, Node kid) {} void setFunctionBox(Node pn, FunctionBox* funbox) {} + Node newFunctionDefinitionForAnnexB(Node pn, Node assignment) { return NodeHoistableDeclaration; } void addFunctionArgument(Node pn, Node argpn) {} Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) { @@ -354,7 +373,7 @@ class SyntaxParseHandler return NodeGeneric; } - bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; } + bool finishInitializerAssignment(Node pn, Node init) { return true; } void setLexicalDeclarationOp(Node pn, JSOp op) {} void setBeginPosition(Node pn, Node oth) {} @@ -427,6 +446,10 @@ class SyntaxParseHandler return node == NodeUnparenthesizedAssignment; } + bool isUnparenthesizedUnaryExpression(Node node) { + return node == NodeUnparenthesizedUnary; + } + bool isReturnStatement(Node node) { return node == NodeReturn; } @@ -465,7 +488,8 @@ class SyntaxParseHandler if (node == NodeUnparenthesizedString || node == NodeUnparenthesizedCommaExpr || node == NodeUnparenthesizedYieldExpr || - node == NodeUnparenthesizedAssignment) + node == NodeUnparenthesizedAssignment || + node == NodeUnparenthesizedUnary) { return NodeGeneric; } diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 1570f4730..770d77d5b 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -1495,11 +1495,9 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) goto out; case '*': -#ifdef JS_HAS_EXPONENTIATION if (matchChar('*')) tp->type = matchChar('=') ? TOK_POWASSIGN : TOK_POW; else -#endif tp->type = matchChar('=') ? TOK_MULASSIGN : TOK_MUL; goto out; @@ -1582,6 +1580,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) reflags = RegExpFlag(reflags | MultilineFlag); else if (c == 'y' && !(reflags & StickyFlag)) reflags = RegExpFlag(reflags | StickyFlag); + else if (c == 'u' && !(reflags & UnicodeFlag)) + reflags = RegExpFlag(reflags | UnicodeFlag); else break; getChar(); diff --git a/js/src/irregexp/NativeRegExpMacroAssembler.cpp b/js/src/irregexp/NativeRegExpMacroAssembler.cpp index a699f5bf1..d0b47f7fb 100644 --- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp +++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp @@ -719,9 +719,10 @@ NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, Label* on_no_ma } void -NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label* on_no_match) +NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label* on_no_match, + bool unicode) { - JitSpew(SPEW_PREFIX "CheckNotBackReferenceIgnoreCase(%d)", start_reg); + JitSpew(SPEW_PREFIX "CheckNotBackReferenceIgnoreCase(%d, %d)", start_reg, unicode); Label fallthrough; @@ -833,8 +834,13 @@ NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label masm.passABIArg(current_character); masm.passABIArg(current_position); masm.passABIArg(temp1); - int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareStrings; - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun)); + if (!unicode) { + int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareStrings; + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun)); + } else { + int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareUCStrings; + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun)); + } masm.storeCallResult(temp0); masm.PopRegsInMask(volatileRegs); diff --git a/js/src/irregexp/NativeRegExpMacroAssembler.h b/js/src/irregexp/NativeRegExpMacroAssembler.h index 996e4e6d2..4b2674c4c 100644 --- a/js/src/irregexp/NativeRegExpMacroAssembler.h +++ b/js/src/irregexp/NativeRegExpMacroAssembler.h @@ -104,7 +104,7 @@ class MOZ_STACK_CLASS NativeRegExpMacroAssembler : public RegExpMacroAssembler void CheckGreedyLoop(jit::Label* on_tos_equals_current_position); void CheckNotAtStart(jit::Label* on_not_at_start); void CheckNotBackReference(int start_reg, jit::Label* on_no_match); - void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match); + void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, bool unicode); void CheckNotCharacter(unsigned c, jit::Label* on_not_equal); void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal); void CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with, diff --git a/js/src/irregexp/RegExpAST.h b/js/src/irregexp/RegExpAST.h index b7e27a4f3..ae76f64d4 100644 --- a/js/src/irregexp/RegExpAST.h +++ b/js/src/irregexp/RegExpAST.h @@ -138,7 +138,9 @@ class RegExpAssertion : public RegExpTree { END_OF_LINE, END_OF_INPUT, BOUNDARY, - NON_BOUNDARY + NON_BOUNDARY, + NOT_AFTER_LEAD_SURROGATE, + NOT_IN_SURROGATE_PAIR }; explicit RegExpAssertion(AssertionType type) : assertion_type_(type) { } virtual void* Accept(RegExpVisitor* visitor, void* data); diff --git a/js/src/irregexp/RegExpBytecode.h b/js/src/irregexp/RegExpBytecode.h index da035c7b5..f31b78c59 100644 --- a/js/src/irregexp/RegExpBytecode.h +++ b/js/src/irregexp/RegExpBytecode.h @@ -90,7 +90,8 @@ V(CHECK_AT_START, 43, 8) /* bc8 pad24 addr32 */ \ V(CHECK_NOT_AT_START, 44, 8) /* bc8 pad24 addr32 */ \ V(CHECK_GREEDY, 45, 8) /* bc8 pad24 addr32 */ \ V(ADVANCE_CP_AND_GOTO, 46, 8) /* bc8 offset24 addr32 */ \ -V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */ +V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */ \ +V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 48, 8) /* bc8 reg_idx24 addr32 */ #define DECLARE_BYTECODES(name, code, length) \ static const int BC_##name = code; diff --git a/js/src/irregexp/RegExpEngine.cpp b/js/src/irregexp/RegExpEngine.cpp index 84dab0c8a..8ba805ce5 100644 --- a/js/src/irregexp/RegExpEngine.cpp +++ b/js/src/irregexp/RegExpEngine.cpp @@ -73,12 +73,45 @@ static const int kSpaceRanges[] = { '\t', '\r' + 1, ' ', ' ' + 1, 0xFEFF, 0xFF00, 0x10000 }; static const int kSpaceRangeCount = ArrayLength(kSpaceRanges); +static const int kSpaceAndSurrogateRanges[] = { '\t', '\r' + 1, ' ', ' ' + 1, + 0x00A0, 0x00A1, 0x1680, 0x1681, 0x180E, 0x180F, 0x2000, 0x200B, + 0x2028, 0x202A, 0x202F, 0x2030, 0x205F, 0x2060, 0x3000, 0x3001, + unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1, + 0xFEFF, 0xFF00, 0x10000 }; +static const int kSpaceAndSurrogateRangeCount = ArrayLength(kSpaceAndSurrogateRanges); static const int kWordRanges[] = { '0', '9' + 1, 'A', 'Z' + 1, '_', '_' + 1, 'a', 'z' + 1, 0x10000 }; static const int kWordRangeCount = ArrayLength(kWordRanges); +static const int kIgnoreCaseWordRanges[] = { + '0', '9' + 1, 'A', 'Z' + 1, '_', '_' + 1, 'a', 'z' + 1, + 0x017F, 0x017F + 1, 0x212A, 0x212A + 1, + 0x10000 }; +static const int kIgnoreCaseWordCount = ArrayLength(kIgnoreCaseWordRanges); +static const int kWordAndSurrogateRanges[] = { + '0', '9' + 1, 'A', 'Z' + 1, '_', '_' + 1, 'a', 'z' + 1, + unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1, + 0x10000 }; +static const int kWordAndSurrogateRangeCount = ArrayLength(kWordAndSurrogateRanges); +static const int kNegatedIgnoreCaseWordAndSurrogateRanges[] = { + 0, '0', '9' + 1, 'A', + 'Z' + 1, '_', '_' + 1, 'a', + 'z' + 1, 0x017F, + 0x017F + 1, 0x212A, + 0x212A + 1, unicode::LeadSurrogateMin, + unicode::TrailSurrogateMax + 1, 0x10000, + 0x10000 }; +static const int kNegatedIgnoreCaseWordAndSurrogateRangeCount = + ArrayLength(kNegatedIgnoreCaseWordAndSurrogateRanges); static const int kDigitRanges[] = { '0', '9' + 1, 0x10000 }; static const int kDigitRangeCount = ArrayLength(kDigitRanges); -static const int kSurrogateRanges[] = { 0xd800, 0xe000, 0x10000 }; +static const int kDigitAndSurrogateRanges[] = { + '0', '9' + 1, + unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1, + 0x10000 }; +static const int kDigitAndSurrogateRangeCount = ArrayLength(kDigitAndSurrogateRanges); +static const int kSurrogateRanges[] = { + unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1, + 0x10000 }; static const int kSurrogateRangeCount = ArrayLength(kSurrogateRanges); static const int kLineTerminatorRanges[] = { 0x000A, 0x000B, 0x000D, 0x000E, 0x2028, 0x202A, 0x10000 }; @@ -165,20 +198,74 @@ CharacterRange::AddClassEscape(LifoAlloc* alloc, char16_t type, } } +// Add class escape, excluding surrogate pair range. +void +CharacterRange::AddClassEscapeUnicode(LifoAlloc* alloc, char16_t type, + CharacterRangeVector* ranges, bool ignore_case) +{ + switch (type) { + case 's': + case 'd': + return AddClassEscape(alloc, type, ranges); + break; + case 'S': + AddClassNegated(kSpaceAndSurrogateRanges, kSpaceAndSurrogateRangeCount, ranges); + break; + case 'w': + if (ignore_case) + AddClass(kIgnoreCaseWordRanges, kIgnoreCaseWordCount, ranges); + else + AddClassEscape(alloc, type, ranges); + break; + case 'W': + if (ignore_case) { + AddClass(kNegatedIgnoreCaseWordAndSurrogateRanges, + kNegatedIgnoreCaseWordAndSurrogateRangeCount, ranges); + } else { + AddClassNegated(kWordAndSurrogateRanges, kWordAndSurrogateRangeCount, ranges); + } + break; + case 'D': + AddClassNegated(kDigitAndSurrogateRanges, kDigitAndSurrogateRangeCount, ranges); + break; + default: + MOZ_CRASH("Bad type!"); + } +} + +#define FOR_EACH_NON_ASCII_TO_ASCII_FOLDING(macro) \ + /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ \ + macro(0x0178, 0x00FF) \ + /* LATIN SMALL LETTER LONG S */ \ + macro(0x017F, 0x0073) \ + /* LATIN CAPITAL LETTER SHARP S */ \ + macro(0x1E9E, 0x00DF) \ + /* KELVIN SIGN */ \ + macro(0x212A, 0x006B) \ + /* ANGSTROM SIGN */ \ + macro(0x212B, 0x00E5) + // We need to check for the following characters: 0x39c 0x3bc 0x178. static inline bool -RangeContainsLatin1Equivalents(CharacterRange range) +RangeContainsLatin1Equivalents(CharacterRange range, bool unicode) { - // TODO(dcarney): this could be a lot more efficient. + /* TODO(dcarney): this could be a lot more efficient. */ + if (unicode) { +#define CHECK_RANGE(C, F) \ + if (range.Contains(C)) return true; +FOR_EACH_NON_ASCII_TO_ASCII_FOLDING(CHECK_RANGE) +#undef CHECK_RANGE + } + return range.Contains(0x39c) || range.Contains(0x3bc) || range.Contains(0x178); } static bool -RangesContainLatin1Equivalents(const CharacterRangeVector& ranges) +RangesContainLatin1Equivalents(const CharacterRangeVector& ranges, bool unicode) { for (size_t i = 0; i < ranges.length(); i++) { // TODO(dcarney): this could be a lot more efficient. - if (RangeContainsLatin1Equivalents(ranges[i])) + if (RangeContainsLatin1Equivalents(ranges[i], unicode)) return true; } return false; @@ -191,27 +278,24 @@ static const size_t kEcma262UnCanonicalizeMaxWidth = 4; static int GetCaseIndependentLetters(char16_t character, bool ascii_subject, + bool unicode, + const char16_t* choices, + size_t choices_length, char16_t* letters) { - const char16_t choices[] = { - character, - unicode::ToLowerCase(character), - unicode::ToUpperCase(character) - }; - size_t count = 0; - for (size_t i = 0; i < ArrayLength(choices); i++) { + for (size_t i = 0; i < choices_length; i++) { char16_t c = choices[i]; // The standard requires that non-ASCII characters cannot have ASCII // character codes in their equivalence class, even though this // situation occurs multiple times in the unicode tables. static const unsigned kMaxAsciiCharCode = 127; - if (character > kMaxAsciiCharCode && c <= kMaxAsciiCharCode) + if (!unicode && character > kMaxAsciiCharCode && c <= kMaxAsciiCharCode) continue; // Skip characters that can't appear in one byte strings. - if (ascii_subject && c > kMaxOneByteCharCode) + if (!unicode && ascii_subject && c > kMaxOneByteCharCode) continue; // Watch for duplicates. @@ -231,10 +315,45 @@ GetCaseIndependentLetters(char16_t character, return count; } +static int +GetCaseIndependentLetters(char16_t character, + bool ascii_subject, + bool unicode, + char16_t* letters) +{ + if (unicode) { + const char16_t choices[] = { + character, + unicode::FoldCase(character), + unicode::ReverseFoldCase1(character), + unicode::ReverseFoldCase2(character), + unicode::ReverseFoldCase3(character), + }; + return GetCaseIndependentLetters(character, ascii_subject, unicode, + choices, ArrayLength(choices), letters); + } + + const char16_t choices[] = { + character, + unicode::ToLowerCase(character), + unicode::ToUpperCase(character) + }; + return GetCaseIndependentLetters(character, ascii_subject, unicode, + choices, ArrayLength(choices), letters); +} + static char16_t -ConvertNonLatin1ToLatin1(char16_t c) +ConvertNonLatin1ToLatin1(char16_t c, bool unicode) { MOZ_ASSERT(c > kMaxOneByteCharCode); + if (unicode) { + switch (c) { +#define CONVERT(C, F) case C: return F; +FOR_EACH_NON_ASCII_TO_ASCII_FOLDING(CONVERT) +#undef CONVERT + } + } + switch (c) { // This are equivalent characters in unicode. case 0x39c: @@ -249,12 +368,12 @@ ConvertNonLatin1ToLatin1(char16_t c) } void -CharacterRange::AddCaseEquivalents(bool is_ascii, CharacterRangeVector* ranges) +CharacterRange::AddCaseEquivalents(bool is_ascii, bool unicode, CharacterRangeVector* ranges) { char16_t bottom = from(); char16_t top = to(); - if (is_ascii && !RangeContainsLatin1Equivalents(*this)) { + if (is_ascii && !RangeContainsLatin1Equivalents(*this, unicode)) { if (bottom > kMaxOneByteCharCode) return; if (top > kMaxOneByteCharCode) @@ -263,7 +382,7 @@ CharacterRange::AddCaseEquivalents(bool is_ascii, CharacterRangeVector* ranges) for (char16_t c = bottom;; c++) { char16_t chars[kEcma262UnCanonicalizeMaxWidth]; - size_t length = GetCaseIndependentLetters(c, is_ascii, chars); + size_t length = GetCaseIndependentLetters(c, is_ascii, unicode, chars); for (size_t i = 0; i < length; i++) { char16_t other = chars[i]; @@ -543,7 +662,7 @@ SeqRegExpNode::FillInBMInfo(int offset, } RegExpNode* -SeqRegExpNode::FilterASCII(int depth, bool ignore_case) +SeqRegExpNode::FilterASCII(int depth, bool ignore_case, bool unicode) { if (info()->replacement_calculated) return replacement(); @@ -553,13 +672,13 @@ SeqRegExpNode::FilterASCII(int depth, bool ignore_case) MOZ_ASSERT(!info()->visited); VisitMarker marker(info()); - return FilterSuccessor(depth - 1, ignore_case); + return FilterSuccessor(depth - 1, ignore_case, unicode); } RegExpNode* -SeqRegExpNode::FilterSuccessor(int depth, bool ignore_case) +SeqRegExpNode::FilterSuccessor(int depth, bool ignore_case, bool unicode) { - RegExpNode* next = on_success_->FilterASCII(depth - 1, ignore_case); + RegExpNode* next = on_success_->FilterASCII(depth - 1, ignore_case, unicode); if (next == nullptr) return set_replacement(nullptr); @@ -702,7 +821,7 @@ TextNode::GreedyLoopTextLength() } RegExpNode* -TextNode::FilterASCII(int depth, bool ignore_case) +TextNode::FilterASCII(int depth, bool ignore_case, bool unicode) { if (info()->replacement_calculated) return replacement(); @@ -726,7 +845,7 @@ TextNode::FilterASCII(int depth, bool ignore_case) // Here, we need to check for characters whose upper and lower cases // are outside the Latin-1 range. - char16_t converted = ConvertNonLatin1ToLatin1(c); + char16_t converted = ConvertNonLatin1ToLatin1(c, unicode); if (converted == 0) { // Character is outside Latin-1 completely return set_replacement(nullptr); @@ -751,7 +870,7 @@ TextNode::FilterASCII(int depth, bool ignore_case) ranges[0].to() >= kMaxOneByteCharCode) { // This will be handled in a later filter. - if (ignore_case && RangesContainLatin1Equivalents(ranges)) + if (ignore_case && RangesContainLatin1Equivalents(ranges, unicode)) continue; return set_replacement(nullptr); } @@ -760,14 +879,14 @@ TextNode::FilterASCII(int depth, bool ignore_case) ranges[0].from() > kMaxOneByteCharCode) { // This will be handled in a later filter. - if (ignore_case && RangesContainLatin1Equivalents(ranges)) + if (ignore_case && RangesContainLatin1Equivalents(ranges, unicode)) continue; return set_replacement(nullptr); } } } } - return FilterSuccessor(depth - 1, ignore_case); + return FilterSuccessor(depth - 1, ignore_case, unicode); } void @@ -785,7 +904,7 @@ TextNode::CalculateOffsets() } } -void TextNode::MakeCaseIndependent(bool is_ascii) +void TextNode::MakeCaseIndependent(bool is_ascii, bool unicode) { int element_count = elements().length(); for (int i = 0; i < element_count; i++) { @@ -801,7 +920,7 @@ void TextNode::MakeCaseIndependent(bool is_ascii) CharacterRangeVector& ranges = cc->ranges(alloc()); int range_count = ranges.length(); for (int j = 0; j < range_count; j++) - ranges[j].AddCaseEquivalents(is_ascii, &ranges); + ranges[j].AddCaseEquivalents(is_ascii, unicode, &ranges); } } } @@ -950,7 +1069,7 @@ ChoiceNode::FillInBMInfo(int offset, } RegExpNode* -ChoiceNode::FilterASCII(int depth, bool ignore_case) +ChoiceNode::FilterASCII(int depth, bool ignore_case, bool unicode) { if (info()->replacement_calculated) return replacement(); @@ -974,7 +1093,7 @@ ChoiceNode::FilterASCII(int depth, bool ignore_case) for (int i = 0; i < choice_count; i++) { GuardedAlternative alternative = alternatives()[i]; RegExpNode* replacement = - alternative.node()->FilterASCII(depth - 1, ignore_case); + alternative.node()->FilterASCII(depth - 1, ignore_case, unicode); MOZ_ASSERT(replacement != this); // No missing EMPTY_MATCH_CHECK. if (replacement != nullptr) { alternatives()[i].set_node(replacement); @@ -995,7 +1114,7 @@ ChoiceNode::FilterASCII(int depth, bool ignore_case) new_alternatives.reserve(surviving); for (int i = 0; i < choice_count; i++) { RegExpNode* replacement = - alternatives()[i].node()->FilterASCII(depth - 1, ignore_case); + alternatives()[i].node()->FilterASCII(depth - 1, ignore_case, unicode); if (replacement != nullptr) { alternatives()[i].set_node(replacement); AutoEnterOOMUnsafeRegion oomUnsafe; @@ -1052,7 +1171,7 @@ NegativeLookaheadChoiceNode::GetQuickCheckDetails(QuickCheckDetails* details, } RegExpNode* -NegativeLookaheadChoiceNode::FilterASCII(int depth, bool ignore_case) +NegativeLookaheadChoiceNode::FilterASCII(int depth, bool ignore_case, bool unicode) { if (info()->replacement_calculated) return replacement(); @@ -1066,14 +1185,14 @@ NegativeLookaheadChoiceNode::FilterASCII(int depth, bool ignore_case) // Alternative 0 is the negative lookahead, alternative 1 is what comes // afterwards. RegExpNode* node = alternatives()[1].node(); - RegExpNode* replacement = node->FilterASCII(depth - 1, ignore_case); + RegExpNode* replacement = node->FilterASCII(depth - 1, ignore_case, unicode); if (replacement == nullptr) return set_replacement(nullptr); alternatives()[1].set_node(replacement); RegExpNode* neg_node = alternatives()[0].node(); - RegExpNode* neg_replacement = neg_node->FilterASCII(depth - 1, ignore_case); + RegExpNode* neg_replacement = neg_node->FilterASCII(depth - 1, ignore_case, unicode); // If the negative lookahead is always going to fail then // we don't need to check it. @@ -1154,7 +1273,7 @@ LoopChoiceNode::FillInBMInfo(int offset, } RegExpNode* -LoopChoiceNode::FilterASCII(int depth, bool ignore_case) +LoopChoiceNode::FilterASCII(int depth, bool ignore_case, bool unicode) { if (info()->replacement_calculated) return replacement(); @@ -1167,7 +1286,7 @@ LoopChoiceNode::FilterASCII(int depth, bool ignore_case) VisitMarker marker(info()); RegExpNode* continue_replacement = - continue_node_->FilterASCII(depth - 1, ignore_case); + continue_node_->FilterASCII(depth - 1, ignore_case, unicode); // If we can't continue after the loop then there is no sense in doing the // loop. @@ -1175,7 +1294,7 @@ LoopChoiceNode::FilterASCII(int depth, bool ignore_case) return set_replacement(nullptr); } - return ChoiceNode::FilterASCII(depth - 1, ignore_case); + return ChoiceNode::FilterASCII(depth - 1, ignore_case, unicode); } // ------------------------------------------------------------------- @@ -1204,7 +1323,7 @@ void Analysis::VisitText(TextNode* that) { if (ignore_case_) - that->MakeCaseIndependent(is_ascii_); + that->MakeCaseIndependent(is_ascii_, unicode_); EnsureAnalyzed(that->on_success()); if (!has_failed()) { that->CalculateOffsets(); @@ -1496,7 +1615,7 @@ class irregexp::RegExpCompiler { public: RegExpCompiler(JSContext* cx, LifoAlloc* alloc, int capture_count, - bool ignore_case, bool is_ascii, bool match_only); + bool ignore_case, bool is_ascii, bool match_only, bool unicode); int AllocateRegister() { if (next_register_ >= RegExpMacroAssembler::kMaxRegister) { @@ -1533,6 +1652,7 @@ class irregexp::RegExpCompiler inline bool ignore_case() { return ignore_case_; } inline bool ascii() { return ascii_; } + inline bool unicode() { return unicode_; } FrequencyCollator* frequency_collator() { return &frequency_collator_; } int current_expansion_factor() { return current_expansion_factor_; } @@ -1554,6 +1674,7 @@ class irregexp::RegExpCompiler bool ignore_case_; bool ascii_; bool match_only_; + bool unicode_; bool reg_exp_too_big_; int current_expansion_factor_; FrequencyCollator frequency_collator_; @@ -1576,12 +1697,13 @@ class RecursionCheck // Attempts to compile the regexp using an Irregexp code generator. Returns // a fixed array or a null handle depending on whether it succeeded. RegExpCompiler::RegExpCompiler(JSContext* cx, LifoAlloc* alloc, int capture_count, - bool ignore_case, bool ascii, bool match_only) + bool ignore_case, bool ascii, bool match_only, bool unicode) : next_register_(2 * (capture_count + 1)), recursion_depth_(0), ignore_case_(ignore_case), ascii_(ascii), match_only_(match_only), + unicode_(unicode), reg_exp_too_big_(false), current_expansion_factor_(1), frequency_collator_(), @@ -1654,7 +1776,8 @@ IsNativeRegExpEnabled(JSContext* cx) RegExpCode irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data, HandleLinearString sample, bool is_global, bool ignore_case, - bool is_ascii, bool match_only, bool force_bytecode, bool sticky) + bool is_ascii, bool match_only, bool force_bytecode, bool sticky, + bool unicode) { if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) { JS_ReportError(cx, "regexp too big"); @@ -1662,7 +1785,8 @@ irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* } LifoAlloc& alloc = cx->tempLifoAlloc(); - RegExpCompiler compiler(cx, &alloc, data->capture_count, ignore_case, is_ascii, match_only); + RegExpCompiler compiler(cx, &alloc, data->capture_count, ignore_case, is_ascii, match_only, + unicode); // Sample some characters from the middle of the string. if (sample->hasLatin1Chars()) { @@ -1708,18 +1832,18 @@ irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* } } if (is_ascii) { - node = node->FilterASCII(RegExpCompiler::kMaxRecursion, ignore_case); + node = node->FilterASCII(RegExpCompiler::kMaxRecursion, ignore_case, unicode); // Do it again to propagate the new nodes to places where they were not // put because they had not been calculated yet. if (node != nullptr) { - node = node->FilterASCII(RegExpCompiler::kMaxRecursion, ignore_case); + node = node->FilterASCII(RegExpCompiler::kMaxRecursion, ignore_case, unicode); } } if (node == nullptr) node = alloc.newInfallible(&alloc, EndNode::BACKTRACK); - Analysis analysis(cx, ignore_case, is_ascii); + Analysis analysis(cx, ignore_case, is_ascii, unicode); analysis.EnsureAnalyzed(node); if (analysis.has_failed()) { JS_ReportError(cx, analysis.errorMessage()); @@ -2061,6 +2185,10 @@ RegExpAssertion::ToNode(RegExpCompiler* compiler, result->AddAlternative(end_alternative); return result; } + case NOT_AFTER_LEAD_SURROGATE: + return AssertionNode::NotAfterLeadSurrogate(on_success); + case NOT_IN_SURROGATE_PAIR: + return AssertionNode::NotInSurrogatePair(on_success); default: MOZ_CRASH("Bad assertion type"); } @@ -2848,6 +2976,65 @@ EmitHat(RegExpCompiler* compiler, RegExpNode* on_success, Trace* trace) on_success->Emit(compiler, &new_trace); } +// Assert that the next character cannot be a part of a surrogate pair. +static void +EmitNotAfterLeadSurrogate(RegExpCompiler* compiler, RegExpNode* on_success, Trace* trace) +{ + RegExpMacroAssembler* assembler = compiler->macro_assembler(); + + // We will be loading the previous character into the current character + // register. + Trace new_trace(*trace); + new_trace.InvalidateCurrentCharacter(); + + jit::Label ok; + if (new_trace.cp_offset() == 0) + assembler->CheckAtStart(&ok); + + // We already checked that we are not at the start of input so it must be + // OK to load the previous character. + assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1, new_trace.backtrack(), false); + assembler->CheckCharacterInRange(unicode::LeadSurrogateMin, unicode::LeadSurrogateMax, + new_trace.backtrack()); + + assembler->Bind(&ok); + on_success->Emit(compiler, &new_trace); +} + +// Assert that the next character is not a trail surrogate that has a +// corresponding lead surrogate. +static void +EmitNotInSurrogatePair(RegExpCompiler* compiler, RegExpNode* on_success, Trace* trace) +{ + RegExpMacroAssembler* assembler = compiler->macro_assembler(); + + jit::Label ok; + assembler->CheckPosition(trace->cp_offset(), &ok); + + // We will be loading the next and previous characters into the current + // character register. + Trace new_trace(*trace); + new_trace.InvalidateCurrentCharacter(); + + if (new_trace.cp_offset() == 0) + assembler->CheckAtStart(&ok); + + // First check if next character is a trail surrogate. + assembler->LoadCurrentCharacter(new_trace.cp_offset(), new_trace.backtrack(), false); + assembler->CheckCharacterNotInRange(unicode::TrailSurrogateMin, unicode::TrailSurrogateMax, + &ok); + + // Next check if previous character is a lead surrogate. + // We already checked that we are not at the start of input so it must be + // OK to load the previous character. + assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1, new_trace.backtrack(), false); + assembler->CheckCharacterInRange(unicode::LeadSurrogateMin, unicode::LeadSurrogateMax, + new_trace.backtrack()); + + assembler->Bind(&ok); + on_success->Emit(compiler, &new_trace); +} + // Check for [0-9A-Z_a-z]. static void EmitWordCheck(RegExpMacroAssembler* assembler, @@ -3001,6 +3188,12 @@ AssertionNode::Emit(RegExpCompiler* compiler, Trace* trace) EmitBoundaryCheck(compiler, trace); return; } + case NOT_AFTER_LEAD_SURROGATE: + EmitNotAfterLeadSurrogate(compiler, on_success(), trace); + return; + case NOT_IN_SURROGATE_PAIR: + EmitNotInSurrogatePair(compiler, on_success(), trace); + return; } on_success()->Emit(compiler, trace); } @@ -3529,7 +3722,7 @@ EmitAtomNonLetter(RegExpCompiler* compiler, RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); bool ascii = compiler->ascii(); char16_t chars[kEcma262UnCanonicalizeMaxWidth]; - int length = GetCaseIndependentLetters(c, ascii, chars); + int length = GetCaseIndependentLetters(c, ascii, compiler->unicode(), chars); if (length < 1) { // This can't match. Must be an ASCII subject and a non-ASCII character. // We do not need to do anything since the ASCII pass already handled this. @@ -3605,7 +3798,7 @@ EmitAtomLetter(RegExpCompiler* compiler, RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); bool ascii = compiler->ascii(); char16_t chars[kEcma262UnCanonicalizeMaxWidth]; - int length = GetCaseIndependentLetters(c, ascii, chars); + int length = GetCaseIndependentLetters(c, ascii, compiler->unicode(), chars); if (length <= 1) return false; // We may not need to check against the end of the input string // if this character lies before a character that matched. @@ -4470,7 +4663,8 @@ BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) MOZ_ASSERT(start_reg_ + 1 == end_reg_); if (compiler->ignore_case()) { assembler->CheckNotBackReferenceIgnoreCase(start_reg_, - trace->backtrack()); + trace->backtrack(), + compiler->unicode()); } else { assembler->CheckNotBackReference(start_reg_, trace->backtrack()); } @@ -4616,6 +4810,7 @@ TextNode::FillInBMInfo(int initial_offset, char16_t chars[kEcma262UnCanonicalizeMaxWidth]; int length = GetCaseIndependentLetters(character, bm->max_char() == kMaxOneByteCharCode, + bm->compiler()->unicode(), chars); for (int j = 0; j < length; j++) bm->Set(offset, chars[j]); @@ -4707,7 +4902,8 @@ TextNode::GetQuickCheckDetails(QuickCheckDetails* details, } if (compiler->ignore_case()) { char16_t chars[kEcma262UnCanonicalizeMaxWidth]; - size_t length = GetCaseIndependentLetters(c, compiler->ascii(), chars); + size_t length = GetCaseIndependentLetters(c, compiler->ascii(), + compiler->unicode(), chars); MOZ_ASSERT(length != 0); // Can only happen if c > char_mask (see above). if (length == 1) { // This letter has no case equivalents, so it's nice and simple diff --git a/js/src/irregexp/RegExpEngine.h b/js/src/irregexp/RegExpEngine.h index b687c6c52..ca315c2ad 100644 --- a/js/src/irregexp/RegExpEngine.h +++ b/js/src/irregexp/RegExpEngine.h @@ -88,7 +88,8 @@ struct RegExpCode RegExpCode CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data, HandleLinearString sample, bool is_global, bool ignore_case, - bool is_ascii, bool match_only, bool force_bytecode, bool sticky); + bool is_ascii, bool match_only, bool force_bytecode, bool sticky, + bool unicode); // Note: this may return RegExpRunStatus_Error if an interrupt was requested // while the code was executing. @@ -144,6 +145,8 @@ class CharacterRange {} static void AddClassEscape(LifoAlloc* alloc, char16_t type, CharacterRangeVector* ranges); + static void AddClassEscapeUnicode(LifoAlloc* alloc, char16_t type, + CharacterRangeVector* ranges, bool ignoreCase); static inline CharacterRange Singleton(char16_t value) { return CharacterRange(value, value); @@ -163,7 +166,7 @@ class CharacterRange bool is_valid() { return from_ <= to_; } bool IsEverything(char16_t max) { return from_ == 0 && to_ >= max; } bool IsSingleton() { return (from_ == to_); } - void AddCaseEquivalents(bool is_ascii, CharacterRangeVector* ranges); + void AddCaseEquivalents(bool is_ascii, bool unicode, CharacterRangeVector* ranges); static void Split(const LifoAlloc* alloc, CharacterRangeVector base, @@ -516,7 +519,7 @@ class RegExpNode // If we know that the input is ASCII then there are some nodes that can // never match. This method returns a node that can be substituted for // itself, or nullptr if the node can never match. - virtual RegExpNode* FilterASCII(int depth, bool ignore_case) { return this; } + virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode) { return this; } // Helper for FilterASCII. RegExpNode* replacement() { @@ -623,14 +626,14 @@ class SeqRegExpNode : public RegExpNode RegExpNode* on_success() { return on_success_; } void set_on_success(RegExpNode* node) { on_success_ = node; } - virtual RegExpNode* FilterASCII(int depth, bool ignore_case); + virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode); virtual bool FillInBMInfo(int offset, int budget, BoyerMooreLookahead* bm, bool not_at_start); protected: - RegExpNode* FilterSuccessor(int depth, bool ignore_case); + RegExpNode* FilterSuccessor(int depth, bool ignore_case, bool unicode); private: RegExpNode* on_success_; @@ -748,7 +751,7 @@ class TextNode : public SeqRegExpNode int characters_filled_in, bool not_at_start); TextElementVector& elements() { return *elements_; } - void MakeCaseIndependent(bool is_ascii); + void MakeCaseIndependent(bool is_ascii, bool unicode); virtual int GreedyLoopTextLength(); virtual RegExpNode* GetSuccessorOfOmnivorousTextNode( RegExpCompiler* compiler); @@ -757,7 +760,7 @@ class TextNode : public SeqRegExpNode BoyerMooreLookahead* bm, bool not_at_start); void CalculateOffsets(); - virtual RegExpNode* FilterASCII(int depth, bool ignore_case); + virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode); private: enum TextEmitPassType { @@ -788,7 +791,9 @@ class AssertionNode : public SeqRegExpNode AT_START, AT_BOUNDARY, AT_NON_BOUNDARY, - AFTER_NEWLINE + AFTER_NEWLINE, + NOT_AFTER_LEAD_SURROGATE, + NOT_IN_SURROGATE_PAIR }; AssertionNode(AssertionType t, RegExpNode* on_success) : SeqRegExpNode(on_success), assertion_type_(t) @@ -809,6 +814,14 @@ class AssertionNode : public SeqRegExpNode static AssertionNode* AfterNewline(RegExpNode* on_success) { return on_success->alloc()->newInfallible(AFTER_NEWLINE, on_success); } + static AssertionNode* NotAfterLeadSurrogate(RegExpNode* on_success) { + return on_success->alloc()->newInfallible(NOT_AFTER_LEAD_SURROGATE, + on_success); + } + static AssertionNode* NotInSurrogatePair(RegExpNode* on_success) { + return on_success->alloc()->newInfallible(NOT_IN_SURROGATE_PAIR, + on_success); + } virtual void Accept(NodeVisitor* visitor); virtual void Emit(RegExpCompiler* compiler, Trace* trace); virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start); @@ -1006,7 +1019,7 @@ class ChoiceNode : public RegExpNode void set_not_at_start() { not_at_start_ = true; } void set_being_calculated(bool b) { being_calculated_ = b; } virtual bool try_to_emit_quick_check_for_alternative(int i) { return true; } - virtual RegExpNode* FilterASCII(int depth, bool ignore_case); + virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode); protected: int GreedyLoopTextLengthForAlternative(GuardedAlternative* alternative); @@ -1059,7 +1072,7 @@ class NegativeLookaheadChoiceNode : public ChoiceNode // characters, but on a negative lookahead the negative branch did not take // part in that calculation (EatsAtLeast) so the assumptions don't hold. virtual bool try_to_emit_quick_check_for_alternative(int i) { return i != 0; } - virtual RegExpNode* FilterASCII(int depth, bool ignore_case); + virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode); }; class LoopChoiceNode : public ChoiceNode @@ -1088,7 +1101,7 @@ class LoopChoiceNode : public ChoiceNode RegExpNode* continue_node() { return continue_node_; } bool body_can_be_zero_length() { return body_can_be_zero_length_; } virtual void Accept(NodeVisitor* visitor); - virtual RegExpNode* FilterASCII(int depth, bool ignore_case); + virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode); private: // AddAlternative is made private for loop nodes because alternatives @@ -1459,10 +1472,11 @@ class NodeVisitor class Analysis : public NodeVisitor { public: - Analysis(JSContext* cx, bool ignore_case, bool is_ascii) + Analysis(JSContext* cx, bool ignore_case, bool is_ascii, bool unicode) : cx(cx), ignore_case_(ignore_case), is_ascii_(is_ascii), + unicode_(unicode), error_message_(nullptr) {} @@ -1487,6 +1501,7 @@ class Analysis : public NodeVisitor JSContext* cx; bool ignore_case_; bool is_ascii_; + bool unicode_; const char* error_message_; Analysis(Analysis&) = delete; diff --git a/js/src/irregexp/RegExpInterpreter.cpp b/js/src/irregexp/RegExpInterpreter.cpp index 2a4bfb20a..1258c203d 100644 --- a/js/src/irregexp/RegExpInterpreter.cpp +++ b/js/src/irregexp/RegExpInterpreter.cpp @@ -442,6 +442,27 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha } break; } + BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE) { + int from = registers[insn >> BYTECODE_SHIFT]; + int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from; + if (from < 0 || len <= 0) { + pc += BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE_LENGTH; + break; + } + if (current + len > length) { + pc = byteCode + Load32Aligned(pc + 4); + break; + } + if (CaseInsensitiveCompareUCStrings(chars + from, chars + current, + len * sizeof(CharT))) + { + current += len; + pc += BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE_LENGTH; + } else { + pc = byteCode + Load32Aligned(pc + 4); + } + break; + } BYTECODE(CHECK_AT_START) if (current == 0) pc = byteCode + Load32Aligned(pc + 4); diff --git a/js/src/irregexp/RegExpMacroAssembler.cpp b/js/src/irregexp/RegExpMacroAssembler.cpp index 197c3f3b4..d66d0d204 100644 --- a/js/src/irregexp/RegExpMacroAssembler.cpp +++ b/js/src/irregexp/RegExpMacroAssembler.cpp @@ -65,6 +65,38 @@ template int irregexp::CaseInsensitiveCompareStrings(const char16_t* substring1, const char16_t* substring2, size_t byteLength); +template +int +irregexp::CaseInsensitiveCompareUCStrings(const CharT* substring1, const CharT* substring2, + size_t byteLength) +{ + MOZ_ASSERT(byteLength % sizeof(CharT) == 0); + size_t length = byteLength / sizeof(CharT); + + for (size_t i = 0; i < length; i++) { + char16_t c1 = substring1[i]; + char16_t c2 = substring2[i]; + if (c1 != c2) { + c1 = unicode::FoldCase(c1); + c2 = unicode::FoldCase(c2); + if (c1 != c2) + return 0; + } + } + + return 1; +} + +template int +irregexp::CaseInsensitiveCompareUCStrings(const Latin1Char* substring1, + const Latin1Char* substring2, + size_t byteLength); + +template int +irregexp::CaseInsensitiveCompareUCStrings(const char16_t* substring1, + const char16_t* substring2, + size_t byteLength); + InterpretedRegExpMacroAssembler::InterpretedRegExpMacroAssembler(LifoAlloc* alloc, RegExpShared* shared, size_t numSavedRegisters) : RegExpMacroAssembler(*alloc, shared, numSavedRegisters), @@ -210,11 +242,16 @@ InterpretedRegExpMacroAssembler::CheckNotBackReference(int start_reg, jit::Label } void -InterpretedRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match) +InterpretedRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, + jit::Label* on_no_match, + bool unicode) { MOZ_ASSERT(start_reg >= 0); MOZ_ASSERT(start_reg <= kMaxRegister); - Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg); + if (unicode) + Emit(BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE, start_reg); + else + Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg); EmitOrLink(on_no_match); } diff --git a/js/src/irregexp/RegExpMacroAssembler.h b/js/src/irregexp/RegExpMacroAssembler.h index 0111e37e0..940033d31 100644 --- a/js/src/irregexp/RegExpMacroAssembler.h +++ b/js/src/irregexp/RegExpMacroAssembler.h @@ -112,7 +112,8 @@ class MOZ_STACK_CLASS RegExpMacroAssembler virtual void CheckGreedyLoop(jit::Label* on_tos_equals_current_position) = 0; virtual void CheckNotAtStart(jit::Label* on_not_at_start) = 0; virtual void CheckNotBackReference(int start_reg, jit::Label* on_no_match) = 0; - virtual void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match) = 0; + virtual void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, + bool unicode) = 0; // Check the current character for a match with a literal character. If we // fail to match then goto the on_failure label. End of input always @@ -221,6 +222,11 @@ template int CaseInsensitiveCompareStrings(const CharT* substring1, const CharT* substring2, size_t byteLength); +template +int +CaseInsensitiveCompareUCStrings(const CharT* substring1, const CharT* substring2, + size_t byteLength); + class MOZ_STACK_CLASS InterpretedRegExpMacroAssembler : public RegExpMacroAssembler { public: @@ -241,7 +247,7 @@ class MOZ_STACK_CLASS InterpretedRegExpMacroAssembler : public RegExpMacroAssemb void CheckGreedyLoop(jit::Label* on_tos_equals_current_position); void CheckNotAtStart(jit::Label* on_not_at_start); void CheckNotBackReference(int start_reg, jit::Label* on_no_match); - void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match); + void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, bool unicode); void CheckNotCharacter(unsigned c, jit::Label* on_not_equal); void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal); void CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with, diff --git a/js/src/irregexp/RegExpParser.cpp b/js/src/irregexp/RegExpParser.cpp index e63092349..d452178e6 100644 --- a/js/src/irregexp/RegExpParser.cpp +++ b/js/src/irregexp/RegExpParser.cpp @@ -205,7 +205,8 @@ RegExpBuilder::AddQuantifierToAtom(int min, int max, template RegExpParser::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc, - const CharT* chars, const CharT* end, bool multiline_mode) + const CharT* chars, const CharT* end, bool multiline_mode, + bool unicode, bool ignore_case) : ts(ts), alloc(alloc), captures_(nullptr), @@ -215,6 +216,8 @@ RegExpParser::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc, capture_count_(0), has_more_(true), multiline_(multiline_mode), + unicode_(unicode), + ignore_case_(ignore_case), simple_(false), contains_anchor_(false), is_scanned_for_captures_(false) @@ -300,6 +303,133 @@ RegExpParser::ParseHexEscape(int length, size_t* value) return true; } +template +bool +RegExpParser::ParseBracedHexEscape(size_t* value) +{ + MOZ_ASSERT(current() == '{'); + Advance(); + + bool first = true; + uint32_t code = 0; + while (true) { + widechar c = current(); + if (c == kEndMarker) { + ReportError(JSMSG_INVALID_UNICODE_ESCAPE); + return false; + } + if (c == '}') { + if (first) { + ReportError(JSMSG_INVALID_UNICODE_ESCAPE); + return false; + } + Advance(); + break; + } + + int d = HexValue(c); + if (d < 0) { + ReportError(JSMSG_INVALID_UNICODE_ESCAPE); + return false; + } + code = (code << 4) | d; + if (code > unicode::NonBMPMax) { + ReportError(JSMSG_UNICODE_OVERFLOW); + return false; + } + Advance(); + first = false; + } + + *value = code; + return true; +} + +template +bool +RegExpParser::ParseTrailSurrogate(size_t* value) +{ + if (current() != '\\') + return false; + + const CharT* start = position(); + Advance(); + if (current() != 'u') { + Reset(start); + return false; + } + Advance(); + if (!ParseHexEscape(4, value)) { + Reset(start); + return false; + } + if (!unicode::IsTrailSurrogate(*value)) { + Reset(start); + return false; + } + return true; +} + +template +bool +RegExpParser::ParseRawSurrogatePair(char16_t* lead, char16_t* trail) +{ + widechar c1 = current(); + if (!unicode::IsLeadSurrogate(c1)) + return false; + + const CharT* start = position(); + Advance(); + widechar c2 = current(); + if (!unicode::IsTrailSurrogate(c2)) { + Reset(start); + return false; + } + Advance(); + *lead = c1; + *trail = c2; + return true; +} + +static inline RegExpTree* +RangeAtom(LifoAlloc* alloc, char16_t from, char16_t to) +{ + CharacterRangeVector* ranges = alloc->newInfallible(*alloc); + ranges->append(CharacterRange::Range(from, to)); + return alloc->newInfallible(ranges, false); +} + +static inline RegExpTree* +NegativeLookahead(LifoAlloc* alloc, char16_t from, char16_t to) +{ + return alloc->newInfallible(RangeAtom(alloc, from, to), false, 0, 0); +} + +static bool +IsSyntaxCharacter(widechar c) +{ + switch (c) { + case '^': + case '$': + case '\\': + case '.': + case '*': + case '+': + case '?': + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + case '|': + case '/': + return true; + default: + return false; + } +} + #ifdef DEBUG // Currently only used in an assert.kASSERT. static bool @@ -317,8 +447,8 @@ IsSpecialClassEscape(widechar c) #endif template -widechar -RegExpParser::ParseClassCharacterEscape() +bool +RegExpParser::ParseClassCharacterEscape(widechar* code) { MOZ_ASSERT(current() == '\\'); MOZ_ASSERT(has_next() && !IsSpecialClassEscape(Next())); @@ -326,92 +456,520 @@ RegExpParser::ParseClassCharacterEscape() switch (current()) { case 'b': Advance(); - return '\b'; + *code = '\b'; + return true; // ControlEscape :: one of // f n r t v case 'f': Advance(); - return '\f'; + *code = '\f'; + return true; case 'n': Advance(); - return '\n'; + *code = '\n'; + return true; case 'r': Advance(); - return '\r'; + *code = '\r'; + return true; case 't': Advance(); - return '\t'; + *code = '\t'; + return true; case 'v': Advance(); - return '\v'; + *code = '\v'; + return true; case 'c': { widechar controlLetter = Next(); widechar letter = controlLetter & ~('A' ^ 'a'); // For compatibility with JSC, inside a character class - // we also accept digits and underscore as control characters. - if ((controlLetter >= '0' && controlLetter <= '9') || - controlLetter == '_' || - (letter >= 'A' && letter <= 'Z')) { + // we also accept digits and underscore as control characters, + // but only in non-unicode mode + if ((!unicode_ && + ((controlLetter >= '0' && controlLetter <= '9') || + controlLetter == '_')) || + (letter >= 'A' && letter <= 'Z')) + { Advance(2); // Control letters mapped to ASCII control characters in the range // 0x00-0x1f. - return controlLetter & 0x1f; + *code = controlLetter & 0x1f; + return true; + } + if (unicode_) { + ReportError(JSMSG_INVALID_IDENTITY_ESCAPE); + return false; } // We match JSC in reading the backslash as a literal // character instead of as starting an escape. - return '\\'; + *code = '\\'; + return true; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': - // For compatibility, we interpret a decimal escape that isn't - // a back reference (and therefore either \0 or not valid according - // to the specification) as a 1..3 digit octal character code. - return ParseOctalLiteral(); + if (unicode_) { + if (current() == '0') { + Advance(); + *code = 0; + return true; + } + ReportError(JSMSG_INVALID_IDENTITY_ESCAPE); + return false; + } + // For compatibility, outside of unicode mode, we interpret a decimal + // escape that isn't a back reference (and therefore either \0 or not + // valid according to the specification) as a 1..3 digit octal + // character code. + *code = ParseOctalLiteral(); + return true; case 'x': { Advance(); size_t value; - if (ParseHexEscape(2, &value)) - return value; + if (ParseHexEscape(2, &value)) { + *code = value; + return true; + } + if (unicode_) { + ReportError(JSMSG_INVALID_IDENTITY_ESCAPE); + return false; + } // If \x is not followed by a two-digit hexadecimal, treat it - // as an identity escape. - return 'x'; + // as an identity escape in non-unicode mode. + *code = 'x'; + return true; } case 'u': { Advance(); size_t value; - if (ParseHexEscape(4, &value)) - return value; - // If \u is not followed by a four-digit hexadecimal, treat it + if (unicode_) { + if (current() == '{') { + if (!ParseBracedHexEscape(&value)) + return false; + *code = value; + return true; + } + if (ParseHexEscape(4, &value)) { + if (unicode::IsLeadSurrogate(value)) { + size_t trail; + if (ParseTrailSurrogate(&trail)) { + *code = unicode::UTF16Decode(value, trail); + return true; + } + } + *code = value; + return true; + } + ReportError(JSMSG_INVALID_UNICODE_ESCAPE); + return false; + } + if (ParseHexEscape(4, &value)) { + *code = value; + return true; + } + // If \u is not followed by a four-digit or braced hexadecimal, treat it // as an identity escape. - return 'u'; + *code = 'u'; + return true; } default: { - // Extended identity escape. We accept any character that hasn't - // been matched by a more specific case, not just the subset required - // by the ECMAScript specification. + // Extended identity escape (non-unicode only). We accept any character + // that hasn't been matched by a more specific case, not just the subset + // required by the ECMAScript specification. widechar result = current(); + if (unicode_ && result != '-' && !IsSyntaxCharacter(result)) { + ReportError(JSMSG_INVALID_IDENTITY_ESCAPE); + return false; + } Advance(); - return result; + *code = result; + return true; } } - return 0; + return true; +} + +class WideCharRange +{ + public: + WideCharRange() + : from_(0), to_(0) + {} + + WideCharRange(widechar from, widechar to) + : from_(from), to_(to) + {} + + static inline WideCharRange Singleton(widechar value) { + return WideCharRange(value, value); + } + static inline WideCharRange Range(widechar from, widechar to) { + MOZ_ASSERT(from <= to); + return WideCharRange(from, to); + } + + bool Contains(widechar i) const { return from_ <= i && i <= to_; } + widechar from() const { return from_; } + widechar to() const { return to_; } + + private: + widechar from_; + widechar to_; +}; + +typedef Vector > WideCharRangeVector; + +static inline CharacterRange +LeadSurrogateRange() +{ + return CharacterRange::Range(unicode::LeadSurrogateMin, unicode::LeadSurrogateMax); +} + +static inline CharacterRange +TrailSurrogateRange() +{ + return CharacterRange::Range(unicode::TrailSurrogateMin, unicode::TrailSurrogateMax); +} + +static inline WideCharRange +NonBMPRange() +{ + return WideCharRange::Range(unicode::NonBMPMin, unicode::NonBMPMax); } static const char16_t kNoCharClass = 0; -// Adds range or pre-defined character class to character ranges. +// Adds a character or pre-defined character class to character ranges. // If char_class is not kInvalidClass, it's interpreted as a class // escape (i.e., 's' means whitespace, from '\s'). static inline void -AddRangeOrEscape(LifoAlloc* alloc, - CharacterRangeVector* ranges, - char16_t char_class, - CharacterRange range) +AddCharOrEscape(LifoAlloc* alloc, + CharacterRangeVector* ranges, + char16_t char_class, + widechar c) { if (char_class != kNoCharClass) CharacterRange::AddClassEscape(alloc, char_class, ranges); else - ranges->append(range); + ranges->append(CharacterRange::Singleton(c)); +} + +static inline void +AddCharOrEscapeUnicode(LifoAlloc* alloc, + CharacterRangeVector* ranges, + CharacterRangeVector* lead_ranges, + CharacterRangeVector* trail_ranges, + WideCharRangeVector* wide_ranges, + char16_t char_class, + widechar c, + bool ignore_case) +{ + if (char_class != kNoCharClass) { + CharacterRange::AddClassEscapeUnicode(alloc, char_class, ranges, ignore_case); + switch (char_class) { + case 'S': + case 'W': + case 'D': + lead_ranges->append(LeadSurrogateRange()); + trail_ranges->append(TrailSurrogateRange()); + wide_ranges->append(NonBMPRange()); + break; + case '.': + MOZ_CRASH("Bad char_class!"); + } + return; + } + + if (unicode::IsLeadSurrogate(c)) + lead_ranges->append(CharacterRange::Singleton(c)); + else if (unicode::IsTrailSurrogate(c)) + trail_ranges->append(CharacterRange::Singleton(c)); + else if (c >= unicode::NonBMPMin) + wide_ranges->append(WideCharRange::Singleton(c)); + else + ranges->append(CharacterRange::Singleton(c)); +} + +static inline void +AddUnicodeRange(LifoAlloc* alloc, + CharacterRangeVector* ranges, + CharacterRangeVector* lead_ranges, + CharacterRangeVector* trail_ranges, + WideCharRangeVector* wide_ranges, + widechar first, + widechar next) +{ + MOZ_ASSERT(first <= next); + if (first < unicode::LeadSurrogateMin) { + if (next < unicode::LeadSurrogateMin) { + ranges->append(CharacterRange::Range(first, next)); + return; + } + ranges->append(CharacterRange::Range(first, unicode::LeadSurrogateMin - 1)); + first = unicode::LeadSurrogateMin; + } + if (first <= unicode::LeadSurrogateMax) { + if (next <= unicode::LeadSurrogateMax) { + lead_ranges->append(CharacterRange::Range(first, next)); + return; + } + lead_ranges->append(CharacterRange::Range(first, unicode::LeadSurrogateMax)); + first = unicode::LeadSurrogateMax + 1; + } + MOZ_ASSERT(unicode::LeadSurrogateMax + 1 == unicode::TrailSurrogateMin); + if (first <= unicode::TrailSurrogateMax) { + if (next <= unicode::TrailSurrogateMax) { + trail_ranges->append(CharacterRange::Range(first, next)); + return; + } + trail_ranges->append(CharacterRange::Range(first, unicode::TrailSurrogateMax)); + first = unicode::TrailSurrogateMax + 1; + } + if (first <= unicode::UTF16Max) { + if (next <= unicode::UTF16Max) { + ranges->append(CharacterRange::Range(first, next)); + return; + } + ranges->append(CharacterRange::Range(first, unicode::UTF16Max)); + first = unicode::NonBMPMin; + } + MOZ_ASSERT(unicode::UTF16Max + 1 == unicode::NonBMPMin); + wide_ranges->append(WideCharRange::Range(first, next)); +} + +// Negate a vector of ranges by subtracting its ranges from a range +// encompassing the full range of possible values. +template +static inline void +NegateUnicodeRanges(LifoAlloc* alloc, Vector >** ranges, + RangeType full_range) +{ + typedef Vector > RangeVector; + RangeVector* tmp_ranges = alloc->newInfallible(*alloc); + tmp_ranges->append(full_range); + RangeVector* result_ranges = alloc->newInfallible(*alloc); + + // Perform the following calculation: + // result_ranges = tmp_ranges - ranges + // with the following steps: + // result_ranges = tmp_ranges - ranges[0] + // SWAP(result_ranges, tmp_ranges) + // result_ranges = tmp_ranges - ranges[1] + // SWAP(result_ranges, tmp_ranges) + // ... + // result_ranges = tmp_ranges - ranges[N-1] + // SWAP(result_ranges, tmp_ranges) + // The last SWAP is just for simplicity of the loop. + for (size_t i = 0; i < (*ranges)->length(); i++) { + result_ranges->clear(); + + const RangeType& range = (**ranges)[i]; + for (size_t j = 0; j < tmp_ranges->length(); j++) { + const RangeType& tmpRange = (*tmp_ranges)[j]; + size_t from1 = tmpRange.from(); + size_t to1 = tmpRange.to(); + size_t from2 = range.from(); + size_t to2 = range.to(); + + if (from1 < from2) { + if (to1 < from2) { + result_ranges->append(tmpRange); + } else if (to1 <= to2) { + result_ranges->append(RangeType::Range(from1, from2 - 1)); + } else { + result_ranges->append(RangeType::Range(from1, from2 - 1)); + result_ranges->append(RangeType::Range(to2 + 1, to1)); + } + } else if (from1 <= to2) { + if (to1 > to2) + result_ranges->append(RangeType::Range(to2 + 1, to1)); + } else { + result_ranges->append(tmpRange); + } + } + + auto tmp = tmp_ranges; + tmp_ranges = result_ranges; + result_ranges = tmp; + } + + // After the loop, result is pointed at by tmp_ranges, instead of + // result_ranges. + *ranges = tmp_ranges; +} + +static bool +WideCharRangesContain(WideCharRangeVector* wide_ranges, widechar c) +{ + for (size_t i = 0; i < wide_ranges->length(); i++) { + const WideCharRange& range = (*wide_ranges)[i]; + if (range.Contains(c)) + return true; + } + return false; +} + +static void +CalculateCaseInsensitiveRanges(LifoAlloc* alloc, widechar from, widechar to, int32_t diff, + WideCharRangeVector* wide_ranges, + WideCharRangeVector** tmp_wide_ranges) +{ + widechar contains_from = 0; + widechar contains_to = 0; + for (widechar c = from; c <= to; c++) { + if (WideCharRangesContain(wide_ranges, c) && + !WideCharRangesContain(wide_ranges, c + diff)) + { + if (contains_from == 0) + contains_from = c; + contains_to = c; + } else if (contains_from != 0) { + if (!*tmp_wide_ranges) + *tmp_wide_ranges = alloc->newInfallible(*alloc); + + (*tmp_wide_ranges)->append(WideCharRange::Range(contains_from + diff, + contains_to + diff)); + contains_from = 0; + } + } + + if (contains_from != 0) { + if (!*tmp_wide_ranges) + *tmp_wide_ranges = alloc->newInfallible(*alloc); + + (*tmp_wide_ranges)->append(WideCharRange::Range(contains_from + diff, + contains_to + diff)); + } +} + +static RegExpTree* +UnicodeRangesAtom(LifoAlloc* alloc, + CharacterRangeVector* ranges, + CharacterRangeVector* lead_ranges, + CharacterRangeVector* trail_ranges, + WideCharRangeVector* wide_ranges, + bool is_negated, + bool ignore_case) +{ + // Calculate case folding for non-BMP first and negate the range if needed. + if (ignore_case) { + WideCharRangeVector* tmp_wide_ranges = nullptr; +#define CALL_CALC(FROM, TO, LEAD, TRAIL_FROM, TRAIL_TO, DIFF) \ + CalculateCaseInsensitiveRanges(alloc, FROM, TO, DIFF, wide_ranges, &tmp_wide_ranges); + FOR_EACH_NON_BMP_CASE_FOLDING(CALL_CALC) +#undef CALL_CALC + + if (tmp_wide_ranges) { + for (size_t i = 0; i < tmp_wide_ranges->length(); i++) + wide_ranges->append((*tmp_wide_ranges)[i]); + } + } + + if (is_negated) { + NegateUnicodeRanges(alloc, &lead_ranges, LeadSurrogateRange()); + NegateUnicodeRanges(alloc, &trail_ranges, TrailSurrogateRange()); + NegateUnicodeRanges(alloc, &wide_ranges, NonBMPRange()); + } + + RegExpBuilder* builder = alloc->newInfallible(alloc); + + bool added = false; + + if (is_negated) { + ranges->append(LeadSurrogateRange()); + ranges->append(TrailSurrogateRange()); + } + if (ranges->length() > 0) { + builder->AddAtom(alloc->newInfallible(ranges, is_negated)); + added = true; + } + + if (lead_ranges->length() > 0) { + if (added) + builder->NewAlternative(); + builder->AddAtom(alloc->newInfallible(lead_ranges, false)); + builder->AddAtom(NegativeLookahead(alloc, unicode::TrailSurrogateMin, + unicode::TrailSurrogateMax)); + added = true; + } + + if (trail_ranges->length() > 0) { + if (added) + builder->NewAlternative(); + builder->AddAssertion(alloc->newInfallible( + RegExpAssertion::NOT_AFTER_LEAD_SURROGATE)); + builder->AddAtom(alloc->newInfallible(trail_ranges, false)); + added = true; + } + + for (size_t i = 0; i < wide_ranges->length(); i++) { + if (added) + builder->NewAlternative(); + + const WideCharRange& range = (*wide_ranges)[i]; + widechar from = range.from(); + widechar to = range.to(); + size_t from_lead, from_trail; + size_t to_lead, to_trail; + + unicode::UTF16Encode(from, &from_lead, &from_trail); + if (from == to) { + builder->AddCharacter(from_lead); + builder->AddCharacter(from_trail); + } else { + unicode::UTF16Encode(to, &to_lead, &to_trail); + if (from_lead == to_lead) { + MOZ_ASSERT(from_trail != to_trail); + builder->AddCharacter(from_lead); + builder->AddAtom(RangeAtom(alloc, from_trail, to_trail)); + } else if (from_trail == unicode::TrailSurrogateMin && + to_trail == unicode::TrailSurrogateMax) + { + builder->AddAtom(RangeAtom(alloc, from_lead, to_lead)); + builder->AddAtom(RangeAtom(alloc, unicode::TrailSurrogateMin, + unicode::TrailSurrogateMax)); + } else if (from_lead + 1 == to_lead) { + builder->AddCharacter(from_lead); + builder->AddAtom(RangeAtom(alloc, from_trail, unicode::TrailSurrogateMax)); + + builder->NewAlternative(); + + builder->AddCharacter(to_lead); + builder->AddAtom(RangeAtom(alloc, unicode::TrailSurrogateMin, to_trail)); + } else if (from_lead + 2 == to_lead) { + builder->AddCharacter(from_lead); + builder->AddAtom(RangeAtom(alloc, from_trail, unicode::TrailSurrogateMax)); + + builder->NewAlternative(); + + builder->AddCharacter(from_lead + 1); + builder->AddAtom(RangeAtom(alloc, unicode::TrailSurrogateMin, + unicode::TrailSurrogateMax)); + + builder->NewAlternative(); + + builder->AddCharacter(to_lead); + builder->AddAtom(RangeAtom(alloc, unicode::TrailSurrogateMin, to_trail)); + } else { + builder->AddCharacter(from_lead); + builder->AddAtom(RangeAtom(alloc, from_trail, unicode::TrailSurrogateMax)); + + builder->NewAlternative(); + + builder->AddAtom(RangeAtom(alloc, from_lead + 1, to_lead - 1)); + builder->AddAtom(RangeAtom(alloc, unicode::TrailSurrogateMin, + unicode::TrailSurrogateMax)); + + builder->NewAlternative(); + + builder->AddCharacter(to_lead); + builder->AddAtom(RangeAtom(alloc, unicode::TrailSurrogateMin, to_trail)); + } + } + added = true; + } + + return builder->ToRegExp(); } template @@ -426,9 +984,19 @@ RegExpParser::ParseCharacterClass() Advance(); } CharacterRangeVector* ranges = alloc->newInfallible(*alloc); + CharacterRangeVector* lead_ranges = nullptr; + CharacterRangeVector* trail_ranges = nullptr; + WideCharRangeVector* wide_ranges = nullptr; + + if (unicode_) { + lead_ranges = alloc->newInfallible(*alloc); + trail_ranges = alloc->newInfallible(*alloc); + wide_ranges = alloc->newInfallible(*alloc); + } + while (has_more() && current() != ']') { char16_t char_class = kNoCharClass; - CharacterRange first; + widechar first = 0; if (!ParseClassAtom(&char_class, &first)) return nullptr; if (current() == '-') { @@ -438,41 +1006,69 @@ RegExpParser::ParseCharacterClass() // following code report an error. break; } else if (current() == ']') { - AddRangeOrEscape(alloc, ranges, char_class, first); + if (unicode_) { + AddCharOrEscapeUnicode(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, + char_class, first, ignore_case_); + } else { + AddCharOrEscape(alloc, ranges, char_class, first); + } ranges->append(CharacterRange::Singleton('-')); break; } char16_t char_class_2 = kNoCharClass; - CharacterRange next; + widechar next = 0; if (!ParseClassAtom(&char_class_2, &next)) return nullptr; if (char_class != kNoCharClass || char_class_2 != kNoCharClass) { + if (unicode_) + return ReportError(JSMSG_RANGE_WITH_CLASS_ESCAPE); + // Either end is an escaped character class. Treat the '-' verbatim. - AddRangeOrEscape(alloc, ranges, char_class, first); + AddCharOrEscape(alloc, ranges, char_class, first); ranges->append(CharacterRange::Singleton('-')); - AddRangeOrEscape(alloc, ranges, char_class_2, next); + AddCharOrEscape(alloc, ranges, char_class_2, next); continue; } - if (first.from() > next.to()) + if (first > next) return ReportError(JSMSG_BAD_CLASS_RANGE); - ranges->append(CharacterRange::Range(first.from(), next.to())); + if (unicode_) + AddUnicodeRange(alloc, ranges, lead_ranges, trail_ranges,wide_ranges, first, next); + else + ranges->append(CharacterRange::Range(first, next)); } else { - AddRangeOrEscape(alloc, ranges, char_class, first); + if (unicode_) { + AddCharOrEscapeUnicode(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, + char_class, first, ignore_case_); + } else { + AddCharOrEscape(alloc, ranges, char_class, first); + } } } if (!has_more()) return ReportError(JSMSG_UNTERM_CLASS); Advance(); - if (ranges->length() == 0) { - ranges->append(CharacterRange::Everything()); - is_negated = !is_negated; + if (!unicode_) { + if (ranges->length() == 0) { + ranges->append(CharacterRange::Everything()); + is_negated = !is_negated; + } + return alloc->newInfallible(ranges, is_negated); } - return alloc->newInfallible(ranges, is_negated); + + if (!is_negated && ranges->length() == 0 && lead_ranges->length() == 0 && + trail_ranges->length() == 0 && wide_ranges->length() == 0) + { + ranges->append(CharacterRange::Everything()); + return alloc->newInfallible(ranges, true); + } + + return UnicodeRangesAtom(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, is_negated, + ignore_case_); } template bool -RegExpParser::ParseClassAtom(char16_t* char_class, CharacterRange* char_range) +RegExpParser::ParseClassAtom(char16_t* char_class, widechar* value) { MOZ_ASSERT(*char_class == kNoCharClass); widechar first = current(); @@ -486,13 +1082,20 @@ RegExpParser::ParseClassAtom(char16_t* char_class, CharacterRange* char_r case kEndMarker: return ReportError(JSMSG_ESCAPE_AT_END_OF_REGEXP); default: - widechar c = ParseClassCharacterEscape(); - *char_range = CharacterRange::Singleton(c); + if (!ParseClassCharacterEscape(value)) + return false; return true; } } else { + if (unicode_) { + char16_t lead, trail; + if (ParseRawSurrogatePair(&lead, &trail)) { + *value = unicode::UTF16Decode(lead, trail); + return true; + } + } Advance(); - *char_range = CharacterRange::Singleton(first); + *value = first; return true; } } @@ -673,6 +1276,120 @@ RegExpParser::ParsePattern() return result; } +static inline RegExpTree* +CaseFoldingSurrogatePairAtom(LifoAlloc* alloc, char16_t lead, char16_t trail, int32_t diff) +{ + RegExpBuilder* builder = alloc->newInfallible(alloc); + + builder->AddCharacter(lead); + CharacterRangeVector* ranges = alloc->newInfallible(*alloc); + ranges->append(CharacterRange::Range(trail, trail)); + ranges->append(CharacterRange::Range(trail + diff, trail + diff)); + builder->AddAtom(alloc->newInfallible(ranges, false)); + + return builder->ToRegExp(); +} + +static inline RegExpTree* +SurrogatePairAtom(LifoAlloc* alloc, char16_t lead, char16_t trail, bool ignore_case) +{ + if (ignore_case) { +#define CALL_ATOM(FROM, TO, LEAD, TRAIL_FROM, TRAIL_TO, DIFF) \ + if (lead == LEAD &&trail >= TRAIL_FROM && trail <= TRAIL_TO) \ + return CaseFoldingSurrogatePairAtom(alloc, lead, trail, DIFF); + FOR_EACH_NON_BMP_CASE_FOLDING(CALL_ATOM) +#undef CALL_ATOM + } + + RegExpBuilder* builder = alloc->newInfallible(alloc); + builder->AddCharacter(lead); + builder->AddCharacter(trail); + return builder->ToRegExp(); +} + +static inline RegExpTree* +LeadSurrogateAtom(LifoAlloc* alloc, char16_t value) +{ + RegExpBuilder* builder = alloc->newInfallible(alloc); + builder->AddCharacter(value); + builder->AddAtom(NegativeLookahead(alloc, unicode::TrailSurrogateMin, + unicode::TrailSurrogateMax)); + return builder->ToRegExp(); +} + +static inline RegExpTree* +TrailSurrogateAtom(LifoAlloc* alloc, char16_t value) +{ + RegExpBuilder* builder = alloc->newInfallible(alloc); + builder->AddAssertion(alloc->newInfallible( + RegExpAssertion::NOT_AFTER_LEAD_SURROGATE)); + builder->AddCharacter(value); + return builder->ToRegExp(); +} + +static inline RegExpTree* +UnicodeEverythingAtom(LifoAlloc* alloc) +{ + RegExpBuilder* builder = alloc->newInfallible(alloc); + + // everything except \x0a, \x0d, \u2028 and \u2029 + + CharacterRangeVector* ranges = alloc->newInfallible(*alloc); + ranges->append(CharacterRange::Range(0x0, 0x09)); + ranges->append(CharacterRange::Range(0x0b, 0x0c)); + ranges->append(CharacterRange::Range(0x0e, 0x2027)); + ranges->append(CharacterRange::Range(0x202A, unicode::LeadSurrogateMin - 1)); + ranges->append(CharacterRange::Range(unicode::TrailSurrogateMax + 1, unicode::UTF16Max)); + builder->AddAtom(alloc->newInfallible(ranges, false)); + + builder->NewAlternative(); + + builder->AddAtom(RangeAtom(alloc, unicode::LeadSurrogateMin, unicode::LeadSurrogateMax)); + builder->AddAtom(NegativeLookahead(alloc, unicode::TrailSurrogateMin, + unicode::TrailSurrogateMax)); + + builder->NewAlternative(); + + builder->AddAssertion(alloc->newInfallible( + RegExpAssertion::NOT_AFTER_LEAD_SURROGATE)); + builder->AddAtom(RangeAtom(alloc, unicode::TrailSurrogateMin, unicode::TrailSurrogateMax)); + + builder->NewAlternative(); + + builder->AddAtom(RangeAtom(alloc, unicode::LeadSurrogateMin, unicode::LeadSurrogateMax)); + builder->AddAtom(RangeAtom(alloc, unicode::TrailSurrogateMin, unicode::TrailSurrogateMax)); + + return builder->ToRegExp(); +} + +RegExpTree* +UnicodeCharacterClassEscapeAtom(LifoAlloc* alloc, char16_t char_class, bool ignore_case) +{ + CharacterRangeVector* ranges = alloc->newInfallible(*alloc); + CharacterRangeVector* lead_ranges = alloc->newInfallible(*alloc); + CharacterRangeVector* trail_ranges = alloc->newInfallible(*alloc); + WideCharRangeVector* wide_ranges = alloc->newInfallible(*alloc); + AddCharOrEscapeUnicode(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, char_class, 0, + ignore_case); + + return UnicodeRangesAtom(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, false, false); +} + +static inline RegExpTree* +UnicodeBackReferenceAtom(LifoAlloc* alloc, RegExpTree* atom) +{ + // If a back reference has a standalone lead surrogate as its last + // character, then that lead surrogate shouldn't match lead surrogates that + // are paired with a corresponding trail surrogate. + RegExpBuilder* builder = alloc->newInfallible(alloc); + + builder->AddAtom(atom); + builder->AddAssertion(alloc->newInfallible( + RegExpAssertion::NOT_IN_SURROGATE_PAIR)); + + return builder->ToRegExp(); +} + // Disjunction :: // Alternative // Alternative | Disjunction @@ -736,6 +1453,8 @@ RegExpParser::ParseDisjunction() capture_index); } builder->AddAtom(body); + if (unicode_ && (group_type == POSITIVE_LOOKAHEAD || group_type == NEGATIVE_LOOKAHEAD)) + continue; // For compatability with JSC and ES3, we allow quantifiers after // lookaheads, and break in all cases. break; @@ -770,6 +1489,10 @@ RegExpParser::ParseDisjunction() case '.': { Advance(); // everything except \x0a, \x0d, \u2028 and \u2029 + if (unicode_) { + builder->AddAtom(UnicodeEverythingAtom(alloc)); + break; + } CharacterRangeVector* ranges = alloc->newInfallible(*alloc); CharacterRange::AddClassEscape(alloc, '.', ranges); RegExpTree* atom = alloc->newInfallible(ranges, false); @@ -833,12 +1556,24 @@ RegExpParser::ParseDisjunction() // // CharacterClassEscape :: one of // d D s S w W - case 'd': case 'D': case 's': case 'S': case 'w': case 'W': { + case 'D': case 'S': case 'W': + if (unicode_) { + Advance(); + builder->AddAtom(UnicodeCharacterClassEscapeAtom(alloc, current(), + ignore_case_)); + Advance(); + break; + } + // Fall through + case 'd': case 's': case 'w': { widechar c = Next(); Advance(2); CharacterRangeVector* ranges = alloc->newInfallible(*alloc); - CharacterRange::AddClassEscape(alloc, c, ranges); + if (unicode_) + CharacterRange::AddClassEscapeUnicode(alloc, c, ranges, ignore_case_); + else + CharacterRange::AddClassEscape(alloc, c, ranges); RegExpTree* atom = alloc->newInfallible(ranges, false); builder->AddAtom(atom); break; @@ -856,9 +1591,14 @@ RegExpParser::ParseDisjunction() break; } RegExpTree* atom = alloc->newInfallible(capture); - builder->AddAtom(atom); + if (unicode_) + builder->AddAtom(UnicodeBackReferenceAtom(alloc, atom)); + else + builder->AddAtom(atom); break; } + if (unicode_) + return ReportError(JSMSG_BACK_REF_OUT_OF_RANGE); widechar first_digit = Next(); if (first_digit == '8' || first_digit == '9') { // Treat as identity escape @@ -869,6 +1609,14 @@ RegExpParser::ParseDisjunction() } // FALLTHROUGH case '0': { + if (unicode_) { + Advance(2); + if (IsDecimalDigit(current())) + return ReportError(JSMSG_INVALID_DECIMAL_ESCAPE); + builder->AddCharacter(0); + break; + } + Advance(); size_t octal = ParseOctalLiteral(); builder->AddCharacter(octal); @@ -903,6 +1651,8 @@ RegExpParser::ParseDisjunction() // Convert lower case letters to uppercase. widechar letter = controlLetter & ~('a' ^ 'A'); if (letter < 'A' || 'Z' < letter) { + if (unicode_) + return ReportError(JSMSG_INVALID_IDENTITY_ESCAPE); // controlLetter is not in range 'A'-'Z' or 'a'-'z'. // This is outside the specification. We match JSC in // reading the backslash as a literal character instead @@ -920,6 +1670,8 @@ RegExpParser::ParseDisjunction() if (ParseHexEscape(2, &value)) { builder->AddCharacter(value); } else { + if (unicode_) + return ReportError(JSMSG_INVALID_IDENTITY_ESCAPE); builder->AddCharacter('x'); } break; @@ -927,6 +1679,41 @@ RegExpParser::ParseDisjunction() case 'u': { Advance(2); size_t value; + if (unicode_) { + if (current() == '{') { + if (!ParseBracedHexEscape(&value)) + return nullptr; + if (unicode::IsLeadSurrogate(value)) { + builder->AddAtom(LeadSurrogateAtom(alloc, value)); + } else if (unicode::IsTrailSurrogate(value)) { + builder->AddAtom(TrailSurrogateAtom(alloc, value)); + } else if (value >= unicode::NonBMPMin) { + size_t lead, trail; + unicode::UTF16Encode(value, &lead, &trail); + builder->AddAtom(SurrogatePairAtom(alloc, lead, trail, + ignore_case_)); + } else { + builder->AddCharacter(value); + } + } else if (ParseHexEscape(4, &value)) { + if (unicode::IsLeadSurrogate(value)) { + size_t trail; + if (ParseTrailSurrogate(&trail)) { + builder->AddAtom(SurrogatePairAtom(alloc, value, trail, + ignore_case_)); + } else { + builder->AddAtom(LeadSurrogateAtom(alloc, value)); + } + } else if (unicode::IsTrailSurrogate(value)) { + builder->AddAtom(TrailSurrogateAtom(alloc, value)); + } else { + builder->AddCharacter(value); + } + } else { + return ReportError(JSMSG_INVALID_UNICODE_ESCAPE); + } + break; + } if (ParseHexEscape(4, &value)) { builder->AddCharacter(value); } else { @@ -936,18 +1723,42 @@ RegExpParser::ParseDisjunction() } default: // Identity escape. + if (unicode_ && !IsSyntaxCharacter(Next())) + return ReportError(JSMSG_INVALID_IDENTITY_ESCAPE); builder->AddCharacter(Next()); Advance(2); break; } break; case '{': { + if (unicode_) + return ReportError(JSMSG_RAW_BRACE_IN_REGEP); int dummy; if (ParseIntervalQuantifier(&dummy, &dummy)) return ReportError(JSMSG_NOTHING_TO_REPEAT); // fallthrough } default: + if (unicode_) { + char16_t lead, trail; + if (ParseRawSurrogatePair(&lead, &trail)) { + builder->AddAtom(SurrogatePairAtom(alloc, lead, trail, ignore_case_)); + } else { + widechar c = current(); + if (unicode::IsLeadSurrogate(c)) + builder->AddAtom(LeadSurrogateAtom(alloc, c)); + else if (unicode::IsTrailSurrogate(c)) + builder->AddAtom(TrailSurrogateAtom(alloc, c)); + else if (c == ']') + return ReportError(JSMSG_RAW_BRACKET_IN_REGEP); + else if (c == '}') + return ReportError(JSMSG_RAW_BRACE_IN_REGEP); + else + builder->AddCharacter(c); + Advance(); + } + break; + } builder->AddCharacter(current()); Advance(); break; @@ -1002,7 +1813,8 @@ template class irregexp::RegExpParser; template static bool ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, size_t length, - bool multiline, bool match_only, RegExpCompileData* data) + bool multiline, bool match_only, bool unicode, bool ignore_case, + RegExpCompileData* data) { if (match_only) { // Try to strip a leading '.*' from the RegExp, but only if it is not @@ -1025,7 +1837,7 @@ ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, si } } - RegExpParser parser(ts, &alloc, chars, chars + length, multiline); + RegExpParser parser(ts, &alloc, chars, chars + length, multiline, unicode, ignore_case); data->tree = parser.ParsePattern(); if (!data->tree) return false; @@ -1038,32 +1850,34 @@ ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, si bool irregexp::ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str, - bool multiline, bool match_only, + bool multiline, bool match_only, bool unicode, bool ignore_case, RegExpCompileData* data) { JS::AutoCheckCannotGC nogc; return str->hasLatin1Chars() ? ::ParsePattern(ts, alloc, str->latin1Chars(nogc), str->length(), - multiline, match_only, data) + multiline, match_only, unicode, ignore_case, data) : ::ParsePattern(ts, alloc, str->twoByteChars(nogc), str->length(), - multiline, match_only, data); + multiline, match_only, unicode, ignore_case, data); } template static bool -ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, size_t length) +ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, size_t length, + bool unicode) { LifoAllocScope scope(&alloc); - RegExpParser parser(ts, &alloc, chars, chars + length, false); + RegExpParser parser(ts, &alloc, chars, chars + length, false, unicode, false); return parser.ParsePattern() != nullptr; } bool -irregexp::ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str) +irregexp::ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str, + bool unicode) { JS::AutoCheckCannotGC nogc; return str->hasLatin1Chars() - ? ::ParsePatternSyntax(ts, alloc, str->latin1Chars(nogc), str->length()) - : ::ParsePatternSyntax(ts, alloc, str->twoByteChars(nogc), str->length()); + ? ::ParsePatternSyntax(ts, alloc, str->latin1Chars(nogc), str->length(), unicode) + : ::ParsePatternSyntax(ts, alloc, str->twoByteChars(nogc), str->length(), unicode); } diff --git a/js/src/irregexp/RegExpParser.h b/js/src/irregexp/RegExpParser.h index e36a47f75..34d0620d6 100644 --- a/js/src/irregexp/RegExpParser.h +++ b/js/src/irregexp/RegExpParser.h @@ -43,11 +43,12 @@ namespace irregexp { bool ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str, - bool multiline, bool match_only, + bool multiline, bool match_only, bool unicode, bool ignore_case, RegExpCompileData* data); bool -ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str); +ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str, + bool unicode); // A BufferedVector is an automatically growing list, just like (and backed // by) a Vector, that is optimized for the case of adding and removing @@ -174,7 +175,8 @@ class RegExpParser { public: RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc, - const CharT* chars, const CharT* end, bool multiline_mode); + const CharT* chars, const CharT* end, bool multiline_mode, bool unicode, + bool ignore_case); RegExpTree* ParsePattern(); RegExpTree* ParseDisjunction(); @@ -184,14 +186,20 @@ class RegExpParser // out parameters. bool ParseIntervalQuantifier(int* min_out, int* max_out); - // Parses and returns a single escaped character. The character - // must not be 'b' or 'B' since they are usually handled specially. - widechar ParseClassCharacterEscape(); + // Tries to parse the input as a single escaped character. If successful + // it stores the result in the output parameter and returns true. + // Otherwise it throws an error and returns false. The character must not + // be 'b' or 'B' since they are usually handled specially. + bool ParseClassCharacterEscape(widechar* code); // Checks whether the following is a length-digit hexadecimal number, // and sets the value if it is. bool ParseHexEscape(int length, size_t* value); + bool ParseBracedHexEscape(size_t* value); + bool ParseTrailSurrogate(size_t* value); + bool ParseRawSurrogatePair(char16_t* lead, char16_t* trail); + size_t ParseOctalLiteral(); // Tries to parse the input as a back reference. If successful it @@ -200,7 +208,7 @@ class RegExpParser // can be reparsed. bool ParseBackReferenceIndex(int* index_out); - bool ParseClassAtom(char16_t* char_class, CharacterRange* char_range); + bool ParseClassAtom(char16_t* char_class, widechar *value); RegExpTree* ReportError(unsigned errorNumber); void Advance(); void Advance(int dist) { @@ -288,6 +296,8 @@ class RegExpParser int capture_count_; bool has_more_; bool multiline_; + bool unicode_; + bool ignore_case_; bool simple_; bool contains_anchor_; bool is_scanned_for_captures_; diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py index ce317d805..8bf3c0cff 100644 --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -59,7 +59,7 @@ def main(argv): help='also run tests marked as slow') op.add_option('--no-slow', dest='run_slow', action='store_false', help='do not run tests marked as slow (the default)') - op.add_option('-t', '--timeout', dest='timeout', type=float, default=150.0, + op.add_option('-t', '--timeout', dest='timeout', type=float, default=200.0, help='set test timeout in seconds') op.add_option('--no-progress', dest='hide_progress', action='store_true', help='hide progress bar') diff --git a/js/src/jit-test/tests/auto-regress/bug771027.js b/js/src/jit-test/tests/auto-regress/bug771027.js index 8c7b9a81a..d9038ad85 100644 --- a/js/src/jit-test/tests/auto-regress/bug771027.js +++ b/js/src/jit-test/tests/auto-regress/bug771027.js @@ -4,6 +4,6 @@ // Flags: // -Array.prototype.iterator = (function() { { while(0) function Uint8ClampedArray() { } } }); +Array.prototype.iterator = (function() { { while(0) { function Uint8ClampedArray() { } } } }); var s = new Set(["testing", "testing", 123]); assertEq(s.size(), 2); diff --git a/js/src/jit-test/tests/baseline/bug1081850.js b/js/src/jit-test/tests/baseline/bug1081850.js deleted file mode 100644 index e77158a1c..000000000 --- a/js/src/jit-test/tests/baseline/bug1081850.js +++ /dev/null @@ -1,18 +0,0 @@ -// |jit-test| ion-eager - -var ARR = []; -try { - function f() { - ARR.push(eval.prototype) - } - f() - function eval()(0) - f() -} catch (e) {} - -if (ARR.length !== 2) - throw new Error("ERROR 1"); -if (typeof(ARR[0]) !== 'undefined') - throw new Error("ERROR 2"); -if (typeof(ARR[1]) !== 'object') - throw new Error("ERROR 3"); diff --git a/js/src/jit-test/tests/basic/bug667504-syntax.js b/js/src/jit-test/tests/basic/bug667504-syntax.js index c2e926d96..606922020 100644 --- a/js/src/jit-test/tests/basic/bug667504-syntax.js +++ b/js/src/jit-test/tests/basic/bug667504-syntax.js @@ -1,2 +1,3 @@ -for (var x in x) +for (var x in x) { function x() {} +} diff --git a/js/src/jit-test/tests/basic/functionRedeclConst.js b/js/src/jit-test/tests/basic/functionRedeclConst.js index 8b0af6546..fd7fec655 100644 --- a/js/src/jit-test/tests/basic/functionRedeclConst.js +++ b/js/src/jit-test/tests/basic/functionRedeclConst.js @@ -1,4 +1,4 @@ -// |jit-test| error: TypeError +// |jit-test| error: SyntaxError { const x = 0; function x() { } diff --git a/js/src/jit-test/tests/basic/functionRedeclGlobalConst.js b/js/src/jit-test/tests/basic/functionRedeclGlobalConst.js index 90784cc18..117811b6b 100644 --- a/js/src/jit-test/tests/basic/functionRedeclGlobalConst.js +++ b/js/src/jit-test/tests/basic/functionRedeclGlobalConst.js @@ -1,3 +1,3 @@ -// |jit-test| error: TypeError +// |jit-test| error: SyntaxError const x = 0; function x() { } diff --git a/js/src/jit-test/tests/basic/functionRedeclLet.js b/js/src/jit-test/tests/basic/functionRedeclLet.js index 6ec6dfd49..7014c3ba8 100644 --- a/js/src/jit-test/tests/basic/functionRedeclLet.js +++ b/js/src/jit-test/tests/basic/functionRedeclLet.js @@ -1,4 +1,4 @@ -// |jit-test| error: TypeError +// |jit-test| error: SyntaxError { let x; function x() { } diff --git a/js/src/jit-test/tests/ion/bug1148973-1.js b/js/src/jit-test/tests/ion/bug1148973-1.js index b8fb36768..34d067144 100644 --- a/js/src/jit-test/tests/ion/bug1148973-1.js +++ b/js/src/jit-test/tests/ion/bug1148973-1.js @@ -8,7 +8,11 @@ try { } }(), function() {})) } catch (e) {}; +var log = ""; +evaluate(` try { function x() {} assertEq(String(b), "function () {}"); -} catch (e) { throw (e); } +} catch (e) { log += "e"; } +`); +assertEq(log, "e"); diff --git a/js/src/jit-test/tests/ion/bug1212605.js b/js/src/jit-test/tests/ion/bug1212605.js index 978e0d8b9..3841231a9 100644 --- a/js/src/jit-test/tests/ion/bug1212605.js +++ b/js/src/jit-test/tests/ion/bug1212605.js @@ -1,3 +1,3 @@ -// |jit-test| error: TypeError +// |jit-test| error: SyntaxError s = newGlobal(); evalcx("let NaN = 0;", s); diff --git a/js/src/jit-test/tests/ion/bug1214013.js b/js/src/jit-test/tests/ion/bug1214013.js index e8841fee4..53001f05a 100644 --- a/js/src/jit-test/tests/ion/bug1214013.js +++ b/js/src/jit-test/tests/ion/bug1214013.js @@ -1,4 +1,4 @@ -// |jit-test| error: TypeError +// |jit-test| error: SyntaxError var hits = 0; with(f_arg => constructor.f_arg([3, 4, 5], null)) var length = 257751; diff --git a/js/src/jit-test/tests/modules/bug-1236875.js b/js/src/jit-test/tests/modules/bug-1236875.js new file mode 100644 index 000000000..41751f947 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1236875.js @@ -0,0 +1,2 @@ +let m = parseModule(`{ function x() {} }`); +m.declarationInstantiation(); diff --git a/js/src/jit-test/tests/modules/duplicate-imports.js b/js/src/jit-test/tests/modules/duplicate-imports.js index 6e0ce853e..fa87ba8eb 100644 --- a/js/src/jit-test/tests/modules/duplicate-imports.js +++ b/js/src/jit-test/tests/modules/duplicate-imports.js @@ -6,19 +6,8 @@ function testNoError(source) { parseModule(source); } -function testParseError(source, expectedError) { - print(source); - assertThrowsInstanceOf(function () { - parseModule(source); - }, expectedError); -} - function testSyntaxError(source) { - testParseError(source, SyntaxError); -} - -function testTypeError(source) { - testParseError(source, TypeError); + assertThrowsInstanceOf(() => parseModule(source), SyntaxError); } testNoError("import { a } from 'm';"); @@ -26,15 +15,13 @@ testNoError("import { a as b } from 'm';"); testNoError("import * as a from 'm';"); testNoError("import a from 'm';"); -// TODO: The spec says redeclaration is a syntax error but we report it as a -// type error. -testTypeError("import { a } from 'm'; let a = 1;"); -testTypeError("let a = 1; import { a } from 'm';"); -testTypeError("import { a } from 'm'; var a = 1;"); -testTypeError("var a = 1; import { a } from 'm';"); -testTypeError("import { a, b } from 'm'; const b = 1;"); -testTypeError("import { a } from 'm'; import { a } from 'm2';"); -testTypeError("import { a } from 'm'; import { b as a } from 'm2';"); -testTypeError("import { a } from 'm'; import * as a from 'm2';"); -testTypeError("import { a } from 'm'; import a from 'm2';"); +testSyntaxError("import { a } from 'm'; let a = 1;"); +testSyntaxError("let a = 1; import { a } from 'm';"); +testSyntaxError("import { a } from 'm'; var a = 1;"); +testSyntaxError("var a = 1; import { a } from 'm';"); +testSyntaxError("import { a, b } from 'm'; const b = 1;"); +testSyntaxError("import { a } from 'm'; import { a } from 'm2';"); +testSyntaxError("import { a } from 'm'; import { b as a } from 'm2';"); +testSyntaxError("import { a } from 'm'; import * as a from 'm2';"); +testSyntaxError("import { a } from 'm'; import a from 'm2';"); diff --git a/js/src/jit-test/tests/tracelogger/bug1304641.js b/js/src/jit-test/tests/tracelogger/bug1304641.js new file mode 100644 index 000000000..7ad48b7a4 --- /dev/null +++ b/js/src/jit-test/tests/tracelogger/bug1304641.js @@ -0,0 +1,13 @@ + +var du = new Debugger(); +if (typeof du.startTraceLogger === "function") { + var failed = false; + try { + newGlobal().startTraceLogger(); + print("z"); + } catch (e) { + failed = true; + } + + assertEq(failed, true); +} diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 79482681a..197763852 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -2270,6 +2270,26 @@ BaselineCompiler::emit_JSOP_BINDGNAME() return emit_JSOP_BINDNAME(); } +typedef JSObject* (*BindVarFn)(JSContext*, HandleObject); +static const VMFunction BindVarInfo = FunctionInfo(jit::BindVar); + +bool +BaselineCompiler::emit_JSOP_BINDVAR() +{ + frame.syncStack(0); + masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); + + prepareVMCall(); + pushArg(R0.scratchReg()); + + if (!callVM(BindVarInfo)) + return false; + + masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); + frame.push(R0); + return true; +} + bool BaselineCompiler::emit_JSOP_SETPROP() { diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index e4840c2c0..c1e69a91b 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -141,6 +141,7 @@ namespace jit { _(JSOP_DELNAME) \ _(JSOP_GETIMPORT) \ _(JSOP_GETINTRINSIC) \ + _(JSOP_BINDVAR) \ _(JSOP_DEFVAR) \ _(JSOP_DEFCONST) \ _(JSOP_DEFLET) \ diff --git a/js/src/jit/BytecodeAnalysis.cpp b/js/src/jit/BytecodeAnalysis.cpp index ca4456e64..19ce05e0c 100644 --- a/js/src/jit/BytecodeAnalysis.cpp +++ b/js/src/jit/BytecodeAnalysis.cpp @@ -158,6 +158,7 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn) case JSOP_GETNAME: case JSOP_BINDNAME: + case JSOP_BINDVAR: case JSOP_SETNAME: case JSOP_STRICTSETNAME: case JSOP_DELNAME: diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index d921f1b3f..63bd5743b 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -8428,6 +8428,16 @@ CodeGenerator::visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint* ool masm.jump(ool->rejoin()); } +typedef JSObject* (*BindVarFn)(JSContext*, HandleObject); +static const VMFunction BindVarInfo = FunctionInfo(jit::BindVar); + +void +CodeGenerator::visitCallBindVar(LCallBindVar* lir) +{ + pushArg(ToRegister(lir->scopeChain())); + callVM(BindVarInfo, lir); +} + typedef bool (*GetPropertyFn)(JSContext*, HandleValue, HandlePropertyName, MutableHandleValue); static const VMFunction GetPropertyInfo = FunctionInfo(GetProperty); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 7a1ae9a2a..65d8f0fb9 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -329,6 +329,7 @@ class CodeGenerator : public CodeGeneratorSpecific void visitSetDOMProperty(LSetDOMProperty* lir); void visitCallDOMNative(LCallDOMNative* lir); void visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir); + void visitCallBindVar(LCallBindVar* lir); void visitIsCallable(LIsCallable* lir); void visitOutOfLineIsCallable(OutOfLineIsCallable* ool); void visitIsObject(LIsObject* lir); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 5157da785..8db7f1755 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -1947,6 +1947,9 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_BINDNAME: return jsop_bindname(info().getName(pc)); + case JSOP_BINDVAR: + return jsop_bindvar(); + case JSOP_DUP: current->pushSlot(current->stackDepth() - 1); return true; @@ -8425,6 +8428,16 @@ IonBuilder::jsop_bindname(PropertyName* name) return resumeAfter(ins); } +bool +IonBuilder::jsop_bindvar() +{ + MOZ_ASSERT(analysis().usesScopeChain()); + MCallBindVar* ins = MCallBindVar::New(alloc(), current->scopeChain()); + current->add(ins); + current->push(ins); + return true; +} + static MIRType GetElemKnownType(bool needsHoleCheck, TemporaryTypeSet* types) { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index f8f7a2cce..534a0d427 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -689,6 +689,7 @@ class IonBuilder bool jsop_intrinsic(PropertyName* name); bool jsop_getimport(PropertyName* name); bool jsop_bindname(PropertyName* name); + bool jsop_bindvar(); bool jsop_getelem(); bool jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType); bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index f1fc578f2..aab664210 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3368,6 +3368,16 @@ LIRGenerator::visitBindNameCache(MBindNameCache* ins) assignSafepoint(lir, ins); } +void +LIRGenerator::visitCallBindVar(MCallBindVar* ins) +{ + MOZ_ASSERT(ins->scopeChain()->type() == MIRType_Object); + MOZ_ASSERT(ins->type() == MIRType_Object); + + LCallBindVar* lir = new(alloc()) LCallBindVar(useRegister(ins->scopeChain())); + define(lir, ins); +} + void LIRGenerator::visitGuardObjectIdentity(MGuardObjectIdentity* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index b7a3c61de..89715dcc4 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -230,6 +230,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitGetPropertyPolymorphic(MGetPropertyPolymorphic* ins); void visitSetPropertyPolymorphic(MSetPropertyPolymorphic* ins); void visitBindNameCache(MBindNameCache* ins); + void visitCallBindVar(MCallBindVar* ins); void visitGuardObjectIdentity(MGuardObjectIdentity* ins); void visitGuardClass(MGuardClass* ins); void visitGuardObject(MGuardObject* ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 6068edfc8..adc99da43 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -10819,6 +10819,39 @@ class MBindNameCache } }; +class MCallBindVar + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MCallBindVar(MDefinition* scopeChain) + : MUnaryInstruction(scopeChain) + { + setResultType(MIRType_Object); + setMovable(); + } + + public: + INSTRUCTION_HEADER(CallBindVar) + + static MCallBindVar* New(TempAllocator& alloc, MDefinition* scopeChain) { + return new(alloc) MCallBindVar(scopeChain); + } + + MDefinition* scopeChain() const { + return getOperand(0); + } + + bool congruentTo(const MDefinition* ins) const override { + if (!ins->isCallBindVar()) + return false; + return congruentIfOperandsEqual(ins); + } + + AliasSet getAliasSet() const override { + return AliasSet::None(); + } +}; + // Guard on an object's shape. class MGuardShape : public MUnaryInstruction, diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 1399cec73..2bb5c464f 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -162,6 +162,7 @@ namespace jit { _(GetPropertyPolymorphic) \ _(SetPropertyPolymorphic) \ _(BindNameCache) \ + _(CallBindVar) \ _(GuardShape) \ _(GuardReceiverPolymorphic) \ _(GuardObjectGroup) \ diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 2e329be96..d1397b629 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -1056,26 +1056,23 @@ class MacroAssembler : public MacroAssemblerSpecific // All barriers are off by default. // They are enabled if necessary at the end of CodeGenerator::generate(). - CodeOffset nopJump = toggledJump(&done); + CodeOffset nopJump = toggledJump(&done); // SHORT writePrebarrierOffset(nopJump); callPreBarrier(address, type); - jump(&done); - - haltingAlign(8); bind(&done); } void canonicalizeDouble(FloatRegister reg) { Label notNaN; - branchDouble(DoubleOrdered, reg, reg, ¬NaN); + branchDouble(DoubleOrdered, reg, reg, ¬NaN); // SHORT loadConstantDouble(JS::GenericNaN(), reg); bind(¬NaN); } void canonicalizeFloat(FloatRegister reg) { Label notNaN; - branchFloat(DoubleOrdered, reg, reg, ¬NaN); + branchFloat(DoubleOrdered, reg, reg, ¬NaN); // SHORT loadConstantFloat32(float(JS::GenericNaN()), reg); bind(¬NaN); } diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 1296b52fe..324d6a5fe 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -167,14 +167,21 @@ CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame, return cx->runtime()->handleInterrupt(cx); } +JSObject* +BindVar(JSContext* cx, HandleObject scopeChain) +{ + JSObject* obj = scopeChain; + while (!obj->isQualifiedVarObj()) + obj = obj->enclosingScope(); + MOZ_ASSERT(obj); + return obj; +} + bool DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain) { // Given the ScopeChain, extract the VarObj. - RootedObject obj(cx, scopeChain); - while (!obj->isQualifiedVarObj()) - obj = obj->enclosingScope(); - + RootedObject obj(cx, BindVar(cx, scopeChain)); return DefVarOperation(cx, obj, dn, attrs); } @@ -185,10 +192,7 @@ DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject sc Rooted lexical(cx, &NearestEnclosingExtensibleLexicalScope(scopeChain)); // Find the variables object. - RootedObject varObj(cx, scopeChain); - while (!varObj->isQualifiedVarObj()) - varObj = varObj->enclosingScope(); - + RootedObject varObj(cx, BindVar(cx, scopeChain)); return DefLexicalOperation(cx, lexical, varObj, dn, attrs); } @@ -853,9 +857,8 @@ bool InitGlobalOrEvalScopeObjects(JSContext* cx, BaselineFrame* frame) { RootedScript script(cx, frame->script()); - RootedObject varObj(cx, frame->scopeChain()); - while (!varObj->isQualifiedVarObj()) - varObj = varObj->enclosingScope(); + RootedObject scopeChain(cx, frame->scopeChain()); + RootedObject varObj(cx, BindVar(cx, scopeChain)); if (script->isForEval()) { // Strict eval needs its own call object. @@ -866,13 +869,12 @@ InitGlobalOrEvalScopeObjects(JSContext* cx, BaselineFrame* frame) if (!frame->initStrictEvalScopeObjects(cx)) return false; } else { - RootedObject scopeChain(cx, frame->scopeChain()); if (!CheckEvalDeclarationConflicts(cx, script, scopeChain, varObj)) return false; } } else { Rooted lexicalScope(cx, - &NearestEnclosingExtensibleLexicalScope(frame->scopeChain())); + &NearestEnclosingExtensibleLexicalScope(scopeChain)); if (!CheckGlobalDeclarationConflicts(cx, script, lexicalScope, varObj)) return false; } diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 2ea3f8aab..67177905d 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -588,6 +588,7 @@ bool CheckOverRecursed(JSContext* cx); bool CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame, uint32_t extra, uint32_t earlyCheck); +JSObject* BindVar(JSContext* cx, HandleObject scopeChain); bool DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain); bool DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain); bool DefGlobalLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 156f5a38a..d60892924 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -5774,6 +5774,22 @@ class LBindNameCache : public LInstructionHelper<1, 1, 0> } }; +class LCallBindVar : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(CallBindVar) + + explicit LCallBindVar(const LAllocation& scopeChain) { + setOperand(0, scopeChain); + } + const LAllocation* scopeChain() { + return getOperand(0); + } + const MCallBindVar* mir() const { + return mir_->toCallBindVar(); + } +}; + // Load a value from an object's dslots or a slots vector. class LLoadSlotV : public LInstructionHelper { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 44f3a92b9..1a7511e96 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -277,6 +277,7 @@ _(GetPropertyPolymorphicV) \ _(GetPropertyPolymorphicT) \ _(BindNameCache) \ + _(CallBindVar) \ _(CallGetProperty) \ _(GetNameCache) \ _(CallGetIntrinsicValue) \ diff --git a/js/src/js.msg b/js/src/js.msg index 2bcd7d454..82405333e 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -63,7 +63,7 @@ MSG_DEF(JSMSG_SPREAD_TOO_LARGE, 0, JSEXN_RANGEERR, "array too large due t MSG_DEF(JSMSG_BAD_WEAKMAP_KEY, 0, JSEXN_TYPEERR, "cannot use the given object as a weak map key") MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 1, JSEXN_TYPEERR, "invalid {0} usage") MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 0, JSEXN_RANGEERR, "invalid array length") -MSG_DEF(JSMSG_REDECLARED_VAR, 2, JSEXN_TYPEERR, "redeclaration of {0} {1}") +MSG_DEF(JSMSG_REDECLARED_VAR, 2, JSEXN_SYNTAXERR, "redeclaration of {0} {1}") MSG_DEF(JSMSG_UNDECLARED_VAR, 1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}") MSG_DEF(JSMSG_GETTER_ONLY, 0, JSEXN_TYPEERR, "setting a property that has only a getter") MSG_DEF(JSMSG_OVERWRITING_ACCESSOR, 1, JSEXN_TYPEERR, "can't overwrite accessor property {0}") @@ -185,7 +185,7 @@ MSG_DEF(JSMSG_UNKNOWN_FORMAT, 1, JSEXN_INTERNALERR, "unknown bytecode f // Frontend MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS, 3, JSEXN_SYNTAXERR, "{0} functions must have {1} argument{2}") MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side") -MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initialiser too large") +MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initializer too large") MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after import *") MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'") MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") @@ -208,6 +208,7 @@ MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_REFERENCEERR, "invalid increment MSG_DEF(JSMSG_BAD_METHOD_DEF, 0, JSEXN_SYNTAXERR, "bad method definition") MSG_DEF(JSMSG_BAD_OCTAL, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant") MSG_DEF(JSMSG_BAD_OPERAND, 1, JSEXN_SYNTAXERR, "invalid {0} operand") +MSG_DEF(JSMSG_BAD_POW_LEFTSIDE, 0, JSEXN_SYNTAXERR, "unparenthesized unary expression can't appear on the left-hand side of '**'") MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id") MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 1, JSEXN_SYNTAXERR, "{0} not in function") MSG_DEF(JSMSG_BAD_STRICT_ASSIGN, 1, JSEXN_SYNTAXERR, "can't assign to {0} in strict mode") @@ -249,6 +250,7 @@ MSG_DEF(JSMSG_DEPRECATED_FLAGS_ARG, 0, JSEXN_NONE, "flags argument of String. MSG_DEF(JSMSG_DEPRECATED_FOR_EACH, 0, JSEXN_NONE, "JavaScript 1.6's for-each-in loops are deprecated; consider using ES6 for-of instead") MSG_DEF(JSMSG_DEPRECATED_OCTAL, 0, JSEXN_SYNTAXERR, "octal literals and octal escape sequences are deprecated") MSG_DEF(JSMSG_DEPRECATED_PRAGMA, 1, JSEXN_NONE, "Using //@ to indicate {0} pragmas is deprecated. Use //# instead") +MSG_DEF(JSMSG_DEPRECATED_BLOCK_SCOPE_FUN_REDECL, 1, JSEXN_NONE, "redeclaration of block-scoped function `{0}' is deprecated") MSG_DEF(JSMSG_DUPLICATE_EXPORT_NAME, 1, JSEXN_SYNTAXERR, "duplicate export name '{0}'") MSG_DEF(JSMSG_DUPLICATE_FORMAL, 1, JSEXN_SYNTAXERR, "duplicate formal argument {0}") MSG_DEF(JSMSG_DUPLICATE_LABEL, 0, JSEXN_SYNTAXERR, "duplicate label") @@ -269,6 +271,9 @@ MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found") MSG_DEF(JSMSG_LET_CLASS_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a class") MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable") MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block") +MSG_DEF(JSMSG_LEXICAL_DECL_LABEL, 1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled") +MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled") +MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks") MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression") MSG_DEF(JSMSG_MALFORMED_ESCAPE, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence") MSG_DEF(JSMSG_MISSING_BINARY_DIGITS, 0, JSEXN_SYNTAXERR, "missing binary digits after '0b'") @@ -443,14 +448,22 @@ MSG_DEF(JSMSG_INVALID_TIME_ZONE, 1, JSEXN_RANGEERR, "invalid time zone in MSG_DEF(JSMSG_UNDEFINED_CURRENCY, 0, JSEXN_TYPEERR, "undefined currency in NumberFormat() with currency style") // RegExp +MSG_DEF(JSMSG_BACK_REF_OUT_OF_RANGE, 0, JSEXN_SYNTAXERR, "back reference out of range in regular expression") MSG_DEF(JSMSG_BAD_CLASS_RANGE, 0, JSEXN_SYNTAXERR, "invalid range in character class") MSG_DEF(JSMSG_ESCAPE_AT_END_OF_REGEXP, 0, JSEXN_SYNTAXERR, "\\ at end of pattern") +MSG_DEF(JSMSG_INVALID_DECIMAL_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid decimal escape in regular expression") MSG_DEF(JSMSG_INVALID_GROUP, 0, JSEXN_SYNTAXERR, "invalid regexp group") +MSG_DEF(JSMSG_INVALID_IDENTITY_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid identity escape in regular expression") +MSG_DEF(JSMSG_INVALID_UNICODE_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid unicode escape in regular expression") MSG_DEF(JSMSG_MISSING_PAREN, 0, JSEXN_SYNTAXERR, "unterminated parenthetical") MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another") MSG_DEF(JSMSG_NOTHING_TO_REPEAT, 0, JSEXN_SYNTAXERR, "nothing to repeat") MSG_DEF(JSMSG_NUMBERS_OUT_OF_ORDER, 0, JSEXN_SYNTAXERR, "numbers out of order in {} quantifier.") +MSG_DEF(JSMSG_RANGE_WITH_CLASS_ESCAPE, 0, JSEXN_SYNTAXERR, "character class escape cannot be used in class range in regular expression") +MSG_DEF(JSMSG_RAW_BRACE_IN_REGEP, 0, JSEXN_SYNTAXERR, "raw brace is not allowed in regular expression with unicode flag") +MSG_DEF(JSMSG_RAW_BRACKET_IN_REGEP, 0, JSEXN_SYNTAXERR, "raw bracket is not allowed in regular expression with unicode flag") MSG_DEF(JSMSG_TOO_MANY_PARENS, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression") +MSG_DEF(JSMSG_UNICODE_OVERFLOW, 0, JSEXN_SYNTAXERR, "unicode codepoint should not be greater than 0x10FFFF in regular expression") MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression") MSG_DEF(JSMSG_UNTERM_CLASS, 0, JSEXN_SYNTAXERR, "unterminated character class") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index adbee56ef..df0a2c7cd 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6064,19 +6064,67 @@ DescribeScriptedCaller(JSContext* cx, AutoFilename* filename, unsigned* lineno, return true; } +// Fast path to get the activation to use for GetScriptedCallerGlobal. If this +// returns false, the fast path didn't work out and the caller has to use the +// (much slower) NonBuiltinFrameIter path. +// +// The optimization here is that we skip Ion-inlined frames and only look at +// 'outer' frames. That's fine: each activation is tied to a single compartment, +// so if an activation contains at least one non-self-hosted frame, we can use +// the activation's global for GetScriptedCallerGlobal. If, however, all 'outer' +// frames are self-hosted, it's possible Ion inlined a non-self-hosted script, +// so we must return false and use the slower path. +static bool +GetScriptedCallerActivationFast(JSContext* cx, Activation** activation) +{ + ActivationIterator activationIter(cx->runtime()); + + while (!activationIter.done() && activationIter->cx() != cx) + ++activationIter; + + if (activationIter.done()) { + *activation = nullptr; + return true; + } + + *activation = activationIter.activation(); + + if (activationIter->isJit()) { + for (jit::JitFrameIterator iter(activationIter); !iter.done(); ++iter) { + if (iter.isScripted() && !iter.script()->selfHosted()) + return true; + } + } else if (activationIter->isInterpreter()) { + for (InterpreterFrameIterator iter((*activation)->asInterpreter()); !iter.done(); ++iter) { + if (!iter.frame()->script()->selfHosted()) + return true; + } + } + + return false; +} + JS_PUBLIC_API(JSObject*) GetScriptedCallerGlobal(JSContext* cx) { - NonBuiltinFrameIter i(cx); - if (i.done()) - return nullptr; + Activation* activation; + + if (GetScriptedCallerActivationFast(cx, &activation)) { + if (!activation) + return nullptr; + } else { + NonBuiltinFrameIter i(cx); + if (i.done()) + return nullptr; + activation = i.activation(); + } // If the caller is hidden, the embedding wants us to return null here so // that it can check its own stack (see HideScriptedCaller). - if (i.activation()->scriptedCallerIsHidden()) + if (activation->scriptedCallerIsHidden()) return nullptr; - GlobalObject* global = i.activation()->compartment()->maybeGlobal(); + GlobalObject* global = activation->compartment()->maybeGlobal(); // Noone should be running code in the atoms compartment or running code in // a compartment without any live objects, so there should definitely be a diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 77aa95251..c094a7af0 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4959,6 +4959,7 @@ JS_ObjectIsDate(JSContext* cx, JS::HandleObject obj, bool* isDate); #define JSREG_GLOB 0x02u /* global exec, creates array of matches */ #define JSREG_MULTILINE 0x04u /* treat ^ and $ as begin and end of line */ #define JSREG_STICKY 0x08u /* only match starting at lastIndex */ +#define JSREG_UNICODE 0x10u /* unicode */ extern JS_PUBLIC_API(JSObject*) JS_NewRegExpObject(JSContext* cx, JS::HandleObject obj, const char* bytes, size_t length, diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index bcf52ce0b..f4ae35d8f 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -772,6 +772,7 @@ struct JSCompartment DeprecatedFlagsArgument = 7, // JS 1.3 or older // NO LONGER USING 8 // NO LONGER USING 9 + DeprecatedBlockScopeFunRedecl = 10, DeprecatedLanguageExtensionCount }; diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 667e82b02..5ca11ac6d 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -969,7 +969,7 @@ IsObjectInContextCompartment(JSObject* obj, const JSContext* cx); JS_FRIEND_API(bool) RunningWithTrustedPrincipals(JSContext* cx); -inline uintptr_t +MOZ_ALWAYS_INLINE uintptr_t GetNativeStackLimit(JSContext* cx, StackKind kind, int extraAllowance = 0) { PerThreadDataFriendFields* mainThread = @@ -983,7 +983,7 @@ GetNativeStackLimit(JSContext* cx, StackKind kind, int extraAllowance = 0) return limit; } -inline uintptr_t +MOZ_ALWAYS_INLINE uintptr_t GetNativeStackLimit(JSContext* cx, int extraAllowance = 0) { StackKind kind = RunningWithTrustedPrincipals(cx) ? StackForTrustedScript @@ -1003,7 +1003,7 @@ GetNativeStackLimit(JSContext* cx, int extraAllowance = 0) #define JS_CHECK_RECURSION_LIMIT(cx, limit, onerror) \ JS_BEGIN_MACRO \ int stackDummy_; \ - if (!JS_CHECK_STACK_SIZE(limit, &stackDummy_)) { \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \ js::ReportOverRecursed(cx); \ onerror; \ } \ @@ -1015,7 +1015,7 @@ GetNativeStackLimit(JSContext* cx, int extraAllowance = 0) #define JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, limit, onerror) \ JS_BEGIN_MACRO \ int stackDummy_; \ - if (!JS_CHECK_STACK_SIZE(limit, &stackDummy_)) { \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \ onerror; \ } \ JS_END_MACRO @@ -1025,14 +1025,14 @@ GetNativeStackLimit(JSContext* cx, int extraAllowance = 0) #define JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, sp, onerror) \ JS_BEGIN_MACRO \ - if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp)) { \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \ onerror; \ } \ JS_END_MACRO #define JS_CHECK_RECURSION_WITH_SP(cx, sp, onerror) \ JS_BEGIN_MACRO \ - if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp)) { \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \ js::ReportOverRecursed(cx); \ onerror; \ } \ diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 34784b2ed..b299beb48 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2276,6 +2276,41 @@ DoMatchLocal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLine return true; } +/* ES6 21.2.5.2.3. */ +static size_t +AdvanceStringIndex(HandleLinearString input, size_t length, size_t index, bool unicode) +{ + /* Steps 1-3 (implicit). */ + + /* Step 4: If input is latin1, there is no surrogate pair. */ + if (!unicode || input->hasLatin1Chars()) + return index + 1; + + JS::AutoCheckCannotGC nogc; + const char16_t* S = input->twoByteChars(nogc); + + /* Step 6. */ + if (index + 1 >= length) + return index + 1; + + /* Step 7. */ + char16_t first = S[index]; + + /* Step 8. */ + if (!unicode::IsLeadSurrogate(first)) + return index + 1; + + /* Step 9. */ + char16_t second = S[index + 1]; + + /* Step 10. */ + if (!unicode::IsTrailSurrogate(second)) + return index + 1; + + /* Step 11. */ + return index + 2; +} + /* ES5 15.5.4.10 step 8. */ static bool DoMatchGlobal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLinearString input, @@ -2331,6 +2366,7 @@ DoMatchGlobal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLin ScopedMatchPairs matches(&cx->tempLifoAlloc()); size_t charsLen = input->length(); RegExpShared& re = g.regExp(); + bool unicode = re.unicode(); for (size_t searchIndex = 0; searchIndex <= charsLen; ) { if (!CheckForInterrupt(cx)) return false; @@ -2348,7 +2384,9 @@ DoMatchGlobal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLin MatchPair& match = matches[0]; // Steps 8f(iii)(1-3). - searchIndex = match.isEmpty() ? match.limit + 1 : match.limit; + searchIndex = match.isEmpty() + ? AdvanceStringIndex(input, charsLen, match.limit, unicode) + : match.limit; // Step 8f(iii)(4-5). JSLinearString* str = NewDependentString(cx, input, match.start, match.length()); @@ -2614,6 +2652,7 @@ static bool DoMatchForReplaceGlobal(JSContext* cx, RegExpStatics* res, HandleLinearString linearStr, RegExpShared& re, ReplaceData& rdata, size_t* rightContextOffset) { + bool unicode = re.unicode(); size_t charsLen = linearStr->length(); ScopedMatchPairs matches(&cx->tempLifoAlloc()); for (size_t count = 0, searchIndex = 0; searchIndex <= charsLen; ++count) { @@ -2628,7 +2667,9 @@ DoMatchForReplaceGlobal(JSContext* cx, RegExpStatics* res, HandleLinearString li break; MatchPair& match = matches[0]; - searchIndex = match.isEmpty() ? match.limit + 1 : match.limit; + searchIndex = match.isEmpty() + ? AdvanceStringIndex(linearStr, charsLen, match.limit, unicode) + : match.limit; *rightContextOffset = match.limit; if (!res->updateFromMatchPairs(cx, linearStr, matches)) @@ -3228,6 +3269,7 @@ StrReplaceRegexpRemove(JSContext* cx, HandleString str, RegExpShared& re) size_t lazyIndex = 0; /* Index before last successful match. */ /* Accumulate StringRanges for unmatched substrings. */ + bool unicode = re.unicode(); while (startIndex <= charsLen) { if (!CheckForInterrupt(cx)) return nullptr; @@ -3248,7 +3290,9 @@ StrReplaceRegexpRemove(JSContext* cx, HandleString str, RegExpShared& re) lazyIndex = lastIndex; lastIndex = match.limit; - startIndex = match.isEmpty() ? match.limit + 1 : match.limit; + startIndex = match.isEmpty() + ? AdvanceStringIndex(linearStr, charsLen, match.limit, unicode) + : match.limit; /* Non-global removal executes at most once. */ if (!re.global()) @@ -3631,7 +3675,7 @@ class SplitMatchResult { template static JSObject* SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher& splitMatch, - HandleObjectGroup group) + HandleObjectGroup group, bool unicode) { size_t strLength = str->length(); SplitMatchResult result; @@ -3696,7 +3740,7 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher /* Step 13(c)(ii). */ if (endIndex == lastEndIndex) { - index++; + index = AdvanceStringIndex(str, strLength, index, unicode); continue; } @@ -3925,14 +3969,14 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp) aobj = CharSplitHelper(cx, linearStr, limit, group); } else { SplitStringMatcher matcher(cx, sepstr); - aobj = SplitHelper(cx, linearStr, limit, matcher, group); + aobj = SplitHelper(cx, linearStr, limit, matcher, group, false); } } else { RegExpStatics* res = cx->global()->getRegExpStatics(cx); if (!res) return false; SplitRegExpMatcher matcher(*re, res); - aobj = SplitHelper(cx, linearStr, limit, matcher, group); + aobj = SplitHelper(cx, linearStr, limit, matcher, group, re->unicode()); } if (!aobj) return false; @@ -3960,7 +4004,7 @@ js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, H return CharSplitHelper(cx, linearStr, limit, group); SplitStringMatcher matcher(cx, linearSep); - return SplitHelper(cx, linearStr, limit, matcher, group); + return SplitHelper(cx, linearStr, limit, matcher, group, false); } /* diff --git a/js/src/jsversion.h b/js/src/jsversion.h index 60100b549..7a4f35a72 100644 --- a/js/src/jsversion.h +++ b/js/src/jsversion.h @@ -37,11 +37,4 @@ */ #define JS_OLD_GETTER_SETTER_METHODS 1 -#ifdef NIGHTLY_BUILD - -/* Support for ES7 Exponentiation proposal. */ -#define JS_HAS_EXPONENTIATION 1 - -#endif // NIGHTLY_BUILD - #endif /* jsversion_h */ diff --git a/js/src/tests/ecma_5/extensions/function-definition-with.js b/js/src/tests/ecma_5/extensions/function-definition-with.js index df0ab9e0b..2277ad45f 100644 --- a/js/src/tests/ecma_5/extensions/function-definition-with.js +++ b/js/src/tests/ecma_5/extensions/function-definition-with.js @@ -19,8 +19,9 @@ var called, obj; function inFile1() { return "in file"; } called = false; obj = { set inFile1(v) { called = true; } }; -with (obj) +with (obj) { function inFile1() { return "in file in with"; }; +} assertEq(inFile1(), "in file in with"); assertEq("set" in Object.getOwnPropertyDescriptor(obj, "inFile1"), true); assertEq(called, false); @@ -28,8 +29,9 @@ assertEq(called, false); evaluate("function notInFile1() { return 'not in file'; }"); called = false; obj = { set notInFile1(v) { called = true; return "not in file 2"; } }; -with (obj) +with (obj) { function notInFile1() { return "not in file in with"; }; +} assertEq(notInFile1(), "not in file in with"); assertEq("set" in Object.getOwnPropertyDescriptor(obj, "notInFile1"), true); assertEq(called, false); @@ -39,8 +41,9 @@ called = false; obj = Object.defineProperty({}, "inFile2", { value: 42, configurable: false, enumerable: false }); -with (obj) +with (obj) { function inFile2() { return "in file 2"; }; +} assertEq(inFile2(), "in file 2"); assertEq(obj.inFile2, 42); diff --git a/js/src/tests/ecma_5/extensions/strict-function-statements.js b/js/src/tests/ecma_5/extensions/strict-function-statements.js index 95b872faf..e6dc15139 100644 --- a/js/src/tests/ecma_5/extensions/strict-function-statements.js +++ b/js/src/tests/ecma_5/extensions/strict-function-statements.js @@ -9,46 +9,41 @@ assertEq(testLenientAndStrict("function f() { }", parsesSuccessfully), true); -// Function statements within blocks are forbidden in strict mode code. -assertEq(testLenientAndStrict("{ function f() { } }", - parsesSuccessfully, - parseRaisesException(SyntaxError)), - true); - // Lambdas are always permitted within blocks. assertEq(testLenientAndStrict("{ (function f() { }) }", parsesSuccessfully, parsesSuccessfully), true); -// Function statements within any sort of statement are forbidden in strict mode code. +// Function statements within unbraced blocks are forbidden in strict mode code. +// They are allowed only under if statements in sloppy mode. assertEq(testLenientAndStrict("if (true) function f() { }", parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("while (true) function f() { }", - parsesSuccessfully, + parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("do function f() { } while (true);", - parsesSuccessfully, + parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("for(;;) function f() { }", - parsesSuccessfully, + parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("for(x in []) function f() { }", - parsesSuccessfully, + parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("with(o) function f() { }", - parsesSuccessfully, + parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("switch(1) { case 1: function f() { } }", parsesSuccessfully, - parseRaisesException(SyntaxError)), + parsesSuccessfully), true); assertEq(testLenientAndStrict("x: function f() { }", parsesSuccessfully, @@ -56,7 +51,7 @@ assertEq(testLenientAndStrict("x: function f() { }", true); assertEq(testLenientAndStrict("try { function f() { } } catch (x) { }", parsesSuccessfully, - parseRaisesException(SyntaxError)), + parsesSuccessfully), true); // Lambdas are always permitted within any sort of statement. @@ -69,7 +64,7 @@ assertEq(testLenientAndStrict("if (true) (function f() { })", assertEq(parsesSuccessfully("function f() { function g() { } }"), true); -// Function statements are permitted in any statement within lenient functions. +// Function statements are permitted in if statement within lenient functions. assertEq(parsesSuccessfully("function f() { if (true) function g() { } }"), true); @@ -77,8 +72,7 @@ assertEq(parseRaisesException(SyntaxError) ("function f() { 'use strict'; if (true) function g() { } }"), true); -assertEq(parseRaisesException(SyntaxError) - ("function f() { 'use strict'; { function g() { } } }"), +assertEq(parsesSuccessfully("function f() { 'use strict'; { function g() { } } }"), true); assertEq(parsesSuccessfully("function f() { 'use strict'; if (true) (function g() { }) }"), @@ -94,7 +88,7 @@ assertEq(testLenientAndStrict("function f() { }", true); assertEq(testLenientAndStrict("{ function f() { } }", completesNormally, - raisesException(SyntaxError)), + completesNormally), true); reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Class/outerBinding.js b/js/src/tests/ecma_6/Class/outerBinding.js index d34df015f..e0f88a494 100644 --- a/js/src/tests/ecma_6/Class/outerBinding.js +++ b/js/src/tests/ecma_6/Class/outerBinding.js @@ -28,7 +28,7 @@ evaluate("const globalConstant = 0; var earlyError = true;"); try { evaluate("earlyError = false; class globalConstant { constructor() { } }"); -} catch (e if e instanceof TypeError) { } +} catch (e if e instanceof SyntaxError) { } assertEq(earlyError, true); function strictEvalShadows() { diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-eval.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-eval.js new file mode 100644 index 000000000..380ebba83 --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-eval.js @@ -0,0 +1,38 @@ +var log = ""; + +function f() { + log += g(); + function g() { return "outer-g"; } + + var o = { g: function () { return "with-g"; } }; + with (o) { + // Annex B.3.3.3 says g should be set on the nearest VariableEnvironment, + // and so should not change o.g. + eval(`{ + function g() { return "eval-g"; } + }`); + } + + log += g(); + log += o.g(); +} + +f(); + +function h() { + eval(` + // Should return true, as var bindings introduced by eval are configurable. + log += (delete q); + { + function q() { log += "q"; } + // Should return false, as lexical bindings introduced by eval are not + // configurable. + log += (delete q); + } + `); + return q; +} + +h()(); + +reportCompare(log, "outer-geval-gwith-gtruefalseq"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-if.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-if.js new file mode 100644 index 000000000..ce63aeeea --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-if.js @@ -0,0 +1,42 @@ +var log = ""; + +function f(x) { + if (x) + function g() { return "g0"; } + else + function g() { return "g1"; } + + log += g(); + + if (x) + function g() { return "g2"; } + else { + } + + log += g(); + + if (x) { + } else + function g() { return "g3"; } + + log += g(); + + if (x) + function g() { return "g4"; } + + log += g(); +} + +f(true); +f(false); + +try { + eval(` + if (1) + l: function foo() {} + `); +} catch (e) { + log += "e"; +} + +reportCompare(log, "g0g2g2g4g1g1g3g3e"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-label.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-label.js new file mode 100644 index 000000000..0fe9f45fc --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-label.js @@ -0,0 +1,43 @@ +function expectSyntaxError(str) { + var threwSyntaxError; + try { + eval(str); + } catch (e) { + threwSyntaxError = e instanceof SyntaxError; + } + assertEq(threwSyntaxError, true); + + try { + eval('"use strict";' + str); + } catch (e) { + threwSyntaxError = e instanceof SyntaxError; + } + assertEq(threwSyntaxError, true); +} + +function expectSloppyPass(str) { + eval(str); + + try { + eval('"use strict";' + str); + } catch (e) { + threwSyntaxError = e instanceof SyntaxError; + } + assertEq(threwSyntaxError, true); +} + +expectSloppyPass(`l: function f1() {}`); +expectSloppyPass(`l0: l: function f1() {}`); +expectSloppyPass(`{ f1(); l: function f1() {} }`); +expectSloppyPass(`{ f1(); l0: l: function f1() {} }`); +expectSloppyPass(`{ f1(); l: function f1() { return 42; } } assertEq(f1(), 42);`); +expectSloppyPass(`eval("fe(); l: function fe() {}")`); +expectSyntaxError(`if (1) l: function f2() {}`); +expectSyntaxError(`if (1) {} else l: function f3() {}`); +expectSyntaxError(`do l: function f4() {} while (0)`); +expectSyntaxError(`while (0) l: function f5() {}`); +expectSyntaxError(`for (;;) l: function f6() {}`); +expectSloppyPass(`switch (1) { case 1: l: function f7() {} }`); +expectSloppyPass(`switch (1) { case 1: assertEq(f8(), 'f8'); case 2: l: function f8() { return 'f8'; } } assertEq(f8(), 'f8');`); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-property.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-property.js new file mode 100644 index 000000000..a295de508 --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-property.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Define a global getter without a setter. +Object.defineProperty(this, "x", { + get: function () { return "get-x"; }, + configurable: true +}); + +// Simulate loading a 2nd script with evaluate, else we would DEFVAR the x and +// the above defineProperty would fail in trying to redefine a non-configurable +// property on the global. +evaluate(`{ + function x() { return "fun-x"; } +}`); + +// Annex B is supposed to be like an assignment. Should not blow away the +// existing setter-less getter. +reportCompare(x, "get-x"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-same-name.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-same-name.js new file mode 100644 index 000000000..b89f91c59 --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-same-name.js @@ -0,0 +1,7 @@ +{ + function f() { return "inner"; } +} + +function f() { return "outer"; } + +reportCompare(f(), "inner"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-with.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-with.js new file mode 100644 index 000000000..d9bdd98dc --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-with.js @@ -0,0 +1,18 @@ +var o = { f: "string-f" }; +with (o) { + var desc = Object.getOwnPropertyDescriptor(this, "f"); + assertEq(desc.value, undefined); + assertEq(desc.writable, true); + assertEq(desc.enumerable, true); + assertEq(desc.configurable, false); + function f() { + return "fun-f"; + } +} + +// Annex B explicitly assigns to the nearest VariableEnvironment, so the +// with-object "o" should have its property unchanged. +assertEq(o.f, "string-f"); +assertEq(f(), "fun-f"); + +reportCompare(true, true) diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b.js new file mode 100644 index 000000000..16c40774c --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b.js @@ -0,0 +1,31 @@ +var log = ""; + +log += typeof f; + +{ + log += f(); + + function f() { + return "f1"; + } +} + +log += f(); + +function g() { + log += typeof h; + + { + log += h(); + + function h() { + return "h1"; + } + } + + log += h(); +} + +g(); + +reportCompare(log, "undefinedf1f1undefinedh1h1"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-deprecated-redecl.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-deprecated-redecl.js new file mode 100644 index 000000000..af32be5d3 --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-deprecated-redecl.js @@ -0,0 +1,78 @@ +{ + assertEq(f(), 4); + function f() { return 3; } + assertEq(f(), 4); + function f() { return 4; } + assertEq(f(), 4); +} + +// Annex B still works. +assertEq(f(), 4); + +// The same thing with labels. +{ + assertEq(f(), 4); + function f() { return 3; } + assertEq(f(), 4); + l: function f() { return 4; } + assertEq(f(), 4); +} + +// Annex B still works. +assertEq(f(), 4); + +function test() { + { + assertEq(f(), 2); + function f() { return 1; } + assertEq(f(), 2); + function f() { return 2; } + assertEq(f(), 2); + } + + // Annex B still works. + assertEq(f(), 2); +} + +test(); + +var log = ''; + +try { + // Strict mode still cannot redeclare. + eval(`"use strict"; + { + function f() { } + function f() { } + }`); +} catch (e) { + assertEq(e instanceof SyntaxError, true); + log += 'e'; +} + +try { + // Redeclaring an explicitly 'let'-declared binding doesn't work. + eval(`{ + let x = 42; + function x() {} + }`); +} catch (e) { + assertEq(e instanceof SyntaxError, true); + log += 'e'; +} + +try { + // Redeclaring an explicitly 'const'-declared binding doesn't work. + eval(`{ + const x = 42; + function x() {} + }`); +} catch (e) { + assertEq(e instanceof SyntaxError, true); + log += 'e'; +} + +assertEq(log, 'eee'); + +if ('reportCompare' in this) + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-hoisted-tdz.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-hoisted-tdz.js new file mode 100644 index 000000000..e5f9baf44 --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-hoisted-tdz.js @@ -0,0 +1,30 @@ +var log = ""; +try { + (function() { + { + let y = f(); + function f() { y; } + } + })() +} catch (e) { + log += e instanceof ReferenceError; +} + +try { + function f() { + switch (1) { + case 0: + let x; + case 1: + (function() { x; })(); + } + } + f(); +} catch (e) { + log += e instanceof ReferenceError; +} + +assertEq(log, "truetrue"); + +if ("reportCompare" in this) + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-strict.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-strict.js new file mode 100644 index 000000000..2b780d7dd --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-strict.js @@ -0,0 +1,45 @@ +"use strict" + +var log = ""; + +function f() { + return "f0"; +} + +log += f(); + +{ + log += f(); + + function f() { + return "f1"; + } + + log += f(); +} + +log += f(); + +function g() { + function h() { + return "h0"; + } + + log += h(); + + { + log += h(); + + function h() { + return "h1"; + } + + log += h(); + } + + log += h(); +} + +g(); + +reportCompare(log, "f0f1f1f0h0h1h1h0"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/for-loop.js b/js/src/tests/ecma_6/LexicalEnvironment/for-loop.js index 8db10a4e0..1bd6a46b7 100644 --- a/js/src/tests/ecma_6/LexicalEnvironment/for-loop.js +++ b/js/src/tests/ecma_6/LexicalEnvironment/for-loop.js @@ -51,10 +51,7 @@ isOK("for (let x = 5, y; ; ) ;"); isOK("for (let [z] = [3]; ; ) ;"); isError("for (let [z, z]; ; ) ;", SyntaxError); // because missing initializer -// This is wrong! Per 13.2.1.1, "It is a Syntax Error if the BoundNames of -// BindingList contains any duplicate entries." But we don't implement this -// yet, so it becomes a TypeError at runtime. -isError("for (let [z, z] = [0, 1]; ; ) ;", TypeError); +isError("for (let [z, z] = [0, 1]; ; ) ;", SyntaxError); // A for-loop with lexical declarations, with a mixture of bindings that are and // aren't aliased. (The mixture stress-tests any code that incorrectly assumes diff --git a/js/src/tests/ecma_6/RegExp/class-null.js b/js/src/tests/ecma_6/RegExp/class-null.js new file mode 100644 index 000000000..c95e5a4d7 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/class-null.js @@ -0,0 +1,15 @@ +var BUGNUMBER = 1279467; +var summary = "Null in character class in RegExp with unicode flag."; + +print(BUGNUMBER + ": " + summary); + +var m = /([\0]+)/u.exec("\u0000"); +assertEq(m.length, 2); +assertEq(m[0], '\u0000'); +assertEq(m[1], '\u0000'); + +var m = /([\0]+)/u.exec("0"); +assertEq(m, null); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/descriptor.js b/js/src/tests/ecma_6/RegExp/descriptor.js index 62dda8521..cc545b3a6 100644 --- a/js/src/tests/ecma_6/RegExp/descriptor.js +++ b/js/src/tests/ecma_6/RegExp/descriptor.js @@ -10,7 +10,7 @@ var getters = [ "multiline", "source", "sticky", - //"unicode", + "unicode", ]; for (var name of getters) { @@ -21,9 +21,5 @@ for (var name of getters) { assertEq("get" in desc, true); } -// When the /u flag is supported, remove this comment and the next line, and -// uncomment "unicode" in |props| above. -assertThrowsInstanceOf(() => RegExp("", "mygui").flags, SyntaxError); - if (typeof reportCompare === "function") reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/flag-accessors.js b/js/src/tests/ecma_6/RegExp/flag-accessors.js index 4f169c8a4..848b916c5 100644 --- a/js/src/tests/ecma_6/RegExp/flag-accessors.js +++ b/js/src/tests/ecma_6/RegExp/flag-accessors.js @@ -8,17 +8,14 @@ var props = [ "ignoreCase", "multiline", "sticky", - //"unicode", + "unicode", ]; testThrows(RegExp.prototype); test(/foo/iymg, [true, true, true, true, false]); test(RegExp(""), [false, false, false, false, false]); test(RegExp("", "mygi"), [true, true, true, true, false]); -// When the /u flag is supported, remove the following line, uncomment the -// next line, and uncomment "unicode" in |props| above. -assertThrowsInstanceOf(() => RegExp("", "mygui").flags, SyntaxError); -// test(RegExp("", "mygiu"), [true, true, true, true, true]); +test(RegExp("", "mygiu"), [true, true, true, true, true]); testThrowsGeneric(); testThrowsGeneric(1); diff --git a/js/src/tests/ecma_6/RegExp/flags.js b/js/src/tests/ecma_6/RegExp/flags.js index 5a0245d99..292fa4f3f 100644 --- a/js/src/tests/ecma_6/RegExp/flags.js +++ b/js/src/tests/ecma_6/RegExp/flags.js @@ -7,16 +7,12 @@ assertEq(RegExp.prototype.flags, ""); assertEq(/foo/iymg.flags, "gimy"); assertEq(RegExp("").flags, ""); assertEq(RegExp("", "mygi").flags, "gimy"); -// TODO: Uncomment lines 12, 16, 19 and remove lines 11, 15, 18 when bug 1135377 is fixed. -assertThrowsInstanceOf(() => RegExp("", "mygui").flags, SyntaxError); -// assertEq(RegExp("", "mygui").flags, "gimuy"); +assertEq(RegExp("", "mygui").flags, "gimuy"); assertEq(genericFlags({}), ""); assertEq(genericFlags({ignoreCase: true}), "i"); -assertEq(genericFlags({sticky:1, unicode:1, global: 0}), "y"); -// assertEq(genericFlags({sticky:1, unicode:1, global: 0}), "uy"); +assertEq(genericFlags({sticky:1, unicode:1, global: 0}), "uy"); assertEq(genericFlags({__proto__: {multiline: true}}), "m"); -assertEq(genericFlags(new Proxy({}, {get(){return true}})), "gimy"); -// assertEq(genericFlags(new Proxy({}, {get(){return true}})), "gimuy"); +assertEq(genericFlags(new Proxy({}, {get(){return true}})), "gimuy"); assertThrowsInstanceOf(() => genericFlags(), TypeError); assertThrowsInstanceOf(() => genericFlags(1), TypeError); diff --git a/js/src/tests/ecma_6/RegExp/unicode-back-reference.js b/js/src/tests/ecma_6/RegExp/unicode-back-reference.js new file mode 100644 index 000000000..2a65432a1 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-back-reference.js @@ -0,0 +1,39 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- back reference should not match lead surrogate that has corresponding trail surrogate."; + +print(BUGNUMBER + ": " + summary); + +// The last character of back reference is not a surrogate. +assertEqArray(/foo(.+)bar\1/u.exec("fooAbarA\uDC00"), + ["fooAbarA", "A"]); +assertEqArray(/foo(.+)bar\1/u.exec("fooAbarA\uD834"), + ["fooAbarA", "A"]); +assertEqArray(/foo(.+)bar\1/u.exec("fooAbarAA"), + ["fooAbarA", "A"]); +assertEqArray(/foo(.+)bar\1/u.exec("fooAbarA"), + ["fooAbarA", "A"]); + +// The last character of back reference is a lead surrogate. +assertEq(/foo(.+)bar\1/u.exec("foo\uD834bar\uD834\uDC00"), null); +assertEqArray(/foo(.+)bar\1/u.exec("foo\uD834bar\uD834\uD834"), + ["foo\uD834bar\uD834", "\uD834"]); +assertEqArray(/foo(.+)bar\1/u.exec("foo\uD834bar\uD834A"), + ["foo\uD834bar\uD834", "\uD834"]); +assertEqArray(/foo(.+)bar\1/u.exec("foo\uD834bar\uD834"), + ["foo\uD834bar\uD834", "\uD834"]); + +// The last character of back reference is a trail surrogate. +assertEqArray(/foo(.+)bar\1/u.exec("foo\uDC00bar\uDC00\uDC00"), + ["foo\uDC00bar\uDC00", "\uDC00"]); +assertEqArray(/foo(.+)bar\1/u.exec("foo\uDC00bar\uDC00\uD834"), + ["foo\uDC00bar\uDC00", "\uDC00"]); +assertEqArray(/foo(.+)bar\1/u.exec("foo\uDC00bar\uDC00A"), + ["foo\uDC00bar\uDC00", "\uDC00"]); +assertEqArray(/foo(.+)bar\1/u.exec("foo\uDC00bar\uDC00"), + ["foo\uDC00bar\uDC00", "\uDC00"]); + +// Pattern should not match to surrogate pair partially. +assertEq(/^(.+)\1$/u.exec("\uDC00foobar\uD834\uDC00foobar\uD834"), null); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-braced.js b/js/src/tests/ecma_6/RegExp/unicode-braced.js new file mode 100644 index 000000000..97df7acab --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-braced.js @@ -0,0 +1,166 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- braced pattern in RegExpUnicodeEscapeSequence."; + +print(BUGNUMBER + ": " + summary); + +// ==== standalone ==== + +assertEqArray(/\u{41}/u.exec("ABC"), + ["A"]); +assertEqArray(/\u{41}/.exec("ABC" + "u".repeat(41)), + ["u".repeat(41)]); + +assertEqArray(/\u{4A}/u.exec("JKL"), + ["J"]); +assertEqArray(/\u{4A}/.exec("JKLu{4A}"), + ["u{4A}"]); + +assertEqArray(/\u{1F438}/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/\u{1F438}/.exec("u{1F438}"), + ["u{1F438}"]); + +assertEqArray(/\u{0}/u.exec("\u{0}"), + ["\u{0}"]); +assertEqArray(/\u{10FFFF}/u.exec("\u{10FFFF}"), + ["\u{10FFFF}"]); +assertEqArray(/\u{10ffff}/u.exec("\u{10FFFF}"), + ["\u{10FFFF}"]); + +// leading 0 +assertEqArray(/\u{0000000000000000000000}/u.exec("\u{0}"), + ["\u{0}"]); +assertEqArray(/\u{000000000000000010FFFF}/u.exec("\u{10FFFF}"), + ["\u{10FFFF}"]); + +// RegExp constructor +assertEqArray(new RegExp("\\u{0}", "u").exec("\u{0}"), + ["\u{0}"]); +assertEqArray(new RegExp("\\u{41}", "u").exec("ABC"), + ["A"]); +assertEqArray(new RegExp("\\u{1F438}", "u").exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(new RegExp("\\u{10FFFF}", "u").exec("\u{10FFFF}"), + ["\u{10FFFF}"]); + +assertEqArray(new RegExp("\\u{0000000000000000}", "u").exec("\u{0}"), + ["\u{0}"]); + +assertEqArray(eval(`/\\u{${"0".repeat(Math.pow(2, 24)) + "1234"}}/u`).exec("\u{1234}"), + ["\u{1234}"]); +assertEqArray(new RegExp(`\\u{${"0".repeat(Math.pow(2, 24)) + "1234"}}`, "u").exec("\u{1234}"), + ["\u{1234}"]); + +// ==== ? ==== + +assertEqArray(/\u{1F438}?/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/\u{1F438}?/u.exec(""), + [""]); + +// lead-only target +assertEqArray(/\u{1F438}?/u.exec("\uD83D"), + [""]); + +// RegExp constructor +assertEqArray(new RegExp("\\u{1F438}?", "u").exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(new RegExp("\\u{1F438}?", "u").exec(""), + [""]); +assertEqArray(new RegExp("\\u{1F438}?", "u").exec("\uD83D"), + [""]); + +// ==== + ==== + +assertEqArray(/\u{1F438}+/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/\u{1F438}+/u.exec("\u{1F438}\u{1F438}"), + ["\u{1F438}\u{1F438}"]); +assertEq(/\u{1F438}+/u.exec(""), + null); + +// lead-only target +assertEq(/\u{1F438}+/u.exec("\uD83D"), + null); +assertEqArray(/\u{1F438}+/u.exec("\uD83D\uDC38\uDC38"), + ["\uD83D\uDC38"]); + +// ==== * ==== + +assertEqArray(/\u{1F438}*/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/\u{1F438}*/u.exec("\u{1F438}\u{1F438}"), + ["\u{1F438}\u{1F438}"]); +assertEqArray(/\u{1F438}*/u.exec(""), + [""]); + +// lead-only target +assertEqArray(/\u{1F438}*/u.exec("\uD83D"), + [""]); +assertEqArray(/\u{1F438}*/u.exec("\uD83D\uDC38\uDC38"), + ["\uD83D\uDC38"]); + +// ==== lead-only ==== + +// match only non-surrogate pair +assertEqArray(/\u{D83D}/u.exec("\uD83D\uDBFF"), + ["\uD83D"]); +assertEq(/\u{D83D}/u.exec("\uD83D\uDC00"), + null); +assertEq(/\u{D83D}/u.exec("\uD83D\uDFFF"), + null); +assertEqArray(/\u{D83D}/u.exec("\uD83D\uE000"), + ["\uD83D"]); + +// match before non-tail char +assertEqArray(/\u{D83D}/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/\u{D83D}/u.exec("\uD83DA"), + ["\uD83D"]); + +// ==== trail-only ==== + +// match only non-surrogate pair +assertEqArray(/\u{DC38}/u.exec("\uD7FF\uDC38"), + ["\uDC38"]); +assertEq(/\u{DC38}/u.exec("\uD800\uDC38"), + null); +assertEq(/\u{DC38}/u.exec("\uDBFF\uDC38"), + null); +assertEqArray(/\u{DC38}/u.exec("\uDC00\uDC38"), + ["\uDC38"]); + +// match after non-lead char +assertEqArray(/\u{DC38}/u.exec("\uDC38"), + ["\uDC38"]); +assertEqArray(/\u{DC38}/u.exec("A\uDC38"), + ["\uDC38"]); + +// ==== wrong patterns ==== + +assertThrowsInstanceOf(() => eval(`/\\u{-1}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{0.0}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{G}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{{/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{110000}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{00110000}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{100000000000000000000000000000}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{ FFFF}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{FFFF }/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{FF FF}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{F F F F}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u{100000001}/u`), SyntaxError); + +// surrogate pair with braced +assertEq(/\u{D83D}\u{DC38}+/u.exec("\uD83D\uDC38\uDC38"), + null); +assertEq(/\uD83D\u{DC38}+/u.exec("\uD83D\uDC38\uDC38"), + null); +assertEq(/\u{D83D}\uDC38+/u.exec("\uD83D\uDC38\uDC38"), + null); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-character-class-escape.js b/js/src/tests/ecma_6/RegExp/unicode-character-class-escape.js new file mode 100644 index 000000000..175207d5a --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-character-class-escape.js @@ -0,0 +1,75 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- CharacterClassEscape."; + +print(BUGNUMBER + ": " + summary); + +// BMP + +assertEqArray(/\d+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["0123456789"]); +assertEqArray(/\D+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["abcxyzABCXYZ"]); + +assertEqArray(/\s+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["\t\r\n\v\x0c\xa0\uFEFF"]); +assertEqArray(/\S+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["abcxyzABCXYZ0123456789_"]); + +assertEqArray(/\w+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["abcxyzABCXYZ0123456789_"]); +assertEqArray(/\W+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["\t\r\n\v\x0c\xa0\uFEFF*"]); + +assertEqArray(/\n+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["\n"]); + +assertEqArray(/[\d]+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["0123456789"]); +assertEqArray(/[\D]+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["abcxyzABCXYZ"]); + +assertEqArray(/[\s]+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["\t\r\n\v\x0c\xa0\uFEFF"]); +assertEqArray(/[\S]+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["abcxyzABCXYZ0123456789_"]); + +assertEqArray(/[\w]+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["abcxyzABCXYZ0123456789_"]); +assertEqArray(/[\W]+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["\t\r\n\v\x0c\xa0\uFEFF*"]); + +assertEqArray(/[\n]+/u.exec("abcxyzABCXYZ0123456789_\t\r\n\v\x0c\xa0\uFEFF*"), + ["\n"]); + +// non-BMP + +function testNonBMP(re) { + assertEqArray(re.exec("\uD83D\uDBFF"), + ["\uD83D"]); + assertEqArray(re.exec("\uD83D\uDC00"), + ["\uD83D\uDC00"]); + assertEqArray(re.exec("\uD83D\uDFFF"), + ["\uD83D\uDFFF"]); + assertEqArray(re.exec("\uD83D\uE000"), + ["\uD83D"]); + + assertEqArray(re.exec("\uD7FF\uDC38"), + ["\uD7FF"]); + assertEqArray(re.exec("\uD800\uDC38"), + ["\uD800\uDC38"]); + assertEqArray(re.exec("\uDBFF\uDC38"), + ["\uDBFF\uDC38"]); + assertEqArray(re.exec("\uDC00\uDC38"), + ["\uDC00"]); +} + +testNonBMP(/\D/u); +testNonBMP(/\S/u); +testNonBMP(/\W/u); + +testNonBMP(/[\D]/u); +testNonBMP(/[\S]/u); +testNonBMP(/[\W]/u); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-class-braced.js b/js/src/tests/ecma_6/RegExp/unicode-class-braced.js new file mode 100644 index 000000000..4b5954012 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-class-braced.js @@ -0,0 +1,236 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- braced pattern in RegExpUnicodeEscapeSequence in CharacterClass."; + +print(BUGNUMBER + ": " + summary); + +// ==== standalone ==== + +assertEqArray(/[\u{41}]/u.exec("ABC"), + ["A"]); + +assertEqArray(/[\u{1F438}]/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEq(/[\u{1F438}]/u.exec("\uD83D"), + null); +assertEq(/[\u{1F438}]/u.exec("\uDC38"), + null); + +assertEqArray(/[\u{0}]/u.exec("\u{0}"), + ["\u{0}"]); +assertEqArray(/[\u{10FFFF}]/u.exec("\u{10FFFF}"), + ["\u{10FFFF}"]); +assertEqArray(/[\u{10ffff}]/u.exec("\u{10FFFF}"), + ["\u{10FFFF}"]); + +// leading 0 +assertEqArray(/[\u{0000000000000000000000}]/u.exec("\u{0}"), + ["\u{0}"]); +assertEqArray(/[\u{000000000000000010FFFF}]/u.exec("\u{10FFFF}"), + ["\u{10FFFF}"]); + +// RegExp constructor +assertEqArray(new RegExp("[\\u{0}]", "u").exec("\u{0}"), + ["\u{0}"]); +assertEqArray(new RegExp("[\\u{41}]", "u").exec("ABC"), + ["A"]); +assertEqArray(new RegExp("[\\u{1F438}]", "u").exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(new RegExp("[\\u{10FFFF}]", "u").exec("\u{10FFFF}"), + ["\u{10FFFF}"]); + +assertEqArray(new RegExp("[\\u{0000000000000000}]", "u").exec("\u{0}"), + ["\u{0}"]); + +assertEqArray(eval(`/[\\u{${"0".repeat(Math.pow(2, 24)) + "1234"}}]/u`).exec("\u{1234}"), + ["\u{1234}"]); +assertEqArray(new RegExp(`[\\u{${"0".repeat(Math.pow(2, 24)) + "1234"}}]`, "u").exec("\u{1234}"), + ["\u{1234}"]); + +// ==== BMP + non-BMP ==== + +assertEqArray(/[A\u{1F438}]/u.exec("A\u{1F438}"), + ["A"]); +assertEqArray(/[A\u{1F438}]/u.exec("\u{1F438}A"), + ["\u{1F438}"]); + +// lead-only target +assertEqArray(/[A\u{1F438}]/u.exec("\uD83DA"), + ["A"]); +assertEq(/[A\u{1F438}]/u.exec("\uD83D"), + null); + +// + +assertEqArray(/[A\u{1F438}]+/u.exec("\u{1F438}A\u{1F438}A"), + ["\u{1F438}A\u{1F438}A"]); + +// trail surrogate + lead surrogate +assertEqArray(/[A\u{1F438}]+/u.exec("\uD83D\uDC38A\uDC38\uD83DA"), + ["\uD83D\uDC38A"]); + +// ==== non-BMP + non-BMP ==== + +assertEqArray(/[\u{1F418}\u{1F438}]/u.exec("\u{1F418}\u{1F438}"), + ["\u{1F418}"]); + +assertEqArray(/[\u{1F418}\u{1F438}]+/u.exec("\u{1F418}\u{1F438}"), + ["\u{1F418}\u{1F438}"]); +assertEqArray(/[\u{1F418}\u{1F438}]+/u.exec("\u{1F418}\uDC38\uD83D"), + ["\u{1F418}"]); +assertEqArray(/[\u{1F418}\u{1F438}]+/u.exec("\uDC18\uD83D\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/[\u{1F418}\u{1F438}]+/u.exec("\uDC18\u{1F438}\uD83D"), + ["\u{1F438}"]); + +// trail surrogate + lead surrogate +assertEq(/[\u{1F418}\u{1F438}]+/u.exec("\uDC18\uDC38\uD83D\uD83D"), + null); + +// ==== non-BMP + non-BMP range (from_lead == to_lead) ==== + +assertEqArray(/[\u{1F418}-\u{1F438}]/u.exec("\u{1F418}"), + ["\u{1F418}"]); +assertEqArray(/[\u{1F418}-\u{1F438}]/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/[\u{1F418}-\u{1F438}]/u.exec("\u{1F427}"), + ["\u{1F427}"]); + +assertEq(/[\u{1F418}-\u{1F438}]/u.exec("\u{1F417}"), + null); +assertEq(/[\u{1F418}-\u{1F438}]/u.exec("\u{1F439}"), + null); + +// ==== non-BMP + non-BMP range (from_lead + 1 == to_lead) ==== + +assertEqArray(/[\u{1F17C}-\u{1F438}]/u.exec("\uD83C\uDD7C"), + ["\uD83C\uDD7C"]); +assertEqArray(/[\u{1F17C}-\u{1F438}]/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/[\u{1F17C}-\u{1F438}]/u.exec("\uD83C\uDF99"), + ["\uD83C\uDF99"]); +assertEqArray(/[\u{1F17C}-\u{1F438}]/u.exec("\uD83D\uDC00"), + ["\uD83D\uDC00"]); + +assertEq(/[\u{1F17C}-\u{1F438}]/u.exec("\uD83C\uDD7B"), + null); +assertEq(/[\u{1F17C}-\u{1F438}]/u.exec("\uD83C\uE000"), + null); +assertEq(/[\u{1F17C}-\u{1F438}]/u.exec("\uD83D\uDB99"), + null); +assertEq(/[\u{1F17C}-\u{1F438}]/u.exec("\uD83D\uDC39"), + null); + +// ==== non-BMP + non-BMP range (from_lead + 2 == to_lead) ==== + +assertEqArray(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83C\uDD7C"), + ["\uD83C\uDD7C"]); +assertEqArray(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83E\uDC29"), + ["\uD83E\uDC29"]); + +assertEqArray(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83C\uDF99"), + ["\uD83C\uDF99"]); +assertEqArray(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83D\uDC00"), + ["\uD83D\uDC00"]); +assertEqArray(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83D\uDF99"), + ["\uD83D\uDF99"]); +assertEqArray(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83E\uDC00"), + ["\uD83E\uDC00"]); + +assertEq(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83C\uDD7B"), + null); +assertEq(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83C\uE000"), + null); +assertEq(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83D\uDB99"), + null); +assertEq(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83D\uE000"), + null); +assertEq(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83E\uDB99"), + null); +assertEq(/[\u{1F17C}-\u{1F829}]/u.exec("\uD83E\uDC30"), + null); + +// ==== non-BMP + non-BMP range (other) ==== + +assertEqArray(/[\u{1D164}-\u{1F438}]/u.exec("\uD834\uDD64"), + ["\uD834\uDD64"]); +assertEqArray(/[\u{1D164}-\u{1F438}]/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/[\u{1D164}-\u{1F438}]/u.exec("\uD836\uDF99"), + ["\uD836\uDF99"]); +assertEqArray(/[\u{1D164}-\u{1F438}]/u.exec("\uD838\uDC00"), + ["\uD838\uDC00"]); + +assertEq(/[\u{1D164}-\u{1F438}]/u.exec("\uD834\uDD63"), + null); +assertEq(/[\u{1D164}-\u{1F438}]/u.exec("\uD83D\uDC39"), + null); + +assertEq(/[\u{1D164}-\u{1F438}]/u.exec("\uD834\uE000"), + null); +assertEq(/[\u{1D164}-\u{1F438}]/u.exec("\uD835\uDB99"), + null); +assertEq(/[\u{1D164}-\u{1F438}]/u.exec("\uD83C\uE000"), + null); +assertEq(/[\u{1D164}-\u{1F438}]/u.exec("\uD83D\uDB99"), + null); + +// ==== BMP + non-BMP range ==== + +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("B"), + ["B"]); +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("C"), + ["C"]); +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uFFFF"), + ["\uFFFF"]); +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uD800\uDC00"), + ["\uD800\uDC00"]); + +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uD800"), + ["\uD800"]); +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uDBFF"), + ["\uDBFF"]); +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uDC00"), + ["\uDC00"]); +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uDFFF"), + ["\uDFFF"]); + +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uDC38"), + ["\uDC38"]); + +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uD83D\uDBFF"), + ["\uD83D"]); +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uD83D\uDC00"), + ["\uD83D\uDC00"]); +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uD83D\uDC38"), + ["\uD83D\uDC38"]); +assertEq(/[\u{42}-\u{1F438}]/u.exec("\uD83D\uDC39"), + null); +assertEq(/[\u{42}-\u{1F438}]/u.exec("\uD83D\uDFFF"), + null); +assertEqArray(/[\u{42}-\u{1F438}]/u.exec("\uD83D\uE000"), + ["\uD83D"]); + +assertEq(/[\u{42}-\u{1F438}]/u.exec("A"), + null); + +// ==== wrong patterns ==== + +assertThrowsInstanceOf(() => eval(`/[\\u{-1}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{0.0}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{G}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{{]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{110000}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{00110000}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{100000000000000000000000000000}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{ FFFF}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{FFFF }]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{FF FF}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{F F F F}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u{100000001}]/u`), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-class-empty.js b/js/src/tests/ecma_6/RegExp/unicode-class-empty.js new file mode 100644 index 000000000..4d187551f --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-class-empty.js @@ -0,0 +1,25 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- empty class should not match anything."; + +print(BUGNUMBER + ": " + summary); + +assertEq(/[]/u.exec("A"), + null); +assertEq(/[]/u.exec("\uD83D"), + null); +assertEq(/[]/u.exec("\uDC38"), + null); +assertEq(/[]/u.exec("\uD83D\uDC38"), + null); + +assertEqArray(/[^]/u.exec("A"), + ["A"]); +assertEqArray(/[^]/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/[^]/u.exec("\uDC38"), + ["\uDC38"]); +assertEqArray(/[^]/u.exec("\uD83D\uDC38"), + ["\uD83D\uDC38"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-class-ignoreCase.js b/js/src/tests/ecma_6/RegExp/unicode-class-ignoreCase.js new file mode 100644 index 000000000..afa7705c7 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-class-ignoreCase.js @@ -0,0 +1,28 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- ignoreCase flag for CharacterClass."; + +print(BUGNUMBER + ": " + summary); + +assertEqArray(/[ABC]+/iu.exec("DCBAabcd"), + ["CBAabc"]); + +assertEqArray(/[A\u{10401}]+/iu.exec("A\u{10401}a\u{10429}"), + ["A\u{10401}a\u{10429}"]); + +assertEqArray(/[\u{10401}-\u{10404}\u{10408}-\u{1040B}]+/iu.exec("\u{10400}\u{10401}\u{10402}\u{10403}\u{10404}\u{10408}\u{10409}\u{1040A}\u{1040B}\u{1040C}"), + ["\u{10401}\u{10402}\u{10403}\u{10404}\u{10408}\u{10409}\u{1040A}\u{1040B}"]); +assertEqArray(/[\u{10401}-\u{10404}\u{10408}-\u{1040B}]+/iu.exec("\u{10428}\u{10429}\u{1042A}\u{1042B}\u{1042C}\u{10430}\u{10431}\u{10432}\u{10433}\u{10434}"), + ["\u{10429}\u{1042A}\u{1042B}\u{1042C}\u{10430}\u{10431}\u{10432}\u{10433}"]); + +assertEqArray(/[\u{10429}-\u{1042C}\u{10430}-\u{10433}]+/iu.exec("\u{10400}\u{10401}\u{10402}\u{10403}\u{10404}\u{10408}\u{10409}\u{1040A}\u{1040B}\u{1040C}"), + ["\u{10401}\u{10402}\u{10403}\u{10404}\u{10408}\u{10409}\u{1040A}\u{1040B}"]); +assertEqArray(/[\u{10429}-\u{1042C}\u{10430}-\u{10433}]+/iu.exec("\u{10428}\u{10429}\u{1042A}\u{1042B}\u{1042C}\u{10430}\u{10431}\u{10432}\u{10433}\u{10434}"), + ["\u{10429}\u{1042A}\u{1042B}\u{1042C}\u{10430}\u{10431}\u{10432}\u{10433}"]); + +assertEqArray(/[\u{10401}-\u{10404}\u{10430}-\u{10433}]+/iu.exec("\u{10400}\u{10401}\u{10402}\u{10403}\u{10404}\u{10408}\u{10409}\u{1040A}\u{1040B}\u{1040C}"), + ["\u{10401}\u{10402}\u{10403}\u{10404}\u{10408}\u{10409}\u{1040A}\u{1040B}"]); +assertEqArray(/[\u{10401}-\u{10404}\u{10430}-\u{10433}]+/iu.exec("\u{10428}\u{10429}\u{1042A}\u{1042B}\u{1042C}\u{10430}\u{10431}\u{10432}\u{10433}\u{10434}"), + ["\u{10429}\u{1042A}\u{1042B}\u{1042C}\u{10430}\u{10431}\u{10432}\u{10433}"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-class-lead-trail.js b/js/src/tests/ecma_6/RegExp/unicode-class-lead-trail.js new file mode 100644 index 000000000..c83bf937d --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-class-lead-trail.js @@ -0,0 +1,142 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- lead and trail pattern in RegExpUnicodeEscapeSequence in CharacterClass."; + +print(BUGNUMBER + ": " + summary); + +// ==== standalone ==== + +assertEqArray(/[\uD83D\uDC38]/u.exec("\uD83D\uDC38"), + ["\uD83D\uDC38"]); +assertEq(/[\uD83D\uDC38]/u.exec("\uD83D"), + null); +assertEq(/[\uD83D\uDC38]/u.exec("\uDC38"), + null); + +// no unicode flag +assertEqArray(/[\uD83D\uDC38]/.exec("\uD83D\uDC38"), + ["\uD83D"]); +assertEqArray(/[\uD83D\uDC38]/.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/[\uD83D\uDC38]/.exec("\uDC38"), + ["\uDC38"]); + +// RegExp constructor +assertEqArray(new RegExp("[\uD83D\uDC38]", "u").exec("\uD83D\uDC38"), + ["\uD83D\uDC38"]); +assertEq(new RegExp("[\uD83D\uDC38]", "u").exec("\uD83D"), + null); +assertEq(new RegExp("[\uD83D\uDC38]", "u").exec("\uDC38"), + null); + +// RegExp constructor, no unicode flag +assertEqArray(new RegExp("[\uD83D\uDC38]", "").exec("\uD83D\uDC38"), + ["\uD83D"]); +assertEqArray(new RegExp("[\uD83D\uDC38]", "").exec("\uD83D"), + ["\uD83D"]); +assertEqArray(new RegExp("[\uD83D\uDC38]", "").exec("\uDC38"), + ["\uDC38"]); + +// ==== lead-only ==== + +// match only non-surrogate pair +assertEqArray(/[\uD83D]/u.exec("\uD83D\uDBFF"), + ["\uD83D"]); +assertEq(/[\uD83D]/u.exec("\uD83D\uDC00"), + null); +assertEq(/[\uD83D]/u.exec("\uD83D\uDFFF"), + null); +assertEqArray(/[\uD83D]/u.exec("\uD83D\uE000"), + ["\uD83D"]); + +// match before non-tail char +assertEqArray(/[\uD83D]/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/[\uD83D]/u.exec("\uD83DA"), + ["\uD83D"]); + +// no unicode flag +assertEqArray(/[\uD83D]/.exec("\uD83D\uDBFF"), + ["\uD83D"]); +assertEqArray(/[\uD83D]/.exec("\uD83D\uDC00"), + ["\uD83D"]); +assertEqArray(/[\uD83D]/.exec("\uD83D\uDFFF"), + ["\uD83D"]); +assertEqArray(/[\uD83D]/.exec("\uD83D\uE000"), + ["\uD83D"]); +assertEqArray(/[\uD83D]/.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/[\uD83D]/.exec("\uD83DA"), + ["\uD83D"]); + +// ==== trail-only ==== + +// match only non-surrogate pair +assertEqArray(/[\uDC38]/u.exec("\uD7FF\uDC38"), + ["\uDC38"]); +assertEq(/[\uDC38]/u.exec("\uD800\uDC38"), + null); +assertEq(/[\uDC38]/u.exec("\uDBFF\uDC38"), + null); +assertEqArray(/[\uDC38]/u.exec("\uDC00\uDC38"), + ["\uDC38"]); + +// match after non-lead char +assertEqArray(/[\uDC38]/u.exec("\uDC38"), + ["\uDC38"]); +assertEqArray(/[\uDC38]/u.exec("A\uDC38"), + ["\uDC38"]); + +// no unicode flag +assertEqArray(/[\uDC38]/.exec("\uD7FF\uDC38"), + ["\uDC38"]); +assertEqArray(/[\uDC38]/.exec("\uD800\uDC38"), + ["\uDC38"]); +assertEqArray(/[\uDC38]/.exec("\uDBFF\uDC38"), + ["\uDC38"]); +assertEqArray(/[\uDC38]/.exec("\uDC00\uDC38"), + ["\uDC38"]); +assertEqArray(/[\uDC38]/.exec("\uDC38"), + ["\uDC38"]); +assertEqArray(/[\uDC38]/.exec("A\uDC38"), + ["\uDC38"]); + +// ==== invalid trail ==== + +assertEqArray(/[\uD83D\u3042]*/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/[\uD83D\u3042]*/u.exec("\uD83D\u3042"), + ["\uD83D\u3042"]); +assertEqArray(/[\uD83D\u3042]*/u.exec("\uD83D\u3042\u3042\uD83D"), + ["\uD83D\u3042\u3042\uD83D"]); + +assertEqArray(/[\uD83D\u{3042}]*/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/[\uD83D\u{3042}]*/u.exec("\uD83D\u3042"), + ["\uD83D\u3042"]); +assertEqArray(/[\uD83D\u{3042}]*/u.exec("\uD83D\u3042\u3042\uD83D"), + ["\uD83D\u3042\u3042\uD83D"]); + +assertEqArray(/[\uD83DA]*/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/[\uD83DA]*/u.exec("\uD83DA"), + ["\uD83DA"]); +assertEqArray(/[\uD83DA]*/u.exec("\uD83DAA\uD83D"), + ["\uD83DAA\uD83D"]); + +// ==== wrong patterns ==== + +assertThrowsInstanceOf(() => eval(`/[\\u]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u0]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u00]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u000]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u000G]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\u0.00]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uD83D\\u]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uD83D\\u0]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uD83D\\u00]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uD83D\\u000]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uD83D\\u000G]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uD83D\\u0.00]/u`), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-class-negated.js b/js/src/tests/ecma_6/RegExp/unicode-class-negated.js new file mode 100644 index 000000000..0b573f6dc --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-class-negated.js @@ -0,0 +1,64 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- negated CharacterClass."; + +print(BUGNUMBER + ": " + summary); + +// ==== BMP ==== + +assertEqArray(/[^A]/u.exec("ABC"), + ["B"]); +assertEqArray(/[^A]/u.exec("A\u{1F438}C"), + ["\u{1F438}"]); +assertEqArray(/[^A]/u.exec("A\uD83DC"), + ["\uD83D"]); +assertEqArray(/[^A]/u.exec("A\uDC38C"), + ["\uDC38"]); + +assertEqArray(/[^\uE000]/u.exec("\uE000\uE001"), + ["\uE001"]); +assertEqArray(/[^\uE000]/u.exec("\uE000\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/[^\uE000]/u.exec("\uE000\uD83D"), + ["\uD83D"]); +assertEqArray(/[^\uE000]/u.exec("\uE000\uDC38"), + ["\uDC38"]); + +// ==== non-BMP ==== + +assertEqArray(/[^\u{1F438}]/u.exec("\u{1F438}A"), + ["A"]); +assertEqArray(/[^\u{1F438}]/u.exec("\u{1F438}\u{1F439}"), + ["\u{1F439}"]); +assertEqArray(/[^\u{1F438}]/u.exec("\u{1F438}\uD83D"), + ["\uD83D"]); +assertEqArray(/[^\u{1F438}]/u.exec("\u{1F438}\uDC38"), + ["\uDC38"]); + +// ==== lead-only ==== + +assertEqArray(/[^\uD83D]/u.exec("\u{1F438}A"), + ["\u{1F438}"]); +assertEqArray(/[^\uD83D]/u.exec("\uD83D\uDBFF"), + ["\uDBFF"]); +assertEqArray(/[^\uD83D]/u.exec("\uD83D\uDC00"), + ["\uD83D\uDC00"]); +assertEqArray(/[^\uD83D]/u.exec("\uD83D\uDFFF"), + ["\uD83D\uDFFF"]); +assertEqArray(/[^\uD83D]/u.exec("\uD83D\uE000"), + ["\uE000"]); + +// ==== trail-only ==== + +assertEqArray(/[^\uDC38]/u.exec("\u{1F438}A"), + ["\u{1F438}"]); +assertEqArray(/[^\uDC38]/u.exec("\uD7FF\uDC38"), + ["\uD7FF"]); +assertEqArray(/[^\uDC38]/u.exec("\uD800\uDC38"), + ["\uD800\uDC38"]); +assertEqArray(/[^\uDC38]/u.exec("\uDBFF\uDC38"), + ["\uDBFF\uDC38"]); +assertEqArray(/[^\uDC38]/u.exec("\uDC00\uDC38"), + ["\uDC00"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-class-range.js b/js/src/tests/ecma_6/RegExp/unicode-class-range.js new file mode 100644 index 000000000..5bb757560 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-class-range.js @@ -0,0 +1,28 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- disallow range with CharacterClassEscape."; + +print(BUGNUMBER + ": " + summary); + +assertThrowsInstanceOf(() => eval(`/[\\w-\\uFFFF]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\W-\\uFFFF]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\d-\\uFFFF]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\D-\\uFFFF]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\s-\\uFFFF]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\S-\\uFFFF]/u`), SyntaxError); + +assertThrowsInstanceOf(() => eval(`/[\\uFFFF-\\w]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uFFFF-\\W]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uFFFF-\\d]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uFFFF-\\D]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uFFFF-\\s]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uFFFF-\\S]/u`), SyntaxError); + +assertThrowsInstanceOf(() => eval(`/[\\w-\\w]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\W-\\W]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\d-\\d]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\D-\\D]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\s-\\s]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\S-\\S]/u`), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-class-raw.js b/js/src/tests/ecma_6/RegExp/unicode-class-raw.js new file mode 100644 index 000000000..742fec2b4 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-class-raw.js @@ -0,0 +1,65 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- raw unicode."; + +print(BUGNUMBER + ": " + summary); + +// ==== standalone ==== + +assertEqArray(eval(`/[\uD83D\uDC38]/u`).exec("\u{1F438}"), + ["\u{1F438}"]); + +// no unicode flag +assertEqArray(eval(`/[\uD83D\uDC38]/`).exec("\u{1F438}"), + ["\uD83D"]); + +// escaped (lead) +assertEq(eval(`/[\\uD83D\uDC38]/u`).exec("\u{1F438}"), + null); +assertEq(eval(`/[\\u{D83D}\uDC38]/u`).exec("\u{1F438}"), + null); + +// escaped (trail) +assertEq(eval(`/[\uD83D\\uDC38]/u`).exec("\u{1F438}"), + null); +assertEq(eval(`/[\uD83D\\u{DC38}]/u`).exec("\u{1F438}"), + null); + +// escaped (lead), no unicode flag +assertEqArray(eval(`/[\\uD83D\uDC38]/`).exec("\u{1F438}"), + ["\uD83D"]); + +// escaped (trail), no unicode flag +assertEqArray(eval(`/[\uD83D\\uDC38]/`).exec("\u{1F438}"), + ["\uD83D"]); + +// ==== RegExp constructor ==== + +assertEqArray(new RegExp("[\uD83D\uDC38]", "u").exec("\u{1F438}"), + ["\u{1F438}"]); + +// no unicode flag +assertEqArray(new RegExp("[\uD83D\uDC38]", "").exec("\u{1F438}"), + ["\uD83D"]); + +// escaped(lead) +assertEq(new RegExp("[\\uD83D\uDC38]", "u").exec("\u{1F438}"), + null); +assertEq(new RegExp("[\\u{D83D}\uDC38]", "u").exec("\u{1F438}"), + null); + +// escaped(trail) +assertEq(new RegExp("[\uD83D\\uDC38]", "u").exec("\u{1F438}"), + null); +assertEq(new RegExp("[\uD83D\\u{DC38}]", "u").exec("\u{1F438}"), + null); + +// escaped(lead), no unicode flag +assertEqArray(new RegExp("[\\uD83D\uDC38]", "").exec("\u{1F438}"), + ["\uD83D"]); + +// escaped(trail), no unicode flag +assertEqArray(new RegExp("[\uD83D\\uDC38]", "").exec("\u{1F438}"), + ["\uD83D"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-disallow-extended.js b/js/src/tests/ecma_6/RegExp/unicode-disallow-extended.js new file mode 100644 index 000000000..d1f775fac --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-disallow-extended.js @@ -0,0 +1,117 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- disallow extended patterns."; + +print(BUGNUMBER + ": " + summary); + +// IdentityEscape + +assertEqArray(/\^\$\\\.\*\+\?\(\)\[\]\{\}\|/u.exec("^$\\.*+?()[]{}|"), + ["^$\\.*+?()[]{}|"]); +assertThrowsInstanceOf(() => eval(`/\\A/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\-/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\U{10}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\U0000/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\uD83D\\U0000/u`), SyntaxError); + +assertEqArray(/[\^\$\\\.\*\+\?\(\)\[\]\{\}\|]+/u.exec("^$\\.*+?()[]{}|"), + ["^$\\.*+?()[]{}|"]); +assertThrowsInstanceOf(() => eval(`/[\\A]/u`), SyntaxError); +assertEqArray(/[A\-Z]+/u.exec("a-zABC"), + ["-"]); +assertThrowsInstanceOf(() => eval(`/[\\U{10}]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\U0000]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\uD83D\\U0000]/u`), SyntaxError); + +// PatternCharacter +assertThrowsInstanceOf(() => eval(`/{}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/{/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/{0}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/{1,}/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/{1,2}/u`), SyntaxError); + +// QuantifiableAssertion +assertEqArray(/.B(?=A)/u.exec("cBaCBA"), + ["CB"]); +assertEqArray(/.B(?!A)/u.exec("CBAcBa"), + ["cB"]); +assertEqArray(/.B(?:A)/u.exec("cBaCBA"), + ["CBA"]); +assertEqArray(/.B(A)/u.exec("cBaCBA"), + ["CBA", "A"]); + +assertThrowsInstanceOf(() => eval(`/.B(?=A)+/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/.B(?!A)+/u`), SyntaxError); +assertEqArray(/.B(?:A)+/u.exec("cBaCBA"), + ["CBA"]); +assertEqArray(/.B(A)+/u.exec("cBaCBA"), + ["CBA", "A"]); + +// ControlLetter +assertEqArray(/\cA/u.exec("\u0001"), + ["\u0001"]); +assertEqArray(/\cZ/u.exec("\u001a"), + ["\u001a"]); +assertEqArray(/\ca/u.exec("\u0001"), + ["\u0001"]); +assertEqArray(/\cz/u.exec("\u001a"), + ["\u001a"]); + +assertEqArray(/[\cA]/u.exec("\u0001"), + ["\u0001"]); +assertEqArray(/[\cZ]/u.exec("\u001a"), + ["\u001a"]); +assertEqArray(/[\ca]/u.exec("\u0001"), + ["\u0001"]); +assertEqArray(/[\cz]/u.exec("\u001a"), + ["\u001a"]); + +assertThrowsInstanceOf(() => eval(`/\\c/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\c1/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\c_/u`), SyntaxError); + +assertThrowsInstanceOf(() => eval(`/[\\c]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\c1]/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/[\\c_]/u`), SyntaxError); + +// HexEscapeSequence +assertThrowsInstanceOf(() => eval(`/\\x/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\x0/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\x1/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\x1G/u`), SyntaxError); + +// LegacyOctalEscapeSequence +assertThrowsInstanceOf(() => eval(`/\\52/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\052/u`), SyntaxError); + +// DecimalEscape +assertEqArray(/\0/u.exec("\0"), + ["\0"]); +assertEqArray(/[\0]/u.exec("\0"), + ["\0"]); +assertEqArray(/\0A/u.exec("\0A"), + ["\0A"]); +assertEqArray(/\0G/u.exec("\0G"), + ["\0G"]); +assertEqArray(/(A.)\1/u.exec("ABACABAB"), + ["ABAB", "AB"]); +assertEqArray(/(A.)(B.)(C.)(D.)(E.)(F.)(G.)(H.)(I.)(J.)(K.)\10/u.exec("A1B2C3D4E5F6G7H8I9JaKbJa"), + ["A1B2C3D4E5F6G7H8I9JaKbJa", "A1", "B2", "C3", "D4", "E5", "F6", "G7", "H8", "I9", "Ja", "Kb"]); + +assertThrowsInstanceOf(() => eval(`/\\00/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\01/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\09/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\1/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\2/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\3/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\4/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\5/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\6/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\7/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\8/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\9/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\10/u`), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-everything.js b/js/src/tests/ecma_6/RegExp/unicode-everything.js new file mode 100644 index 000000000..a18ac2867 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-everything.js @@ -0,0 +1,59 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- everything Atom."; + +print(BUGNUMBER + ": " + summary); + +// ==== standalone ==== + +assertEqArray(/./u.exec("ABC"), + ["A"]); +assertEqArray(/./u.exec("\u{1F438}BC"), + ["\u{1F438}"]); + +assertEqArray(/./u.exec("\uD83D\uDBFF"), + ["\uD83D"]); +assertEqArray(/./u.exec("\uD83D\uDC00"), + ["\uD83D\uDC00"]); +assertEqArray(/./u.exec("\uD83D\uDFFF"), + ["\uD83D\uDFFF"]); +assertEqArray(/./u.exec("\uD83D\uE000"), + ["\uD83D"]); +assertEqArray(/./u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/./u.exec("\uD83DA"), + ["\uD83D"]); + +assertEqArray(/./u.exec("\uD7FF\uDC38"), + ["\uD7FF"]); +assertEqArray(/./u.exec("\uD800\uDC38"), + ["\uD800\uDC38"]); +assertEqArray(/./u.exec("\uDBFF\uDC38"), + ["\uDBFF\uDC38"]); +assertEqArray(/./u.exec("\uDC00\uDC38"), + ["\uDC00"]); +assertEqArray(/./u.exec("\uDC38"), + ["\uDC38"]); +assertEqArray(/./u.exec("A\uDC38"), + ["A"]); + +assertEqArray(/.A/u.exec("\uD7FF\uDC38A"), + ["\uDC38A"]); +assertEqArray(/.A/u.exec("\uD800\uDC38A"), + ["\uD800\uDC38A"]); +assertEqArray(/.A/u.exec("\uDBFF\uDC38A"), + ["\uDBFF\uDC38A"]); +assertEqArray(/.A/u.exec("\uDC00\uDC38A"), + ["\uDC38A"]); + +// ==== leading multiple ==== + +assertEqArray(/.*A/u.exec("\u{1F438}\u{1F438}\u{1F438}A"), + ["\u{1F438}\u{1F438}\u{1F438}A"]); + +// ==== trailing multiple ==== + +assertEqArray(/A.*/u.exec("A\u{1F438}\u{1F438}\u{1F438}"), + ["A\u{1F438}\u{1F438}\u{1F438}"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-ignoreCase-ascii.js b/js/src/tests/ecma_6/RegExp/unicode-ignoreCase-ascii.js new file mode 100644 index 000000000..6d453290e --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-ignoreCase-ascii.js @@ -0,0 +1,45 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- ignoreCase flag with non-ascii to ascii map."; + +print(BUGNUMBER + ": " + summary); + +// LATIN CAPITAL LETTER Y WITH DIAERESIS +assertEqArray(/\u0178/iu.exec("\u00FF"), + ["\u00FF"]); +assertEqArray(/\u00FF/iu.exec("\u0178"), + ["\u0178"]); + +// LATIN SMALL LETTER LONG S +assertEqArray(/\u017F/iu.exec("S"), + ["S"]); +assertEqArray(/\u017F/iu.exec("s"), + ["s"]); +assertEqArray(/S/iu.exec("\u017F"), + ["\u017F"]); +assertEqArray(/s/iu.exec("\u017F"), + ["\u017F"]); + +// LATIN CAPITAL LETTER SHARP S +assertEqArray(/\u1E9E/iu.exec("\u00DF"), + ["\u00DF"]); +assertEqArray(/\u00DF/iu.exec("\u1E9E"), + ["\u1E9E"]); + +// KELVIN SIGN +assertEqArray(/\u212A/iu.exec("K"), + ["K"]); +assertEqArray(/\u212A/iu.exec("k"), + ["k"]); +assertEqArray(/K/iu.exec("\u212A"), + ["\u212A"]); +assertEqArray(/k/iu.exec("\u212A"), + ["\u212A"]); + +// ANGSTROM SIGN +assertEqArray(/\u212B/iu.exec("\u00E5"), + ["\u00E5"]); +assertEqArray(/\u00E5/iu.exec("\u212B"), + ["\u212B"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-ignoreCase-escape.js b/js/src/tests/ecma_6/RegExp/unicode-ignoreCase-escape.js new file mode 100644 index 000000000..d731fb6bb --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-ignoreCase-escape.js @@ -0,0 +1,71 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- ignoreCase flag with character class escape."; + +// \W doesn't match S or K from the change in +// https://github.com/tc39/ecma262/pull/525 +// (bug 1281739) + +print(BUGNUMBER + ": " + summary); + +// LATIN SMALL LETTER LONG S + +assertEqArray(/\w/iu.exec("S"), + ["S"]); +assertEqArray(/\w/iu.exec("s"), + ["s"]); +assertEqArray(/\w/iu.exec("\u017F"), + ["\u017F"]); + +assertEqArray(/[^\W]/iu.exec("S"), + ["S"]); +assertEqArray(/[^\W]/iu.exec("s"), + ["s"]); +assertEqArray(/[^\W]/iu.exec("\u017F"), + ["\u017F"]); + +assertEq(/\W/iu.exec("S"), + null); +assertEq(/\W/iu.exec("s"), + null); +assertEq(/\W/iu.exec("\u017F"), + null); + +assertEq(/[^\w]/iu.exec("S"), + null); +assertEq(/[^\w]/iu.exec("s"), + null); +assertEq(/[^\w]/iu.exec("\u017F"), + null); + +// KELVIN SIGN + +assertEqArray(/\w/iu.exec("k"), + ["k"]); +assertEqArray(/\w/iu.exec("k"), + ["k"]); +assertEqArray(/\w/iu.exec("\u212A"), + ["\u212A"]); + +assertEqArray(/[^\W]/iu.exec("k"), + ["k"]); +assertEqArray(/[^\W]/iu.exec("k"), + ["k"]); +assertEqArray(/[^\W]/iu.exec("\u212A"), + ["\u212A"]); + +assertEq(/\W/iu.exec("k"), + null); +assertEq(/\W/iu.exec("k"), + null); +assertEq(/\W/iu.exec("\u212A"), + null); + +assertEq(/[^\w]/iu.exec("k"), + null); +assertEq(/[^\w]/iu.exec("k"), + null); +assertEq(/[^\w]/iu.exec("\u212A"), + null); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-ignoreCase-negated.js b/js/src/tests/ecma_6/RegExp/unicode-ignoreCase-negated.js new file mode 100644 index 000000000..30909a515 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-ignoreCase-negated.js @@ -0,0 +1,19 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- ignoreCase flag with negated character class."; + +print(BUGNUMBER + ": " + summary); + +assertEq(/[^A]/iu.exec("A"), + null); +assertEq(/[^a]/iu.exec("A"), + null); +assertEq(/[^A]/iu.exec("a"), + null); +assertEq(/[^a]/iu.exec("a"), + null); + +assertEqArray(/[^A]/iu.exec("b"), + ["b"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-ignoreCase.js b/js/src/tests/ecma_6/RegExp/unicode-ignoreCase.js new file mode 100644 index 000000000..dac67abba --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-ignoreCase.js @@ -0,0 +1,2491 @@ +/* Generated by make_unicode.py DO NOT MODIFY */ + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- ignoreCase flag."; + +print(BUGNUMBER + ": " + summary); + +function test(code, ...equivs) { + var codeRe = new RegExp(String.fromCodePoint(code) + "+", "iu"); + var ans = String.fromCodePoint(code) + equivs.map(c => String.fromCodePoint(c)).join(""); + assertEqArray(codeRe.exec("<" + ans + ">"), [ans]); + codeRe = new RegExp("[" + String.fromCodePoint(code) + "]+", "iu"); + assertEqArray(codeRe.exec("<" + ans + ">"), [ans]); +} +test(0x41,0x61); +test(0x42,0x62); +test(0x43,0x63); +test(0x44,0x64); +test(0x45,0x65); +test(0x46,0x66); +test(0x47,0x67); +test(0x48,0x68); +test(0x49,0x69); +test(0x4a,0x6a); +test(0x4b,0x6b,0x212a); +test(0x4c,0x6c); +test(0x4d,0x6d); +test(0x4e,0x6e); +test(0x4f,0x6f); +test(0x50,0x70); +test(0x51,0x71); +test(0x52,0x72); +test(0x53,0x73,0x17f); +test(0x54,0x74); +test(0x55,0x75); +test(0x56,0x76); +test(0x57,0x77); +test(0x58,0x78); +test(0x59,0x79); +test(0x5a,0x7a); +test(0x61,0x41); +test(0x62,0x42); +test(0x63,0x43); +test(0x64,0x44); +test(0x65,0x45); +test(0x66,0x46); +test(0x67,0x47); +test(0x68,0x48); +test(0x69,0x49); +test(0x6a,0x4a); +test(0x6b,0x4b,0x212a); +test(0x6c,0x4c); +test(0x6d,0x4d); +test(0x6e,0x4e); +test(0x6f,0x4f); +test(0x70,0x50); +test(0x71,0x51); +test(0x72,0x52); +test(0x73,0x53,0x17f); +test(0x74,0x54); +test(0x75,0x55); +test(0x76,0x56); +test(0x77,0x57); +test(0x78,0x58); +test(0x79,0x59); +test(0x7a,0x5a); +test(0xb5,0x3bc,0x39c); +test(0xc0,0xe0); +test(0xc1,0xe1); +test(0xc2,0xe2); +test(0xc3,0xe3); +test(0xc4,0xe4); +test(0xc5,0xe5,0x212b); +test(0xc6,0xe6); +test(0xc7,0xe7); +test(0xc8,0xe8); +test(0xc9,0xe9); +test(0xca,0xea); +test(0xcb,0xeb); +test(0xcc,0xec); +test(0xcd,0xed); +test(0xce,0xee); +test(0xcf,0xef); +test(0xd0,0xf0); +test(0xd1,0xf1); +test(0xd2,0xf2); +test(0xd3,0xf3); +test(0xd4,0xf4); +test(0xd5,0xf5); +test(0xd6,0xf6); +test(0xd8,0xf8); +test(0xd9,0xf9); +test(0xda,0xfa); +test(0xdb,0xfb); +test(0xdc,0xfc); +test(0xdd,0xfd); +test(0xde,0xfe); +test(0xdf,0x1e9e); +test(0xe0,0xc0); +test(0xe1,0xc1); +test(0xe2,0xc2); +test(0xe3,0xc3); +test(0xe4,0xc4); +test(0xe5,0xc5,0x212b); +test(0xe6,0xc6); +test(0xe7,0xc7); +test(0xe8,0xc8); +test(0xe9,0xc9); +test(0xea,0xca); +test(0xeb,0xcb); +test(0xec,0xcc); +test(0xed,0xcd); +test(0xee,0xce); +test(0xef,0xcf); +test(0xf0,0xd0); +test(0xf1,0xd1); +test(0xf2,0xd2); +test(0xf3,0xd3); +test(0xf4,0xd4); +test(0xf5,0xd5); +test(0xf6,0xd6); +test(0xf8,0xd8); +test(0xf9,0xd9); +test(0xfa,0xda); +test(0xfb,0xdb); +test(0xfc,0xdc); +test(0xfd,0xdd); +test(0xfe,0xde); +test(0xff,0x178); +test(0x100,0x101); +test(0x101,0x100); +test(0x102,0x103); +test(0x103,0x102); +test(0x104,0x105); +test(0x105,0x104); +test(0x106,0x107); +test(0x107,0x106); +test(0x108,0x109); +test(0x109,0x108); +test(0x10a,0x10b); +test(0x10b,0x10a); +test(0x10c,0x10d); +test(0x10d,0x10c); +test(0x10e,0x10f); +test(0x10f,0x10e); +test(0x110,0x111); +test(0x111,0x110); +test(0x112,0x113); +test(0x113,0x112); +test(0x114,0x115); +test(0x115,0x114); +test(0x116,0x117); +test(0x117,0x116); +test(0x118,0x119); +test(0x119,0x118); +test(0x11a,0x11b); +test(0x11b,0x11a); +test(0x11c,0x11d); +test(0x11d,0x11c); +test(0x11e,0x11f); +test(0x11f,0x11e); +test(0x120,0x121); +test(0x121,0x120); +test(0x122,0x123); +test(0x123,0x122); +test(0x124,0x125); +test(0x125,0x124); +test(0x126,0x127); +test(0x127,0x126); +test(0x128,0x129); +test(0x129,0x128); +test(0x12a,0x12b); +test(0x12b,0x12a); +test(0x12c,0x12d); +test(0x12d,0x12c); +test(0x12e,0x12f); +test(0x12f,0x12e); +test(0x132,0x133); +test(0x133,0x132); +test(0x134,0x135); +test(0x135,0x134); +test(0x136,0x137); +test(0x137,0x136); +test(0x139,0x13a); +test(0x13a,0x139); +test(0x13b,0x13c); +test(0x13c,0x13b); +test(0x13d,0x13e); +test(0x13e,0x13d); +test(0x13f,0x140); +test(0x140,0x13f); +test(0x141,0x142); +test(0x142,0x141); +test(0x143,0x144); +test(0x144,0x143); +test(0x145,0x146); +test(0x146,0x145); +test(0x147,0x148); +test(0x148,0x147); +test(0x14a,0x14b); +test(0x14b,0x14a); +test(0x14c,0x14d); +test(0x14d,0x14c); +test(0x14e,0x14f); +test(0x14f,0x14e); +test(0x150,0x151); +test(0x151,0x150); +test(0x152,0x153); +test(0x153,0x152); +test(0x154,0x155); +test(0x155,0x154); +test(0x156,0x157); +test(0x157,0x156); +test(0x158,0x159); +test(0x159,0x158); +test(0x15a,0x15b); +test(0x15b,0x15a); +test(0x15c,0x15d); +test(0x15d,0x15c); +test(0x15e,0x15f); +test(0x15f,0x15e); +test(0x160,0x161); +test(0x161,0x160); +test(0x162,0x163); +test(0x163,0x162); +test(0x164,0x165); +test(0x165,0x164); +test(0x166,0x167); +test(0x167,0x166); +test(0x168,0x169); +test(0x169,0x168); +test(0x16a,0x16b); +test(0x16b,0x16a); +test(0x16c,0x16d); +test(0x16d,0x16c); +test(0x16e,0x16f); +test(0x16f,0x16e); +test(0x170,0x171); +test(0x171,0x170); +test(0x172,0x173); +test(0x173,0x172); +test(0x174,0x175); +test(0x175,0x174); +test(0x176,0x177); +test(0x177,0x176); +test(0x178,0xff); +test(0x179,0x17a); +test(0x17a,0x179); +test(0x17b,0x17c); +test(0x17c,0x17b); +test(0x17d,0x17e); +test(0x17e,0x17d); +test(0x17f,0x73,0x53); +test(0x180,0x243); +test(0x181,0x253); +test(0x182,0x183); +test(0x183,0x182); +test(0x184,0x185); +test(0x185,0x184); +test(0x186,0x254); +test(0x187,0x188); +test(0x188,0x187); +test(0x189,0x256); +test(0x18a,0x257); +test(0x18b,0x18c); +test(0x18c,0x18b); +test(0x18e,0x1dd); +test(0x18f,0x259); +test(0x190,0x25b); +test(0x191,0x192); +test(0x192,0x191); +test(0x193,0x260); +test(0x194,0x263); +test(0x195,0x1f6); +test(0x196,0x269); +test(0x197,0x268); +test(0x198,0x199); +test(0x199,0x198); +test(0x19a,0x23d); +test(0x19c,0x26f); +test(0x19d,0x272); +test(0x19e,0x220); +test(0x19f,0x275); +test(0x1a0,0x1a1); +test(0x1a1,0x1a0); +test(0x1a2,0x1a3); +test(0x1a3,0x1a2); +test(0x1a4,0x1a5); +test(0x1a5,0x1a4); +test(0x1a6,0x280); +test(0x1a7,0x1a8); +test(0x1a8,0x1a7); +test(0x1a9,0x283); +test(0x1ac,0x1ad); +test(0x1ad,0x1ac); +test(0x1ae,0x288); +test(0x1af,0x1b0); +test(0x1b0,0x1af); +test(0x1b1,0x28a); +test(0x1b2,0x28b); +test(0x1b3,0x1b4); +test(0x1b4,0x1b3); +test(0x1b5,0x1b6); +test(0x1b6,0x1b5); +test(0x1b7,0x292); +test(0x1b8,0x1b9); +test(0x1b9,0x1b8); +test(0x1bc,0x1bd); +test(0x1bd,0x1bc); +test(0x1bf,0x1f7); +test(0x1c4,0x1c6,0x1c5); +test(0x1c5,0x1c6,0x1c4); +test(0x1c6,0x1c4,0x1c5); +test(0x1c7,0x1c9,0x1c8); +test(0x1c8,0x1c9,0x1c7); +test(0x1c9,0x1c7,0x1c8); +test(0x1ca,0x1cc,0x1cb); +test(0x1cb,0x1cc,0x1ca); +test(0x1cc,0x1ca,0x1cb); +test(0x1cd,0x1ce); +test(0x1ce,0x1cd); +test(0x1cf,0x1d0); +test(0x1d0,0x1cf); +test(0x1d1,0x1d2); +test(0x1d2,0x1d1); +test(0x1d3,0x1d4); +test(0x1d4,0x1d3); +test(0x1d5,0x1d6); +test(0x1d6,0x1d5); +test(0x1d7,0x1d8); +test(0x1d8,0x1d7); +test(0x1d9,0x1da); +test(0x1da,0x1d9); +test(0x1db,0x1dc); +test(0x1dc,0x1db); +test(0x1dd,0x18e); +test(0x1de,0x1df); +test(0x1df,0x1de); +test(0x1e0,0x1e1); +test(0x1e1,0x1e0); +test(0x1e2,0x1e3); +test(0x1e3,0x1e2); +test(0x1e4,0x1e5); +test(0x1e5,0x1e4); +test(0x1e6,0x1e7); +test(0x1e7,0x1e6); +test(0x1e8,0x1e9); +test(0x1e9,0x1e8); +test(0x1ea,0x1eb); +test(0x1eb,0x1ea); +test(0x1ec,0x1ed); +test(0x1ed,0x1ec); +test(0x1ee,0x1ef); +test(0x1ef,0x1ee); +test(0x1f1,0x1f3,0x1f2); +test(0x1f2,0x1f3,0x1f1); +test(0x1f3,0x1f1,0x1f2); +test(0x1f4,0x1f5); +test(0x1f5,0x1f4); +test(0x1f6,0x195); +test(0x1f7,0x1bf); +test(0x1f8,0x1f9); +test(0x1f9,0x1f8); +test(0x1fa,0x1fb); +test(0x1fb,0x1fa); +test(0x1fc,0x1fd); +test(0x1fd,0x1fc); +test(0x1fe,0x1ff); +test(0x1ff,0x1fe); +test(0x200,0x201); +test(0x201,0x200); +test(0x202,0x203); +test(0x203,0x202); +test(0x204,0x205); +test(0x205,0x204); +test(0x206,0x207); +test(0x207,0x206); +test(0x208,0x209); +test(0x209,0x208); +test(0x20a,0x20b); +test(0x20b,0x20a); +test(0x20c,0x20d); +test(0x20d,0x20c); +test(0x20e,0x20f); +test(0x20f,0x20e); +test(0x210,0x211); +test(0x211,0x210); +test(0x212,0x213); +test(0x213,0x212); +test(0x214,0x215); +test(0x215,0x214); +test(0x216,0x217); +test(0x217,0x216); +test(0x218,0x219); +test(0x219,0x218); +test(0x21a,0x21b); +test(0x21b,0x21a); +test(0x21c,0x21d); +test(0x21d,0x21c); +test(0x21e,0x21f); +test(0x21f,0x21e); +test(0x220,0x19e); +test(0x222,0x223); +test(0x223,0x222); +test(0x224,0x225); +test(0x225,0x224); +test(0x226,0x227); +test(0x227,0x226); +test(0x228,0x229); +test(0x229,0x228); +test(0x22a,0x22b); +test(0x22b,0x22a); +test(0x22c,0x22d); +test(0x22d,0x22c); +test(0x22e,0x22f); +test(0x22f,0x22e); +test(0x230,0x231); +test(0x231,0x230); +test(0x232,0x233); +test(0x233,0x232); +test(0x23a,0x2c65); +test(0x23b,0x23c); +test(0x23c,0x23b); +test(0x23d,0x19a); +test(0x23e,0x2c66); +test(0x23f,0x2c7e); +test(0x240,0x2c7f); +test(0x241,0x242); +test(0x242,0x241); +test(0x243,0x180); +test(0x244,0x289); +test(0x245,0x28c); +test(0x246,0x247); +test(0x247,0x246); +test(0x248,0x249); +test(0x249,0x248); +test(0x24a,0x24b); +test(0x24b,0x24a); +test(0x24c,0x24d); +test(0x24d,0x24c); +test(0x24e,0x24f); +test(0x24f,0x24e); +test(0x250,0x2c6f); +test(0x251,0x2c6d); +test(0x252,0x2c70); +test(0x253,0x181); +test(0x254,0x186); +test(0x256,0x189); +test(0x257,0x18a); +test(0x259,0x18f); +test(0x25b,0x190); +test(0x25c,0xa7ab); +test(0x260,0x193); +test(0x261,0xa7ac); +test(0x263,0x194); +test(0x265,0xa78d); +test(0x266,0xa7aa); +test(0x268,0x197); +test(0x269,0x196); +test(0x26b,0x2c62); +test(0x26c,0xa7ad); +test(0x26f,0x19c); +test(0x271,0x2c6e); +test(0x272,0x19d); +test(0x275,0x19f); +test(0x27d,0x2c64); +test(0x280,0x1a6); +test(0x283,0x1a9); +test(0x287,0xa7b1); +test(0x288,0x1ae); +test(0x289,0x244); +test(0x28a,0x1b1); +test(0x28b,0x1b2); +test(0x28c,0x245); +test(0x292,0x1b7); +test(0x29d,0xa7b2); +test(0x29e,0xa7b0); +test(0x345,0x3b9,0x399,0x1fbe); +test(0x370,0x371); +test(0x371,0x370); +test(0x372,0x373); +test(0x373,0x372); +test(0x376,0x377); +test(0x377,0x376); +test(0x37b,0x3fd); +test(0x37c,0x3fe); +test(0x37d,0x3ff); +test(0x37f,0x3f3); +test(0x386,0x3ac); +test(0x388,0x3ad); +test(0x389,0x3ae); +test(0x38a,0x3af); +test(0x38c,0x3cc); +test(0x38e,0x3cd); +test(0x38f,0x3ce); +test(0x391,0x3b1); +test(0x392,0x3b2,0x3d0); +test(0x393,0x3b3); +test(0x394,0x3b4); +test(0x395,0x3b5,0x3f5); +test(0x396,0x3b6); +test(0x397,0x3b7); +test(0x398,0x3b8,0x3d1,0x3f4); +test(0x399,0x3b9,0x345,0x1fbe); +test(0x39a,0x3ba,0x3f0); +test(0x39b,0x3bb); +test(0x39c,0x3bc,0xb5); +test(0x39d,0x3bd); +test(0x39e,0x3be); +test(0x39f,0x3bf); +test(0x3a0,0x3c0,0x3d6); +test(0x3a1,0x3c1,0x3f1); +test(0x3a3,0x3c3,0x3c2); +test(0x3a4,0x3c4); +test(0x3a5,0x3c5); +test(0x3a6,0x3c6,0x3d5); +test(0x3a7,0x3c7); +test(0x3a8,0x3c8); +test(0x3a9,0x3c9,0x2126); +test(0x3aa,0x3ca); +test(0x3ab,0x3cb); +test(0x3ac,0x386); +test(0x3ad,0x388); +test(0x3ae,0x389); +test(0x3af,0x38a); +test(0x3b1,0x391); +test(0x3b2,0x392,0x3d0); +test(0x3b3,0x393); +test(0x3b4,0x394); +test(0x3b5,0x395,0x3f5); +test(0x3b6,0x396); +test(0x3b7,0x397); +test(0x3b8,0x398,0x3d1,0x3f4); +test(0x3b9,0x345,0x399,0x1fbe); +test(0x3ba,0x39a,0x3f0); +test(0x3bb,0x39b); +test(0x3bc,0xb5,0x39c); +test(0x3bd,0x39d); +test(0x3be,0x39e); +test(0x3bf,0x39f); +test(0x3c0,0x3a0,0x3d6); +test(0x3c1,0x3a1,0x3f1); +test(0x3c2,0x3c3,0x3a3); +test(0x3c3,0x3a3,0x3c2); +test(0x3c4,0x3a4); +test(0x3c5,0x3a5); +test(0x3c6,0x3a6,0x3d5); +test(0x3c7,0x3a7); +test(0x3c8,0x3a8); +test(0x3c9,0x3a9,0x2126); +test(0x3ca,0x3aa); +test(0x3cb,0x3ab); +test(0x3cc,0x38c); +test(0x3cd,0x38e); +test(0x3ce,0x38f); +test(0x3cf,0x3d7); +test(0x3d0,0x3b2,0x392); +test(0x3d1,0x3b8,0x398,0x3f4); +test(0x3d5,0x3c6,0x3a6); +test(0x3d6,0x3c0,0x3a0); +test(0x3d7,0x3cf); +test(0x3d8,0x3d9); +test(0x3d9,0x3d8); +test(0x3da,0x3db); +test(0x3db,0x3da); +test(0x3dc,0x3dd); +test(0x3dd,0x3dc); +test(0x3de,0x3df); +test(0x3df,0x3de); +test(0x3e0,0x3e1); +test(0x3e1,0x3e0); +test(0x3e2,0x3e3); +test(0x3e3,0x3e2); +test(0x3e4,0x3e5); +test(0x3e5,0x3e4); +test(0x3e6,0x3e7); +test(0x3e7,0x3e6); +test(0x3e8,0x3e9); +test(0x3e9,0x3e8); +test(0x3ea,0x3eb); +test(0x3eb,0x3ea); +test(0x3ec,0x3ed); +test(0x3ed,0x3ec); +test(0x3ee,0x3ef); +test(0x3ef,0x3ee); +test(0x3f0,0x3ba,0x39a); +test(0x3f1,0x3c1,0x3a1); +test(0x3f2,0x3f9); +test(0x3f3,0x37f); +test(0x3f4,0x3b8,0x398,0x3d1); +test(0x3f5,0x3b5,0x395); +test(0x3f7,0x3f8); +test(0x3f8,0x3f7); +test(0x3f9,0x3f2); +test(0x3fa,0x3fb); +test(0x3fb,0x3fa); +test(0x3fd,0x37b); +test(0x3fe,0x37c); +test(0x3ff,0x37d); +test(0x400,0x450); +test(0x401,0x451); +test(0x402,0x452); +test(0x403,0x453); +test(0x404,0x454); +test(0x405,0x455); +test(0x406,0x456); +test(0x407,0x457); +test(0x408,0x458); +test(0x409,0x459); +test(0x40a,0x45a); +test(0x40b,0x45b); +test(0x40c,0x45c); +test(0x40d,0x45d); +test(0x40e,0x45e); +test(0x40f,0x45f); +test(0x410,0x430); +test(0x411,0x431); +test(0x412,0x432); +test(0x413,0x433); +test(0x414,0x434); +test(0x415,0x435); +test(0x416,0x436); +test(0x417,0x437); +test(0x418,0x438); +test(0x419,0x439); +test(0x41a,0x43a); +test(0x41b,0x43b); +test(0x41c,0x43c); +test(0x41d,0x43d); +test(0x41e,0x43e); +test(0x41f,0x43f); +test(0x420,0x440); +test(0x421,0x441); +test(0x422,0x442); +test(0x423,0x443); +test(0x424,0x444); +test(0x425,0x445); +test(0x426,0x446); +test(0x427,0x447); +test(0x428,0x448); +test(0x429,0x449); +test(0x42a,0x44a); +test(0x42b,0x44b); +test(0x42c,0x44c); +test(0x42d,0x44d); +test(0x42e,0x44e); +test(0x42f,0x44f); +test(0x430,0x410); +test(0x431,0x411); +test(0x432,0x412); +test(0x433,0x413); +test(0x434,0x414); +test(0x435,0x415); +test(0x436,0x416); +test(0x437,0x417); +test(0x438,0x418); +test(0x439,0x419); +test(0x43a,0x41a); +test(0x43b,0x41b); +test(0x43c,0x41c); +test(0x43d,0x41d); +test(0x43e,0x41e); +test(0x43f,0x41f); +test(0x440,0x420); +test(0x441,0x421); +test(0x442,0x422); +test(0x443,0x423); +test(0x444,0x424); +test(0x445,0x425); +test(0x446,0x426); +test(0x447,0x427); +test(0x448,0x428); +test(0x449,0x429); +test(0x44a,0x42a); +test(0x44b,0x42b); +test(0x44c,0x42c); +test(0x44d,0x42d); +test(0x44e,0x42e); +test(0x44f,0x42f); +test(0x450,0x400); +test(0x451,0x401); +test(0x452,0x402); +test(0x453,0x403); +test(0x454,0x404); +test(0x455,0x405); +test(0x456,0x406); +test(0x457,0x407); +test(0x458,0x408); +test(0x459,0x409); +test(0x45a,0x40a); +test(0x45b,0x40b); +test(0x45c,0x40c); +test(0x45d,0x40d); +test(0x45e,0x40e); +test(0x45f,0x40f); +test(0x460,0x461); +test(0x461,0x460); +test(0x462,0x463); +test(0x463,0x462); +test(0x464,0x465); +test(0x465,0x464); +test(0x466,0x467); +test(0x467,0x466); +test(0x468,0x469); +test(0x469,0x468); +test(0x46a,0x46b); +test(0x46b,0x46a); +test(0x46c,0x46d); +test(0x46d,0x46c); +test(0x46e,0x46f); +test(0x46f,0x46e); +test(0x470,0x471); +test(0x471,0x470); +test(0x472,0x473); +test(0x473,0x472); +test(0x474,0x475); +test(0x475,0x474); +test(0x476,0x477); +test(0x477,0x476); +test(0x478,0x479); +test(0x479,0x478); +test(0x47a,0x47b); +test(0x47b,0x47a); +test(0x47c,0x47d); +test(0x47d,0x47c); +test(0x47e,0x47f); +test(0x47f,0x47e); +test(0x480,0x481); +test(0x481,0x480); +test(0x48a,0x48b); +test(0x48b,0x48a); +test(0x48c,0x48d); +test(0x48d,0x48c); +test(0x48e,0x48f); +test(0x48f,0x48e); +test(0x490,0x491); +test(0x491,0x490); +test(0x492,0x493); +test(0x493,0x492); +test(0x494,0x495); +test(0x495,0x494); +test(0x496,0x497); +test(0x497,0x496); +test(0x498,0x499); +test(0x499,0x498); +test(0x49a,0x49b); +test(0x49b,0x49a); +test(0x49c,0x49d); +test(0x49d,0x49c); +test(0x49e,0x49f); +test(0x49f,0x49e); +test(0x4a0,0x4a1); +test(0x4a1,0x4a0); +test(0x4a2,0x4a3); +test(0x4a3,0x4a2); +test(0x4a4,0x4a5); +test(0x4a5,0x4a4); +test(0x4a6,0x4a7); +test(0x4a7,0x4a6); +test(0x4a8,0x4a9); +test(0x4a9,0x4a8); +test(0x4aa,0x4ab); +test(0x4ab,0x4aa); +test(0x4ac,0x4ad); +test(0x4ad,0x4ac); +test(0x4ae,0x4af); +test(0x4af,0x4ae); +test(0x4b0,0x4b1); +test(0x4b1,0x4b0); +test(0x4b2,0x4b3); +test(0x4b3,0x4b2); +test(0x4b4,0x4b5); +test(0x4b5,0x4b4); +test(0x4b6,0x4b7); +test(0x4b7,0x4b6); +test(0x4b8,0x4b9); +test(0x4b9,0x4b8); +test(0x4ba,0x4bb); +test(0x4bb,0x4ba); +test(0x4bc,0x4bd); +test(0x4bd,0x4bc); +test(0x4be,0x4bf); +test(0x4bf,0x4be); +test(0x4c0,0x4cf); +test(0x4c1,0x4c2); +test(0x4c2,0x4c1); +test(0x4c3,0x4c4); +test(0x4c4,0x4c3); +test(0x4c5,0x4c6); +test(0x4c6,0x4c5); +test(0x4c7,0x4c8); +test(0x4c8,0x4c7); +test(0x4c9,0x4ca); +test(0x4ca,0x4c9); +test(0x4cb,0x4cc); +test(0x4cc,0x4cb); +test(0x4cd,0x4ce); +test(0x4ce,0x4cd); +test(0x4cf,0x4c0); +test(0x4d0,0x4d1); +test(0x4d1,0x4d0); +test(0x4d2,0x4d3); +test(0x4d3,0x4d2); +test(0x4d4,0x4d5); +test(0x4d5,0x4d4); +test(0x4d6,0x4d7); +test(0x4d7,0x4d6); +test(0x4d8,0x4d9); +test(0x4d9,0x4d8); +test(0x4da,0x4db); +test(0x4db,0x4da); +test(0x4dc,0x4dd); +test(0x4dd,0x4dc); +test(0x4de,0x4df); +test(0x4df,0x4de); +test(0x4e0,0x4e1); +test(0x4e1,0x4e0); +test(0x4e2,0x4e3); +test(0x4e3,0x4e2); +test(0x4e4,0x4e5); +test(0x4e5,0x4e4); +test(0x4e6,0x4e7); +test(0x4e7,0x4e6); +test(0x4e8,0x4e9); +test(0x4e9,0x4e8); +test(0x4ea,0x4eb); +test(0x4eb,0x4ea); +test(0x4ec,0x4ed); +test(0x4ed,0x4ec); +test(0x4ee,0x4ef); +test(0x4ef,0x4ee); +test(0x4f0,0x4f1); +test(0x4f1,0x4f0); +test(0x4f2,0x4f3); +test(0x4f3,0x4f2); +test(0x4f4,0x4f5); +test(0x4f5,0x4f4); +test(0x4f6,0x4f7); +test(0x4f7,0x4f6); +test(0x4f8,0x4f9); +test(0x4f9,0x4f8); +test(0x4fa,0x4fb); +test(0x4fb,0x4fa); +test(0x4fc,0x4fd); +test(0x4fd,0x4fc); +test(0x4fe,0x4ff); +test(0x4ff,0x4fe); +test(0x500,0x501); +test(0x501,0x500); +test(0x502,0x503); +test(0x503,0x502); +test(0x504,0x505); +test(0x505,0x504); +test(0x506,0x507); +test(0x507,0x506); +test(0x508,0x509); +test(0x509,0x508); +test(0x50a,0x50b); +test(0x50b,0x50a); +test(0x50c,0x50d); +test(0x50d,0x50c); +test(0x50e,0x50f); +test(0x50f,0x50e); +test(0x510,0x511); +test(0x511,0x510); +test(0x512,0x513); +test(0x513,0x512); +test(0x514,0x515); +test(0x515,0x514); +test(0x516,0x517); +test(0x517,0x516); +test(0x518,0x519); +test(0x519,0x518); +test(0x51a,0x51b); +test(0x51b,0x51a); +test(0x51c,0x51d); +test(0x51d,0x51c); +test(0x51e,0x51f); +test(0x51f,0x51e); +test(0x520,0x521); +test(0x521,0x520); +test(0x522,0x523); +test(0x523,0x522); +test(0x524,0x525); +test(0x525,0x524); +test(0x526,0x527); +test(0x527,0x526); +test(0x528,0x529); +test(0x529,0x528); +test(0x52a,0x52b); +test(0x52b,0x52a); +test(0x52c,0x52d); +test(0x52d,0x52c); +test(0x52e,0x52f); +test(0x52f,0x52e); +test(0x531,0x561); +test(0x532,0x562); +test(0x533,0x563); +test(0x534,0x564); +test(0x535,0x565); +test(0x536,0x566); +test(0x537,0x567); +test(0x538,0x568); +test(0x539,0x569); +test(0x53a,0x56a); +test(0x53b,0x56b); +test(0x53c,0x56c); +test(0x53d,0x56d); +test(0x53e,0x56e); +test(0x53f,0x56f); +test(0x540,0x570); +test(0x541,0x571); +test(0x542,0x572); +test(0x543,0x573); +test(0x544,0x574); +test(0x545,0x575); +test(0x546,0x576); +test(0x547,0x577); +test(0x548,0x578); +test(0x549,0x579); +test(0x54a,0x57a); +test(0x54b,0x57b); +test(0x54c,0x57c); +test(0x54d,0x57d); +test(0x54e,0x57e); +test(0x54f,0x57f); +test(0x550,0x580); +test(0x551,0x581); +test(0x552,0x582); +test(0x553,0x583); +test(0x554,0x584); +test(0x555,0x585); +test(0x556,0x586); +test(0x561,0x531); +test(0x562,0x532); +test(0x563,0x533); +test(0x564,0x534); +test(0x565,0x535); +test(0x566,0x536); +test(0x567,0x537); +test(0x568,0x538); +test(0x569,0x539); +test(0x56a,0x53a); +test(0x56b,0x53b); +test(0x56c,0x53c); +test(0x56d,0x53d); +test(0x56e,0x53e); +test(0x56f,0x53f); +test(0x570,0x540); +test(0x571,0x541); +test(0x572,0x542); +test(0x573,0x543); +test(0x574,0x544); +test(0x575,0x545); +test(0x576,0x546); +test(0x577,0x547); +test(0x578,0x548); +test(0x579,0x549); +test(0x57a,0x54a); +test(0x57b,0x54b); +test(0x57c,0x54c); +test(0x57d,0x54d); +test(0x57e,0x54e); +test(0x57f,0x54f); +test(0x580,0x550); +test(0x581,0x551); +test(0x582,0x552); +test(0x583,0x553); +test(0x584,0x554); +test(0x585,0x555); +test(0x586,0x556); +test(0x10a0,0x2d00); +test(0x10a1,0x2d01); +test(0x10a2,0x2d02); +test(0x10a3,0x2d03); +test(0x10a4,0x2d04); +test(0x10a5,0x2d05); +test(0x10a6,0x2d06); +test(0x10a7,0x2d07); +test(0x10a8,0x2d08); +test(0x10a9,0x2d09); +test(0x10aa,0x2d0a); +test(0x10ab,0x2d0b); +test(0x10ac,0x2d0c); +test(0x10ad,0x2d0d); +test(0x10ae,0x2d0e); +test(0x10af,0x2d0f); +test(0x10b0,0x2d10); +test(0x10b1,0x2d11); +test(0x10b2,0x2d12); +test(0x10b3,0x2d13); +test(0x10b4,0x2d14); +test(0x10b5,0x2d15); +test(0x10b6,0x2d16); +test(0x10b7,0x2d17); +test(0x10b8,0x2d18); +test(0x10b9,0x2d19); +test(0x10ba,0x2d1a); +test(0x10bb,0x2d1b); +test(0x10bc,0x2d1c); +test(0x10bd,0x2d1d); +test(0x10be,0x2d1e); +test(0x10bf,0x2d1f); +test(0x10c0,0x2d20); +test(0x10c1,0x2d21); +test(0x10c2,0x2d22); +test(0x10c3,0x2d23); +test(0x10c4,0x2d24); +test(0x10c5,0x2d25); +test(0x10c7,0x2d27); +test(0x10cd,0x2d2d); +test(0x13a0,0xab70); +test(0x13a1,0xab71); +test(0x13a2,0xab72); +test(0x13a3,0xab73); +test(0x13a4,0xab74); +test(0x13a5,0xab75); +test(0x13a6,0xab76); +test(0x13a7,0xab77); +test(0x13a8,0xab78); +test(0x13a9,0xab79); +test(0x13aa,0xab7a); +test(0x13ab,0xab7b); +test(0x13ac,0xab7c); +test(0x13ad,0xab7d); +test(0x13ae,0xab7e); +test(0x13af,0xab7f); +test(0x13b0,0xab80); +test(0x13b1,0xab81); +test(0x13b2,0xab82); +test(0x13b3,0xab83); +test(0x13b4,0xab84); +test(0x13b5,0xab85); +test(0x13b6,0xab86); +test(0x13b7,0xab87); +test(0x13b8,0xab88); +test(0x13b9,0xab89); +test(0x13ba,0xab8a); +test(0x13bb,0xab8b); +test(0x13bc,0xab8c); +test(0x13bd,0xab8d); +test(0x13be,0xab8e); +test(0x13bf,0xab8f); +test(0x13c0,0xab90); +test(0x13c1,0xab91); +test(0x13c2,0xab92); +test(0x13c3,0xab93); +test(0x13c4,0xab94); +test(0x13c5,0xab95); +test(0x13c6,0xab96); +test(0x13c7,0xab97); +test(0x13c8,0xab98); +test(0x13c9,0xab99); +test(0x13ca,0xab9a); +test(0x13cb,0xab9b); +test(0x13cc,0xab9c); +test(0x13cd,0xab9d); +test(0x13ce,0xab9e); +test(0x13cf,0xab9f); +test(0x13d0,0xaba0); +test(0x13d1,0xaba1); +test(0x13d2,0xaba2); +test(0x13d3,0xaba3); +test(0x13d4,0xaba4); +test(0x13d5,0xaba5); +test(0x13d6,0xaba6); +test(0x13d7,0xaba7); +test(0x13d8,0xaba8); +test(0x13d9,0xaba9); +test(0x13da,0xabaa); +test(0x13db,0xabab); +test(0x13dc,0xabac); +test(0x13dd,0xabad); +test(0x13de,0xabae); +test(0x13df,0xabaf); +test(0x13e0,0xabb0); +test(0x13e1,0xabb1); +test(0x13e2,0xabb2); +test(0x13e3,0xabb3); +test(0x13e4,0xabb4); +test(0x13e5,0xabb5); +test(0x13e6,0xabb6); +test(0x13e7,0xabb7); +test(0x13e8,0xabb8); +test(0x13e9,0xabb9); +test(0x13ea,0xabba); +test(0x13eb,0xabbb); +test(0x13ec,0xabbc); +test(0x13ed,0xabbd); +test(0x13ee,0xabbe); +test(0x13ef,0xabbf); +test(0x13f0,0x13f8); +test(0x13f1,0x13f9); +test(0x13f2,0x13fa); +test(0x13f3,0x13fb); +test(0x13f4,0x13fc); +test(0x13f5,0x13fd); +test(0x13f8,0x13f0); +test(0x13f9,0x13f1); +test(0x13fa,0x13f2); +test(0x13fb,0x13f3); +test(0x13fc,0x13f4); +test(0x13fd,0x13f5); +test(0x1d79,0xa77d); +test(0x1d7d,0x2c63); +test(0x1e00,0x1e01); +test(0x1e01,0x1e00); +test(0x1e02,0x1e03); +test(0x1e03,0x1e02); +test(0x1e04,0x1e05); +test(0x1e05,0x1e04); +test(0x1e06,0x1e07); +test(0x1e07,0x1e06); +test(0x1e08,0x1e09); +test(0x1e09,0x1e08); +test(0x1e0a,0x1e0b); +test(0x1e0b,0x1e0a); +test(0x1e0c,0x1e0d); +test(0x1e0d,0x1e0c); +test(0x1e0e,0x1e0f); +test(0x1e0f,0x1e0e); +test(0x1e10,0x1e11); +test(0x1e11,0x1e10); +test(0x1e12,0x1e13); +test(0x1e13,0x1e12); +test(0x1e14,0x1e15); +test(0x1e15,0x1e14); +test(0x1e16,0x1e17); +test(0x1e17,0x1e16); +test(0x1e18,0x1e19); +test(0x1e19,0x1e18); +test(0x1e1a,0x1e1b); +test(0x1e1b,0x1e1a); +test(0x1e1c,0x1e1d); +test(0x1e1d,0x1e1c); +test(0x1e1e,0x1e1f); +test(0x1e1f,0x1e1e); +test(0x1e20,0x1e21); +test(0x1e21,0x1e20); +test(0x1e22,0x1e23); +test(0x1e23,0x1e22); +test(0x1e24,0x1e25); +test(0x1e25,0x1e24); +test(0x1e26,0x1e27); +test(0x1e27,0x1e26); +test(0x1e28,0x1e29); +test(0x1e29,0x1e28); +test(0x1e2a,0x1e2b); +test(0x1e2b,0x1e2a); +test(0x1e2c,0x1e2d); +test(0x1e2d,0x1e2c); +test(0x1e2e,0x1e2f); +test(0x1e2f,0x1e2e); +test(0x1e30,0x1e31); +test(0x1e31,0x1e30); +test(0x1e32,0x1e33); +test(0x1e33,0x1e32); +test(0x1e34,0x1e35); +test(0x1e35,0x1e34); +test(0x1e36,0x1e37); +test(0x1e37,0x1e36); +test(0x1e38,0x1e39); +test(0x1e39,0x1e38); +test(0x1e3a,0x1e3b); +test(0x1e3b,0x1e3a); +test(0x1e3c,0x1e3d); +test(0x1e3d,0x1e3c); +test(0x1e3e,0x1e3f); +test(0x1e3f,0x1e3e); +test(0x1e40,0x1e41); +test(0x1e41,0x1e40); +test(0x1e42,0x1e43); +test(0x1e43,0x1e42); +test(0x1e44,0x1e45); +test(0x1e45,0x1e44); +test(0x1e46,0x1e47); +test(0x1e47,0x1e46); +test(0x1e48,0x1e49); +test(0x1e49,0x1e48); +test(0x1e4a,0x1e4b); +test(0x1e4b,0x1e4a); +test(0x1e4c,0x1e4d); +test(0x1e4d,0x1e4c); +test(0x1e4e,0x1e4f); +test(0x1e4f,0x1e4e); +test(0x1e50,0x1e51); +test(0x1e51,0x1e50); +test(0x1e52,0x1e53); +test(0x1e53,0x1e52); +test(0x1e54,0x1e55); +test(0x1e55,0x1e54); +test(0x1e56,0x1e57); +test(0x1e57,0x1e56); +test(0x1e58,0x1e59); +test(0x1e59,0x1e58); +test(0x1e5a,0x1e5b); +test(0x1e5b,0x1e5a); +test(0x1e5c,0x1e5d); +test(0x1e5d,0x1e5c); +test(0x1e5e,0x1e5f); +test(0x1e5f,0x1e5e); +test(0x1e60,0x1e61,0x1e9b); +test(0x1e61,0x1e60,0x1e9b); +test(0x1e62,0x1e63); +test(0x1e63,0x1e62); +test(0x1e64,0x1e65); +test(0x1e65,0x1e64); +test(0x1e66,0x1e67); +test(0x1e67,0x1e66); +test(0x1e68,0x1e69); +test(0x1e69,0x1e68); +test(0x1e6a,0x1e6b); +test(0x1e6b,0x1e6a); +test(0x1e6c,0x1e6d); +test(0x1e6d,0x1e6c); +test(0x1e6e,0x1e6f); +test(0x1e6f,0x1e6e); +test(0x1e70,0x1e71); +test(0x1e71,0x1e70); +test(0x1e72,0x1e73); +test(0x1e73,0x1e72); +test(0x1e74,0x1e75); +test(0x1e75,0x1e74); +test(0x1e76,0x1e77); +test(0x1e77,0x1e76); +test(0x1e78,0x1e79); +test(0x1e79,0x1e78); +test(0x1e7a,0x1e7b); +test(0x1e7b,0x1e7a); +test(0x1e7c,0x1e7d); +test(0x1e7d,0x1e7c); +test(0x1e7e,0x1e7f); +test(0x1e7f,0x1e7e); +test(0x1e80,0x1e81); +test(0x1e81,0x1e80); +test(0x1e82,0x1e83); +test(0x1e83,0x1e82); +test(0x1e84,0x1e85); +test(0x1e85,0x1e84); +test(0x1e86,0x1e87); +test(0x1e87,0x1e86); +test(0x1e88,0x1e89); +test(0x1e89,0x1e88); +test(0x1e8a,0x1e8b); +test(0x1e8b,0x1e8a); +test(0x1e8c,0x1e8d); +test(0x1e8d,0x1e8c); +test(0x1e8e,0x1e8f); +test(0x1e8f,0x1e8e); +test(0x1e90,0x1e91); +test(0x1e91,0x1e90); +test(0x1e92,0x1e93); +test(0x1e93,0x1e92); +test(0x1e94,0x1e95); +test(0x1e95,0x1e94); +test(0x1e9b,0x1e61,0x1e60); +test(0x1e9e,0xdf); +test(0x1ea0,0x1ea1); +test(0x1ea1,0x1ea0); +test(0x1ea2,0x1ea3); +test(0x1ea3,0x1ea2); +test(0x1ea4,0x1ea5); +test(0x1ea5,0x1ea4); +test(0x1ea6,0x1ea7); +test(0x1ea7,0x1ea6); +test(0x1ea8,0x1ea9); +test(0x1ea9,0x1ea8); +test(0x1eaa,0x1eab); +test(0x1eab,0x1eaa); +test(0x1eac,0x1ead); +test(0x1ead,0x1eac); +test(0x1eae,0x1eaf); +test(0x1eaf,0x1eae); +test(0x1eb0,0x1eb1); +test(0x1eb1,0x1eb0); +test(0x1eb2,0x1eb3); +test(0x1eb3,0x1eb2); +test(0x1eb4,0x1eb5); +test(0x1eb5,0x1eb4); +test(0x1eb6,0x1eb7); +test(0x1eb7,0x1eb6); +test(0x1eb8,0x1eb9); +test(0x1eb9,0x1eb8); +test(0x1eba,0x1ebb); +test(0x1ebb,0x1eba); +test(0x1ebc,0x1ebd); +test(0x1ebd,0x1ebc); +test(0x1ebe,0x1ebf); +test(0x1ebf,0x1ebe); +test(0x1ec0,0x1ec1); +test(0x1ec1,0x1ec0); +test(0x1ec2,0x1ec3); +test(0x1ec3,0x1ec2); +test(0x1ec4,0x1ec5); +test(0x1ec5,0x1ec4); +test(0x1ec6,0x1ec7); +test(0x1ec7,0x1ec6); +test(0x1ec8,0x1ec9); +test(0x1ec9,0x1ec8); +test(0x1eca,0x1ecb); +test(0x1ecb,0x1eca); +test(0x1ecc,0x1ecd); +test(0x1ecd,0x1ecc); +test(0x1ece,0x1ecf); +test(0x1ecf,0x1ece); +test(0x1ed0,0x1ed1); +test(0x1ed1,0x1ed0); +test(0x1ed2,0x1ed3); +test(0x1ed3,0x1ed2); +test(0x1ed4,0x1ed5); +test(0x1ed5,0x1ed4); +test(0x1ed6,0x1ed7); +test(0x1ed7,0x1ed6); +test(0x1ed8,0x1ed9); +test(0x1ed9,0x1ed8); +test(0x1eda,0x1edb); +test(0x1edb,0x1eda); +test(0x1edc,0x1edd); +test(0x1edd,0x1edc); +test(0x1ede,0x1edf); +test(0x1edf,0x1ede); +test(0x1ee0,0x1ee1); +test(0x1ee1,0x1ee0); +test(0x1ee2,0x1ee3); +test(0x1ee3,0x1ee2); +test(0x1ee4,0x1ee5); +test(0x1ee5,0x1ee4); +test(0x1ee6,0x1ee7); +test(0x1ee7,0x1ee6); +test(0x1ee8,0x1ee9); +test(0x1ee9,0x1ee8); +test(0x1eea,0x1eeb); +test(0x1eeb,0x1eea); +test(0x1eec,0x1eed); +test(0x1eed,0x1eec); +test(0x1eee,0x1eef); +test(0x1eef,0x1eee); +test(0x1ef0,0x1ef1); +test(0x1ef1,0x1ef0); +test(0x1ef2,0x1ef3); +test(0x1ef3,0x1ef2); +test(0x1ef4,0x1ef5); +test(0x1ef5,0x1ef4); +test(0x1ef6,0x1ef7); +test(0x1ef7,0x1ef6); +test(0x1ef8,0x1ef9); +test(0x1ef9,0x1ef8); +test(0x1efa,0x1efb); +test(0x1efb,0x1efa); +test(0x1efc,0x1efd); +test(0x1efd,0x1efc); +test(0x1efe,0x1eff); +test(0x1eff,0x1efe); +test(0x1f00,0x1f08); +test(0x1f01,0x1f09); +test(0x1f02,0x1f0a); +test(0x1f03,0x1f0b); +test(0x1f04,0x1f0c); +test(0x1f05,0x1f0d); +test(0x1f06,0x1f0e); +test(0x1f07,0x1f0f); +test(0x1f08,0x1f00); +test(0x1f09,0x1f01); +test(0x1f0a,0x1f02); +test(0x1f0b,0x1f03); +test(0x1f0c,0x1f04); +test(0x1f0d,0x1f05); +test(0x1f0e,0x1f06); +test(0x1f0f,0x1f07); +test(0x1f10,0x1f18); +test(0x1f11,0x1f19); +test(0x1f12,0x1f1a); +test(0x1f13,0x1f1b); +test(0x1f14,0x1f1c); +test(0x1f15,0x1f1d); +test(0x1f18,0x1f10); +test(0x1f19,0x1f11); +test(0x1f1a,0x1f12); +test(0x1f1b,0x1f13); +test(0x1f1c,0x1f14); +test(0x1f1d,0x1f15); +test(0x1f20,0x1f28); +test(0x1f21,0x1f29); +test(0x1f22,0x1f2a); +test(0x1f23,0x1f2b); +test(0x1f24,0x1f2c); +test(0x1f25,0x1f2d); +test(0x1f26,0x1f2e); +test(0x1f27,0x1f2f); +test(0x1f28,0x1f20); +test(0x1f29,0x1f21); +test(0x1f2a,0x1f22); +test(0x1f2b,0x1f23); +test(0x1f2c,0x1f24); +test(0x1f2d,0x1f25); +test(0x1f2e,0x1f26); +test(0x1f2f,0x1f27); +test(0x1f30,0x1f38); +test(0x1f31,0x1f39); +test(0x1f32,0x1f3a); +test(0x1f33,0x1f3b); +test(0x1f34,0x1f3c); +test(0x1f35,0x1f3d); +test(0x1f36,0x1f3e); +test(0x1f37,0x1f3f); +test(0x1f38,0x1f30); +test(0x1f39,0x1f31); +test(0x1f3a,0x1f32); +test(0x1f3b,0x1f33); +test(0x1f3c,0x1f34); +test(0x1f3d,0x1f35); +test(0x1f3e,0x1f36); +test(0x1f3f,0x1f37); +test(0x1f40,0x1f48); +test(0x1f41,0x1f49); +test(0x1f42,0x1f4a); +test(0x1f43,0x1f4b); +test(0x1f44,0x1f4c); +test(0x1f45,0x1f4d); +test(0x1f48,0x1f40); +test(0x1f49,0x1f41); +test(0x1f4a,0x1f42); +test(0x1f4b,0x1f43); +test(0x1f4c,0x1f44); +test(0x1f4d,0x1f45); +test(0x1f51,0x1f59); +test(0x1f53,0x1f5b); +test(0x1f55,0x1f5d); +test(0x1f57,0x1f5f); +test(0x1f59,0x1f51); +test(0x1f5b,0x1f53); +test(0x1f5d,0x1f55); +test(0x1f5f,0x1f57); +test(0x1f60,0x1f68); +test(0x1f61,0x1f69); +test(0x1f62,0x1f6a); +test(0x1f63,0x1f6b); +test(0x1f64,0x1f6c); +test(0x1f65,0x1f6d); +test(0x1f66,0x1f6e); +test(0x1f67,0x1f6f); +test(0x1f68,0x1f60); +test(0x1f69,0x1f61); +test(0x1f6a,0x1f62); +test(0x1f6b,0x1f63); +test(0x1f6c,0x1f64); +test(0x1f6d,0x1f65); +test(0x1f6e,0x1f66); +test(0x1f6f,0x1f67); +test(0x1f70,0x1fba); +test(0x1f71,0x1fbb); +test(0x1f72,0x1fc8); +test(0x1f73,0x1fc9); +test(0x1f74,0x1fca); +test(0x1f75,0x1fcb); +test(0x1f76,0x1fda); +test(0x1f77,0x1fdb); +test(0x1f78,0x1ff8); +test(0x1f79,0x1ff9); +test(0x1f7a,0x1fea); +test(0x1f7b,0x1feb); +test(0x1f7c,0x1ffa); +test(0x1f7d,0x1ffb); +test(0x1f80,0x1f88); +test(0x1f81,0x1f89); +test(0x1f82,0x1f8a); +test(0x1f83,0x1f8b); +test(0x1f84,0x1f8c); +test(0x1f85,0x1f8d); +test(0x1f86,0x1f8e); +test(0x1f87,0x1f8f); +test(0x1f88,0x1f80); +test(0x1f89,0x1f81); +test(0x1f8a,0x1f82); +test(0x1f8b,0x1f83); +test(0x1f8c,0x1f84); +test(0x1f8d,0x1f85); +test(0x1f8e,0x1f86); +test(0x1f8f,0x1f87); +test(0x1f90,0x1f98); +test(0x1f91,0x1f99); +test(0x1f92,0x1f9a); +test(0x1f93,0x1f9b); +test(0x1f94,0x1f9c); +test(0x1f95,0x1f9d); +test(0x1f96,0x1f9e); +test(0x1f97,0x1f9f); +test(0x1f98,0x1f90); +test(0x1f99,0x1f91); +test(0x1f9a,0x1f92); +test(0x1f9b,0x1f93); +test(0x1f9c,0x1f94); +test(0x1f9d,0x1f95); +test(0x1f9e,0x1f96); +test(0x1f9f,0x1f97); +test(0x1fa0,0x1fa8); +test(0x1fa1,0x1fa9); +test(0x1fa2,0x1faa); +test(0x1fa3,0x1fab); +test(0x1fa4,0x1fac); +test(0x1fa5,0x1fad); +test(0x1fa6,0x1fae); +test(0x1fa7,0x1faf); +test(0x1fa8,0x1fa0); +test(0x1fa9,0x1fa1); +test(0x1faa,0x1fa2); +test(0x1fab,0x1fa3); +test(0x1fac,0x1fa4); +test(0x1fad,0x1fa5); +test(0x1fae,0x1fa6); +test(0x1faf,0x1fa7); +test(0x1fb0,0x1fb8); +test(0x1fb1,0x1fb9); +test(0x1fb3,0x1fbc); +test(0x1fb8,0x1fb0); +test(0x1fb9,0x1fb1); +test(0x1fba,0x1f70); +test(0x1fbb,0x1f71); +test(0x1fbc,0x1fb3); +test(0x1fbe,0x3b9,0x345,0x399); +test(0x1fc3,0x1fcc); +test(0x1fc8,0x1f72); +test(0x1fc9,0x1f73); +test(0x1fca,0x1f74); +test(0x1fcb,0x1f75); +test(0x1fcc,0x1fc3); +test(0x1fd0,0x1fd8); +test(0x1fd1,0x1fd9); +test(0x1fd8,0x1fd0); +test(0x1fd9,0x1fd1); +test(0x1fda,0x1f76); +test(0x1fdb,0x1f77); +test(0x1fe0,0x1fe8); +test(0x1fe1,0x1fe9); +test(0x1fe5,0x1fec); +test(0x1fe8,0x1fe0); +test(0x1fe9,0x1fe1); +test(0x1fea,0x1f7a); +test(0x1feb,0x1f7b); +test(0x1fec,0x1fe5); +test(0x1ff3,0x1ffc); +test(0x1ff8,0x1f78); +test(0x1ff9,0x1f79); +test(0x1ffa,0x1f7c); +test(0x1ffb,0x1f7d); +test(0x1ffc,0x1ff3); +test(0x2126,0x3c9,0x3a9); +test(0x212a,0x6b,0x4b); +test(0x212b,0xe5,0xc5); +test(0x2132,0x214e); +test(0x214e,0x2132); +test(0x2160,0x2170); +test(0x2161,0x2171); +test(0x2162,0x2172); +test(0x2163,0x2173); +test(0x2164,0x2174); +test(0x2165,0x2175); +test(0x2166,0x2176); +test(0x2167,0x2177); +test(0x2168,0x2178); +test(0x2169,0x2179); +test(0x216a,0x217a); +test(0x216b,0x217b); +test(0x216c,0x217c); +test(0x216d,0x217d); +test(0x216e,0x217e); +test(0x216f,0x217f); +test(0x2170,0x2160); +test(0x2171,0x2161); +test(0x2172,0x2162); +test(0x2173,0x2163); +test(0x2174,0x2164); +test(0x2175,0x2165); +test(0x2176,0x2166); +test(0x2177,0x2167); +test(0x2178,0x2168); +test(0x2179,0x2169); +test(0x217a,0x216a); +test(0x217b,0x216b); +test(0x217c,0x216c); +test(0x217d,0x216d); +test(0x217e,0x216e); +test(0x217f,0x216f); +test(0x2183,0x2184); +test(0x2184,0x2183); +test(0x24b6,0x24d0); +test(0x24b7,0x24d1); +test(0x24b8,0x24d2); +test(0x24b9,0x24d3); +test(0x24ba,0x24d4); +test(0x24bb,0x24d5); +test(0x24bc,0x24d6); +test(0x24bd,0x24d7); +test(0x24be,0x24d8); +test(0x24bf,0x24d9); +test(0x24c0,0x24da); +test(0x24c1,0x24db); +test(0x24c2,0x24dc); +test(0x24c3,0x24dd); +test(0x24c4,0x24de); +test(0x24c5,0x24df); +test(0x24c6,0x24e0); +test(0x24c7,0x24e1); +test(0x24c8,0x24e2); +test(0x24c9,0x24e3); +test(0x24ca,0x24e4); +test(0x24cb,0x24e5); +test(0x24cc,0x24e6); +test(0x24cd,0x24e7); +test(0x24ce,0x24e8); +test(0x24cf,0x24e9); +test(0x24d0,0x24b6); +test(0x24d1,0x24b7); +test(0x24d2,0x24b8); +test(0x24d3,0x24b9); +test(0x24d4,0x24ba); +test(0x24d5,0x24bb); +test(0x24d6,0x24bc); +test(0x24d7,0x24bd); +test(0x24d8,0x24be); +test(0x24d9,0x24bf); +test(0x24da,0x24c0); +test(0x24db,0x24c1); +test(0x24dc,0x24c2); +test(0x24dd,0x24c3); +test(0x24de,0x24c4); +test(0x24df,0x24c5); +test(0x24e0,0x24c6); +test(0x24e1,0x24c7); +test(0x24e2,0x24c8); +test(0x24e3,0x24c9); +test(0x24e4,0x24ca); +test(0x24e5,0x24cb); +test(0x24e6,0x24cc); +test(0x24e7,0x24cd); +test(0x24e8,0x24ce); +test(0x24e9,0x24cf); +test(0x2c00,0x2c30); +test(0x2c01,0x2c31); +test(0x2c02,0x2c32); +test(0x2c03,0x2c33); +test(0x2c04,0x2c34); +test(0x2c05,0x2c35); +test(0x2c06,0x2c36); +test(0x2c07,0x2c37); +test(0x2c08,0x2c38); +test(0x2c09,0x2c39); +test(0x2c0a,0x2c3a); +test(0x2c0b,0x2c3b); +test(0x2c0c,0x2c3c); +test(0x2c0d,0x2c3d); +test(0x2c0e,0x2c3e); +test(0x2c0f,0x2c3f); +test(0x2c10,0x2c40); +test(0x2c11,0x2c41); +test(0x2c12,0x2c42); +test(0x2c13,0x2c43); +test(0x2c14,0x2c44); +test(0x2c15,0x2c45); +test(0x2c16,0x2c46); +test(0x2c17,0x2c47); +test(0x2c18,0x2c48); +test(0x2c19,0x2c49); +test(0x2c1a,0x2c4a); +test(0x2c1b,0x2c4b); +test(0x2c1c,0x2c4c); +test(0x2c1d,0x2c4d); +test(0x2c1e,0x2c4e); +test(0x2c1f,0x2c4f); +test(0x2c20,0x2c50); +test(0x2c21,0x2c51); +test(0x2c22,0x2c52); +test(0x2c23,0x2c53); +test(0x2c24,0x2c54); +test(0x2c25,0x2c55); +test(0x2c26,0x2c56); +test(0x2c27,0x2c57); +test(0x2c28,0x2c58); +test(0x2c29,0x2c59); +test(0x2c2a,0x2c5a); +test(0x2c2b,0x2c5b); +test(0x2c2c,0x2c5c); +test(0x2c2d,0x2c5d); +test(0x2c2e,0x2c5e); +test(0x2c30,0x2c00); +test(0x2c31,0x2c01); +test(0x2c32,0x2c02); +test(0x2c33,0x2c03); +test(0x2c34,0x2c04); +test(0x2c35,0x2c05); +test(0x2c36,0x2c06); +test(0x2c37,0x2c07); +test(0x2c38,0x2c08); +test(0x2c39,0x2c09); +test(0x2c3a,0x2c0a); +test(0x2c3b,0x2c0b); +test(0x2c3c,0x2c0c); +test(0x2c3d,0x2c0d); +test(0x2c3e,0x2c0e); +test(0x2c3f,0x2c0f); +test(0x2c40,0x2c10); +test(0x2c41,0x2c11); +test(0x2c42,0x2c12); +test(0x2c43,0x2c13); +test(0x2c44,0x2c14); +test(0x2c45,0x2c15); +test(0x2c46,0x2c16); +test(0x2c47,0x2c17); +test(0x2c48,0x2c18); +test(0x2c49,0x2c19); +test(0x2c4a,0x2c1a); +test(0x2c4b,0x2c1b); +test(0x2c4c,0x2c1c); +test(0x2c4d,0x2c1d); +test(0x2c4e,0x2c1e); +test(0x2c4f,0x2c1f); +test(0x2c50,0x2c20); +test(0x2c51,0x2c21); +test(0x2c52,0x2c22); +test(0x2c53,0x2c23); +test(0x2c54,0x2c24); +test(0x2c55,0x2c25); +test(0x2c56,0x2c26); +test(0x2c57,0x2c27); +test(0x2c58,0x2c28); +test(0x2c59,0x2c29); +test(0x2c5a,0x2c2a); +test(0x2c5b,0x2c2b); +test(0x2c5c,0x2c2c); +test(0x2c5d,0x2c2d); +test(0x2c5e,0x2c2e); +test(0x2c60,0x2c61); +test(0x2c61,0x2c60); +test(0x2c62,0x26b); +test(0x2c63,0x1d7d); +test(0x2c64,0x27d); +test(0x2c65,0x23a); +test(0x2c66,0x23e); +test(0x2c67,0x2c68); +test(0x2c68,0x2c67); +test(0x2c69,0x2c6a); +test(0x2c6a,0x2c69); +test(0x2c6b,0x2c6c); +test(0x2c6c,0x2c6b); +test(0x2c6d,0x251); +test(0x2c6e,0x271); +test(0x2c6f,0x250); +test(0x2c70,0x252); +test(0x2c72,0x2c73); +test(0x2c73,0x2c72); +test(0x2c75,0x2c76); +test(0x2c76,0x2c75); +test(0x2c7e,0x23f); +test(0x2c7f,0x240); +test(0x2c80,0x2c81); +test(0x2c81,0x2c80); +test(0x2c82,0x2c83); +test(0x2c83,0x2c82); +test(0x2c84,0x2c85); +test(0x2c85,0x2c84); +test(0x2c86,0x2c87); +test(0x2c87,0x2c86); +test(0x2c88,0x2c89); +test(0x2c89,0x2c88); +test(0x2c8a,0x2c8b); +test(0x2c8b,0x2c8a); +test(0x2c8c,0x2c8d); +test(0x2c8d,0x2c8c); +test(0x2c8e,0x2c8f); +test(0x2c8f,0x2c8e); +test(0x2c90,0x2c91); +test(0x2c91,0x2c90); +test(0x2c92,0x2c93); +test(0x2c93,0x2c92); +test(0x2c94,0x2c95); +test(0x2c95,0x2c94); +test(0x2c96,0x2c97); +test(0x2c97,0x2c96); +test(0x2c98,0x2c99); +test(0x2c99,0x2c98); +test(0x2c9a,0x2c9b); +test(0x2c9b,0x2c9a); +test(0x2c9c,0x2c9d); +test(0x2c9d,0x2c9c); +test(0x2c9e,0x2c9f); +test(0x2c9f,0x2c9e); +test(0x2ca0,0x2ca1); +test(0x2ca1,0x2ca0); +test(0x2ca2,0x2ca3); +test(0x2ca3,0x2ca2); +test(0x2ca4,0x2ca5); +test(0x2ca5,0x2ca4); +test(0x2ca6,0x2ca7); +test(0x2ca7,0x2ca6); +test(0x2ca8,0x2ca9); +test(0x2ca9,0x2ca8); +test(0x2caa,0x2cab); +test(0x2cab,0x2caa); +test(0x2cac,0x2cad); +test(0x2cad,0x2cac); +test(0x2cae,0x2caf); +test(0x2caf,0x2cae); +test(0x2cb0,0x2cb1); +test(0x2cb1,0x2cb0); +test(0x2cb2,0x2cb3); +test(0x2cb3,0x2cb2); +test(0x2cb4,0x2cb5); +test(0x2cb5,0x2cb4); +test(0x2cb6,0x2cb7); +test(0x2cb7,0x2cb6); +test(0x2cb8,0x2cb9); +test(0x2cb9,0x2cb8); +test(0x2cba,0x2cbb); +test(0x2cbb,0x2cba); +test(0x2cbc,0x2cbd); +test(0x2cbd,0x2cbc); +test(0x2cbe,0x2cbf); +test(0x2cbf,0x2cbe); +test(0x2cc0,0x2cc1); +test(0x2cc1,0x2cc0); +test(0x2cc2,0x2cc3); +test(0x2cc3,0x2cc2); +test(0x2cc4,0x2cc5); +test(0x2cc5,0x2cc4); +test(0x2cc6,0x2cc7); +test(0x2cc7,0x2cc6); +test(0x2cc8,0x2cc9); +test(0x2cc9,0x2cc8); +test(0x2cca,0x2ccb); +test(0x2ccb,0x2cca); +test(0x2ccc,0x2ccd); +test(0x2ccd,0x2ccc); +test(0x2cce,0x2ccf); +test(0x2ccf,0x2cce); +test(0x2cd0,0x2cd1); +test(0x2cd1,0x2cd0); +test(0x2cd2,0x2cd3); +test(0x2cd3,0x2cd2); +test(0x2cd4,0x2cd5); +test(0x2cd5,0x2cd4); +test(0x2cd6,0x2cd7); +test(0x2cd7,0x2cd6); +test(0x2cd8,0x2cd9); +test(0x2cd9,0x2cd8); +test(0x2cda,0x2cdb); +test(0x2cdb,0x2cda); +test(0x2cdc,0x2cdd); +test(0x2cdd,0x2cdc); +test(0x2cde,0x2cdf); +test(0x2cdf,0x2cde); +test(0x2ce0,0x2ce1); +test(0x2ce1,0x2ce0); +test(0x2ce2,0x2ce3); +test(0x2ce3,0x2ce2); +test(0x2ceb,0x2cec); +test(0x2cec,0x2ceb); +test(0x2ced,0x2cee); +test(0x2cee,0x2ced); +test(0x2cf2,0x2cf3); +test(0x2cf3,0x2cf2); +test(0x2d00,0x10a0); +test(0x2d01,0x10a1); +test(0x2d02,0x10a2); +test(0x2d03,0x10a3); +test(0x2d04,0x10a4); +test(0x2d05,0x10a5); +test(0x2d06,0x10a6); +test(0x2d07,0x10a7); +test(0x2d08,0x10a8); +test(0x2d09,0x10a9); +test(0x2d0a,0x10aa); +test(0x2d0b,0x10ab); +test(0x2d0c,0x10ac); +test(0x2d0d,0x10ad); +test(0x2d0e,0x10ae); +test(0x2d0f,0x10af); +test(0x2d10,0x10b0); +test(0x2d11,0x10b1); +test(0x2d12,0x10b2); +test(0x2d13,0x10b3); +test(0x2d14,0x10b4); +test(0x2d15,0x10b5); +test(0x2d16,0x10b6); +test(0x2d17,0x10b7); +test(0x2d18,0x10b8); +test(0x2d19,0x10b9); +test(0x2d1a,0x10ba); +test(0x2d1b,0x10bb); +test(0x2d1c,0x10bc); +test(0x2d1d,0x10bd); +test(0x2d1e,0x10be); +test(0x2d1f,0x10bf); +test(0x2d20,0x10c0); +test(0x2d21,0x10c1); +test(0x2d22,0x10c2); +test(0x2d23,0x10c3); +test(0x2d24,0x10c4); +test(0x2d25,0x10c5); +test(0x2d27,0x10c7); +test(0x2d2d,0x10cd); +test(0xa640,0xa641); +test(0xa641,0xa640); +test(0xa642,0xa643); +test(0xa643,0xa642); +test(0xa644,0xa645); +test(0xa645,0xa644); +test(0xa646,0xa647); +test(0xa647,0xa646); +test(0xa648,0xa649); +test(0xa649,0xa648); +test(0xa64a,0xa64b); +test(0xa64b,0xa64a); +test(0xa64c,0xa64d); +test(0xa64d,0xa64c); +test(0xa64e,0xa64f); +test(0xa64f,0xa64e); +test(0xa650,0xa651); +test(0xa651,0xa650); +test(0xa652,0xa653); +test(0xa653,0xa652); +test(0xa654,0xa655); +test(0xa655,0xa654); +test(0xa656,0xa657); +test(0xa657,0xa656); +test(0xa658,0xa659); +test(0xa659,0xa658); +test(0xa65a,0xa65b); +test(0xa65b,0xa65a); +test(0xa65c,0xa65d); +test(0xa65d,0xa65c); +test(0xa65e,0xa65f); +test(0xa65f,0xa65e); +test(0xa660,0xa661); +test(0xa661,0xa660); +test(0xa662,0xa663); +test(0xa663,0xa662); +test(0xa664,0xa665); +test(0xa665,0xa664); +test(0xa666,0xa667); +test(0xa667,0xa666); +test(0xa668,0xa669); +test(0xa669,0xa668); +test(0xa66a,0xa66b); +test(0xa66b,0xa66a); +test(0xa66c,0xa66d); +test(0xa66d,0xa66c); +test(0xa680,0xa681); +test(0xa681,0xa680); +test(0xa682,0xa683); +test(0xa683,0xa682); +test(0xa684,0xa685); +test(0xa685,0xa684); +test(0xa686,0xa687); +test(0xa687,0xa686); +test(0xa688,0xa689); +test(0xa689,0xa688); +test(0xa68a,0xa68b); +test(0xa68b,0xa68a); +test(0xa68c,0xa68d); +test(0xa68d,0xa68c); +test(0xa68e,0xa68f); +test(0xa68f,0xa68e); +test(0xa690,0xa691); +test(0xa691,0xa690); +test(0xa692,0xa693); +test(0xa693,0xa692); +test(0xa694,0xa695); +test(0xa695,0xa694); +test(0xa696,0xa697); +test(0xa697,0xa696); +test(0xa698,0xa699); +test(0xa699,0xa698); +test(0xa69a,0xa69b); +test(0xa69b,0xa69a); +test(0xa722,0xa723); +test(0xa723,0xa722); +test(0xa724,0xa725); +test(0xa725,0xa724); +test(0xa726,0xa727); +test(0xa727,0xa726); +test(0xa728,0xa729); +test(0xa729,0xa728); +test(0xa72a,0xa72b); +test(0xa72b,0xa72a); +test(0xa72c,0xa72d); +test(0xa72d,0xa72c); +test(0xa72e,0xa72f); +test(0xa72f,0xa72e); +test(0xa732,0xa733); +test(0xa733,0xa732); +test(0xa734,0xa735); +test(0xa735,0xa734); +test(0xa736,0xa737); +test(0xa737,0xa736); +test(0xa738,0xa739); +test(0xa739,0xa738); +test(0xa73a,0xa73b); +test(0xa73b,0xa73a); +test(0xa73c,0xa73d); +test(0xa73d,0xa73c); +test(0xa73e,0xa73f); +test(0xa73f,0xa73e); +test(0xa740,0xa741); +test(0xa741,0xa740); +test(0xa742,0xa743); +test(0xa743,0xa742); +test(0xa744,0xa745); +test(0xa745,0xa744); +test(0xa746,0xa747); +test(0xa747,0xa746); +test(0xa748,0xa749); +test(0xa749,0xa748); +test(0xa74a,0xa74b); +test(0xa74b,0xa74a); +test(0xa74c,0xa74d); +test(0xa74d,0xa74c); +test(0xa74e,0xa74f); +test(0xa74f,0xa74e); +test(0xa750,0xa751); +test(0xa751,0xa750); +test(0xa752,0xa753); +test(0xa753,0xa752); +test(0xa754,0xa755); +test(0xa755,0xa754); +test(0xa756,0xa757); +test(0xa757,0xa756); +test(0xa758,0xa759); +test(0xa759,0xa758); +test(0xa75a,0xa75b); +test(0xa75b,0xa75a); +test(0xa75c,0xa75d); +test(0xa75d,0xa75c); +test(0xa75e,0xa75f); +test(0xa75f,0xa75e); +test(0xa760,0xa761); +test(0xa761,0xa760); +test(0xa762,0xa763); +test(0xa763,0xa762); +test(0xa764,0xa765); +test(0xa765,0xa764); +test(0xa766,0xa767); +test(0xa767,0xa766); +test(0xa768,0xa769); +test(0xa769,0xa768); +test(0xa76a,0xa76b); +test(0xa76b,0xa76a); +test(0xa76c,0xa76d); +test(0xa76d,0xa76c); +test(0xa76e,0xa76f); +test(0xa76f,0xa76e); +test(0xa779,0xa77a); +test(0xa77a,0xa779); +test(0xa77b,0xa77c); +test(0xa77c,0xa77b); +test(0xa77d,0x1d79); +test(0xa77e,0xa77f); +test(0xa77f,0xa77e); +test(0xa780,0xa781); +test(0xa781,0xa780); +test(0xa782,0xa783); +test(0xa783,0xa782); +test(0xa784,0xa785); +test(0xa785,0xa784); +test(0xa786,0xa787); +test(0xa787,0xa786); +test(0xa78b,0xa78c); +test(0xa78c,0xa78b); +test(0xa78d,0x265); +test(0xa790,0xa791); +test(0xa791,0xa790); +test(0xa792,0xa793); +test(0xa793,0xa792); +test(0xa796,0xa797); +test(0xa797,0xa796); +test(0xa798,0xa799); +test(0xa799,0xa798); +test(0xa79a,0xa79b); +test(0xa79b,0xa79a); +test(0xa79c,0xa79d); +test(0xa79d,0xa79c); +test(0xa79e,0xa79f); +test(0xa79f,0xa79e); +test(0xa7a0,0xa7a1); +test(0xa7a1,0xa7a0); +test(0xa7a2,0xa7a3); +test(0xa7a3,0xa7a2); +test(0xa7a4,0xa7a5); +test(0xa7a5,0xa7a4); +test(0xa7a6,0xa7a7); +test(0xa7a7,0xa7a6); +test(0xa7a8,0xa7a9); +test(0xa7a9,0xa7a8); +test(0xa7aa,0x266); +test(0xa7ab,0x25c); +test(0xa7ac,0x261); +test(0xa7ad,0x26c); +test(0xa7b0,0x29e); +test(0xa7b1,0x287); +test(0xa7b2,0x29d); +test(0xa7b3,0xab53); +test(0xa7b4,0xa7b5); +test(0xa7b5,0xa7b4); +test(0xa7b6,0xa7b7); +test(0xa7b7,0xa7b6); +test(0xab53,0xa7b3); +test(0xab70,0x13a0); +test(0xab71,0x13a1); +test(0xab72,0x13a2); +test(0xab73,0x13a3); +test(0xab74,0x13a4); +test(0xab75,0x13a5); +test(0xab76,0x13a6); +test(0xab77,0x13a7); +test(0xab78,0x13a8); +test(0xab79,0x13a9); +test(0xab7a,0x13aa); +test(0xab7b,0x13ab); +test(0xab7c,0x13ac); +test(0xab7d,0x13ad); +test(0xab7e,0x13ae); +test(0xab7f,0x13af); +test(0xab80,0x13b0); +test(0xab81,0x13b1); +test(0xab82,0x13b2); +test(0xab83,0x13b3); +test(0xab84,0x13b4); +test(0xab85,0x13b5); +test(0xab86,0x13b6); +test(0xab87,0x13b7); +test(0xab88,0x13b8); +test(0xab89,0x13b9); +test(0xab8a,0x13ba); +test(0xab8b,0x13bb); +test(0xab8c,0x13bc); +test(0xab8d,0x13bd); +test(0xab8e,0x13be); +test(0xab8f,0x13bf); +test(0xab90,0x13c0); +test(0xab91,0x13c1); +test(0xab92,0x13c2); +test(0xab93,0x13c3); +test(0xab94,0x13c4); +test(0xab95,0x13c5); +test(0xab96,0x13c6); +test(0xab97,0x13c7); +test(0xab98,0x13c8); +test(0xab99,0x13c9); +test(0xab9a,0x13ca); +test(0xab9b,0x13cb); +test(0xab9c,0x13cc); +test(0xab9d,0x13cd); +test(0xab9e,0x13ce); +test(0xab9f,0x13cf); +test(0xaba0,0x13d0); +test(0xaba1,0x13d1); +test(0xaba2,0x13d2); +test(0xaba3,0x13d3); +test(0xaba4,0x13d4); +test(0xaba5,0x13d5); +test(0xaba6,0x13d6); +test(0xaba7,0x13d7); +test(0xaba8,0x13d8); +test(0xaba9,0x13d9); +test(0xabaa,0x13da); +test(0xabab,0x13db); +test(0xabac,0x13dc); +test(0xabad,0x13dd); +test(0xabae,0x13de); +test(0xabaf,0x13df); +test(0xabb0,0x13e0); +test(0xabb1,0x13e1); +test(0xabb2,0x13e2); +test(0xabb3,0x13e3); +test(0xabb4,0x13e4); +test(0xabb5,0x13e5); +test(0xabb6,0x13e6); +test(0xabb7,0x13e7); +test(0xabb8,0x13e8); +test(0xabb9,0x13e9); +test(0xabba,0x13ea); +test(0xabbb,0x13eb); +test(0xabbc,0x13ec); +test(0xabbd,0x13ed); +test(0xabbe,0x13ee); +test(0xabbf,0x13ef); +test(0xff21,0xff41); +test(0xff22,0xff42); +test(0xff23,0xff43); +test(0xff24,0xff44); +test(0xff25,0xff45); +test(0xff26,0xff46); +test(0xff27,0xff47); +test(0xff28,0xff48); +test(0xff29,0xff49); +test(0xff2a,0xff4a); +test(0xff2b,0xff4b); +test(0xff2c,0xff4c); +test(0xff2d,0xff4d); +test(0xff2e,0xff4e); +test(0xff2f,0xff4f); +test(0xff30,0xff50); +test(0xff31,0xff51); +test(0xff32,0xff52); +test(0xff33,0xff53); +test(0xff34,0xff54); +test(0xff35,0xff55); +test(0xff36,0xff56); +test(0xff37,0xff57); +test(0xff38,0xff58); +test(0xff39,0xff59); +test(0xff3a,0xff5a); +test(0xff41,0xff21); +test(0xff42,0xff22); +test(0xff43,0xff23); +test(0xff44,0xff24); +test(0xff45,0xff25); +test(0xff46,0xff26); +test(0xff47,0xff27); +test(0xff48,0xff28); +test(0xff49,0xff29); +test(0xff4a,0xff2a); +test(0xff4b,0xff2b); +test(0xff4c,0xff2c); +test(0xff4d,0xff2d); +test(0xff4e,0xff2e); +test(0xff4f,0xff2f); +test(0xff50,0xff30); +test(0xff51,0xff31); +test(0xff52,0xff32); +test(0xff53,0xff33); +test(0xff54,0xff34); +test(0xff55,0xff35); +test(0xff56,0xff36); +test(0xff57,0xff37); +test(0xff58,0xff38); +test(0xff59,0xff39); +test(0xff5a,0xff3a); +test(0x10400,0x10428); +test(0x10401,0x10429); +test(0x10402,0x1042a); +test(0x10403,0x1042b); +test(0x10404,0x1042c); +test(0x10405,0x1042d); +test(0x10406,0x1042e); +test(0x10407,0x1042f); +test(0x10408,0x10430); +test(0x10409,0x10431); +test(0x1040a,0x10432); +test(0x1040b,0x10433); +test(0x1040c,0x10434); +test(0x1040d,0x10435); +test(0x1040e,0x10436); +test(0x1040f,0x10437); +test(0x10410,0x10438); +test(0x10411,0x10439); +test(0x10412,0x1043a); +test(0x10413,0x1043b); +test(0x10414,0x1043c); +test(0x10415,0x1043d); +test(0x10416,0x1043e); +test(0x10417,0x1043f); +test(0x10418,0x10440); +test(0x10419,0x10441); +test(0x1041a,0x10442); +test(0x1041b,0x10443); +test(0x1041c,0x10444); +test(0x1041d,0x10445); +test(0x1041e,0x10446); +test(0x1041f,0x10447); +test(0x10420,0x10448); +test(0x10421,0x10449); +test(0x10422,0x1044a); +test(0x10423,0x1044b); +test(0x10424,0x1044c); +test(0x10425,0x1044d); +test(0x10426,0x1044e); +test(0x10427,0x1044f); +test(0x10428,0x10400); +test(0x10429,0x10401); +test(0x1042a,0x10402); +test(0x1042b,0x10403); +test(0x1042c,0x10404); +test(0x1042d,0x10405); +test(0x1042e,0x10406); +test(0x1042f,0x10407); +test(0x10430,0x10408); +test(0x10431,0x10409); +test(0x10432,0x1040a); +test(0x10433,0x1040b); +test(0x10434,0x1040c); +test(0x10435,0x1040d); +test(0x10436,0x1040e); +test(0x10437,0x1040f); +test(0x10438,0x10410); +test(0x10439,0x10411); +test(0x1043a,0x10412); +test(0x1043b,0x10413); +test(0x1043c,0x10414); +test(0x1043d,0x10415); +test(0x1043e,0x10416); +test(0x1043f,0x10417); +test(0x10440,0x10418); +test(0x10441,0x10419); +test(0x10442,0x1041a); +test(0x10443,0x1041b); +test(0x10444,0x1041c); +test(0x10445,0x1041d); +test(0x10446,0x1041e); +test(0x10447,0x1041f); +test(0x10448,0x10420); +test(0x10449,0x10421); +test(0x1044a,0x10422); +test(0x1044b,0x10423); +test(0x1044c,0x10424); +test(0x1044d,0x10425); +test(0x1044e,0x10426); +test(0x1044f,0x10427); +test(0x10c80,0x10cc0); +test(0x10c81,0x10cc1); +test(0x10c82,0x10cc2); +test(0x10c83,0x10cc3); +test(0x10c84,0x10cc4); +test(0x10c85,0x10cc5); +test(0x10c86,0x10cc6); +test(0x10c87,0x10cc7); +test(0x10c88,0x10cc8); +test(0x10c89,0x10cc9); +test(0x10c8a,0x10cca); +test(0x10c8b,0x10ccb); +test(0x10c8c,0x10ccc); +test(0x10c8d,0x10ccd); +test(0x10c8e,0x10cce); +test(0x10c8f,0x10ccf); +test(0x10c90,0x10cd0); +test(0x10c91,0x10cd1); +test(0x10c92,0x10cd2); +test(0x10c93,0x10cd3); +test(0x10c94,0x10cd4); +test(0x10c95,0x10cd5); +test(0x10c96,0x10cd6); +test(0x10c97,0x10cd7); +test(0x10c98,0x10cd8); +test(0x10c99,0x10cd9); +test(0x10c9a,0x10cda); +test(0x10c9b,0x10cdb); +test(0x10c9c,0x10cdc); +test(0x10c9d,0x10cdd); +test(0x10c9e,0x10cde); +test(0x10c9f,0x10cdf); +test(0x10ca0,0x10ce0); +test(0x10ca1,0x10ce1); +test(0x10ca2,0x10ce2); +test(0x10ca3,0x10ce3); +test(0x10ca4,0x10ce4); +test(0x10ca5,0x10ce5); +test(0x10ca6,0x10ce6); +test(0x10ca7,0x10ce7); +test(0x10ca8,0x10ce8); +test(0x10ca9,0x10ce9); +test(0x10caa,0x10cea); +test(0x10cab,0x10ceb); +test(0x10cac,0x10cec); +test(0x10cad,0x10ced); +test(0x10cae,0x10cee); +test(0x10caf,0x10cef); +test(0x10cb0,0x10cf0); +test(0x10cb1,0x10cf1); +test(0x10cb2,0x10cf2); +test(0x10cc0,0x10c80); +test(0x10cc1,0x10c81); +test(0x10cc2,0x10c82); +test(0x10cc3,0x10c83); +test(0x10cc4,0x10c84); +test(0x10cc5,0x10c85); +test(0x10cc6,0x10c86); +test(0x10cc7,0x10c87); +test(0x10cc8,0x10c88); +test(0x10cc9,0x10c89); +test(0x10cca,0x10c8a); +test(0x10ccb,0x10c8b); +test(0x10ccc,0x10c8c); +test(0x10ccd,0x10c8d); +test(0x10cce,0x10c8e); +test(0x10ccf,0x10c8f); +test(0x10cd0,0x10c90); +test(0x10cd1,0x10c91); +test(0x10cd2,0x10c92); +test(0x10cd3,0x10c93); +test(0x10cd4,0x10c94); +test(0x10cd5,0x10c95); +test(0x10cd6,0x10c96); +test(0x10cd7,0x10c97); +test(0x10cd8,0x10c98); +test(0x10cd9,0x10c99); +test(0x10cda,0x10c9a); +test(0x10cdb,0x10c9b); +test(0x10cdc,0x10c9c); +test(0x10cdd,0x10c9d); +test(0x10cde,0x10c9e); +test(0x10cdf,0x10c9f); +test(0x10ce0,0x10ca0); +test(0x10ce1,0x10ca1); +test(0x10ce2,0x10ca2); +test(0x10ce3,0x10ca3); +test(0x10ce4,0x10ca4); +test(0x10ce5,0x10ca5); +test(0x10ce6,0x10ca6); +test(0x10ce7,0x10ca7); +test(0x10ce8,0x10ca8); +test(0x10ce9,0x10ca9); +test(0x10cea,0x10caa); +test(0x10ceb,0x10cab); +test(0x10cec,0x10cac); +test(0x10ced,0x10cad); +test(0x10cee,0x10cae); +test(0x10cef,0x10caf); +test(0x10cf0,0x10cb0); +test(0x10cf1,0x10cb1); +test(0x10cf2,0x10cb2); +test(0x118a0,0x118c0); +test(0x118a1,0x118c1); +test(0x118a2,0x118c2); +test(0x118a3,0x118c3); +test(0x118a4,0x118c4); +test(0x118a5,0x118c5); +test(0x118a6,0x118c6); +test(0x118a7,0x118c7); +test(0x118a8,0x118c8); +test(0x118a9,0x118c9); +test(0x118aa,0x118ca); +test(0x118ab,0x118cb); +test(0x118ac,0x118cc); +test(0x118ad,0x118cd); +test(0x118ae,0x118ce); +test(0x118af,0x118cf); +test(0x118b0,0x118d0); +test(0x118b1,0x118d1); +test(0x118b2,0x118d2); +test(0x118b3,0x118d3); +test(0x118b4,0x118d4); +test(0x118b5,0x118d5); +test(0x118b6,0x118d6); +test(0x118b7,0x118d7); +test(0x118b8,0x118d8); +test(0x118b9,0x118d9); +test(0x118ba,0x118da); +test(0x118bb,0x118db); +test(0x118bc,0x118dc); +test(0x118bd,0x118dd); +test(0x118be,0x118de); +test(0x118bf,0x118df); +test(0x118c0,0x118a0); +test(0x118c1,0x118a1); +test(0x118c2,0x118a2); +test(0x118c3,0x118a3); +test(0x118c4,0x118a4); +test(0x118c5,0x118a5); +test(0x118c6,0x118a6); +test(0x118c7,0x118a7); +test(0x118c8,0x118a8); +test(0x118c9,0x118a9); +test(0x118ca,0x118aa); +test(0x118cb,0x118ab); +test(0x118cc,0x118ac); +test(0x118cd,0x118ad); +test(0x118ce,0x118ae); +test(0x118cf,0x118af); +test(0x118d0,0x118b0); +test(0x118d1,0x118b1); +test(0x118d2,0x118b2); +test(0x118d3,0x118b3); +test(0x118d4,0x118b4); +test(0x118d5,0x118b5); +test(0x118d6,0x118b6); +test(0x118d7,0x118b7); +test(0x118d8,0x118b8); +test(0x118d9,0x118b9); +test(0x118da,0x118ba); +test(0x118db,0x118bb); +test(0x118dc,0x118bc); +test(0x118dd,0x118bd); +test(0x118de,0x118be); +test(0x118df,0x118bf); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-index.js b/js/src/tests/ecma_6/RegExp/unicode-index.js new file mode 100644 index 000000000..a4b2eb203 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-index.js @@ -0,0 +1,17 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- Pattern match should start from lead surrogate when lastIndex points corresponding trail surrogate."; + +print(BUGNUMBER + ": " + summary); + +var r = /\uD83D\uDC38/ug; +r.lastIndex = 1; +var str = "\uD83D\uDC38"; +var result = r.exec(str); +assertEq(result.length, 1); +assertEq(result[0], "\uD83D\uDC38"); + +// This does not match to ES6 spec, but the spec will be changed. +assertEq(result.index, 0); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-lead-trail.js b/js/src/tests/ecma_6/RegExp/unicode-lead-trail.js new file mode 100644 index 000000000..7ecdb9ace --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-lead-trail.js @@ -0,0 +1,218 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- lead and trail patterns in RegExpUnicodeEscapeSequence."; + +print(BUGNUMBER + ": " + summary); + +// ==== standalone ==== + +assertEqArray(/\uD83D\uDC38/u.exec("\u{1F438}"), + ["\u{1F438}"]); + +// no unicode flag +assertEqArray(/\uD83D\uDC38/.exec("\u{1F438}"), + ["\u{1F438}"]); + +// RegExp constructor +assertEqArray(new RegExp("\\uD83D\\uDC38", "u").exec("\u{1F438}"), + ["\u{1F438}"]); + +// RegExp constructor, no unicode flag +assertEqArray(new RegExp("\\uD83D\\uDC38", "").exec("\u{1F438}"), + ["\u{1F438}"]); + +// ==== ? ==== + +assertEqArray(/\uD83D\uDC38?/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/\uD83D\uDC38?/u.exec(""), + [""]); + +// lead-only target +assertEqArray(/\uD83D\uDC38?/u.exec("\uD83D"), + [""]); + +// no unicode flag +assertEqArray(/\uD83D\uDC38?/.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEq(/\uD83D\uDC38?/.exec(""), + null); + +assertEqArray(/\uD83D\uDC38?/.exec("\uD83D"), + ["\uD83D"]); + +// RegExp constructor +assertEqArray(new RegExp("\\uD83D\\uDC38?", "u").exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(new RegExp("\\uD83D\\uDC38?", "u").exec(""), + [""]); + +assertEqArray(new RegExp("\\uD83D\\uDC38?", "u").exec("\uD83D"), + [""]); + +// RegExp constructor, no unicode flag +assertEqArray(new RegExp("\\uD83D\\uDC38?", "").exec("\u{1F438}"), + ["\u{1F438}"]); +assertEq(new RegExp("\\uD83D\\uDC38?", "").exec(""), + null); + +assertEqArray(new RegExp("\\uD83D\\uDC38?", "").exec("\uD83D"), + ["\uD83D"]); + +// ==== + ==== + +assertEqArray(/\uD83D\uDC38+/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/\uD83D\uDC38+/u.exec("\u{1F438}\u{1F438}"), + ["\u{1F438}\u{1F438}"]); +assertEq(/\uD83D\uDC38+/u.exec(""), + null); + +// lead-only target +assertEq(/\uD83D\uDC38+/u.exec("\uD83D"), + null); +assertEqArray(/\uD83D\uDC38+/u.exec("\uD83D\uDC38\uDC38"), + ["\uD83D\uDC38"]); + +// no unicode flag +assertEqArray(/\uD83D\uDC38+/.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/\uD83D\uDC38+/.exec("\u{1F438}\u{1F438}"), + ["\u{1F438}"]); +assertEq(/\uD83D\uDC38+/.exec("\uD83D"), + null); +assertEqArray(/\uD83D\uDC38+/.exec("\uD83D\uDC38\uDC38"), + ["\uD83D\uDC38\uDC38"]); +assertEq(/\uD83D\uDC38+/.exec(""), + null); + +// ==== * ==== + +assertEqArray(/\uD83D\uDC38*/u.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/\uD83D\uDC38*/u.exec("\u{1F438}\u{1F438}"), + ["\u{1F438}\u{1F438}"]); +assertEqArray(/\uD83D\uDC38*/u.exec(""), + [""]); + +// lead-only target +assertEqArray(/\uD83D\uDC38*/u.exec("\uD83D"), + [""]); +assertEqArray(/\uD83D\uDC38*/u.exec("\uD83D\uDC38\uDC38"), + ["\uD83D\uDC38"]); + +// no unicode flag +assertEqArray(/\uD83D\uDC38*/.exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/\uD83D\uDC38*/.exec("\u{1F438}\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(/\uD83D\uDC38*/.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/\uD83D\uDC38*/.exec("\uD83D\uDC38\uDC38"), + ["\uD83D\uDC38\uDC38"]); +assertEq(/\uD83D\uDC38*/.exec(""), + null); + +// ==== lead-only ==== + +// match only non-surrogate pair +assertEqArray(/\uD83D/u.exec("\uD83D\uDBFF"), + ["\uD83D"]); +assertEq(/\uD83D/u.exec("\uD83D\uDC00"), + null); +assertEq(/\uD83D/u.exec("\uD83D\uDFFF"), + null); +assertEqArray(/\uD83D/u.exec("\uD83D\uE000"), + ["\uD83D"]); + +// match before non-tail char +assertEqArray(/\uD83D/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/\uD83D/u.exec("\uD83DA"), + ["\uD83D"]); + +// no unicode flag +assertEqArray(/\uD83D/.exec("\uD83D\uDBFF"), + ["\uD83D"]); +assertEqArray(/\uD83D/.exec("\uD83D\uDC00"), + ["\uD83D"]); +assertEqArray(/\uD83D/.exec("\uD83D\uDFFF"), + ["\uD83D"]); +assertEqArray(/\uD83D/.exec("\uD83D\uE000"), + ["\uD83D"]); +assertEqArray(/\uD83D/.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/\uD83D/.exec("\uD83DA"), + ["\uD83D"]); + +// ==== trail-only ==== + +// match only non-surrogate pair +assertEqArray(/\uDC38/u.exec("\uD7FF\uDC38"), + ["\uDC38"]); +assertEq(/\uDC38/u.exec("\uD800\uDC38"), + null); +assertEq(/\uDC38/u.exec("\uDBFF\uDC38"), + null); +assertEqArray(/\uDC38/u.exec("\uDC00\uDC38"), + ["\uDC38"]); + +// match after non-lead char +assertEqArray(/\uDC38/u.exec("\uDC38"), + ["\uDC38"]); +assertEqArray(/\uDC38/u.exec("A\uDC38"), + ["\uDC38"]); + +// no unicode flag +assertEqArray(/\uDC38/.exec("\uD7FF\uDC38"), + ["\uDC38"]); +assertEqArray(/\uDC38/.exec("\uD800\uDC38"), + ["\uDC38"]); +assertEqArray(/\uDC38/.exec("\uDBFF\uDC38"), + ["\uDC38"]); +assertEqArray(/\uDC38/.exec("\uDC00\uDC38"), + ["\uDC38"]); +assertEqArray(/\uDC38/.exec("\uDC38"), + ["\uDC38"]); +assertEqArray(/\uDC38/.exec("A\uDC38"), + ["\uDC38"]); + +// ==== invalid trail ==== + +assertEqArray(/\uD83D\u3042*/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/\uD83D\u3042*/u.exec("\uD83D\u3042"), + ["\uD83D\u3042"]); +assertEqArray(/\uD83D\u3042*/u.exec("\uD83D\u3042\u3042"), + ["\uD83D\u3042\u3042"]); + +assertEqArray(/\uD83D\u{3042}*/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/\uD83D\u{3042}*/u.exec("\uD83D\u3042"), + ["\uD83D\u3042"]); +assertEqArray(/\uD83D\u{3042}*/u.exec("\uD83D\u3042\u3042"), + ["\uD83D\u3042\u3042"]); + +assertEqArray(/\uD83DA*/u.exec("\uD83D"), + ["\uD83D"]); +assertEqArray(/\uD83DA*/u.exec("\uD83DA"), + ["\uD83DA"]); +assertEqArray(/\uD83DA*/u.exec("\uD83DAA"), + ["\uD83DAA"]); + +// ==== wrong patterns ==== + +assertThrowsInstanceOf(() => eval(`/\\u/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u0/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u00/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u000/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u000G/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\u0.00/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\uD83D\\u/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\uD83D\\u0/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\uD83D\\u00/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\uD83D\\u000/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\uD83D\\u000G/u`), SyntaxError); +assertThrowsInstanceOf(() => eval(`/\\uD83D\\u0.00/u`), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/RegExp/unicode-raw.js b/js/src/tests/ecma_6/RegExp/unicode-raw.js new file mode 100644 index 000000000..37b572cd8 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/unicode-raw.js @@ -0,0 +1,139 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- raw unicode."; + +print(BUGNUMBER + ": " + summary); + +// ==== standalone ==== + +assertEqArray(eval(`/\uD83D\uDC38/u`).exec("\u{1F438}"), + ["\u{1F438}"]); + +// no unicode flag +assertEqArray(eval(`/\uD83D\uDC38/`).exec("\u{1F438}"), + ["\u{1F438}"]); + +// escaped (lead) +assertEq(eval(`/\\uD83D\uDC38/u`).exec("\u{1F438}"), + null); +assertEq(eval(`/\\u{D83D}\uDC38/u`).exec("\u{1F438}"), + null); + +// escaped (trail) +assertEq(eval(`/\uD83D\\uDC38/u`).exec("\u{1F438}"), + null); +assertEq(eval(`/\uD83D\\u{DC38}/u`).exec("\u{1F438}"), + null); + +// escaped (lead), no unicode flag +assertEqArray(eval(`/\\uD83D\uDC38/`).exec("\u{1F438}"), + ["\u{1F438}"]); + +// escaped (trail), no unicode flag +assertEqArray(eval(`/\uD83D\\uDC38/`).exec("\u{1F438}"), + ["\u{1F438}"]); + +// ==== RegExp constructor ==== + +assertEqArray(new RegExp("\uD83D\uDC38", "u").exec("\u{1F438}"), + ["\u{1F438}"]); + +// no unicode flag +assertEqArray(new RegExp("\uD83D\uDC38", "").exec("\u{1F438}"), + ["\u{1F438}"]); + +// escaped(lead) +assertEq(new RegExp("\\uD83D\uDC38", "u").exec("\u{1F438}"), + null); +assertEq(new RegExp("\\u{D83D}\uDC38", "u").exec("\u{1F438}"), + null); + +// escaped(trail) +assertEq(new RegExp("\uD83D\\uDC38", "u").exec("\u{1F438}"), + null); +assertEq(new RegExp("\uD83D\\u{DC38}", "u").exec("\u{1F438}"), + null); + +// escaped(lead), no unicode flag +assertEqArray(new RegExp("\\uD83D\uDC38", "").exec("\u{1F438}"), + ["\u{1F438}"]); + +// escaped(trail), no unicode flag +assertEqArray(new RegExp("\uD83D\\uDC38", "").exec("\u{1F438}"), + ["\u{1F438}"]); + +// ==== ? ==== + +assertEqArray(eval(`/\uD83D\uDC38?/u`).exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(eval(`/\uD83D\uDC38?/u`).exec(""), + [""]); + +assertEqArray(eval(`/\uD83D\uDC38?/u`).exec("\uD83D"), + [""]); + +// no unicode flag +assertEqArray(eval(`/\uD83D\uDC38?/`).exec("\u{1F438}"), + ["\u{1F438}"]); +assertEq(eval(`/\uD83D\uDC38?/`).exec(""), + null); + +assertEqArray(eval(`/\uD83D\uDC38?/`).exec("\uD83D"), + ["\uD83D"]); + +// escaped (lead) +assertEq(eval(`/\\uD83D\uDC38?/u`).exec("\u{1F438}"), + null); +assertEq(eval(`/\\uD83D\uDC38?/u`).exec(""), + null); + +assertEqArray(eval(`/\\uD83D\uDC38?/u`).exec("\uD83D"), + ["\uD83D"]); + +// escaped (trail) +assertEq(eval(`/\uD83D\\uDC38?/u`).exec("\u{1F438}"), + null); +assertEq(eval(`/\uD83D\\uDC38?/u`).exec(""), + null); + +assertEqArray(eval(`/\uD83D\\uDC38?/u`).exec("\uD83D"), + ["\uD83D"]); + +// escaped (lead), no unicode flag +assertEqArray(eval(`/\\uD83D\uDC38?/`).exec("\u{1F438}"), + ["\u{1F438}"]); +assertEq(eval(`/\\uD83D\uDC38?/`).exec(""), + null); + +assertEqArray(eval(`/\\uD83D\uDC38?/`).exec("\uD83D"), + ["\uD83D"]); + +// escaped (trail), no unicode flag +assertEqArray(eval(`/\uD83D\\uDC38?/`).exec("\u{1F438}"), + ["\u{1F438}"]); +assertEq(eval(`/\uD83D\\uDC38?/`).exec(""), + null); + +assertEqArray(eval(`/\uD83D\\uDC38?/`).exec("\uD83D"), + ["\uD83D"]); + +// ==== RegExp constructor, ? ==== + +assertEqArray(new RegExp("\uD83D\uDC38?", "u").exec("\u{1F438}"), + ["\u{1F438}"]); +assertEqArray(new RegExp("\uD83D\uDC38?", "u").exec(""), + [""]); + +assertEqArray(new RegExp("\uD83D\uDC38?", "u").exec("\uD83D"), + [""]); + +// no unicode flag +assertEqArray(new RegExp("\uD83D\uDC38?", "").exec("\u{1F438}"), + ["\u{1F438}"]); +assertEq(new RegExp("\uD83D\uDC38?", "").exec(""), + null); + +assertEqArray(new RegExp("\uD83D\uDC38?", "").exec("\uD83D"), + ["\uD83D"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/String/AdvanceStringIndex.js b/js/src/tests/ecma_6/String/AdvanceStringIndex.js new file mode 100644 index 000000000..fc4046603 --- /dev/null +++ b/js/src/tests/ecma_6/String/AdvanceStringIndex.js @@ -0,0 +1,43 @@ +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- AdvanceStringIndex in global match and replace."; + +print(BUGNUMBER + ": " + summary); + +// ==== String.prototype.match ==== + +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".match(/\uD83D|X|/gu), + ["", "", "X", "", ""]); +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".match(/\uDC38|X|/gu), + ["", "", "X", "", ""]); +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".match(/\uD83D\uDC38|X|/gu), + ["\uD83D\uDC38", "", "X", "", ""]); + +// ==== String.prototype.replace ==== + +// empty string replacement (optimized) +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".replace(/\uD83D|X|/gu, ""), + "\uD83D\uDC38\uD83D\uDC39\uD83D\uDC3A"); +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".replace(/\uDC38|X|/gu, ""), + "\uD83D\uDC38\uD83D\uDC39\uD83D\uDC3A"); +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".replace(/\uD83D\uDC38|X|/gu, ""), + "\uD83D\uDC39\uD83D\uDC3A"); + +// non-empty string replacement +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".replace(/\uD83D|X|/gu, "x"), + "x\uD83D\uDC38x\uD83D\uDC39xx\uD83D\uDC3Ax"); +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".replace(/\uDC38|X|/gu, "x"), + "x\uD83D\uDC38x\uD83D\uDC39xx\uD83D\uDC3Ax"); +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".replace(/\uD83D\uDC38|X|/gu, "x"), + "xx\uD83D\uDC39xx\uD83D\uDC3Ax"); + +// ==== String.prototype.split ==== + +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".split(/\uD83D|X|/u), + ["\uD83D\uDC38", "\uD83D\uDC39", "\uD83D\uDC3A"]); +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".split(/\uDC38|X|/u), + ["\uD83D\uDC38", "\uD83D\uDC39", "\uD83D\uDC3A"]); +assertEqArray("\uD83D\uDC38\uD83D\uDC39X\uD83D\uDC3A".split(/\uD83D\uDC38|X|/u), + ["", "\uD83D\uDC39", "\uD83D\uDC3A"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js b/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js index ea2494bda..9f6fd6467 100644 --- a/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js +++ b/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js @@ -18,26 +18,30 @@ print(BUGNUMBER + ": " + summary); * BEGIN TEST * **************/ -for (let x = 0; x < 9; ++x) +for (let x = 0; x < 9; ++x) { function q1() {} +} { - for (let x = 0; x < 9; ++x) + for (let x = 0; x < 9; ++x) { function q2() {} + } } function f1() { - for (let x = 0; x < 9; ++x) + for (let x = 0; x < 9; ++x) { function q3() {} + } } f1(); function f2() { { - for (let x = 0; x < 9; ++x) + for (let x = 0; x < 9; ++x) { function q4() {} + } } } f2(); diff --git a/js/src/tests/ecma_7/Math/Pow.js b/js/src/tests/ecma_7/Math/Pow.js index c7cba3391..7c1af8c88 100644 --- a/js/src/tests/ecma_7/Math/Pow.js +++ b/js/src/tests/ecma_7/Math/Pow.js @@ -9,8 +9,6 @@ var summary = "Implement the exponentiation operator"; print(BUGNUMBER + ": " + summary); -var test = ` - // Constant folding assertEq(2 ** 2 ** 3, 256); assertEq(1 ** 1 ** 4, 1); @@ -53,6 +51,28 @@ assertEq(a**/**b**/c/**/**/**d**/e, 16); // Two stars separated should not parse as exp operator assertThrows(function() { return Reflect.parse("2 * * 3"); }, SyntaxError); +// Left-hand side expression must not be a unary expression. +for (let unaryOp of ["delete", "typeof", "void", "+", "-", "!", "~"]) { + assertThrowsInstanceOf(() => eval(unaryOp + " a ** 2"), SyntaxError); + assertThrowsInstanceOf(() => eval(unaryOp + " " + unaryOp + " a ** 2"), SyntaxError); +} + +// Test the other |delete| operators (DELETENAME and DELETEEXPR are already tested above). +assertThrowsInstanceOf(() => eval("delete a.name ** 2"), SyntaxError); +assertThrowsInstanceOf(() => eval("delete a[0] ** 2"), SyntaxError); + +// Unary expression lhs is valid if parenthesized. +for (let unaryOp of ["delete", "void", "+", "-", "!", "~"]) { + let a = 0; + eval("(" + unaryOp + " a) ** 2"); + eval("(" + unaryOp + " " + unaryOp + " a) ** 2"); +} +{ + let a = {}; + (delete a.name) ** 2; + (delete a[0]) ** 2; +} + // Check if error propagation works var thrower = { get value() { @@ -93,26 +113,5 @@ assertEq(parseTree.body[0].expression.right.operator, "**"); assertEq(parseTree.body[0].expression.right.left.name, "b"); assertEq(parseTree.body[0].expression.right.right.name, "c"); - -function assertTrue(v) { - assertEq(v, true); -} - -function assertFalse(v) { - assertEq(v, false); -} -`; - -function exponentiationEnabled() { - try { - Function("1 ** 1"); - return true; - } catch (e if e instanceof SyntaxError) { } - return false; -} - -if (exponentiationEnabled()) - eval(test); - if (typeof reportCompare === "function") reportCompare(true, true); diff --git a/js/src/tests/js1_5/Regress/regress-326453.js b/js/src/tests/js1_5/Regress/regress-326453.js deleted file mode 100644 index c9e39f9d6..000000000 --- a/js/src/tests/js1_5/Regress/regress-326453.js +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Blake Kaplan - */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 326453; -var summary = 'Do not assert: while decompiling'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -function f() { with({})function g() { }; printStatus(); } - -printStatus(f.toString()); - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-245795.js b/js/src/tests/js1_5/extensions/regress-245795.js index af77f5419..a823fc2f6 100644 --- a/js/src/tests/js1_5/extensions/regress-245795.js +++ b/js/src/tests/js1_5/extensions/regress-245795.js @@ -12,23 +12,20 @@ var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); -if (typeof uneval != 'undefined') +function a() { - function a() - { - b = function() {}; - } - - var r = "function a() { b = function() {}; }"; - eval(uneval(a)); - - var v = a.toString().replace(/[ \n]+/g, ' '); - print(v) - - printStatus("[" + v + "]"); - - expect = r; - actual = v; - - reportCompare(expect, actual, summary); + b = function() {}; } + +var r = "function a() { b = function() {}; }"; +eval(uneval(a)); + +var v = a.toString().replace(/[ \n]+/g, ' '); +print(v) + +printStatus("[" + v + "]"); + +expect = r; +actual = v; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-336409-1.js b/js/src/tests/js1_5/extensions/regress-336409-1.js index 32dbb3633..929868117 100644 --- a/js/src/tests/js1_5/extensions/regress-336409-1.js +++ b/js/src/tests/js1_5/extensions/regress-336409-1.js @@ -8,7 +8,7 @@ var BUGNUMBER = 336409; var summary = 'Integer overflow in js_obj_toSource'; var actual = 'No Crash'; -var expect = 'No Crash'; +var expect = /(No Crash|InternalError: allocation size overflow|out of memory)/; printBugNumber(BUGNUMBER); printStatus (summary); @@ -42,9 +42,8 @@ try } catch(ex) { - expect = 'InternalError: allocation size overflow'; actual = ex + ''; print(actual); } -reportCompare(expect, actual, summary); +reportMatch(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-336410-1.js b/js/src/tests/js1_5/extensions/regress-336410-1.js index 5362d0d94..be4afe807 100644 --- a/js/src/tests/js1_5/extensions/regress-336410-1.js +++ b/js/src/tests/js1_5/extensions/regress-336410-1.js @@ -8,7 +8,7 @@ var BUGNUMBER = 336410; var summary = 'Integer overflow in array_toSource'; var actual = 'No Crash'; -var expect = 'No Crash'; +var expect = /(No Crash|InternalError: allocation size overflow|out of memory)/; printBugNumber(BUGNUMBER); printStatus (summary); @@ -42,9 +42,8 @@ try } catch(ex) { - expect = '\(InternalError: allocation size overflow|out of memory\)'; actual = ex + ''; print(actual); } -reportCompare(expect, actual, summary); +reportMatch(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-406572.js b/js/src/tests/js1_5/extensions/regress-406572.js index e93448c8d..4911d05bc 100644 --- a/js/src/tests/js1_5/extensions/regress-406572.js +++ b/js/src/tests/js1_5/extensions/regress-406572.js @@ -22,12 +22,14 @@ if (typeof window != 'undefined') window = 1; reportCompare(windowString, String(window), "window should be readonly"); - actual = ""; // We should reach this line, and throw an exception after it - if (1) function window() { return 1; } - actual = "FAIL: this line should never be reached"; + // We should reach this line without throwing. Annex B means the + // block-scoped function above gets an assignment to 'window' in the + // nearest 'var' environment, but since 'window' is read-only, the + // assignment silently fails. + actual = ""; // The test harness might rely on window having its original value: // restore it. diff --git a/js/src/tests/js1_8_1/regress/regress-452498-185.js b/js/src/tests/js1_8_1/regress/regress-452498-185.js index f8ca37f6f..1c88dc348 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-185.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-185.js @@ -21,7 +21,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = 'TypeError: redeclaration of var e'; + expect = 'SyntaxError: redeclaration of var e'; try { eval('{ var e = 3; let e = ""; } print(typeof e);'); diff --git a/js/src/tests/js1_8_1/regress/regress-452498-191.js b/js/src/tests/js1_8_1/regress/regress-452498-191.js index 4d3516df2..1628d7f87 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-191.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-191.js @@ -32,7 +32,7 @@ function test() } reportCompare(expect, actual, summary); - expect = 'TypeError: redeclaration of let x'; + expect = 'SyntaxError: redeclaration of let x'; try { eval('{ let x; {var x;} }'); diff --git a/js/src/tests/js1_8_5/reflect-parse/classes.js b/js/src/tests/js1_8_5/reflect-parse/classes.js index 10bedf43d..d94ce9efb 100644 --- a/js/src/tests/js1_8_5/reflect-parse/classes.js +++ b/js/src/tests/js1_8_5/reflect-parse/classes.js @@ -199,15 +199,15 @@ function testClasses() { // Class statements bind lexically, so they should collide with other // in-block lexical bindings, but class expressions don't. let FooCtor = ctorWithName("Foo"); - assertError("{ let Foo; class Foo { constructor() { } } }", TypeError); + assertError("{ let Foo; class Foo { constructor() { } } }", SyntaxError); assertStmt("{ let Foo; (class Foo { constructor() { } }) }", blockStmt([letDecl([{id: ident("Foo"), init: null}]), exprStmt(classExpr(ident("Foo"), null, [FooCtor]))])); - assertError("{ const Foo = 0; class Foo { constructor() { } } }", TypeError); + assertError("{ const Foo = 0; class Foo { constructor() { } } }", SyntaxError); assertStmt("{ const Foo = 0; (class Foo { constructor() { } }) }", blockStmt([constDecl([{id: ident("Foo"), init: lit(0)}]), exprStmt(classExpr(ident("Foo"), null, [FooCtor]))])); - assertError("{ class Foo { constructor() { } } class Foo { constructor() { } } }", TypeError); + assertError("{ class Foo { constructor() { } } class Foo { constructor() { } } }", SyntaxError); assertStmt(`{ (class Foo { constructor() { } diff --git a/js/src/tests/js1_8_5/reflect-parse/declarations.js b/js/src/tests/js1_8_5/reflect-parse/declarations.js index 86f524fd3..480f58777 100644 --- a/js/src/tests/js1_8_5/reflect-parse/declarations.js +++ b/js/src/tests/js1_8_5/reflect-parse/declarations.js @@ -4,7 +4,7 @@ function test() { // Bug 632056: constant-folding program([exprStmt(ident("f")), ifStmt(lit(1), - funDecl(ident("f"), [], blockStmt([])), + blockStmt([funDecl(ident("f"), [], blockStmt([]))]), null)]).assert(Reflect.parse("f; if (1) function f(){}")); // declarations @@ -86,4 +86,12 @@ assertProg("f.p = 1; var f; f.p; function f(){}", funDecl(ident("f"), [], blockStmt([]))]); } +assertBlockStmt("{ function f(x) {} }", + blockStmt([funDecl(ident("f"), [ident("x")], blockStmt([]))])); + +// Annex B semantics should not change parse tree. +assertBlockStmt("{ let f; { function f(x) {} } }", + blockStmt([letDecl([{ id: ident("f"), init: null }]), + blockStmt([funDecl(ident("f"), [ident("x")], blockStmt([]))])])); + runtest(test); diff --git a/js/src/vm/CaseFolding.txt b/js/src/vm/CaseFolding.txt new file mode 100644 index 000000000..0197a6c40 --- /dev/null +++ b/js/src/vm/CaseFolding.txt @@ -0,0 +1,1414 @@ +# CaseFolding-8.0.0.txt +# Date: 2015-01-13, 18:16:36 GMT [MD] +# +# Unicode Character Database +# Copyright (c) 1991-2015 Unicode, Inc. +# For terms of use, see http://www.unicode.org/terms_of_use.html +# For documentation, see http://www.unicode.org/reports/tr44/ +# +# Case Folding Properties +# +# This file is a supplement to the UnicodeData file. +# It provides a case folding mapping generated from the Unicode Character Database. +# If all characters are mapped according to the full mapping below, then +# case differences (according to UnicodeData.txt and SpecialCasing.txt) +# are eliminated. +# +# The data supports both implementations that require simple case foldings +# (where string lengths don't change), and implementations that allow full case folding +# (where string lengths may grow). Note that where they can be supported, the +# full case foldings are superior: for example, they allow "MASSE" and "Maße" to match. +# +# All code points not listed in this file map to themselves. +# +# NOTE: case folding does not preserve normalization formats! +# +# For information on case folding, including how to have case folding +# preserve normalization formats, see Section 3.13 Default Case Algorithms in +# The Unicode Standard. +# +# ================================================================================ +# Format +# ================================================================================ +# The entries in this file are in the following machine-readable format: +# +# ; ; ; # +# +# The status field is: +# C: common case folding, common mappings shared by both simple and full mappings. +# F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. +# S: simple case folding, mappings to single characters where different from F. +# T: special case for uppercase I and dotted uppercase I +# - For non-Turkic languages, this mapping is normally not used. +# - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. +# Note that the Turkic mappings do not maintain canonical equivalence without additional processing. +# See the discussions of case mapping in the Unicode Standard for more information. +# +# Usage: +# A. To do a simple case folding, use the mappings with status C + S. +# B. To do a full case folding, use the mappings with status C + F. +# +# The mappings with status T can be used or omitted depending on the desired case-folding +# behavior. (The default option is to exclude them.) +# +# ================================================================= + +# Property: Case_Folding + +# All code points not explicitly listed for Case_Folding +# have the value C for the status field, and the code point itself for the mapping field. + +# ================================================================= +0041; C; 0061; # LATIN CAPITAL LETTER A +0042; C; 0062; # LATIN CAPITAL LETTER B +0043; C; 0063; # LATIN CAPITAL LETTER C +0044; C; 0064; # LATIN CAPITAL LETTER D +0045; C; 0065; # LATIN CAPITAL LETTER E +0046; C; 0066; # LATIN CAPITAL LETTER F +0047; C; 0067; # LATIN CAPITAL LETTER G +0048; C; 0068; # LATIN CAPITAL LETTER H +0049; C; 0069; # LATIN CAPITAL LETTER I +0049; T; 0131; # LATIN CAPITAL LETTER I +004A; C; 006A; # LATIN CAPITAL LETTER J +004B; C; 006B; # LATIN CAPITAL LETTER K +004C; C; 006C; # LATIN CAPITAL LETTER L +004D; C; 006D; # LATIN CAPITAL LETTER M +004E; C; 006E; # LATIN CAPITAL LETTER N +004F; C; 006F; # LATIN CAPITAL LETTER O +0050; C; 0070; # LATIN CAPITAL LETTER P +0051; C; 0071; # LATIN CAPITAL LETTER Q +0052; C; 0072; # LATIN CAPITAL LETTER R +0053; C; 0073; # LATIN CAPITAL LETTER S +0054; C; 0074; # LATIN CAPITAL LETTER T +0055; C; 0075; # LATIN CAPITAL LETTER U +0056; C; 0076; # LATIN CAPITAL LETTER V +0057; C; 0077; # LATIN CAPITAL LETTER W +0058; C; 0078; # LATIN CAPITAL LETTER X +0059; C; 0079; # LATIN CAPITAL LETTER Y +005A; C; 007A; # LATIN CAPITAL LETTER Z +00B5; C; 03BC; # MICRO SIGN +00C0; C; 00E0; # LATIN CAPITAL LETTER A WITH GRAVE +00C1; C; 00E1; # LATIN CAPITAL LETTER A WITH ACUTE +00C2; C; 00E2; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX +00C3; C; 00E3; # LATIN CAPITAL LETTER A WITH TILDE +00C4; C; 00E4; # LATIN CAPITAL LETTER A WITH DIAERESIS +00C5; C; 00E5; # LATIN CAPITAL LETTER A WITH RING ABOVE +00C6; C; 00E6; # LATIN CAPITAL LETTER AE +00C7; C; 00E7; # LATIN CAPITAL LETTER C WITH CEDILLA +00C8; C; 00E8; # LATIN CAPITAL LETTER E WITH GRAVE +00C9; C; 00E9; # LATIN CAPITAL LETTER E WITH ACUTE +00CA; C; 00EA; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX +00CB; C; 00EB; # LATIN CAPITAL LETTER E WITH DIAERESIS +00CC; C; 00EC; # LATIN CAPITAL LETTER I WITH GRAVE +00CD; C; 00ED; # LATIN CAPITAL LETTER I WITH ACUTE +00CE; C; 00EE; # LATIN CAPITAL LETTER I WITH CIRCUMFLEX +00CF; C; 00EF; # LATIN CAPITAL LETTER I WITH DIAERESIS +00D0; C; 00F0; # LATIN CAPITAL LETTER ETH +00D1; C; 00F1; # LATIN CAPITAL LETTER N WITH TILDE +00D2; C; 00F2; # LATIN CAPITAL LETTER O WITH GRAVE +00D3; C; 00F3; # LATIN CAPITAL LETTER O WITH ACUTE +00D4; C; 00F4; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX +00D5; C; 00F5; # LATIN CAPITAL LETTER O WITH TILDE +00D6; C; 00F6; # LATIN CAPITAL LETTER O WITH DIAERESIS +00D8; C; 00F8; # LATIN CAPITAL LETTER O WITH STROKE +00D9; C; 00F9; # LATIN CAPITAL LETTER U WITH GRAVE +00DA; C; 00FA; # LATIN CAPITAL LETTER U WITH ACUTE +00DB; C; 00FB; # LATIN CAPITAL LETTER U WITH CIRCUMFLEX +00DC; C; 00FC; # LATIN CAPITAL LETTER U WITH DIAERESIS +00DD; C; 00FD; # LATIN CAPITAL LETTER Y WITH ACUTE +00DE; C; 00FE; # LATIN CAPITAL LETTER THORN +00DF; F; 0073 0073; # LATIN SMALL LETTER SHARP S +0100; C; 0101; # LATIN CAPITAL LETTER A WITH MACRON +0102; C; 0103; # LATIN CAPITAL LETTER A WITH BREVE +0104; C; 0105; # LATIN CAPITAL LETTER A WITH OGONEK +0106; C; 0107; # LATIN CAPITAL LETTER C WITH ACUTE +0108; C; 0109; # LATIN CAPITAL LETTER C WITH CIRCUMFLEX +010A; C; 010B; # LATIN CAPITAL LETTER C WITH DOT ABOVE +010C; C; 010D; # LATIN CAPITAL LETTER C WITH CARON +010E; C; 010F; # LATIN CAPITAL LETTER D WITH CARON +0110; C; 0111; # LATIN CAPITAL LETTER D WITH STROKE +0112; C; 0113; # LATIN CAPITAL LETTER E WITH MACRON +0114; C; 0115; # LATIN CAPITAL LETTER E WITH BREVE +0116; C; 0117; # LATIN CAPITAL LETTER E WITH DOT ABOVE +0118; C; 0119; # LATIN CAPITAL LETTER E WITH OGONEK +011A; C; 011B; # LATIN CAPITAL LETTER E WITH CARON +011C; C; 011D; # LATIN CAPITAL LETTER G WITH CIRCUMFLEX +011E; C; 011F; # LATIN CAPITAL LETTER G WITH BREVE +0120; C; 0121; # LATIN CAPITAL LETTER G WITH DOT ABOVE +0122; C; 0123; # LATIN CAPITAL LETTER G WITH CEDILLA +0124; C; 0125; # LATIN CAPITAL LETTER H WITH CIRCUMFLEX +0126; C; 0127; # LATIN CAPITAL LETTER H WITH STROKE +0128; C; 0129; # LATIN CAPITAL LETTER I WITH TILDE +012A; C; 012B; # LATIN CAPITAL LETTER I WITH MACRON +012C; C; 012D; # LATIN CAPITAL LETTER I WITH BREVE +012E; C; 012F; # LATIN CAPITAL LETTER I WITH OGONEK +0130; F; 0069 0307; # LATIN CAPITAL LETTER I WITH DOT ABOVE +0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE +0132; C; 0133; # LATIN CAPITAL LIGATURE IJ +0134; C; 0135; # LATIN CAPITAL LETTER J WITH CIRCUMFLEX +0136; C; 0137; # LATIN CAPITAL LETTER K WITH CEDILLA +0139; C; 013A; # LATIN CAPITAL LETTER L WITH ACUTE +013B; C; 013C; # LATIN CAPITAL LETTER L WITH CEDILLA +013D; C; 013E; # LATIN CAPITAL LETTER L WITH CARON +013F; C; 0140; # LATIN CAPITAL LETTER L WITH MIDDLE DOT +0141; C; 0142; # LATIN CAPITAL LETTER L WITH STROKE +0143; C; 0144; # LATIN CAPITAL LETTER N WITH ACUTE +0145; C; 0146; # LATIN CAPITAL LETTER N WITH CEDILLA +0147; C; 0148; # LATIN CAPITAL LETTER N WITH CARON +0149; F; 02BC 006E; # LATIN SMALL LETTER N PRECEDED BY APOSTROPHE +014A; C; 014B; # LATIN CAPITAL LETTER ENG +014C; C; 014D; # LATIN CAPITAL LETTER O WITH MACRON +014E; C; 014F; # LATIN CAPITAL LETTER O WITH BREVE +0150; C; 0151; # LATIN CAPITAL LETTER O WITH DOUBLE ACUTE +0152; C; 0153; # LATIN CAPITAL LIGATURE OE +0154; C; 0155; # LATIN CAPITAL LETTER R WITH ACUTE +0156; C; 0157; # LATIN CAPITAL LETTER R WITH CEDILLA +0158; C; 0159; # LATIN CAPITAL LETTER R WITH CARON +015A; C; 015B; # LATIN CAPITAL LETTER S WITH ACUTE +015C; C; 015D; # LATIN CAPITAL LETTER S WITH CIRCUMFLEX +015E; C; 015F; # LATIN CAPITAL LETTER S WITH CEDILLA +0160; C; 0161; # LATIN CAPITAL LETTER S WITH CARON +0162; C; 0163; # LATIN CAPITAL LETTER T WITH CEDILLA +0164; C; 0165; # LATIN CAPITAL LETTER T WITH CARON +0166; C; 0167; # LATIN CAPITAL LETTER T WITH STROKE +0168; C; 0169; # LATIN CAPITAL LETTER U WITH TILDE +016A; C; 016B; # LATIN CAPITAL LETTER U WITH MACRON +016C; C; 016D; # LATIN CAPITAL LETTER U WITH BREVE +016E; C; 016F; # LATIN CAPITAL LETTER U WITH RING ABOVE +0170; C; 0171; # LATIN CAPITAL LETTER U WITH DOUBLE ACUTE +0172; C; 0173; # LATIN CAPITAL LETTER U WITH OGONEK +0174; C; 0175; # LATIN CAPITAL LETTER W WITH CIRCUMFLEX +0176; C; 0177; # LATIN CAPITAL LETTER Y WITH CIRCUMFLEX +0178; C; 00FF; # LATIN CAPITAL LETTER Y WITH DIAERESIS +0179; C; 017A; # LATIN CAPITAL LETTER Z WITH ACUTE +017B; C; 017C; # LATIN CAPITAL LETTER Z WITH DOT ABOVE +017D; C; 017E; # LATIN CAPITAL LETTER Z WITH CARON +017F; C; 0073; # LATIN SMALL LETTER LONG S +0181; C; 0253; # LATIN CAPITAL LETTER B WITH HOOK +0182; C; 0183; # LATIN CAPITAL LETTER B WITH TOPBAR +0184; C; 0185; # LATIN CAPITAL LETTER TONE SIX +0186; C; 0254; # LATIN CAPITAL LETTER OPEN O +0187; C; 0188; # LATIN CAPITAL LETTER C WITH HOOK +0189; C; 0256; # LATIN CAPITAL LETTER AFRICAN D +018A; C; 0257; # LATIN CAPITAL LETTER D WITH HOOK +018B; C; 018C; # LATIN CAPITAL LETTER D WITH TOPBAR +018E; C; 01DD; # LATIN CAPITAL LETTER REVERSED E +018F; C; 0259; # LATIN CAPITAL LETTER SCHWA +0190; C; 025B; # LATIN CAPITAL LETTER OPEN E +0191; C; 0192; # LATIN CAPITAL LETTER F WITH HOOK +0193; C; 0260; # LATIN CAPITAL LETTER G WITH HOOK +0194; C; 0263; # LATIN CAPITAL LETTER GAMMA +0196; C; 0269; # LATIN CAPITAL LETTER IOTA +0197; C; 0268; # LATIN CAPITAL LETTER I WITH STROKE +0198; C; 0199; # LATIN CAPITAL LETTER K WITH HOOK +019C; C; 026F; # LATIN CAPITAL LETTER TURNED M +019D; C; 0272; # LATIN CAPITAL LETTER N WITH LEFT HOOK +019F; C; 0275; # LATIN CAPITAL LETTER O WITH MIDDLE TILDE +01A0; C; 01A1; # LATIN CAPITAL LETTER O WITH HORN +01A2; C; 01A3; # LATIN CAPITAL LETTER OI +01A4; C; 01A5; # LATIN CAPITAL LETTER P WITH HOOK +01A6; C; 0280; # LATIN LETTER YR +01A7; C; 01A8; # LATIN CAPITAL LETTER TONE TWO +01A9; C; 0283; # LATIN CAPITAL LETTER ESH +01AC; C; 01AD; # LATIN CAPITAL LETTER T WITH HOOK +01AE; C; 0288; # LATIN CAPITAL LETTER T WITH RETROFLEX HOOK +01AF; C; 01B0; # LATIN CAPITAL LETTER U WITH HORN +01B1; C; 028A; # LATIN CAPITAL LETTER UPSILON +01B2; C; 028B; # LATIN CAPITAL LETTER V WITH HOOK +01B3; C; 01B4; # LATIN CAPITAL LETTER Y WITH HOOK +01B5; C; 01B6; # LATIN CAPITAL LETTER Z WITH STROKE +01B7; C; 0292; # LATIN CAPITAL LETTER EZH +01B8; C; 01B9; # LATIN CAPITAL LETTER EZH REVERSED +01BC; C; 01BD; # LATIN CAPITAL LETTER TONE FIVE +01C4; C; 01C6; # LATIN CAPITAL LETTER DZ WITH CARON +01C5; C; 01C6; # LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON +01C7; C; 01C9; # LATIN CAPITAL LETTER LJ +01C8; C; 01C9; # LATIN CAPITAL LETTER L WITH SMALL LETTER J +01CA; C; 01CC; # LATIN CAPITAL LETTER NJ +01CB; C; 01CC; # LATIN CAPITAL LETTER N WITH SMALL LETTER J +01CD; C; 01CE; # LATIN CAPITAL LETTER A WITH CARON +01CF; C; 01D0; # LATIN CAPITAL LETTER I WITH CARON +01D1; C; 01D2; # LATIN CAPITAL LETTER O WITH CARON +01D3; C; 01D4; # LATIN CAPITAL LETTER U WITH CARON +01D5; C; 01D6; # LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON +01D7; C; 01D8; # LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE +01D9; C; 01DA; # LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON +01DB; C; 01DC; # LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE +01DE; C; 01DF; # LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON +01E0; C; 01E1; # LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON +01E2; C; 01E3; # LATIN CAPITAL LETTER AE WITH MACRON +01E4; C; 01E5; # LATIN CAPITAL LETTER G WITH STROKE +01E6; C; 01E7; # LATIN CAPITAL LETTER G WITH CARON +01E8; C; 01E9; # LATIN CAPITAL LETTER K WITH CARON +01EA; C; 01EB; # LATIN CAPITAL LETTER O WITH OGONEK +01EC; C; 01ED; # LATIN CAPITAL LETTER O WITH OGONEK AND MACRON +01EE; C; 01EF; # LATIN CAPITAL LETTER EZH WITH CARON +01F0; F; 006A 030C; # LATIN SMALL LETTER J WITH CARON +01F1; C; 01F3; # LATIN CAPITAL LETTER DZ +01F2; C; 01F3; # LATIN CAPITAL LETTER D WITH SMALL LETTER Z +01F4; C; 01F5; # LATIN CAPITAL LETTER G WITH ACUTE +01F6; C; 0195; # LATIN CAPITAL LETTER HWAIR +01F7; C; 01BF; # LATIN CAPITAL LETTER WYNN +01F8; C; 01F9; # LATIN CAPITAL LETTER N WITH GRAVE +01FA; C; 01FB; # LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE +01FC; C; 01FD; # LATIN CAPITAL LETTER AE WITH ACUTE +01FE; C; 01FF; # LATIN CAPITAL LETTER O WITH STROKE AND ACUTE +0200; C; 0201; # LATIN CAPITAL LETTER A WITH DOUBLE GRAVE +0202; C; 0203; # LATIN CAPITAL LETTER A WITH INVERTED BREVE +0204; C; 0205; # LATIN CAPITAL LETTER E WITH DOUBLE GRAVE +0206; C; 0207; # LATIN CAPITAL LETTER E WITH INVERTED BREVE +0208; C; 0209; # LATIN CAPITAL LETTER I WITH DOUBLE GRAVE +020A; C; 020B; # LATIN CAPITAL LETTER I WITH INVERTED BREVE +020C; C; 020D; # LATIN CAPITAL LETTER O WITH DOUBLE GRAVE +020E; C; 020F; # LATIN CAPITAL LETTER O WITH INVERTED BREVE +0210; C; 0211; # LATIN CAPITAL LETTER R WITH DOUBLE GRAVE +0212; C; 0213; # LATIN CAPITAL LETTER R WITH INVERTED BREVE +0214; C; 0215; # LATIN CAPITAL LETTER U WITH DOUBLE GRAVE +0216; C; 0217; # LATIN CAPITAL LETTER U WITH INVERTED BREVE +0218; C; 0219; # LATIN CAPITAL LETTER S WITH COMMA BELOW +021A; C; 021B; # LATIN CAPITAL LETTER T WITH COMMA BELOW +021C; C; 021D; # LATIN CAPITAL LETTER YOGH +021E; C; 021F; # LATIN CAPITAL LETTER H WITH CARON +0220; C; 019E; # LATIN CAPITAL LETTER N WITH LONG RIGHT LEG +0222; C; 0223; # LATIN CAPITAL LETTER OU +0224; C; 0225; # LATIN CAPITAL LETTER Z WITH HOOK +0226; C; 0227; # LATIN CAPITAL LETTER A WITH DOT ABOVE +0228; C; 0229; # LATIN CAPITAL LETTER E WITH CEDILLA +022A; C; 022B; # LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON +022C; C; 022D; # LATIN CAPITAL LETTER O WITH TILDE AND MACRON +022E; C; 022F; # LATIN CAPITAL LETTER O WITH DOT ABOVE +0230; C; 0231; # LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON +0232; C; 0233; # LATIN CAPITAL LETTER Y WITH MACRON +023A; C; 2C65; # LATIN CAPITAL LETTER A WITH STROKE +023B; C; 023C; # LATIN CAPITAL LETTER C WITH STROKE +023D; C; 019A; # LATIN CAPITAL LETTER L WITH BAR +023E; C; 2C66; # LATIN CAPITAL LETTER T WITH DIAGONAL STROKE +0241; C; 0242; # LATIN CAPITAL LETTER GLOTTAL STOP +0243; C; 0180; # LATIN CAPITAL LETTER B WITH STROKE +0244; C; 0289; # LATIN CAPITAL LETTER U BAR +0245; C; 028C; # LATIN CAPITAL LETTER TURNED V +0246; C; 0247; # LATIN CAPITAL LETTER E WITH STROKE +0248; C; 0249; # LATIN CAPITAL LETTER J WITH STROKE +024A; C; 024B; # LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL +024C; C; 024D; # LATIN CAPITAL LETTER R WITH STROKE +024E; C; 024F; # LATIN CAPITAL LETTER Y WITH STROKE +0345; C; 03B9; # COMBINING GREEK YPOGEGRAMMENI +0370; C; 0371; # GREEK CAPITAL LETTER HETA +0372; C; 0373; # GREEK CAPITAL LETTER ARCHAIC SAMPI +0376; C; 0377; # GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA +037F; C; 03F3; # GREEK CAPITAL LETTER YOT +0386; C; 03AC; # GREEK CAPITAL LETTER ALPHA WITH TONOS +0388; C; 03AD; # GREEK CAPITAL LETTER EPSILON WITH TONOS +0389; C; 03AE; # GREEK CAPITAL LETTER ETA WITH TONOS +038A; C; 03AF; # GREEK CAPITAL LETTER IOTA WITH TONOS +038C; C; 03CC; # GREEK CAPITAL LETTER OMICRON WITH TONOS +038E; C; 03CD; # GREEK CAPITAL LETTER UPSILON WITH TONOS +038F; C; 03CE; # GREEK CAPITAL LETTER OMEGA WITH TONOS +0390; F; 03B9 0308 0301; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS +0391; C; 03B1; # GREEK CAPITAL LETTER ALPHA +0392; C; 03B2; # GREEK CAPITAL LETTER BETA +0393; C; 03B3; # GREEK CAPITAL LETTER GAMMA +0394; C; 03B4; # GREEK CAPITAL LETTER DELTA +0395; C; 03B5; # GREEK CAPITAL LETTER EPSILON +0396; C; 03B6; # GREEK CAPITAL LETTER ZETA +0397; C; 03B7; # GREEK CAPITAL LETTER ETA +0398; C; 03B8; # GREEK CAPITAL LETTER THETA +0399; C; 03B9; # GREEK CAPITAL LETTER IOTA +039A; C; 03BA; # GREEK CAPITAL LETTER KAPPA +039B; C; 03BB; # GREEK CAPITAL LETTER LAMDA +039C; C; 03BC; # GREEK CAPITAL LETTER MU +039D; C; 03BD; # GREEK CAPITAL LETTER NU +039E; C; 03BE; # GREEK CAPITAL LETTER XI +039F; C; 03BF; # GREEK CAPITAL LETTER OMICRON +03A0; C; 03C0; # GREEK CAPITAL LETTER PI +03A1; C; 03C1; # GREEK CAPITAL LETTER RHO +03A3; C; 03C3; # GREEK CAPITAL LETTER SIGMA +03A4; C; 03C4; # GREEK CAPITAL LETTER TAU +03A5; C; 03C5; # GREEK CAPITAL LETTER UPSILON +03A6; C; 03C6; # GREEK CAPITAL LETTER PHI +03A7; C; 03C7; # GREEK CAPITAL LETTER CHI +03A8; C; 03C8; # GREEK CAPITAL LETTER PSI +03A9; C; 03C9; # GREEK CAPITAL LETTER OMEGA +03AA; C; 03CA; # GREEK CAPITAL LETTER IOTA WITH DIALYTIKA +03AB; C; 03CB; # GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA +03B0; F; 03C5 0308 0301; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS +03C2; C; 03C3; # GREEK SMALL LETTER FINAL SIGMA +03CF; C; 03D7; # GREEK CAPITAL KAI SYMBOL +03D0; C; 03B2; # GREEK BETA SYMBOL +03D1; C; 03B8; # GREEK THETA SYMBOL +03D5; C; 03C6; # GREEK PHI SYMBOL +03D6; C; 03C0; # GREEK PI SYMBOL +03D8; C; 03D9; # GREEK LETTER ARCHAIC KOPPA +03DA; C; 03DB; # GREEK LETTER STIGMA +03DC; C; 03DD; # GREEK LETTER DIGAMMA +03DE; C; 03DF; # GREEK LETTER KOPPA +03E0; C; 03E1; # GREEK LETTER SAMPI +03E2; C; 03E3; # COPTIC CAPITAL LETTER SHEI +03E4; C; 03E5; # COPTIC CAPITAL LETTER FEI +03E6; C; 03E7; # COPTIC CAPITAL LETTER KHEI +03E8; C; 03E9; # COPTIC CAPITAL LETTER HORI +03EA; C; 03EB; # COPTIC CAPITAL LETTER GANGIA +03EC; C; 03ED; # COPTIC CAPITAL LETTER SHIMA +03EE; C; 03EF; # COPTIC CAPITAL LETTER DEI +03F0; C; 03BA; # GREEK KAPPA SYMBOL +03F1; C; 03C1; # GREEK RHO SYMBOL +03F4; C; 03B8; # GREEK CAPITAL THETA SYMBOL +03F5; C; 03B5; # GREEK LUNATE EPSILON SYMBOL +03F7; C; 03F8; # GREEK CAPITAL LETTER SHO +03F9; C; 03F2; # GREEK CAPITAL LUNATE SIGMA SYMBOL +03FA; C; 03FB; # GREEK CAPITAL LETTER SAN +03FD; C; 037B; # GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL +03FE; C; 037C; # GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL +03FF; C; 037D; # GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL +0400; C; 0450; # CYRILLIC CAPITAL LETTER IE WITH GRAVE +0401; C; 0451; # CYRILLIC CAPITAL LETTER IO +0402; C; 0452; # CYRILLIC CAPITAL LETTER DJE +0403; C; 0453; # CYRILLIC CAPITAL LETTER GJE +0404; C; 0454; # CYRILLIC CAPITAL LETTER UKRAINIAN IE +0405; C; 0455; # CYRILLIC CAPITAL LETTER DZE +0406; C; 0456; # CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I +0407; C; 0457; # CYRILLIC CAPITAL LETTER YI +0408; C; 0458; # CYRILLIC CAPITAL LETTER JE +0409; C; 0459; # CYRILLIC CAPITAL LETTER LJE +040A; C; 045A; # CYRILLIC CAPITAL LETTER NJE +040B; C; 045B; # CYRILLIC CAPITAL LETTER TSHE +040C; C; 045C; # CYRILLIC CAPITAL LETTER KJE +040D; C; 045D; # CYRILLIC CAPITAL LETTER I WITH GRAVE +040E; C; 045E; # CYRILLIC CAPITAL LETTER SHORT U +040F; C; 045F; # CYRILLIC CAPITAL LETTER DZHE +0410; C; 0430; # CYRILLIC CAPITAL LETTER A +0411; C; 0431; # CYRILLIC CAPITAL LETTER BE +0412; C; 0432; # CYRILLIC CAPITAL LETTER VE +0413; C; 0433; # CYRILLIC CAPITAL LETTER GHE +0414; C; 0434; # CYRILLIC CAPITAL LETTER DE +0415; C; 0435; # CYRILLIC CAPITAL LETTER IE +0416; C; 0436; # CYRILLIC CAPITAL LETTER ZHE +0417; C; 0437; # CYRILLIC CAPITAL LETTER ZE +0418; C; 0438; # CYRILLIC CAPITAL LETTER I +0419; C; 0439; # CYRILLIC CAPITAL LETTER SHORT I +041A; C; 043A; # CYRILLIC CAPITAL LETTER KA +041B; C; 043B; # CYRILLIC CAPITAL LETTER EL +041C; C; 043C; # CYRILLIC CAPITAL LETTER EM +041D; C; 043D; # CYRILLIC CAPITAL LETTER EN +041E; C; 043E; # CYRILLIC CAPITAL LETTER O +041F; C; 043F; # CYRILLIC CAPITAL LETTER PE +0420; C; 0440; # CYRILLIC CAPITAL LETTER ER +0421; C; 0441; # CYRILLIC CAPITAL LETTER ES +0422; C; 0442; # CYRILLIC CAPITAL LETTER TE +0423; C; 0443; # CYRILLIC CAPITAL LETTER U +0424; C; 0444; # CYRILLIC CAPITAL LETTER EF +0425; C; 0445; # CYRILLIC CAPITAL LETTER HA +0426; C; 0446; # CYRILLIC CAPITAL LETTER TSE +0427; C; 0447; # CYRILLIC CAPITAL LETTER CHE +0428; C; 0448; # CYRILLIC CAPITAL LETTER SHA +0429; C; 0449; # CYRILLIC CAPITAL LETTER SHCHA +042A; C; 044A; # CYRILLIC CAPITAL LETTER HARD SIGN +042B; C; 044B; # CYRILLIC CAPITAL LETTER YERU +042C; C; 044C; # CYRILLIC CAPITAL LETTER SOFT SIGN +042D; C; 044D; # CYRILLIC CAPITAL LETTER E +042E; C; 044E; # CYRILLIC CAPITAL LETTER YU +042F; C; 044F; # CYRILLIC CAPITAL LETTER YA +0460; C; 0461; # CYRILLIC CAPITAL LETTER OMEGA +0462; C; 0463; # CYRILLIC CAPITAL LETTER YAT +0464; C; 0465; # CYRILLIC CAPITAL LETTER IOTIFIED E +0466; C; 0467; # CYRILLIC CAPITAL LETTER LITTLE YUS +0468; C; 0469; # CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS +046A; C; 046B; # CYRILLIC CAPITAL LETTER BIG YUS +046C; C; 046D; # CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS +046E; C; 046F; # CYRILLIC CAPITAL LETTER KSI +0470; C; 0471; # CYRILLIC CAPITAL LETTER PSI +0472; C; 0473; # CYRILLIC CAPITAL LETTER FITA +0474; C; 0475; # CYRILLIC CAPITAL LETTER IZHITSA +0476; C; 0477; # CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT +0478; C; 0479; # CYRILLIC CAPITAL LETTER UK +047A; C; 047B; # CYRILLIC CAPITAL LETTER ROUND OMEGA +047C; C; 047D; # CYRILLIC CAPITAL LETTER OMEGA WITH TITLO +047E; C; 047F; # CYRILLIC CAPITAL LETTER OT +0480; C; 0481; # CYRILLIC CAPITAL LETTER KOPPA +048A; C; 048B; # CYRILLIC CAPITAL LETTER SHORT I WITH TAIL +048C; C; 048D; # CYRILLIC CAPITAL LETTER SEMISOFT SIGN +048E; C; 048F; # CYRILLIC CAPITAL LETTER ER WITH TICK +0490; C; 0491; # CYRILLIC CAPITAL LETTER GHE WITH UPTURN +0492; C; 0493; # CYRILLIC CAPITAL LETTER GHE WITH STROKE +0494; C; 0495; # CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK +0496; C; 0497; # CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER +0498; C; 0499; # CYRILLIC CAPITAL LETTER ZE WITH DESCENDER +049A; C; 049B; # CYRILLIC CAPITAL LETTER KA WITH DESCENDER +049C; C; 049D; # CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE +049E; C; 049F; # CYRILLIC CAPITAL LETTER KA WITH STROKE +04A0; C; 04A1; # CYRILLIC CAPITAL LETTER BASHKIR KA +04A2; C; 04A3; # CYRILLIC CAPITAL LETTER EN WITH DESCENDER +04A4; C; 04A5; # CYRILLIC CAPITAL LIGATURE EN GHE +04A6; C; 04A7; # CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK +04A8; C; 04A9; # CYRILLIC CAPITAL LETTER ABKHASIAN HA +04AA; C; 04AB; # CYRILLIC CAPITAL LETTER ES WITH DESCENDER +04AC; C; 04AD; # CYRILLIC CAPITAL LETTER TE WITH DESCENDER +04AE; C; 04AF; # CYRILLIC CAPITAL LETTER STRAIGHT U +04B0; C; 04B1; # CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE +04B2; C; 04B3; # CYRILLIC CAPITAL LETTER HA WITH DESCENDER +04B4; C; 04B5; # CYRILLIC CAPITAL LIGATURE TE TSE +04B6; C; 04B7; # CYRILLIC CAPITAL LETTER CHE WITH DESCENDER +04B8; C; 04B9; # CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE +04BA; C; 04BB; # CYRILLIC CAPITAL LETTER SHHA +04BC; C; 04BD; # CYRILLIC CAPITAL LETTER ABKHASIAN CHE +04BE; C; 04BF; # CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER +04C0; C; 04CF; # CYRILLIC LETTER PALOCHKA +04C1; C; 04C2; # CYRILLIC CAPITAL LETTER ZHE WITH BREVE +04C3; C; 04C4; # CYRILLIC CAPITAL LETTER KA WITH HOOK +04C5; C; 04C6; # CYRILLIC CAPITAL LETTER EL WITH TAIL +04C7; C; 04C8; # CYRILLIC CAPITAL LETTER EN WITH HOOK +04C9; C; 04CA; # CYRILLIC CAPITAL LETTER EN WITH TAIL +04CB; C; 04CC; # CYRILLIC CAPITAL LETTER KHAKASSIAN CHE +04CD; C; 04CE; # CYRILLIC CAPITAL LETTER EM WITH TAIL +04D0; C; 04D1; # CYRILLIC CAPITAL LETTER A WITH BREVE +04D2; C; 04D3; # CYRILLIC CAPITAL LETTER A WITH DIAERESIS +04D4; C; 04D5; # CYRILLIC CAPITAL LIGATURE A IE +04D6; C; 04D7; # CYRILLIC CAPITAL LETTER IE WITH BREVE +04D8; C; 04D9; # CYRILLIC CAPITAL LETTER SCHWA +04DA; C; 04DB; # CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS +04DC; C; 04DD; # CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS +04DE; C; 04DF; # CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS +04E0; C; 04E1; # CYRILLIC CAPITAL LETTER ABKHASIAN DZE +04E2; C; 04E3; # CYRILLIC CAPITAL LETTER I WITH MACRON +04E4; C; 04E5; # CYRILLIC CAPITAL LETTER I WITH DIAERESIS +04E6; C; 04E7; # CYRILLIC CAPITAL LETTER O WITH DIAERESIS +04E8; C; 04E9; # CYRILLIC CAPITAL LETTER BARRED O +04EA; C; 04EB; # CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS +04EC; C; 04ED; # CYRILLIC CAPITAL LETTER E WITH DIAERESIS +04EE; C; 04EF; # CYRILLIC CAPITAL LETTER U WITH MACRON +04F0; C; 04F1; # CYRILLIC CAPITAL LETTER U WITH DIAERESIS +04F2; C; 04F3; # CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE +04F4; C; 04F5; # CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS +04F6; C; 04F7; # CYRILLIC CAPITAL LETTER GHE WITH DESCENDER +04F8; C; 04F9; # CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS +04FA; C; 04FB; # CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK +04FC; C; 04FD; # CYRILLIC CAPITAL LETTER HA WITH HOOK +04FE; C; 04FF; # CYRILLIC CAPITAL LETTER HA WITH STROKE +0500; C; 0501; # CYRILLIC CAPITAL LETTER KOMI DE +0502; C; 0503; # CYRILLIC CAPITAL LETTER KOMI DJE +0504; C; 0505; # CYRILLIC CAPITAL LETTER KOMI ZJE +0506; C; 0507; # CYRILLIC CAPITAL LETTER KOMI DZJE +0508; C; 0509; # CYRILLIC CAPITAL LETTER KOMI LJE +050A; C; 050B; # CYRILLIC CAPITAL LETTER KOMI NJE +050C; C; 050D; # CYRILLIC CAPITAL LETTER KOMI SJE +050E; C; 050F; # CYRILLIC CAPITAL LETTER KOMI TJE +0510; C; 0511; # CYRILLIC CAPITAL LETTER REVERSED ZE +0512; C; 0513; # CYRILLIC CAPITAL LETTER EL WITH HOOK +0514; C; 0515; # CYRILLIC CAPITAL LETTER LHA +0516; C; 0517; # CYRILLIC CAPITAL LETTER RHA +0518; C; 0519; # CYRILLIC CAPITAL LETTER YAE +051A; C; 051B; # CYRILLIC CAPITAL LETTER QA +051C; C; 051D; # CYRILLIC CAPITAL LETTER WE +051E; C; 051F; # CYRILLIC CAPITAL LETTER ALEUT KA +0520; C; 0521; # CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK +0522; C; 0523; # CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK +0524; C; 0525; # CYRILLIC CAPITAL LETTER PE WITH DESCENDER +0526; C; 0527; # CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER +0528; C; 0529; # CYRILLIC CAPITAL LETTER EN WITH LEFT HOOK +052A; C; 052B; # CYRILLIC CAPITAL LETTER DZZHE +052C; C; 052D; # CYRILLIC CAPITAL LETTER DCHE +052E; C; 052F; # CYRILLIC CAPITAL LETTER EL WITH DESCENDER +0531; C; 0561; # ARMENIAN CAPITAL LETTER AYB +0532; C; 0562; # ARMENIAN CAPITAL LETTER BEN +0533; C; 0563; # ARMENIAN CAPITAL LETTER GIM +0534; C; 0564; # ARMENIAN CAPITAL LETTER DA +0535; C; 0565; # ARMENIAN CAPITAL LETTER ECH +0536; C; 0566; # ARMENIAN CAPITAL LETTER ZA +0537; C; 0567; # ARMENIAN CAPITAL LETTER EH +0538; C; 0568; # ARMENIAN CAPITAL LETTER ET +0539; C; 0569; # ARMENIAN CAPITAL LETTER TO +053A; C; 056A; # ARMENIAN CAPITAL LETTER ZHE +053B; C; 056B; # ARMENIAN CAPITAL LETTER INI +053C; C; 056C; # ARMENIAN CAPITAL LETTER LIWN +053D; C; 056D; # ARMENIAN CAPITAL LETTER XEH +053E; C; 056E; # ARMENIAN CAPITAL LETTER CA +053F; C; 056F; # ARMENIAN CAPITAL LETTER KEN +0540; C; 0570; # ARMENIAN CAPITAL LETTER HO +0541; C; 0571; # ARMENIAN CAPITAL LETTER JA +0542; C; 0572; # ARMENIAN CAPITAL LETTER GHAD +0543; C; 0573; # ARMENIAN CAPITAL LETTER CHEH +0544; C; 0574; # ARMENIAN CAPITAL LETTER MEN +0545; C; 0575; # ARMENIAN CAPITAL LETTER YI +0546; C; 0576; # ARMENIAN CAPITAL LETTER NOW +0547; C; 0577; # ARMENIAN CAPITAL LETTER SHA +0548; C; 0578; # ARMENIAN CAPITAL LETTER VO +0549; C; 0579; # ARMENIAN CAPITAL LETTER CHA +054A; C; 057A; # ARMENIAN CAPITAL LETTER PEH +054B; C; 057B; # ARMENIAN CAPITAL LETTER JHEH +054C; C; 057C; # ARMENIAN CAPITAL LETTER RA +054D; C; 057D; # ARMENIAN CAPITAL LETTER SEH +054E; C; 057E; # ARMENIAN CAPITAL LETTER VEW +054F; C; 057F; # ARMENIAN CAPITAL LETTER TIWN +0550; C; 0580; # ARMENIAN CAPITAL LETTER REH +0551; C; 0581; # ARMENIAN CAPITAL LETTER CO +0552; C; 0582; # ARMENIAN CAPITAL LETTER YIWN +0553; C; 0583; # ARMENIAN CAPITAL LETTER PIWR +0554; C; 0584; # ARMENIAN CAPITAL LETTER KEH +0555; C; 0585; # ARMENIAN CAPITAL LETTER OH +0556; C; 0586; # ARMENIAN CAPITAL LETTER FEH +0587; F; 0565 0582; # ARMENIAN SMALL LIGATURE ECH YIWN +10A0; C; 2D00; # GEORGIAN CAPITAL LETTER AN +10A1; C; 2D01; # GEORGIAN CAPITAL LETTER BAN +10A2; C; 2D02; # GEORGIAN CAPITAL LETTER GAN +10A3; C; 2D03; # GEORGIAN CAPITAL LETTER DON +10A4; C; 2D04; # GEORGIAN CAPITAL LETTER EN +10A5; C; 2D05; # GEORGIAN CAPITAL LETTER VIN +10A6; C; 2D06; # GEORGIAN CAPITAL LETTER ZEN +10A7; C; 2D07; # GEORGIAN CAPITAL LETTER TAN +10A8; C; 2D08; # GEORGIAN CAPITAL LETTER IN +10A9; C; 2D09; # GEORGIAN CAPITAL LETTER KAN +10AA; C; 2D0A; # GEORGIAN CAPITAL LETTER LAS +10AB; C; 2D0B; # GEORGIAN CAPITAL LETTER MAN +10AC; C; 2D0C; # GEORGIAN CAPITAL LETTER NAR +10AD; C; 2D0D; # GEORGIAN CAPITAL LETTER ON +10AE; C; 2D0E; # GEORGIAN CAPITAL LETTER PAR +10AF; C; 2D0F; # GEORGIAN CAPITAL LETTER ZHAR +10B0; C; 2D10; # GEORGIAN CAPITAL LETTER RAE +10B1; C; 2D11; # GEORGIAN CAPITAL LETTER SAN +10B2; C; 2D12; # GEORGIAN CAPITAL LETTER TAR +10B3; C; 2D13; # GEORGIAN CAPITAL LETTER UN +10B4; C; 2D14; # GEORGIAN CAPITAL LETTER PHAR +10B5; C; 2D15; # GEORGIAN CAPITAL LETTER KHAR +10B6; C; 2D16; # GEORGIAN CAPITAL LETTER GHAN +10B7; C; 2D17; # GEORGIAN CAPITAL LETTER QAR +10B8; C; 2D18; # GEORGIAN CAPITAL LETTER SHIN +10B9; C; 2D19; # GEORGIAN CAPITAL LETTER CHIN +10BA; C; 2D1A; # GEORGIAN CAPITAL LETTER CAN +10BB; C; 2D1B; # GEORGIAN CAPITAL LETTER JIL +10BC; C; 2D1C; # GEORGIAN CAPITAL LETTER CIL +10BD; C; 2D1D; # GEORGIAN CAPITAL LETTER CHAR +10BE; C; 2D1E; # GEORGIAN CAPITAL LETTER XAN +10BF; C; 2D1F; # GEORGIAN CAPITAL LETTER JHAN +10C0; C; 2D20; # GEORGIAN CAPITAL LETTER HAE +10C1; C; 2D21; # GEORGIAN CAPITAL LETTER HE +10C2; C; 2D22; # GEORGIAN CAPITAL LETTER HIE +10C3; C; 2D23; # GEORGIAN CAPITAL LETTER WE +10C4; C; 2D24; # GEORGIAN CAPITAL LETTER HAR +10C5; C; 2D25; # GEORGIAN CAPITAL LETTER HOE +10C7; C; 2D27; # GEORGIAN CAPITAL LETTER YN +10CD; C; 2D2D; # GEORGIAN CAPITAL LETTER AEN +13F8; C; 13F0; # CHEROKEE SMALL LETTER YE +13F9; C; 13F1; # CHEROKEE SMALL LETTER YI +13FA; C; 13F2; # CHEROKEE SMALL LETTER YO +13FB; C; 13F3; # CHEROKEE SMALL LETTER YU +13FC; C; 13F4; # CHEROKEE SMALL LETTER YV +13FD; C; 13F5; # CHEROKEE SMALL LETTER MV +1E00; C; 1E01; # LATIN CAPITAL LETTER A WITH RING BELOW +1E02; C; 1E03; # LATIN CAPITAL LETTER B WITH DOT ABOVE +1E04; C; 1E05; # LATIN CAPITAL LETTER B WITH DOT BELOW +1E06; C; 1E07; # LATIN CAPITAL LETTER B WITH LINE BELOW +1E08; C; 1E09; # LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE +1E0A; C; 1E0B; # LATIN CAPITAL LETTER D WITH DOT ABOVE +1E0C; C; 1E0D; # LATIN CAPITAL LETTER D WITH DOT BELOW +1E0E; C; 1E0F; # LATIN CAPITAL LETTER D WITH LINE BELOW +1E10; C; 1E11; # LATIN CAPITAL LETTER D WITH CEDILLA +1E12; C; 1E13; # LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW +1E14; C; 1E15; # LATIN CAPITAL LETTER E WITH MACRON AND GRAVE +1E16; C; 1E17; # LATIN CAPITAL LETTER E WITH MACRON AND ACUTE +1E18; C; 1E19; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW +1E1A; C; 1E1B; # LATIN CAPITAL LETTER E WITH TILDE BELOW +1E1C; C; 1E1D; # LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE +1E1E; C; 1E1F; # LATIN CAPITAL LETTER F WITH DOT ABOVE +1E20; C; 1E21; # LATIN CAPITAL LETTER G WITH MACRON +1E22; C; 1E23; # LATIN CAPITAL LETTER H WITH DOT ABOVE +1E24; C; 1E25; # LATIN CAPITAL LETTER H WITH DOT BELOW +1E26; C; 1E27; # LATIN CAPITAL LETTER H WITH DIAERESIS +1E28; C; 1E29; # LATIN CAPITAL LETTER H WITH CEDILLA +1E2A; C; 1E2B; # LATIN CAPITAL LETTER H WITH BREVE BELOW +1E2C; C; 1E2D; # LATIN CAPITAL LETTER I WITH TILDE BELOW +1E2E; C; 1E2F; # LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE +1E30; C; 1E31; # LATIN CAPITAL LETTER K WITH ACUTE +1E32; C; 1E33; # LATIN CAPITAL LETTER K WITH DOT BELOW +1E34; C; 1E35; # LATIN CAPITAL LETTER K WITH LINE BELOW +1E36; C; 1E37; # LATIN CAPITAL LETTER L WITH DOT BELOW +1E38; C; 1E39; # LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON +1E3A; C; 1E3B; # LATIN CAPITAL LETTER L WITH LINE BELOW +1E3C; C; 1E3D; # LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW +1E3E; C; 1E3F; # LATIN CAPITAL LETTER M WITH ACUTE +1E40; C; 1E41; # LATIN CAPITAL LETTER M WITH DOT ABOVE +1E42; C; 1E43; # LATIN CAPITAL LETTER M WITH DOT BELOW +1E44; C; 1E45; # LATIN CAPITAL LETTER N WITH DOT ABOVE +1E46; C; 1E47; # LATIN CAPITAL LETTER N WITH DOT BELOW +1E48; C; 1E49; # LATIN CAPITAL LETTER N WITH LINE BELOW +1E4A; C; 1E4B; # LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW +1E4C; C; 1E4D; # LATIN CAPITAL LETTER O WITH TILDE AND ACUTE +1E4E; C; 1E4F; # LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS +1E50; C; 1E51; # LATIN CAPITAL LETTER O WITH MACRON AND GRAVE +1E52; C; 1E53; # LATIN CAPITAL LETTER O WITH MACRON AND ACUTE +1E54; C; 1E55; # LATIN CAPITAL LETTER P WITH ACUTE +1E56; C; 1E57; # LATIN CAPITAL LETTER P WITH DOT ABOVE +1E58; C; 1E59; # LATIN CAPITAL LETTER R WITH DOT ABOVE +1E5A; C; 1E5B; # LATIN CAPITAL LETTER R WITH DOT BELOW +1E5C; C; 1E5D; # LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON +1E5E; C; 1E5F; # LATIN CAPITAL LETTER R WITH LINE BELOW +1E60; C; 1E61; # LATIN CAPITAL LETTER S WITH DOT ABOVE +1E62; C; 1E63; # LATIN CAPITAL LETTER S WITH DOT BELOW +1E64; C; 1E65; # LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE +1E66; C; 1E67; # LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE +1E68; C; 1E69; # LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE +1E6A; C; 1E6B; # LATIN CAPITAL LETTER T WITH DOT ABOVE +1E6C; C; 1E6D; # LATIN CAPITAL LETTER T WITH DOT BELOW +1E6E; C; 1E6F; # LATIN CAPITAL LETTER T WITH LINE BELOW +1E70; C; 1E71; # LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW +1E72; C; 1E73; # LATIN CAPITAL LETTER U WITH DIAERESIS BELOW +1E74; C; 1E75; # LATIN CAPITAL LETTER U WITH TILDE BELOW +1E76; C; 1E77; # LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW +1E78; C; 1E79; # LATIN CAPITAL LETTER U WITH TILDE AND ACUTE +1E7A; C; 1E7B; # LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS +1E7C; C; 1E7D; # LATIN CAPITAL LETTER V WITH TILDE +1E7E; C; 1E7F; # LATIN CAPITAL LETTER V WITH DOT BELOW +1E80; C; 1E81; # LATIN CAPITAL LETTER W WITH GRAVE +1E82; C; 1E83; # LATIN CAPITAL LETTER W WITH ACUTE +1E84; C; 1E85; # LATIN CAPITAL LETTER W WITH DIAERESIS +1E86; C; 1E87; # LATIN CAPITAL LETTER W WITH DOT ABOVE +1E88; C; 1E89; # LATIN CAPITAL LETTER W WITH DOT BELOW +1E8A; C; 1E8B; # LATIN CAPITAL LETTER X WITH DOT ABOVE +1E8C; C; 1E8D; # LATIN CAPITAL LETTER X WITH DIAERESIS +1E8E; C; 1E8F; # LATIN CAPITAL LETTER Y WITH DOT ABOVE +1E90; C; 1E91; # LATIN CAPITAL LETTER Z WITH CIRCUMFLEX +1E92; C; 1E93; # LATIN CAPITAL LETTER Z WITH DOT BELOW +1E94; C; 1E95; # LATIN CAPITAL LETTER Z WITH LINE BELOW +1E96; F; 0068 0331; # LATIN SMALL LETTER H WITH LINE BELOW +1E97; F; 0074 0308; # LATIN SMALL LETTER T WITH DIAERESIS +1E98; F; 0077 030A; # LATIN SMALL LETTER W WITH RING ABOVE +1E99; F; 0079 030A; # LATIN SMALL LETTER Y WITH RING ABOVE +1E9A; F; 0061 02BE; # LATIN SMALL LETTER A WITH RIGHT HALF RING +1E9B; C; 1E61; # LATIN SMALL LETTER LONG S WITH DOT ABOVE +1E9E; F; 0073 0073; # LATIN CAPITAL LETTER SHARP S +1E9E; S; 00DF; # LATIN CAPITAL LETTER SHARP S +1EA0; C; 1EA1; # LATIN CAPITAL LETTER A WITH DOT BELOW +1EA2; C; 1EA3; # LATIN CAPITAL LETTER A WITH HOOK ABOVE +1EA4; C; 1EA5; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE +1EA6; C; 1EA7; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE +1EA8; C; 1EA9; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE +1EAA; C; 1EAB; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE +1EAC; C; 1EAD; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW +1EAE; C; 1EAF; # LATIN CAPITAL LETTER A WITH BREVE AND ACUTE +1EB0; C; 1EB1; # LATIN CAPITAL LETTER A WITH BREVE AND GRAVE +1EB2; C; 1EB3; # LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE +1EB4; C; 1EB5; # LATIN CAPITAL LETTER A WITH BREVE AND TILDE +1EB6; C; 1EB7; # LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW +1EB8; C; 1EB9; # LATIN CAPITAL LETTER E WITH DOT BELOW +1EBA; C; 1EBB; # LATIN CAPITAL LETTER E WITH HOOK ABOVE +1EBC; C; 1EBD; # LATIN CAPITAL LETTER E WITH TILDE +1EBE; C; 1EBF; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE +1EC0; C; 1EC1; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE +1EC2; C; 1EC3; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE +1EC4; C; 1EC5; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE +1EC6; C; 1EC7; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW +1EC8; C; 1EC9; # LATIN CAPITAL LETTER I WITH HOOK ABOVE +1ECA; C; 1ECB; # LATIN CAPITAL LETTER I WITH DOT BELOW +1ECC; C; 1ECD; # LATIN CAPITAL LETTER O WITH DOT BELOW +1ECE; C; 1ECF; # LATIN CAPITAL LETTER O WITH HOOK ABOVE +1ED0; C; 1ED1; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE +1ED2; C; 1ED3; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE +1ED4; C; 1ED5; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE +1ED6; C; 1ED7; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE +1ED8; C; 1ED9; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW +1EDA; C; 1EDB; # LATIN CAPITAL LETTER O WITH HORN AND ACUTE +1EDC; C; 1EDD; # LATIN CAPITAL LETTER O WITH HORN AND GRAVE +1EDE; C; 1EDF; # LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE +1EE0; C; 1EE1; # LATIN CAPITAL LETTER O WITH HORN AND TILDE +1EE2; C; 1EE3; # LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW +1EE4; C; 1EE5; # LATIN CAPITAL LETTER U WITH DOT BELOW +1EE6; C; 1EE7; # LATIN CAPITAL LETTER U WITH HOOK ABOVE +1EE8; C; 1EE9; # LATIN CAPITAL LETTER U WITH HORN AND ACUTE +1EEA; C; 1EEB; # LATIN CAPITAL LETTER U WITH HORN AND GRAVE +1EEC; C; 1EED; # LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE +1EEE; C; 1EEF; # LATIN CAPITAL LETTER U WITH HORN AND TILDE +1EF0; C; 1EF1; # LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW +1EF2; C; 1EF3; # LATIN CAPITAL LETTER Y WITH GRAVE +1EF4; C; 1EF5; # LATIN CAPITAL LETTER Y WITH DOT BELOW +1EF6; C; 1EF7; # LATIN CAPITAL LETTER Y WITH HOOK ABOVE +1EF8; C; 1EF9; # LATIN CAPITAL LETTER Y WITH TILDE +1EFA; C; 1EFB; # LATIN CAPITAL LETTER MIDDLE-WELSH LL +1EFC; C; 1EFD; # LATIN CAPITAL LETTER MIDDLE-WELSH V +1EFE; C; 1EFF; # LATIN CAPITAL LETTER Y WITH LOOP +1F08; C; 1F00; # GREEK CAPITAL LETTER ALPHA WITH PSILI +1F09; C; 1F01; # GREEK CAPITAL LETTER ALPHA WITH DASIA +1F0A; C; 1F02; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA +1F0B; C; 1F03; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA +1F0C; C; 1F04; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA +1F0D; C; 1F05; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA +1F0E; C; 1F06; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI +1F0F; C; 1F07; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI +1F18; C; 1F10; # GREEK CAPITAL LETTER EPSILON WITH PSILI +1F19; C; 1F11; # GREEK CAPITAL LETTER EPSILON WITH DASIA +1F1A; C; 1F12; # GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA +1F1B; C; 1F13; # GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA +1F1C; C; 1F14; # GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA +1F1D; C; 1F15; # GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA +1F28; C; 1F20; # GREEK CAPITAL LETTER ETA WITH PSILI +1F29; C; 1F21; # GREEK CAPITAL LETTER ETA WITH DASIA +1F2A; C; 1F22; # GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA +1F2B; C; 1F23; # GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA +1F2C; C; 1F24; # GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA +1F2D; C; 1F25; # GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA +1F2E; C; 1F26; # GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI +1F2F; C; 1F27; # GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI +1F38; C; 1F30; # GREEK CAPITAL LETTER IOTA WITH PSILI +1F39; C; 1F31; # GREEK CAPITAL LETTER IOTA WITH DASIA +1F3A; C; 1F32; # GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA +1F3B; C; 1F33; # GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA +1F3C; C; 1F34; # GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA +1F3D; C; 1F35; # GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA +1F3E; C; 1F36; # GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI +1F3F; C; 1F37; # GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI +1F48; C; 1F40; # GREEK CAPITAL LETTER OMICRON WITH PSILI +1F49; C; 1F41; # GREEK CAPITAL LETTER OMICRON WITH DASIA +1F4A; C; 1F42; # GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA +1F4B; C; 1F43; # GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA +1F4C; C; 1F44; # GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA +1F4D; C; 1F45; # GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA +1F50; F; 03C5 0313; # GREEK SMALL LETTER UPSILON WITH PSILI +1F52; F; 03C5 0313 0300; # GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA +1F54; F; 03C5 0313 0301; # GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA +1F56; F; 03C5 0313 0342; # GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI +1F59; C; 1F51; # GREEK CAPITAL LETTER UPSILON WITH DASIA +1F5B; C; 1F53; # GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA +1F5D; C; 1F55; # GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA +1F5F; C; 1F57; # GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI +1F68; C; 1F60; # GREEK CAPITAL LETTER OMEGA WITH PSILI +1F69; C; 1F61; # GREEK CAPITAL LETTER OMEGA WITH DASIA +1F6A; C; 1F62; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA +1F6B; C; 1F63; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA +1F6C; C; 1F64; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA +1F6D; C; 1F65; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA +1F6E; C; 1F66; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI +1F6F; C; 1F67; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI +1F80; F; 1F00 03B9; # GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI +1F81; F; 1F01 03B9; # GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI +1F82; F; 1F02 03B9; # GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI +1F83; F; 1F03 03B9; # GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI +1F84; F; 1F04 03B9; # GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI +1F85; F; 1F05 03B9; # GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI +1F86; F; 1F06 03B9; # GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI +1F87; F; 1F07 03B9; # GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI +1F88; F; 1F00 03B9; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI +1F88; S; 1F80; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI +1F89; F; 1F01 03B9; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI +1F89; S; 1F81; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI +1F8A; F; 1F02 03B9; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI +1F8A; S; 1F82; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI +1F8B; F; 1F03 03B9; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI +1F8B; S; 1F83; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI +1F8C; F; 1F04 03B9; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI +1F8C; S; 1F84; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI +1F8D; F; 1F05 03B9; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI +1F8D; S; 1F85; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI +1F8E; F; 1F06 03B9; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +1F8E; S; 1F86; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +1F8F; F; 1F07 03B9; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +1F8F; S; 1F87; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +1F90; F; 1F20 03B9; # GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI +1F91; F; 1F21 03B9; # GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI +1F92; F; 1F22 03B9; # GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI +1F93; F; 1F23 03B9; # GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI +1F94; F; 1F24 03B9; # GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI +1F95; F; 1F25 03B9; # GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI +1F96; F; 1F26 03B9; # GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI +1F97; F; 1F27 03B9; # GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI +1F98; F; 1F20 03B9; # GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI +1F98; S; 1F90; # GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI +1F99; F; 1F21 03B9; # GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI +1F99; S; 1F91; # GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI +1F9A; F; 1F22 03B9; # GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI +1F9A; S; 1F92; # GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI +1F9B; F; 1F23 03B9; # GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI +1F9B; S; 1F93; # GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI +1F9C; F; 1F24 03B9; # GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI +1F9C; S; 1F94; # GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI +1F9D; F; 1F25 03B9; # GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI +1F9D; S; 1F95; # GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI +1F9E; F; 1F26 03B9; # GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +1F9E; S; 1F96; # GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +1F9F; F; 1F27 03B9; # GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +1F9F; S; 1F97; # GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +1FA0; F; 1F60 03B9; # GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI +1FA1; F; 1F61 03B9; # GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI +1FA2; F; 1F62 03B9; # GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI +1FA3; F; 1F63 03B9; # GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI +1FA4; F; 1F64 03B9; # GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI +1FA5; F; 1F65 03B9; # GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI +1FA6; F; 1F66 03B9; # GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI +1FA7; F; 1F67 03B9; # GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI +1FA8; F; 1F60 03B9; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI +1FA8; S; 1FA0; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI +1FA9; F; 1F61 03B9; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI +1FA9; S; 1FA1; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI +1FAA; F; 1F62 03B9; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI +1FAA; S; 1FA2; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI +1FAB; F; 1F63 03B9; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI +1FAB; S; 1FA3; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI +1FAC; F; 1F64 03B9; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI +1FAC; S; 1FA4; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI +1FAD; F; 1F65 03B9; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI +1FAD; S; 1FA5; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI +1FAE; F; 1F66 03B9; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +1FAE; S; 1FA6; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +1FAF; F; 1F67 03B9; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +1FAF; S; 1FA7; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +1FB2; F; 1F70 03B9; # GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI +1FB3; F; 03B1 03B9; # GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI +1FB4; F; 03AC 03B9; # GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI +1FB6; F; 03B1 0342; # GREEK SMALL LETTER ALPHA WITH PERISPOMENI +1FB7; F; 03B1 0342 03B9; # GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI +1FB8; C; 1FB0; # GREEK CAPITAL LETTER ALPHA WITH VRACHY +1FB9; C; 1FB1; # GREEK CAPITAL LETTER ALPHA WITH MACRON +1FBA; C; 1F70; # GREEK CAPITAL LETTER ALPHA WITH VARIA +1FBB; C; 1F71; # GREEK CAPITAL LETTER ALPHA WITH OXIA +1FBC; F; 03B1 03B9; # GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI +1FBC; S; 1FB3; # GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI +1FBE; C; 03B9; # GREEK PROSGEGRAMMENI +1FC2; F; 1F74 03B9; # GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI +1FC3; F; 03B7 03B9; # GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI +1FC4; F; 03AE 03B9; # GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI +1FC6; F; 03B7 0342; # GREEK SMALL LETTER ETA WITH PERISPOMENI +1FC7; F; 03B7 0342 03B9; # GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI +1FC8; C; 1F72; # GREEK CAPITAL LETTER EPSILON WITH VARIA +1FC9; C; 1F73; # GREEK CAPITAL LETTER EPSILON WITH OXIA +1FCA; C; 1F74; # GREEK CAPITAL LETTER ETA WITH VARIA +1FCB; C; 1F75; # GREEK CAPITAL LETTER ETA WITH OXIA +1FCC; F; 03B7 03B9; # GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI +1FCC; S; 1FC3; # GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI +1FD2; F; 03B9 0308 0300; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA +1FD3; F; 03B9 0308 0301; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA +1FD6; F; 03B9 0342; # GREEK SMALL LETTER IOTA WITH PERISPOMENI +1FD7; F; 03B9 0308 0342; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI +1FD8; C; 1FD0; # GREEK CAPITAL LETTER IOTA WITH VRACHY +1FD9; C; 1FD1; # GREEK CAPITAL LETTER IOTA WITH MACRON +1FDA; C; 1F76; # GREEK CAPITAL LETTER IOTA WITH VARIA +1FDB; C; 1F77; # GREEK CAPITAL LETTER IOTA WITH OXIA +1FE2; F; 03C5 0308 0300; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA +1FE3; F; 03C5 0308 0301; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA +1FE4; F; 03C1 0313; # GREEK SMALL LETTER RHO WITH PSILI +1FE6; F; 03C5 0342; # GREEK SMALL LETTER UPSILON WITH PERISPOMENI +1FE7; F; 03C5 0308 0342; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI +1FE8; C; 1FE0; # GREEK CAPITAL LETTER UPSILON WITH VRACHY +1FE9; C; 1FE1; # GREEK CAPITAL LETTER UPSILON WITH MACRON +1FEA; C; 1F7A; # GREEK CAPITAL LETTER UPSILON WITH VARIA +1FEB; C; 1F7B; # GREEK CAPITAL LETTER UPSILON WITH OXIA +1FEC; C; 1FE5; # GREEK CAPITAL LETTER RHO WITH DASIA +1FF2; F; 1F7C 03B9; # GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI +1FF3; F; 03C9 03B9; # GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI +1FF4; F; 03CE 03B9; # GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI +1FF6; F; 03C9 0342; # GREEK SMALL LETTER OMEGA WITH PERISPOMENI +1FF7; F; 03C9 0342 03B9; # GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI +1FF8; C; 1F78; # GREEK CAPITAL LETTER OMICRON WITH VARIA +1FF9; C; 1F79; # GREEK CAPITAL LETTER OMICRON WITH OXIA +1FFA; C; 1F7C; # GREEK CAPITAL LETTER OMEGA WITH VARIA +1FFB; C; 1F7D; # GREEK CAPITAL LETTER OMEGA WITH OXIA +1FFC; F; 03C9 03B9; # GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI +1FFC; S; 1FF3; # GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI +2126; C; 03C9; # OHM SIGN +212A; C; 006B; # KELVIN SIGN +212B; C; 00E5; # ANGSTROM SIGN +2132; C; 214E; # TURNED CAPITAL F +2160; C; 2170; # ROMAN NUMERAL ONE +2161; C; 2171; # ROMAN NUMERAL TWO +2162; C; 2172; # ROMAN NUMERAL THREE +2163; C; 2173; # ROMAN NUMERAL FOUR +2164; C; 2174; # ROMAN NUMERAL FIVE +2165; C; 2175; # ROMAN NUMERAL SIX +2166; C; 2176; # ROMAN NUMERAL SEVEN +2167; C; 2177; # ROMAN NUMERAL EIGHT +2168; C; 2178; # ROMAN NUMERAL NINE +2169; C; 2179; # ROMAN NUMERAL TEN +216A; C; 217A; # ROMAN NUMERAL ELEVEN +216B; C; 217B; # ROMAN NUMERAL TWELVE +216C; C; 217C; # ROMAN NUMERAL FIFTY +216D; C; 217D; # ROMAN NUMERAL ONE HUNDRED +216E; C; 217E; # ROMAN NUMERAL FIVE HUNDRED +216F; C; 217F; # ROMAN NUMERAL ONE THOUSAND +2183; C; 2184; # ROMAN NUMERAL REVERSED ONE HUNDRED +24B6; C; 24D0; # CIRCLED LATIN CAPITAL LETTER A +24B7; C; 24D1; # CIRCLED LATIN CAPITAL LETTER B +24B8; C; 24D2; # CIRCLED LATIN CAPITAL LETTER C +24B9; C; 24D3; # CIRCLED LATIN CAPITAL LETTER D +24BA; C; 24D4; # CIRCLED LATIN CAPITAL LETTER E +24BB; C; 24D5; # CIRCLED LATIN CAPITAL LETTER F +24BC; C; 24D6; # CIRCLED LATIN CAPITAL LETTER G +24BD; C; 24D7; # CIRCLED LATIN CAPITAL LETTER H +24BE; C; 24D8; # CIRCLED LATIN CAPITAL LETTER I +24BF; C; 24D9; # CIRCLED LATIN CAPITAL LETTER J +24C0; C; 24DA; # CIRCLED LATIN CAPITAL LETTER K +24C1; C; 24DB; # CIRCLED LATIN CAPITAL LETTER L +24C2; C; 24DC; # CIRCLED LATIN CAPITAL LETTER M +24C3; C; 24DD; # CIRCLED LATIN CAPITAL LETTER N +24C4; C; 24DE; # CIRCLED LATIN CAPITAL LETTER O +24C5; C; 24DF; # CIRCLED LATIN CAPITAL LETTER P +24C6; C; 24E0; # CIRCLED LATIN CAPITAL LETTER Q +24C7; C; 24E1; # CIRCLED LATIN CAPITAL LETTER R +24C8; C; 24E2; # CIRCLED LATIN CAPITAL LETTER S +24C9; C; 24E3; # CIRCLED LATIN CAPITAL LETTER T +24CA; C; 24E4; # CIRCLED LATIN CAPITAL LETTER U +24CB; C; 24E5; # CIRCLED LATIN CAPITAL LETTER V +24CC; C; 24E6; # CIRCLED LATIN CAPITAL LETTER W +24CD; C; 24E7; # CIRCLED LATIN CAPITAL LETTER X +24CE; C; 24E8; # CIRCLED LATIN CAPITAL LETTER Y +24CF; C; 24E9; # CIRCLED LATIN CAPITAL LETTER Z +2C00; C; 2C30; # GLAGOLITIC CAPITAL LETTER AZU +2C01; C; 2C31; # GLAGOLITIC CAPITAL LETTER BUKY +2C02; C; 2C32; # GLAGOLITIC CAPITAL LETTER VEDE +2C03; C; 2C33; # GLAGOLITIC CAPITAL LETTER GLAGOLI +2C04; C; 2C34; # GLAGOLITIC CAPITAL LETTER DOBRO +2C05; C; 2C35; # GLAGOLITIC CAPITAL LETTER YESTU +2C06; C; 2C36; # GLAGOLITIC CAPITAL LETTER ZHIVETE +2C07; C; 2C37; # GLAGOLITIC CAPITAL LETTER DZELO +2C08; C; 2C38; # GLAGOLITIC CAPITAL LETTER ZEMLJA +2C09; C; 2C39; # GLAGOLITIC CAPITAL LETTER IZHE +2C0A; C; 2C3A; # GLAGOLITIC CAPITAL LETTER INITIAL IZHE +2C0B; C; 2C3B; # GLAGOLITIC CAPITAL LETTER I +2C0C; C; 2C3C; # GLAGOLITIC CAPITAL LETTER DJERVI +2C0D; C; 2C3D; # GLAGOLITIC CAPITAL LETTER KAKO +2C0E; C; 2C3E; # GLAGOLITIC CAPITAL LETTER LJUDIJE +2C0F; C; 2C3F; # GLAGOLITIC CAPITAL LETTER MYSLITE +2C10; C; 2C40; # GLAGOLITIC CAPITAL LETTER NASHI +2C11; C; 2C41; # GLAGOLITIC CAPITAL LETTER ONU +2C12; C; 2C42; # GLAGOLITIC CAPITAL LETTER POKOJI +2C13; C; 2C43; # GLAGOLITIC CAPITAL LETTER RITSI +2C14; C; 2C44; # GLAGOLITIC CAPITAL LETTER SLOVO +2C15; C; 2C45; # GLAGOLITIC CAPITAL LETTER TVRIDO +2C16; C; 2C46; # GLAGOLITIC CAPITAL LETTER UKU +2C17; C; 2C47; # GLAGOLITIC CAPITAL LETTER FRITU +2C18; C; 2C48; # GLAGOLITIC CAPITAL LETTER HERU +2C19; C; 2C49; # GLAGOLITIC CAPITAL LETTER OTU +2C1A; C; 2C4A; # GLAGOLITIC CAPITAL LETTER PE +2C1B; C; 2C4B; # GLAGOLITIC CAPITAL LETTER SHTA +2C1C; C; 2C4C; # GLAGOLITIC CAPITAL LETTER TSI +2C1D; C; 2C4D; # GLAGOLITIC CAPITAL LETTER CHRIVI +2C1E; C; 2C4E; # GLAGOLITIC CAPITAL LETTER SHA +2C1F; C; 2C4F; # GLAGOLITIC CAPITAL LETTER YERU +2C20; C; 2C50; # GLAGOLITIC CAPITAL LETTER YERI +2C21; C; 2C51; # GLAGOLITIC CAPITAL LETTER YATI +2C22; C; 2C52; # GLAGOLITIC CAPITAL LETTER SPIDERY HA +2C23; C; 2C53; # GLAGOLITIC CAPITAL LETTER YU +2C24; C; 2C54; # GLAGOLITIC CAPITAL LETTER SMALL YUS +2C25; C; 2C55; # GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL +2C26; C; 2C56; # GLAGOLITIC CAPITAL LETTER YO +2C27; C; 2C57; # GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS +2C28; C; 2C58; # GLAGOLITIC CAPITAL LETTER BIG YUS +2C29; C; 2C59; # GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS +2C2A; C; 2C5A; # GLAGOLITIC CAPITAL LETTER FITA +2C2B; C; 2C5B; # GLAGOLITIC CAPITAL LETTER IZHITSA +2C2C; C; 2C5C; # GLAGOLITIC CAPITAL LETTER SHTAPIC +2C2D; C; 2C5D; # GLAGOLITIC CAPITAL LETTER TROKUTASTI A +2C2E; C; 2C5E; # GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE +2C60; C; 2C61; # LATIN CAPITAL LETTER L WITH DOUBLE BAR +2C62; C; 026B; # LATIN CAPITAL LETTER L WITH MIDDLE TILDE +2C63; C; 1D7D; # LATIN CAPITAL LETTER P WITH STROKE +2C64; C; 027D; # LATIN CAPITAL LETTER R WITH TAIL +2C67; C; 2C68; # LATIN CAPITAL LETTER H WITH DESCENDER +2C69; C; 2C6A; # LATIN CAPITAL LETTER K WITH DESCENDER +2C6B; C; 2C6C; # LATIN CAPITAL LETTER Z WITH DESCENDER +2C6D; C; 0251; # LATIN CAPITAL LETTER ALPHA +2C6E; C; 0271; # LATIN CAPITAL LETTER M WITH HOOK +2C6F; C; 0250; # LATIN CAPITAL LETTER TURNED A +2C70; C; 0252; # LATIN CAPITAL LETTER TURNED ALPHA +2C72; C; 2C73; # LATIN CAPITAL LETTER W WITH HOOK +2C75; C; 2C76; # LATIN CAPITAL LETTER HALF H +2C7E; C; 023F; # LATIN CAPITAL LETTER S WITH SWASH TAIL +2C7F; C; 0240; # LATIN CAPITAL LETTER Z WITH SWASH TAIL +2C80; C; 2C81; # COPTIC CAPITAL LETTER ALFA +2C82; C; 2C83; # COPTIC CAPITAL LETTER VIDA +2C84; C; 2C85; # COPTIC CAPITAL LETTER GAMMA +2C86; C; 2C87; # COPTIC CAPITAL LETTER DALDA +2C88; C; 2C89; # COPTIC CAPITAL LETTER EIE +2C8A; C; 2C8B; # COPTIC CAPITAL LETTER SOU +2C8C; C; 2C8D; # COPTIC CAPITAL LETTER ZATA +2C8E; C; 2C8F; # COPTIC CAPITAL LETTER HATE +2C90; C; 2C91; # COPTIC CAPITAL LETTER THETHE +2C92; C; 2C93; # COPTIC CAPITAL LETTER IAUDA +2C94; C; 2C95; # COPTIC CAPITAL LETTER KAPA +2C96; C; 2C97; # COPTIC CAPITAL LETTER LAULA +2C98; C; 2C99; # COPTIC CAPITAL LETTER MI +2C9A; C; 2C9B; # COPTIC CAPITAL LETTER NI +2C9C; C; 2C9D; # COPTIC CAPITAL LETTER KSI +2C9E; C; 2C9F; # COPTIC CAPITAL LETTER O +2CA0; C; 2CA1; # COPTIC CAPITAL LETTER PI +2CA2; C; 2CA3; # COPTIC CAPITAL LETTER RO +2CA4; C; 2CA5; # COPTIC CAPITAL LETTER SIMA +2CA6; C; 2CA7; # COPTIC CAPITAL LETTER TAU +2CA8; C; 2CA9; # COPTIC CAPITAL LETTER UA +2CAA; C; 2CAB; # COPTIC CAPITAL LETTER FI +2CAC; C; 2CAD; # COPTIC CAPITAL LETTER KHI +2CAE; C; 2CAF; # COPTIC CAPITAL LETTER PSI +2CB0; C; 2CB1; # COPTIC CAPITAL LETTER OOU +2CB2; C; 2CB3; # COPTIC CAPITAL LETTER DIALECT-P ALEF +2CB4; C; 2CB5; # COPTIC CAPITAL LETTER OLD COPTIC AIN +2CB6; C; 2CB7; # COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE +2CB8; C; 2CB9; # COPTIC CAPITAL LETTER DIALECT-P KAPA +2CBA; C; 2CBB; # COPTIC CAPITAL LETTER DIALECT-P NI +2CBC; C; 2CBD; # COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI +2CBE; C; 2CBF; # COPTIC CAPITAL LETTER OLD COPTIC OOU +2CC0; C; 2CC1; # COPTIC CAPITAL LETTER SAMPI +2CC2; C; 2CC3; # COPTIC CAPITAL LETTER CROSSED SHEI +2CC4; C; 2CC5; # COPTIC CAPITAL LETTER OLD COPTIC SHEI +2CC6; C; 2CC7; # COPTIC CAPITAL LETTER OLD COPTIC ESH +2CC8; C; 2CC9; # COPTIC CAPITAL LETTER AKHMIMIC KHEI +2CCA; C; 2CCB; # COPTIC CAPITAL LETTER DIALECT-P HORI +2CCC; C; 2CCD; # COPTIC CAPITAL LETTER OLD COPTIC HORI +2CCE; C; 2CCF; # COPTIC CAPITAL LETTER OLD COPTIC HA +2CD0; C; 2CD1; # COPTIC CAPITAL LETTER L-SHAPED HA +2CD2; C; 2CD3; # COPTIC CAPITAL LETTER OLD COPTIC HEI +2CD4; C; 2CD5; # COPTIC CAPITAL LETTER OLD COPTIC HAT +2CD6; C; 2CD7; # COPTIC CAPITAL LETTER OLD COPTIC GANGIA +2CD8; C; 2CD9; # COPTIC CAPITAL LETTER OLD COPTIC DJA +2CDA; C; 2CDB; # COPTIC CAPITAL LETTER OLD COPTIC SHIMA +2CDC; C; 2CDD; # COPTIC CAPITAL LETTER OLD NUBIAN SHIMA +2CDE; C; 2CDF; # COPTIC CAPITAL LETTER OLD NUBIAN NGI +2CE0; C; 2CE1; # COPTIC CAPITAL LETTER OLD NUBIAN NYI +2CE2; C; 2CE3; # COPTIC CAPITAL LETTER OLD NUBIAN WAU +2CEB; C; 2CEC; # COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI +2CED; C; 2CEE; # COPTIC CAPITAL LETTER CRYPTOGRAMMIC GANGIA +2CF2; C; 2CF3; # COPTIC CAPITAL LETTER BOHAIRIC KHEI +A640; C; A641; # CYRILLIC CAPITAL LETTER ZEMLYA +A642; C; A643; # CYRILLIC CAPITAL LETTER DZELO +A644; C; A645; # CYRILLIC CAPITAL LETTER REVERSED DZE +A646; C; A647; # CYRILLIC CAPITAL LETTER IOTA +A648; C; A649; # CYRILLIC CAPITAL LETTER DJERV +A64A; C; A64B; # CYRILLIC CAPITAL LETTER MONOGRAPH UK +A64C; C; A64D; # CYRILLIC CAPITAL LETTER BROAD OMEGA +A64E; C; A64F; # CYRILLIC CAPITAL LETTER NEUTRAL YER +A650; C; A651; # CYRILLIC CAPITAL LETTER YERU WITH BACK YER +A652; C; A653; # CYRILLIC CAPITAL LETTER IOTIFIED YAT +A654; C; A655; # CYRILLIC CAPITAL LETTER REVERSED YU +A656; C; A657; # CYRILLIC CAPITAL LETTER IOTIFIED A +A658; C; A659; # CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS +A65A; C; A65B; # CYRILLIC CAPITAL LETTER BLENDED YUS +A65C; C; A65D; # CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS +A65E; C; A65F; # CYRILLIC CAPITAL LETTER YN +A660; C; A661; # CYRILLIC CAPITAL LETTER REVERSED TSE +A662; C; A663; # CYRILLIC CAPITAL LETTER SOFT DE +A664; C; A665; # CYRILLIC CAPITAL LETTER SOFT EL +A666; C; A667; # CYRILLIC CAPITAL LETTER SOFT EM +A668; C; A669; # CYRILLIC CAPITAL LETTER MONOCULAR O +A66A; C; A66B; # CYRILLIC CAPITAL LETTER BINOCULAR O +A66C; C; A66D; # CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O +A680; C; A681; # CYRILLIC CAPITAL LETTER DWE +A682; C; A683; # CYRILLIC CAPITAL LETTER DZWE +A684; C; A685; # CYRILLIC CAPITAL LETTER ZHWE +A686; C; A687; # CYRILLIC CAPITAL LETTER CCHE +A688; C; A689; # CYRILLIC CAPITAL LETTER DZZE +A68A; C; A68B; # CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK +A68C; C; A68D; # CYRILLIC CAPITAL LETTER TWE +A68E; C; A68F; # CYRILLIC CAPITAL LETTER TSWE +A690; C; A691; # CYRILLIC CAPITAL LETTER TSSE +A692; C; A693; # CYRILLIC CAPITAL LETTER TCHE +A694; C; A695; # CYRILLIC CAPITAL LETTER HWE +A696; C; A697; # CYRILLIC CAPITAL LETTER SHWE +A698; C; A699; # CYRILLIC CAPITAL LETTER DOUBLE O +A69A; C; A69B; # CYRILLIC CAPITAL LETTER CROSSED O +A722; C; A723; # LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF +A724; C; A725; # LATIN CAPITAL LETTER EGYPTOLOGICAL AIN +A726; C; A727; # LATIN CAPITAL LETTER HENG +A728; C; A729; # LATIN CAPITAL LETTER TZ +A72A; C; A72B; # LATIN CAPITAL LETTER TRESILLO +A72C; C; A72D; # LATIN CAPITAL LETTER CUATRILLO +A72E; C; A72F; # LATIN CAPITAL LETTER CUATRILLO WITH COMMA +A732; C; A733; # LATIN CAPITAL LETTER AA +A734; C; A735; # LATIN CAPITAL LETTER AO +A736; C; A737; # LATIN CAPITAL LETTER AU +A738; C; A739; # LATIN CAPITAL LETTER AV +A73A; C; A73B; # LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR +A73C; C; A73D; # LATIN CAPITAL LETTER AY +A73E; C; A73F; # LATIN CAPITAL LETTER REVERSED C WITH DOT +A740; C; A741; # LATIN CAPITAL LETTER K WITH STROKE +A742; C; A743; # LATIN CAPITAL LETTER K WITH DIAGONAL STROKE +A744; C; A745; # LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE +A746; C; A747; # LATIN CAPITAL LETTER BROKEN L +A748; C; A749; # LATIN CAPITAL LETTER L WITH HIGH STROKE +A74A; C; A74B; # LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY +A74C; C; A74D; # LATIN CAPITAL LETTER O WITH LOOP +A74E; C; A74F; # LATIN CAPITAL LETTER OO +A750; C; A751; # LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER +A752; C; A753; # LATIN CAPITAL LETTER P WITH FLOURISH +A754; C; A755; # LATIN CAPITAL LETTER P WITH SQUIRREL TAIL +A756; C; A757; # LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER +A758; C; A759; # LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE +A75A; C; A75B; # LATIN CAPITAL LETTER R ROTUNDA +A75C; C; A75D; # LATIN CAPITAL LETTER RUM ROTUNDA +A75E; C; A75F; # LATIN CAPITAL LETTER V WITH DIAGONAL STROKE +A760; C; A761; # LATIN CAPITAL LETTER VY +A762; C; A763; # LATIN CAPITAL LETTER VISIGOTHIC Z +A764; C; A765; # LATIN CAPITAL LETTER THORN WITH STROKE +A766; C; A767; # LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER +A768; C; A769; # LATIN CAPITAL LETTER VEND +A76A; C; A76B; # LATIN CAPITAL LETTER ET +A76C; C; A76D; # LATIN CAPITAL LETTER IS +A76E; C; A76F; # LATIN CAPITAL LETTER CON +A779; C; A77A; # LATIN CAPITAL LETTER INSULAR D +A77B; C; A77C; # LATIN CAPITAL LETTER INSULAR F +A77D; C; 1D79; # LATIN CAPITAL LETTER INSULAR G +A77E; C; A77F; # LATIN CAPITAL LETTER TURNED INSULAR G +A780; C; A781; # LATIN CAPITAL LETTER TURNED L +A782; C; A783; # LATIN CAPITAL LETTER INSULAR R +A784; C; A785; # LATIN CAPITAL LETTER INSULAR S +A786; C; A787; # LATIN CAPITAL LETTER INSULAR T +A78B; C; A78C; # LATIN CAPITAL LETTER SALTILLO +A78D; C; 0265; # LATIN CAPITAL LETTER TURNED H +A790; C; A791; # LATIN CAPITAL LETTER N WITH DESCENDER +A792; C; A793; # LATIN CAPITAL LETTER C WITH BAR +A796; C; A797; # LATIN CAPITAL LETTER B WITH FLOURISH +A798; C; A799; # LATIN CAPITAL LETTER F WITH STROKE +A79A; C; A79B; # LATIN CAPITAL LETTER VOLAPUK AE +A79C; C; A79D; # LATIN CAPITAL LETTER VOLAPUK OE +A79E; C; A79F; # LATIN CAPITAL LETTER VOLAPUK UE +A7A0; C; A7A1; # LATIN CAPITAL LETTER G WITH OBLIQUE STROKE +A7A2; C; A7A3; # LATIN CAPITAL LETTER K WITH OBLIQUE STROKE +A7A4; C; A7A5; # LATIN CAPITAL LETTER N WITH OBLIQUE STROKE +A7A6; C; A7A7; # LATIN CAPITAL LETTER R WITH OBLIQUE STROKE +A7A8; C; A7A9; # LATIN CAPITAL LETTER S WITH OBLIQUE STROKE +A7AA; C; 0266; # LATIN CAPITAL LETTER H WITH HOOK +A7AB; C; 025C; # LATIN CAPITAL LETTER REVERSED OPEN E +A7AC; C; 0261; # LATIN CAPITAL LETTER SCRIPT G +A7AD; C; 026C; # LATIN CAPITAL LETTER L WITH BELT +A7B0; C; 029E; # LATIN CAPITAL LETTER TURNED K +A7B1; C; 0287; # LATIN CAPITAL LETTER TURNED T +A7B2; C; 029D; # LATIN CAPITAL LETTER J WITH CROSSED-TAIL +A7B3; C; AB53; # LATIN CAPITAL LETTER CHI +A7B4; C; A7B5; # LATIN CAPITAL LETTER BETA +A7B6; C; A7B7; # LATIN CAPITAL LETTER OMEGA +AB70; C; 13A0; # CHEROKEE SMALL LETTER A +AB71; C; 13A1; # CHEROKEE SMALL LETTER E +AB72; C; 13A2; # CHEROKEE SMALL LETTER I +AB73; C; 13A3; # CHEROKEE SMALL LETTER O +AB74; C; 13A4; # CHEROKEE SMALL LETTER U +AB75; C; 13A5; # CHEROKEE SMALL LETTER V +AB76; C; 13A6; # CHEROKEE SMALL LETTER GA +AB77; C; 13A7; # CHEROKEE SMALL LETTER KA +AB78; C; 13A8; # CHEROKEE SMALL LETTER GE +AB79; C; 13A9; # CHEROKEE SMALL LETTER GI +AB7A; C; 13AA; # CHEROKEE SMALL LETTER GO +AB7B; C; 13AB; # CHEROKEE SMALL LETTER GU +AB7C; C; 13AC; # CHEROKEE SMALL LETTER GV +AB7D; C; 13AD; # CHEROKEE SMALL LETTER HA +AB7E; C; 13AE; # CHEROKEE SMALL LETTER HE +AB7F; C; 13AF; # CHEROKEE SMALL LETTER HI +AB80; C; 13B0; # CHEROKEE SMALL LETTER HO +AB81; C; 13B1; # CHEROKEE SMALL LETTER HU +AB82; C; 13B2; # CHEROKEE SMALL LETTER HV +AB83; C; 13B3; # CHEROKEE SMALL LETTER LA +AB84; C; 13B4; # CHEROKEE SMALL LETTER LE +AB85; C; 13B5; # CHEROKEE SMALL LETTER LI +AB86; C; 13B6; # CHEROKEE SMALL LETTER LO +AB87; C; 13B7; # CHEROKEE SMALL LETTER LU +AB88; C; 13B8; # CHEROKEE SMALL LETTER LV +AB89; C; 13B9; # CHEROKEE SMALL LETTER MA +AB8A; C; 13BA; # CHEROKEE SMALL LETTER ME +AB8B; C; 13BB; # CHEROKEE SMALL LETTER MI +AB8C; C; 13BC; # CHEROKEE SMALL LETTER MO +AB8D; C; 13BD; # CHEROKEE SMALL LETTER MU +AB8E; C; 13BE; # CHEROKEE SMALL LETTER NA +AB8F; C; 13BF; # CHEROKEE SMALL LETTER HNA +AB90; C; 13C0; # CHEROKEE SMALL LETTER NAH +AB91; C; 13C1; # CHEROKEE SMALL LETTER NE +AB92; C; 13C2; # CHEROKEE SMALL LETTER NI +AB93; C; 13C3; # CHEROKEE SMALL LETTER NO +AB94; C; 13C4; # CHEROKEE SMALL LETTER NU +AB95; C; 13C5; # CHEROKEE SMALL LETTER NV +AB96; C; 13C6; # CHEROKEE SMALL LETTER QUA +AB97; C; 13C7; # CHEROKEE SMALL LETTER QUE +AB98; C; 13C8; # CHEROKEE SMALL LETTER QUI +AB99; C; 13C9; # CHEROKEE SMALL LETTER QUO +AB9A; C; 13CA; # CHEROKEE SMALL LETTER QUU +AB9B; C; 13CB; # CHEROKEE SMALL LETTER QUV +AB9C; C; 13CC; # CHEROKEE SMALL LETTER SA +AB9D; C; 13CD; # CHEROKEE SMALL LETTER S +AB9E; C; 13CE; # CHEROKEE SMALL LETTER SE +AB9F; C; 13CF; # CHEROKEE SMALL LETTER SI +ABA0; C; 13D0; # CHEROKEE SMALL LETTER SO +ABA1; C; 13D1; # CHEROKEE SMALL LETTER SU +ABA2; C; 13D2; # CHEROKEE SMALL LETTER SV +ABA3; C; 13D3; # CHEROKEE SMALL LETTER DA +ABA4; C; 13D4; # CHEROKEE SMALL LETTER TA +ABA5; C; 13D5; # CHEROKEE SMALL LETTER DE +ABA6; C; 13D6; # CHEROKEE SMALL LETTER TE +ABA7; C; 13D7; # CHEROKEE SMALL LETTER DI +ABA8; C; 13D8; # CHEROKEE SMALL LETTER TI +ABA9; C; 13D9; # CHEROKEE SMALL LETTER DO +ABAA; C; 13DA; # CHEROKEE SMALL LETTER DU +ABAB; C; 13DB; # CHEROKEE SMALL LETTER DV +ABAC; C; 13DC; # CHEROKEE SMALL LETTER DLA +ABAD; C; 13DD; # CHEROKEE SMALL LETTER TLA +ABAE; C; 13DE; # CHEROKEE SMALL LETTER TLE +ABAF; C; 13DF; # CHEROKEE SMALL LETTER TLI +ABB0; C; 13E0; # CHEROKEE SMALL LETTER TLO +ABB1; C; 13E1; # CHEROKEE SMALL LETTER TLU +ABB2; C; 13E2; # CHEROKEE SMALL LETTER TLV +ABB3; C; 13E3; # CHEROKEE SMALL LETTER TSA +ABB4; C; 13E4; # CHEROKEE SMALL LETTER TSE +ABB5; C; 13E5; # CHEROKEE SMALL LETTER TSI +ABB6; C; 13E6; # CHEROKEE SMALL LETTER TSO +ABB7; C; 13E7; # CHEROKEE SMALL LETTER TSU +ABB8; C; 13E8; # CHEROKEE SMALL LETTER TSV +ABB9; C; 13E9; # CHEROKEE SMALL LETTER WA +ABBA; C; 13EA; # CHEROKEE SMALL LETTER WE +ABBB; C; 13EB; # CHEROKEE SMALL LETTER WI +ABBC; C; 13EC; # CHEROKEE SMALL LETTER WO +ABBD; C; 13ED; # CHEROKEE SMALL LETTER WU +ABBE; C; 13EE; # CHEROKEE SMALL LETTER WV +ABBF; C; 13EF; # CHEROKEE SMALL LETTER YA +FB00; F; 0066 0066; # LATIN SMALL LIGATURE FF +FB01; F; 0066 0069; # LATIN SMALL LIGATURE FI +FB02; F; 0066 006C; # LATIN SMALL LIGATURE FL +FB03; F; 0066 0066 0069; # LATIN SMALL LIGATURE FFI +FB04; F; 0066 0066 006C; # LATIN SMALL LIGATURE FFL +FB05; F; 0073 0074; # LATIN SMALL LIGATURE LONG S T +FB06; F; 0073 0074; # LATIN SMALL LIGATURE ST +FB13; F; 0574 0576; # ARMENIAN SMALL LIGATURE MEN NOW +FB14; F; 0574 0565; # ARMENIAN SMALL LIGATURE MEN ECH +FB15; F; 0574 056B; # ARMENIAN SMALL LIGATURE MEN INI +FB16; F; 057E 0576; # ARMENIAN SMALL LIGATURE VEW NOW +FB17; F; 0574 056D; # ARMENIAN SMALL LIGATURE MEN XEH +FF21; C; FF41; # FULLWIDTH LATIN CAPITAL LETTER A +FF22; C; FF42; # FULLWIDTH LATIN CAPITAL LETTER B +FF23; C; FF43; # FULLWIDTH LATIN CAPITAL LETTER C +FF24; C; FF44; # FULLWIDTH LATIN CAPITAL LETTER D +FF25; C; FF45; # FULLWIDTH LATIN CAPITAL LETTER E +FF26; C; FF46; # FULLWIDTH LATIN CAPITAL LETTER F +FF27; C; FF47; # FULLWIDTH LATIN CAPITAL LETTER G +FF28; C; FF48; # FULLWIDTH LATIN CAPITAL LETTER H +FF29; C; FF49; # FULLWIDTH LATIN CAPITAL LETTER I +FF2A; C; FF4A; # FULLWIDTH LATIN CAPITAL LETTER J +FF2B; C; FF4B; # FULLWIDTH LATIN CAPITAL LETTER K +FF2C; C; FF4C; # FULLWIDTH LATIN CAPITAL LETTER L +FF2D; C; FF4D; # FULLWIDTH LATIN CAPITAL LETTER M +FF2E; C; FF4E; # FULLWIDTH LATIN CAPITAL LETTER N +FF2F; C; FF4F; # FULLWIDTH LATIN CAPITAL LETTER O +FF30; C; FF50; # FULLWIDTH LATIN CAPITAL LETTER P +FF31; C; FF51; # FULLWIDTH LATIN CAPITAL LETTER Q +FF32; C; FF52; # FULLWIDTH LATIN CAPITAL LETTER R +FF33; C; FF53; # FULLWIDTH LATIN CAPITAL LETTER S +FF34; C; FF54; # FULLWIDTH LATIN CAPITAL LETTER T +FF35; C; FF55; # FULLWIDTH LATIN CAPITAL LETTER U +FF36; C; FF56; # FULLWIDTH LATIN CAPITAL LETTER V +FF37; C; FF57; # FULLWIDTH LATIN CAPITAL LETTER W +FF38; C; FF58; # FULLWIDTH LATIN CAPITAL LETTER X +FF39; C; FF59; # FULLWIDTH LATIN CAPITAL LETTER Y +FF3A; C; FF5A; # FULLWIDTH LATIN CAPITAL LETTER Z +10400; C; 10428; # DESERET CAPITAL LETTER LONG I +10401; C; 10429; # DESERET CAPITAL LETTER LONG E +10402; C; 1042A; # DESERET CAPITAL LETTER LONG A +10403; C; 1042B; # DESERET CAPITAL LETTER LONG AH +10404; C; 1042C; # DESERET CAPITAL LETTER LONG O +10405; C; 1042D; # DESERET CAPITAL LETTER LONG OO +10406; C; 1042E; # DESERET CAPITAL LETTER SHORT I +10407; C; 1042F; # DESERET CAPITAL LETTER SHORT E +10408; C; 10430; # DESERET CAPITAL LETTER SHORT A +10409; C; 10431; # DESERET CAPITAL LETTER SHORT AH +1040A; C; 10432; # DESERET CAPITAL LETTER SHORT O +1040B; C; 10433; # DESERET CAPITAL LETTER SHORT OO +1040C; C; 10434; # DESERET CAPITAL LETTER AY +1040D; C; 10435; # DESERET CAPITAL LETTER OW +1040E; C; 10436; # DESERET CAPITAL LETTER WU +1040F; C; 10437; # DESERET CAPITAL LETTER YEE +10410; C; 10438; # DESERET CAPITAL LETTER H +10411; C; 10439; # DESERET CAPITAL LETTER PEE +10412; C; 1043A; # DESERET CAPITAL LETTER BEE +10413; C; 1043B; # DESERET CAPITAL LETTER TEE +10414; C; 1043C; # DESERET CAPITAL LETTER DEE +10415; C; 1043D; # DESERET CAPITAL LETTER CHEE +10416; C; 1043E; # DESERET CAPITAL LETTER JEE +10417; C; 1043F; # DESERET CAPITAL LETTER KAY +10418; C; 10440; # DESERET CAPITAL LETTER GAY +10419; C; 10441; # DESERET CAPITAL LETTER EF +1041A; C; 10442; # DESERET CAPITAL LETTER VEE +1041B; C; 10443; # DESERET CAPITAL LETTER ETH +1041C; C; 10444; # DESERET CAPITAL LETTER THEE +1041D; C; 10445; # DESERET CAPITAL LETTER ES +1041E; C; 10446; # DESERET CAPITAL LETTER ZEE +1041F; C; 10447; # DESERET CAPITAL LETTER ESH +10420; C; 10448; # DESERET CAPITAL LETTER ZHEE +10421; C; 10449; # DESERET CAPITAL LETTER ER +10422; C; 1044A; # DESERET CAPITAL LETTER EL +10423; C; 1044B; # DESERET CAPITAL LETTER EM +10424; C; 1044C; # DESERET CAPITAL LETTER EN +10425; C; 1044D; # DESERET CAPITAL LETTER ENG +10426; C; 1044E; # DESERET CAPITAL LETTER OI +10427; C; 1044F; # DESERET CAPITAL LETTER EW +10C80; C; 10CC0; # OLD HUNGARIAN CAPITAL LETTER A +10C81; C; 10CC1; # OLD HUNGARIAN CAPITAL LETTER AA +10C82; C; 10CC2; # OLD HUNGARIAN CAPITAL LETTER EB +10C83; C; 10CC3; # OLD HUNGARIAN CAPITAL LETTER AMB +10C84; C; 10CC4; # OLD HUNGARIAN CAPITAL LETTER EC +10C85; C; 10CC5; # OLD HUNGARIAN CAPITAL LETTER ENC +10C86; C; 10CC6; # OLD HUNGARIAN CAPITAL LETTER ECS +10C87; C; 10CC7; # OLD HUNGARIAN CAPITAL LETTER ED +10C88; C; 10CC8; # OLD HUNGARIAN CAPITAL LETTER AND +10C89; C; 10CC9; # OLD HUNGARIAN CAPITAL LETTER E +10C8A; C; 10CCA; # OLD HUNGARIAN CAPITAL LETTER CLOSE E +10C8B; C; 10CCB; # OLD HUNGARIAN CAPITAL LETTER EE +10C8C; C; 10CCC; # OLD HUNGARIAN CAPITAL LETTER EF +10C8D; C; 10CCD; # OLD HUNGARIAN CAPITAL LETTER EG +10C8E; C; 10CCE; # OLD HUNGARIAN CAPITAL LETTER EGY +10C8F; C; 10CCF; # OLD HUNGARIAN CAPITAL LETTER EH +10C90; C; 10CD0; # OLD HUNGARIAN CAPITAL LETTER I +10C91; C; 10CD1; # OLD HUNGARIAN CAPITAL LETTER II +10C92; C; 10CD2; # OLD HUNGARIAN CAPITAL LETTER EJ +10C93; C; 10CD3; # OLD HUNGARIAN CAPITAL LETTER EK +10C94; C; 10CD4; # OLD HUNGARIAN CAPITAL LETTER AK +10C95; C; 10CD5; # OLD HUNGARIAN CAPITAL LETTER UNK +10C96; C; 10CD6; # OLD HUNGARIAN CAPITAL LETTER EL +10C97; C; 10CD7; # OLD HUNGARIAN CAPITAL LETTER ELY +10C98; C; 10CD8; # OLD HUNGARIAN CAPITAL LETTER EM +10C99; C; 10CD9; # OLD HUNGARIAN CAPITAL LETTER EN +10C9A; C; 10CDA; # OLD HUNGARIAN CAPITAL LETTER ENY +10C9B; C; 10CDB; # OLD HUNGARIAN CAPITAL LETTER O +10C9C; C; 10CDC; # OLD HUNGARIAN CAPITAL LETTER OO +10C9D; C; 10CDD; # OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG OE +10C9E; C; 10CDE; # OLD HUNGARIAN CAPITAL LETTER RUDIMENTA OE +10C9F; C; 10CDF; # OLD HUNGARIAN CAPITAL LETTER OEE +10CA0; C; 10CE0; # OLD HUNGARIAN CAPITAL LETTER EP +10CA1; C; 10CE1; # OLD HUNGARIAN CAPITAL LETTER EMP +10CA2; C; 10CE2; # OLD HUNGARIAN CAPITAL LETTER ER +10CA3; C; 10CE3; # OLD HUNGARIAN CAPITAL LETTER SHORT ER +10CA4; C; 10CE4; # OLD HUNGARIAN CAPITAL LETTER ES +10CA5; C; 10CE5; # OLD HUNGARIAN CAPITAL LETTER ESZ +10CA6; C; 10CE6; # OLD HUNGARIAN CAPITAL LETTER ET +10CA7; C; 10CE7; # OLD HUNGARIAN CAPITAL LETTER ENT +10CA8; C; 10CE8; # OLD HUNGARIAN CAPITAL LETTER ETY +10CA9; C; 10CE9; # OLD HUNGARIAN CAPITAL LETTER ECH +10CAA; C; 10CEA; # OLD HUNGARIAN CAPITAL LETTER U +10CAB; C; 10CEB; # OLD HUNGARIAN CAPITAL LETTER UU +10CAC; C; 10CEC; # OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG UE +10CAD; C; 10CED; # OLD HUNGARIAN CAPITAL LETTER RUDIMENTA UE +10CAE; C; 10CEE; # OLD HUNGARIAN CAPITAL LETTER EV +10CAF; C; 10CEF; # OLD HUNGARIAN CAPITAL LETTER EZ +10CB0; C; 10CF0; # OLD HUNGARIAN CAPITAL LETTER EZS +10CB1; C; 10CF1; # OLD HUNGARIAN CAPITAL LETTER ENT-SHAPED SIGN +10CB2; C; 10CF2; # OLD HUNGARIAN CAPITAL LETTER US +118A0; C; 118C0; # WARANG CITI CAPITAL LETTER NGAA +118A1; C; 118C1; # WARANG CITI CAPITAL LETTER A +118A2; C; 118C2; # WARANG CITI CAPITAL LETTER WI +118A3; C; 118C3; # WARANG CITI CAPITAL LETTER YU +118A4; C; 118C4; # WARANG CITI CAPITAL LETTER YA +118A5; C; 118C5; # WARANG CITI CAPITAL LETTER YO +118A6; C; 118C6; # WARANG CITI CAPITAL LETTER II +118A7; C; 118C7; # WARANG CITI CAPITAL LETTER UU +118A8; C; 118C8; # WARANG CITI CAPITAL LETTER E +118A9; C; 118C9; # WARANG CITI CAPITAL LETTER O +118AA; C; 118CA; # WARANG CITI CAPITAL LETTER ANG +118AB; C; 118CB; # WARANG CITI CAPITAL LETTER GA +118AC; C; 118CC; # WARANG CITI CAPITAL LETTER KO +118AD; C; 118CD; # WARANG CITI CAPITAL LETTER ENY +118AE; C; 118CE; # WARANG CITI CAPITAL LETTER YUJ +118AF; C; 118CF; # WARANG CITI CAPITAL LETTER UC +118B0; C; 118D0; # WARANG CITI CAPITAL LETTER ENN +118B1; C; 118D1; # WARANG CITI CAPITAL LETTER ODD +118B2; C; 118D2; # WARANG CITI CAPITAL LETTER TTE +118B3; C; 118D3; # WARANG CITI CAPITAL LETTER NUNG +118B4; C; 118D4; # WARANG CITI CAPITAL LETTER DA +118B5; C; 118D5; # WARANG CITI CAPITAL LETTER AT +118B6; C; 118D6; # WARANG CITI CAPITAL LETTER AM +118B7; C; 118D7; # WARANG CITI CAPITAL LETTER BU +118B8; C; 118D8; # WARANG CITI CAPITAL LETTER PU +118B9; C; 118D9; # WARANG CITI CAPITAL LETTER HIYO +118BA; C; 118DA; # WARANG CITI CAPITAL LETTER HOLO +118BB; C; 118DB; # WARANG CITI CAPITAL LETTER HORR +118BC; C; 118DC; # WARANG CITI CAPITAL LETTER HAR +118BD; C; 118DD; # WARANG CITI CAPITAL LETTER SSUU +118BE; C; 118DE; # WARANG CITI CAPITAL LETTER SII +118BF; C; 118DF; # WARANG CITI CAPITAL LETTER VIYO +# +# EOF diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 13cb4e7b1..66e392668 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1753,7 +1753,6 @@ CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED211) CASE(JSOP_UNUSED212) -CASE(JSOP_UNUSED213) CASE(JSOP_UNUSED219) CASE(JSOP_UNUSED220) CASE(JSOP_UNUSED221) @@ -2089,6 +2088,12 @@ CASE(JSOP_BINDNAME) } END_CASE(JSOP_BINDNAME) +CASE(JSOP_BINDVAR) +{ + PUSH_OBJECT(REGS.fp()->varObj()); +} +END_CASE(JSOP_BINDVAR) + #define BITWISE_OP(OP) \ JS_BEGIN_MACRO \ int32_t i, j; \ diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 1af441e0f..eae3e5333 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -2052,7 +2052,15 @@ macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED212, 212, "unused212", NULL, 1, 0, 0, JOF_BYTE) \ - macro(JSOP_UNUSED213, 213, "unused213", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pushes the nearest 'var' environment. + * + * Category: Variables and Scopes + * Type: Free Variables + * Operands: + * Stack: => scope + */ \ + macro(JSOP_BINDVAR, 213, "bindvar", NULL, 1, 0, 1, JOF_BYTE) \ /* * Pushes the global scope onto the stack if the script doesn't have a * non-syntactic global scope. Otherwise will act like JSOP_BINDNAME. diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 9d0dd97d0..f6973edeb 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -39,6 +39,7 @@ JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD); JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB); JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE); JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY); +JS_STATIC_ASSERT(UnicodeFlag == JSREG_UNICODE); RegExpObject* js::RegExpAlloc(ExclusiveContext* cx, HandleObject proto /* = nullptr */) @@ -219,7 +220,7 @@ RegExpObject::createNoStatics(ExclusiveContext* cx, HandleAtom source, RegExpFla tokenStream = dummyTokenStream.ptr(); } - if (!irregexp::ParsePatternSyntax(*tokenStream, alloc, source)) + if (!irregexp::ParsePatternSyntax(*tokenStream, alloc, source, flags & UnicodeFlag)) return nullptr; Rooted regexp(cx, RegExpAlloc(cx)); @@ -267,6 +268,7 @@ RegExpObject::initIgnoringLastIndex(HandleAtom source, RegExpFlag flags) setIgnoreCase(flags & IgnoreCaseFlag); setMultiline(flags & MultilineFlag); setSticky(flags & StickyFlag); + setUnicode(flags & UnicodeFlag); } void @@ -455,6 +457,8 @@ RegExpObject::toString(JSContext* cx) const return nullptr; if (multiline() && !sb.append('m')) return nullptr; + if (unicode() && !sb.append('u')) + return nullptr; if (sticky() && !sb.append('y')) return nullptr; @@ -515,7 +519,7 @@ RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString inpu /* Parse the pattern. */ irregexp::RegExpCompileData data; if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern, - multiline(), mode == MatchOnly, &data)) + multiline(), mode == MatchOnly, unicode(), ignoreCase(), &data)) { return false; } @@ -528,7 +532,7 @@ RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString inpu input->hasLatin1Chars(), mode == MatchOnly, force == ForceByteCode, - sticky()); + sticky(), unicode()); if (code.empty()) return false; @@ -949,6 +953,10 @@ ParseRegExpFlags(const CharT* chars, size_t length, RegExpFlag* flagsOut, char16 if (!HandleRegExpFlag(StickyFlag, flagsOut)) return false; break; + case 'u': + if (!HandleRegExpFlag(UnicodeFlag, flagsOut)) + return false; + break; default: return false; } diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index 24e88deb5..dbf9efa98 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -51,9 +51,10 @@ enum RegExpFlag GlobalFlag = 0x02, MultilineFlag = 0x04, StickyFlag = 0x08, + UnicodeFlag = 0x10, NoFlags = 0x00, - AllFlags = 0x0f + AllFlags = 0x1f }; enum RegExpRunStatus @@ -186,6 +187,7 @@ class RegExpShared bool global() const { return flags & GlobalFlag; } bool multiline() const { return flags & MultilineFlag; } bool sticky() const { return flags & StickyFlag; } + bool unicode() const { return flags & UnicodeFlag; } bool isCompiled(CompilationMode mode, bool latin1, ForceByteCodeEnum force = DontForceByteCode) const { @@ -340,9 +342,10 @@ class RegExpObject : public NativeObject static const unsigned IGNORE_CASE_FLAG_SLOT = 3; static const unsigned MULTILINE_FLAG_SLOT = 4; static const unsigned STICKY_FLAG_SLOT = 5; + static const unsigned UNICODE_FLAG_SLOT = 6; public: - static const unsigned RESERVED_SLOTS = 6; + static const unsigned RESERVED_SLOTS = 7; static const unsigned PRIVATE_SLOT = 7; static const Class class_; @@ -407,6 +410,7 @@ class RegExpObject : public NativeObject flags |= ignoreCase() ? IgnoreCaseFlag : 0; flags |= multiline() ? MultilineFlag : 0; flags |= sticky() ? StickyFlag : 0; + flags |= unicode() ? UnicodeFlag : 0; return RegExpFlag(flags); } @@ -432,10 +436,15 @@ class RegExpObject : public NativeObject setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled)); } + void setUnicode(bool enabled) { + setSlot(UNICODE_FLAG_SLOT, BooleanValue(enabled)); + } + bool ignoreCase() const { return getFixedSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); } bool global() const { return getFixedSlot(GLOBAL_FLAG_SLOT).toBoolean(); } bool multiline() const { return getFixedSlot(MULTILINE_FLAG_SLOT).toBoolean(); } bool sticky() const { return getFixedSlot(STICKY_FLAG_SLOT).toBoolean(); } + bool unicode() const { return getFixedSlot(UNICODE_FLAG_SLOT).toBoolean(); } bool getShared(JSContext* cx, RegExpGuard* g); diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 77d7c5362..8a08927c1 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -853,6 +853,20 @@ class StaticBlockObject : public BlockObject setSlotValue(i, PrivateValue(def)); } + // XXXshu Used only for phasing in block-scope function early + // XXXshu errors. + // XXXshu + // XXXshu Back out when major version >= 50. See [1]. + // XXXshu + // XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10 + void updateDefinitionParseNode(unsigned i, + frontend::Definition* oldDef, + frontend::Definition* newDef) + { + MOZ_ASSERT(definitionParseNode(i) == oldDef); + setSlotValue(i, PrivateValue(newDef)); + } + frontend::Definition* definitionParseNode(unsigned i) { Value v = slotValue(i); return reinterpret_cast(v.toPrivate()); diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp index ce7acc6e9..90575dbc5 100644 --- a/js/src/vm/TraceLogging.cpp +++ b/js/src/vm/TraceLogging.cpp @@ -256,9 +256,9 @@ TraceLoggerThread::enable(JSContext* cx) script = fp->script(); engine = TraceLogger_Interpreter; - if (script->compartment() != cx->compartment()) - return fail(cx, "compartment mismatch"); } + if (script->compartment() != cx->compartment()) + return fail(cx, "compartment mismatch"); TraceLoggerEvent event(this, TraceLogger_Scripts, script); startEvent(event); diff --git a/js/src/vm/Unicode.cpp b/js/src/vm/Unicode.cpp index b110fbc5d..9214450ac 100644 --- a/js/src/vm/Unicode.cpp +++ b/js/src/vm/Unicode.cpp @@ -772,4 +772,439 @@ const uint8_t unicode::index2[] = { 5, 5, 5, 0, 0, 0, }; +const FoldingInfo unicode::js_foldinfo[] = { + {0, 0, 0, 0}, + {32, 0, 0, 0}, + {32, 8415, 0, 0}, + {32, 300, 0, 0}, + {0, 65504, 0, 0}, + {0, 65504, 8383, 0}, + {0, 65504, 268, 0}, + {775, 743, 0, 0}, + {32, 8294, 0, 0}, + {0, 7615, 0, 0}, + {0, 65504, 8262, 0}, + {0, 121, 0, 0}, + {1, 0, 0, 0}, + {0, 65535, 0, 0}, + {65415, 0, 0, 0}, + {65268, 65236, 0, 0}, + {0, 195, 0, 0}, + {210, 0, 0, 0}, + {206, 0, 0, 0}, + {205, 0, 0, 0}, + {79, 0, 0, 0}, + {202, 0, 0, 0}, + {203, 0, 0, 0}, + {207, 0, 0, 0}, + {0, 97, 0, 0}, + {211, 0, 0, 0}, + {209, 0, 0, 0}, + {0, 163, 0, 0}, + {213, 0, 0, 0}, + {0, 130, 0, 0}, + {214, 0, 0, 0}, + {218, 0, 0, 0}, + {217, 0, 0, 0}, + {219, 0, 0, 0}, + {0, 56, 0, 0}, + {2, 1, 0, 0}, + {1, 65535, 0, 0}, + {0, 65534, 65535, 0}, + {0, 65457, 0, 0}, + {65439, 0, 0, 0}, + {65480, 0, 0, 0}, + {65406, 0, 0, 0}, + {10795, 0, 0, 0}, + {65373, 0, 0, 0}, + {10792, 0, 0, 0}, + {0, 10815, 0, 0}, + {65341, 0, 0, 0}, + {69, 0, 0, 0}, + {71, 0, 0, 0}, + {0, 10783, 0, 0}, + {0, 10780, 0, 0}, + {0, 10782, 0, 0}, + {0, 65326, 0, 0}, + {0, 65330, 0, 0}, + {0, 65331, 0, 0}, + {0, 65334, 0, 0}, + {0, 65333, 0, 0}, + {0, 42319, 0, 0}, + {0, 42315, 0, 0}, + {0, 65329, 0, 0}, + {0, 42280, 0, 0}, + {0, 42308, 0, 0}, + {0, 65327, 0, 0}, + {0, 65325, 0, 0}, + {0, 10743, 0, 0}, + {0, 42305, 0, 0}, + {0, 10749, 0, 0}, + {0, 65323, 0, 0}, + {0, 65322, 0, 0}, + {0, 10727, 0, 0}, + {0, 65318, 0, 0}, + {0, 42282, 0, 0}, + {0, 65467, 0, 0}, + {0, 65319, 0, 0}, + {0, 65465, 0, 0}, + {0, 65317, 0, 0}, + {0, 42261, 0, 0}, + {0, 42258, 0, 0}, + {116, 84, 7289, 0}, + {116, 0, 0, 0}, + {38, 0, 0, 0}, + {37, 0, 0, 0}, + {64, 0, 0, 0}, + {63, 0, 0, 0}, + {32, 62, 0, 0}, + {32, 96, 0, 0}, + {32, 57, 92, 0}, + {32, 65452, 7205, 0}, + {32, 86, 0, 0}, + {32, 64793, 0, 0}, + {32, 54, 0, 0}, + {32, 80, 0, 0}, + {32, 31, 0, 0}, + {32, 47, 0, 0}, + {32, 7549, 0, 0}, + {0, 65498, 0, 0}, + {0, 65499, 0, 0}, + {0, 65504, 30, 0}, + {0, 65504, 64, 0}, + {0, 65504, 25, 60}, + {0, 65420, 65504, 7173}, + {0, 65504, 54, 0}, + {0, 64761, 65504, 0}, + {0, 65504, 22, 0}, + {0, 65504, 48, 0}, + {1, 65505, 0, 0}, + {0, 65504, 65535, 0}, + {0, 65504, 15, 0}, + {0, 65504, 7517, 0}, + {0, 65472, 0, 0}, + {0, 65473, 0, 0}, + {8, 0, 0, 0}, + {65506, 65474, 0, 0}, + {65511, 65479, 35, 0}, + {65521, 65489, 0, 0}, + {65514, 65482, 0, 0}, + {0, 65528, 0, 0}, + {65482, 65450, 0, 0}, + {65488, 65456, 0, 0}, + {0, 7, 0, 0}, + {0, 65420, 0, 0}, + {65476, 65444, 65501, 0}, + {65472, 65440, 0, 0}, + {65529, 0, 0, 0}, + {80, 0, 0, 0}, + {0, 65456, 0, 0}, + {15, 0, 0, 0}, + {0, 65521, 0, 0}, + {48, 0, 0, 0}, + {0, 65488, 0, 0}, + {7264, 0, 0, 0}, + {0, 38864, 0, 0}, + {0, 8, 0, 0}, + {65528, 0, 0, 0}, + {0, 35332, 0, 0}, + {0, 3814, 0, 0}, + {1, 59, 0, 0}, + {0, 65535, 58, 0}, + {65478, 65477, 0, 0}, + {57921, 0, 0, 0}, + {0, 74, 0, 0}, + {0, 86, 0, 0}, + {0, 100, 0, 0}, + {0, 128, 0, 0}, + {0, 112, 0, 0}, + {0, 126, 0, 0}, + {0, 9, 0, 0}, + {65462, 0, 0, 0}, + {65527, 0, 0, 0}, + {58363, 58247, 58331, 0}, + {65450, 0, 0, 0}, + {65436, 0, 0, 0}, + {65424, 0, 0, 0}, + {65408, 0, 0, 0}, + {65410, 0, 0, 0}, + {58019, 57987, 0, 0}, + {57153, 57121, 0, 0}, + {57274, 57242, 0, 0}, + {28, 0, 0, 0}, + {0, 65508, 0, 0}, + {16, 0, 0, 0}, + {0, 65520, 0, 0}, + {26, 0, 0, 0}, + {0, 65510, 0, 0}, + {54793, 0, 0, 0}, + {61722, 0, 0, 0}, + {54809, 0, 0, 0}, + {0, 54741, 0, 0}, + {0, 54744, 0, 0}, + {54756, 0, 0, 0}, + {54787, 0, 0, 0}, + {54753, 0, 0, 0}, + {54754, 0, 0, 0}, + {54721, 0, 0, 0}, + {0, 58272, 0, 0}, + {30204, 0, 0, 0}, + {23256, 0, 0, 0}, + {23228, 0, 0, 0}, + {23217, 0, 0, 0}, + {23221, 0, 0, 0}, + {23231, 0, 0, 0}, + {23278, 0, 0, 0}, + {23254, 0, 0, 0}, + {23275, 0, 0, 0}, + {928, 0, 0, 0}, + {0, 64608, 0, 0}, + {26672, 0, 0, 0}, +}; + +const uint8_t unicode::folding_index1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 22, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 23, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 26, 27, 28, 26, 29, 30, + 31, 32, 0, 0, 0, 0, 33, 34, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, 26, 40, + 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, + 43, 0, 44, 45, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 50, 0, 0, +}; + +const uint8_t unicode::folding_index2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 6, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 8, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 9, 4, 4, 4, 4, 4, 10, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, + 4, 4, 4, 11, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 0, 0, + 12, 13, 12, 13, 12, 13, 0, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, + 13, 12, 13, 12, 13, 0, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 14, 12, + 13, 12, 13, 12, 13, 15, 16, 17, 12, 13, 12, 13, 18, 12, 13, 19, 19, 12, + 13, 0, 20, 21, 22, 12, 13, 19, 23, 24, 25, 26, 12, 13, 27, 0, 25, 28, + 29, 30, 12, 13, 12, 13, 12, 13, 31, 12, 13, 31, 0, 0, 12, 13, 31, 12, + 13, 32, 32, 12, 13, 12, 13, 33, 12, 13, 0, 0, 12, 13, 0, 34, 0, 0, + 0, 0, 35, 36, 37, 35, 36, 37, 35, 36, 37, 12, 13, 12, 13, 12, 13, 12, + 13, 12, 13, 12, 13, 12, 13, 12, 13, 38, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 0, 35, 36, 37, 12, 13, 39, 40, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 41, 0, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 0, 0, 0, 0, 0, 0, 42, 12, 13, 43, 44, 45, + 45, 12, 13, 46, 47, 48, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 49, 50, + 51, 52, 53, 0, 54, 54, 0, 55, 0, 56, 57, 0, 0, 0, 54, 58, 0, 59, + 0, 60, 61, 0, 62, 63, 0, 64, 65, 0, 0, 63, 0, 66, 67, 0, 0, 68, + 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 70, 0, 0, 70, 0, 0, 0, 71, + 70, 72, 73, 73, 74, 0, 0, 0, 0, 0, 75, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 76, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 13, 12, 13, + 0, 0, 12, 13, 0, 0, 0, 29, 29, 29, 0, 79, 0, 0, 0, 0, 0, 0, + 80, 0, 81, 81, 81, 0, 82, 0, 83, 83, 0, 1, 84, 1, 1, 85, 1, 1, + 86, 87, 88, 1, 89, 1, 1, 1, 90, 91, 0, 92, 1, 1, 93, 1, 1, 94, + 1, 1, 95, 96, 96, 96, 0, 4, 97, 4, 4, 98, 4, 4, 99, 100, 101, 4, + 102, 4, 4, 4, 103, 104, 105, 106, 4, 4, 107, 4, 4, 108, 4, 4, 109, 110, + 110, 111, 112, 113, 0, 0, 0, 114, 115, 116, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 117, 118, + 119, 120, 121, 122, 0, 12, 13, 123, 12, 13, 0, 41, 41, 41, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 0, 0, 0, 0, 0, 0, 0, 0, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 126, 12, 13, 12, 13, 12, 13, 12, 13, 12, + 13, 12, 13, 12, 13, 127, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 0, 130, 0, 0, 0, 0, 0, 130, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 132, 132, 132, 132, 132, 132, 0, 0, 133, 133, 133, 133, 133, 133, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 134, 0, 0, 0, 135, 0, 0, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 136, 137, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 0, 0, 0, 0, + 0, 138, 0, 0, 139, 0, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 132, 132, 132, 132, 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, + 132, 132, 132, 132, 132, 132, 0, 0, 133, 133, 133, 133, 133, 133, 0, 0, 132, 132, + 132, 132, 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, + 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, 132, 132, + 0, 0, 133, 133, 133, 133, 133, 133, 0, 0, 0, 132, 0, 132, 0, 132, 0, 132, + 0, 133, 0, 133, 0, 133, 0, 133, 132, 132, 132, 132, 132, 132, 132, 132, 133, 133, + 133, 133, 133, 133, 133, 133, 140, 140, 141, 141, 141, 141, 142, 142, 143, 143, 144, 144, + 145, 145, 0, 0, 132, 132, 132, 132, 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, + 133, 133, 132, 132, 132, 132, 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, + 132, 132, 132, 132, 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, + 0, 146, 0, 0, 0, 0, 133, 133, 147, 147, 148, 0, 149, 0, 0, 0, 0, 146, + 0, 0, 0, 0, 150, 150, 150, 150, 148, 0, 0, 0, 132, 132, 0, 0, 0, 0, + 0, 0, 133, 133, 151, 151, 0, 0, 0, 0, 132, 132, 0, 0, 0, 119, 0, 0, + 133, 133, 152, 152, 123, 0, 0, 0, 0, 0, 0, 146, 0, 0, 0, 0, 153, 153, + 154, 154, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 155, 0, 0, 0, 156, 157, 0, 0, 0, 0, + 0, 0, 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 160, 160, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 0, 0, 0, 12, 13, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 0, 12, 13, 164, 165, 166, 167, 168, 12, 13, 12, + 13, 12, 13, 169, 170, 171, 172, 0, 12, 13, 0, 12, 13, 0, 0, 0, 0, 0, + 0, 0, 173, 173, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 0, 0, 0, 0, 0, 0, 0, 12, 13, 12, 13, 0, 0, 0, + 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 174, 174, 174, + 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, + 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 0, 174, + 0, 0, 0, 0, 0, 174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 0, 0, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 13, 12, 13, 175, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, + 0, 0, 0, 12, 13, 176, 0, 0, 12, 13, 12, 13, 0, 0, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 177, 178, + 179, 180, 0, 0, 181, 182, 183, 184, 12, 13, 12, 13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186, 186, 186, 186, + 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + 186, 186, 186, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; + diff --git a/js/src/vm/Unicode.h b/js/src/vm/Unicode.h index ea853442c..16ae39891 100644 --- a/js/src/vm/Unicode.h +++ b/js/src/vm/Unicode.h @@ -234,7 +234,102 @@ CanLowerCase(char16_t ch) return CharInfo(ch).lowerCase != 0; } +class FoldingInfo { + public: + uint16_t folding; + uint16_t reverse1; + uint16_t reverse2; + uint16_t reverse3; +}; + +extern const uint8_t folding_index1[]; +extern const uint8_t folding_index2[]; +extern const FoldingInfo js_foldinfo[]; + +inline const FoldingInfo& +CaseFoldInfo(char16_t code) +{ + const size_t shift = 6; + size_t index = folding_index1[code >> shift]; + index = folding_index2[(index << shift) + (code & ((1 << shift) - 1))]; + return js_foldinfo[index]; +} + +inline char16_t +FoldCase(char16_t ch) +{ + const FoldingInfo& info = CaseFoldInfo(ch); + return uint16_t(ch) + info.folding; +} + +inline char16_t +ReverseFoldCase1(char16_t ch) +{ + const FoldingInfo& info = CaseFoldInfo(ch); + return uint16_t(ch) + info.reverse1; +} + +inline char16_t +ReverseFoldCase2(char16_t ch) +{ + const FoldingInfo& info = CaseFoldInfo(ch); + return uint16_t(ch) + info.reverse2; +} + +inline char16_t +ReverseFoldCase3(char16_t ch) +{ + const FoldingInfo& info = CaseFoldInfo(ch); + return uint16_t(ch) + info.reverse3; +} + +const size_t LeadSurrogateMin = 0xD800; +const size_t LeadSurrogateMax = 0xDBFF; +const size_t TrailSurrogateMin = 0xDC00; +const size_t TrailSurrogateMax = 0xDFFF; +const size_t UTF16Max = 0xFFFF; +const size_t NonBMPMin = 0x10000; +const size_t NonBMPMax = 0x10FFFF; + +inline bool +IsLeadSurrogate(size_t value) +{ + return value >= LeadSurrogateMin && value <= LeadSurrogateMax; +} + +inline bool +IsTrailSurrogate(size_t value) +{ + return value >= TrailSurrogateMin && value <= TrailSurrogateMax; +} + +inline void +UTF16Encode(size_t cp, size_t* lead, size_t* trail) +{ + MOZ_ASSERT(cp >= NonBMPMin && cp <= NonBMPMax); + + *lead = (cp - NonBMPMin) / 1024 + LeadSurrogateMin; + *trail = ((cp - NonBMPMin) % 1024) + TrailSurrogateMin; +} + +inline size_t +UTF16Decode(size_t lead, size_t trail) +{ + MOZ_ASSERT(IsLeadSurrogate(lead)); + MOZ_ASSERT(IsTrailSurrogate(trail)); + + return (lead - LeadSurrogateMin) * 1024 + (trail - TrailSurrogateMin) + NonBMPMin; +} + } /* namespace unicode */ } /* namespace js */ +#define FOR_EACH_NON_BMP_CASE_FOLDING(macro) \ + macro(0x10400, 0x10427, 0xD801, 0xDC00, 0xDC27, 0x28) \ + macro(0x10428, 0x1044F, 0xD801, 0xDC28, 0xDC4F, -0x28) \ + macro(0x10C80, 0x10CB2, 0xD803, 0xDC80, 0xDCB2, 0x40) \ + macro(0x10CC0, 0x10CF2, 0xD803, 0xDCC0, 0xDCF2, -0x40) \ + macro(0x118A0, 0x118bf, 0xD806, 0xDCA0, 0xDCBF, 0x20) \ + macro(0x118C0, 0x118df, 0xD806, 0xDCC0, 0xDCDF, -0x20) + #endif /* vm_Unicode_h */ diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 032eacf3f..fa796bcda 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,11 +29,11 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 330; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 336; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 421, +static_assert(JSErr_Limit == 434, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " diff --git a/js/src/vm/make_unicode.py b/js/src/vm/make_unicode.py index 16e521da9..63d9f0654 100644 --- a/js/src/vm/make_unicode.py +++ b/js/src/vm/make_unicode.py @@ -84,13 +84,33 @@ def read_unicode_data(unicode_file): row[0] = int(row[0], 16) yield row -def generate_unicode_stuff(unicode_data, data_file, test_mapping, test_space): +def read_case_folding(case_folding): + for line in case_folding: + if line == '\n' or line.startswith('#'): + continue + row = line.split('; ') + if row[1] in ['F', 'T']: + continue + row[0] = int(row[0], 16) + row[2] = int(row[2], 16) + yield row + +def generate_unicode_stuff(unicode_data, case_folding, + data_file, test_mapping, test_space, test_icase): dummy = (0, 0, 0) table = [dummy] cache = {dummy: 0} index = [0] * (MAX + 1) + folding_map = {} + rev_folding_map = {} + folding_dummy = (0, 0, 0, 0) + folding_table = [folding_dummy] + folding_cache = {folding_dummy: 0} + folding_index = [0] * (MAX + 1) test_table = {} test_space_table = [] + folding_tests = [] + folding_codes = set() for row in read_unicode_data(unicode_data): code = row[0] @@ -143,6 +163,64 @@ def generate_unicode_stuff(unicode_data, data_file, test_mapping, test_space): table.append(item) index[code] = i + for row in read_case_folding(case_folding): + code = row[0] + mapping = row[2] + folding_map[code] = mapping + + if mapping not in rev_folding_map: + rev_folding_map[mapping] = [code] + else: + rev_folding_map[mapping].append(code) + + folding_codes.add(code) + folding_codes.add(mapping) + + for code in sorted(folding_codes): + if code in folding_map: + folding = folding_map[code] + else: + folding = code + + if code in rev_folding_map: + rev_folding = rev_folding_map[code] + elif folding in rev_folding_map: + rev_folding = [c for c in rev_folding_map[folding] if c != code] + else: + rev_folding = [] + + assert len(rev_folding) <= 3 + + if folding != code or len(rev_folding): + item = [code] + if folding != code: + item.append(folding) + folding_tests.append(item + rev_folding) + + if code > MAX: + continue + + folding_d = folding - code + rev_folding_ds = [v - code for v in rev_folding] + + assert folding_d > -65535 and folding_d < 65535 + assert all([v > -65535 and v < 65535 for v in rev_folding]) + + folding = folding_d & 0xffff + rev_folding = [v & 0xffff for v in rev_folding_ds] + rev_folding_0 = rev_folding[0] if len(rev_folding) >= 1 else 0 + rev_folding_1 = rev_folding[1] if len(rev_folding) >= 2 else 0 + rev_folding_2 = rev_folding[2] if len(rev_folding) >= 3 else 0 + + item = (folding, rev_folding_0, rev_folding_1, rev_folding_2) + + i = folding_cache.get(item) + if i is None: + assert item not in folding_table + folding_cache[item] = i = len(folding_table) + folding_table.append(item) + folding_index[code] = i + test_mapping.write('/* Generated by make_unicode.py DO NOT MODIFY */\n') test_mapping.write(public_domain) test_mapping.write('var mapping = [\n') @@ -180,6 +258,29 @@ assertEq((onlySpace + 'aaaa').trim(), 'aaaa'); assertEq(('aaaa' + onlySpace).trim(), 'aaaa'); assertEq((onlySpace + 'aaaa' + onlySpace).trim(), 'aaaa'); +if (typeof reportCompare === "function") + reportCompare(true, true); +""") + + test_icase.write('/* Generated by make_unicode.py DO NOT MODIFY */\n') + test_icase.write(public_domain) + test_icase.write(""" +var BUGNUMBER = 1135377; +var summary = "Implement RegExp unicode flag -- ignoreCase flag."; + +print(BUGNUMBER + ": " + summary); + +function test(code, ...equivs) { + var codeRe = new RegExp(String.fromCodePoint(code) + "+", "iu"); + var ans = String.fromCodePoint(code) + equivs.map(c => String.fromCodePoint(c)).join(""); + assertEqArray(codeRe.exec("<" + ans + ">"), [ans]); + codeRe = new RegExp("[" + String.fromCodePoint(code) + "]+", "iu"); + assertEqArray(codeRe.exec("<" + ans + ">"), [ans]); +} +""") + for args in folding_tests: + test_icase.write('test(' + ','.join([hex(c) for c in args]) + ');\n') + test_icase.write(""" if (typeof reportCompare === "function") reportCompare(true, true); """) @@ -189,6 +290,11 @@ if (typeof reportCompare === "function") # Don't forget to update CharInfo in Unicode.cpp if you need to change this assert shift == 5 + folding_index1, folding_index2, folding_shift = splitbins(folding_index) + + # Don't forget to update CharInfo in Unicode.cpp if you need to change this + assert folding_shift == 6 + # verify correctness for char in index: test = table[index[char]] @@ -198,6 +304,14 @@ if (typeof reportCompare === "function") assert test == table[idx] + # verify correctness + for char in folding_index: + test = folding_table[folding_index[char]] + + idx = folding_index1[char >> folding_shift] + idx = folding_index2[(idx << folding_shift) + (char & ((1 << folding_shift) - 1))] + + assert test == folding_table[idx] comment = """ /* @@ -284,6 +398,19 @@ if (typeof reportCompare === "function") dump(index2, 'index2', data_file) data_file.write('\n') + data_file.write('const FoldingInfo unicode::js_foldinfo[] = {\n') + for d in folding_table: + data_file.write(' {') + data_file.write(', '.join((str(e) for e in d))) + data_file.write('},\n') + data_file.write('};\n') + data_file.write('\n') + + dump(folding_index1, 'folding_index1', data_file) + data_file.write('\n') + dump(folding_index2, 'folding_index2', data_file) + data_file.write('\n') + data_file.write('\n') def getsize(data): @@ -362,7 +489,7 @@ if __name__ == '__main__': print('Always make sure you have the newest UnicodeData.txt!') unicode_data = open(sys.argv[1], 'r') else: - print('Downloading...') + print('Downloading UnicodeData.txt...') reader = urllib2.urlopen('http://unicode.org/Public/UNIDATA/UnicodeData.txt') data = reader.read() reader.close() @@ -370,8 +497,21 @@ if __name__ == '__main__': unicode_data.write(data) unicode_data.seek(0) + if len(sys.argv) > 2: + print('Always make sure you have the newest CaseFolding.txt!') + case_folding = open(sys.argv[2], 'r') + else: + print('Downloading CaseFolding.txt...') + reader = urllib2.urlopen('http://unicode.org/Public/UNIDATA/CaseFolding.txt') + data = reader.read() + reader.close() + case_folding = open('CaseFolding.txt', 'w+') + case_folding.write(data) + case_folding.seek(0) + print('Generating...') - generate_unicode_stuff(unicode_data, + generate_unicode_stuff(unicode_data, case_folding, open('Unicode.cpp', 'w'), open('../tests/ecma_5/String/string-upper-lower-mapping.js', 'w'), - open('../tests/ecma_5/String/string-space-trim.js', 'w')) + open('../tests/ecma_5/String/string-space-trim.js', 'w'), + open('../tests/ecma_6/RegExp/unicode-ignoreCase.js', 'w')) diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index f600dd0ee..cbcd427d2 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -198,7 +198,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 gPrototypeProperties['RegExp'] = ["constructor", "toSource", "toString", "compile", "exec", "test", - "flags", "global", "ignoreCase", "multiline", "source", "sticky", + "flags", "global", "ignoreCase", "multiline", "source", "sticky", "unicode", "lastIndex"]; // Sort an array that may contain symbols as well as strings. @@ -612,7 +612,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 // Test with modified flags accessors iwin.eval(` -var props = ["global", "ignoreCase", "multiline", "sticky", "source"]; +var props = ["global", "ignoreCase", "multiline", "sticky", "source", "unicode"]; var origDescs = {}; for (var prop of props) { origDescs[prop] = Object.getOwnPropertyDescriptor(RegExp.prototype, prop); diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index 00576e007..efc339e6f 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -75,9 +75,8 @@ AccessCheck::wrapperSubsumes(JSObject* wrapper) bool AccessCheck::isChrome(JSCompartment* compartment) { - bool privileged; nsIPrincipal* principal = GetCompartmentPrincipal(compartment); - return NS_SUCCEEDED(nsXPConnect::SecurityManager()->IsSystemPrincipal(principal, &privileged)) && privileged; + return nsXPConnect::SystemPrincipal() == principal; } bool diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 2e66c4e18..864f83682 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -1046,7 +1046,8 @@ public: mContainerBounds(aContainerBounds), mParameters(aParameters), mPaintedLayerDataTree(*this, aBackgroundColor), - mFlattenToSingleLayer(aFlattenToSingleLayer) + mFlattenToSingleLayer(aFlattenToSingleLayer), + mLastDisplayPortAGR(nullptr) { nsPresContext* presContext = aContainerFrame->PresContext(); mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); @@ -1364,6 +1365,12 @@ protected: bool ChooseAnimatedGeometryRoot(const nsDisplayList& aList, AnimatedGeometryRoot **aAnimatedGeometryRoot); + /** + * Get the display port for an AGR. + * The result would be cached for later reusing. + */ + nsRect GetDisplayPortForAnimatedGeometryRoot(AnimatedGeometryRoot* aAnimatedGeometryRoot); + nsDisplayListBuilder* mBuilder; LayerManager* mManager; FrameLayerBuilder* mLayerBuilder; @@ -1420,6 +1427,8 @@ protected: nsDataHashtable, RefPtr> mRecycledMaskImageLayers; + AnimatedGeometryRoot* mLastDisplayPortAGR; + nsRect mLastDisplayPortRect; }; class PaintedDisplayItemLayerUserData : public LayerUserData @@ -3566,6 +3575,10 @@ PaintedLayerData::AccumulateEventRegions(ContainerState* aState, nsDisplayLayerE tmp.Or(mVerticalPanRegion, aEventRegions->VerticalPanRegion()); mVerticalPanRegion = tmp; #endif + // Avoid quadratic performance as a result of the region growing to include + // and arbitrarily large number of rects, which can happen on some pages. + mMaybeHitRegion.SimplifyOutward(8); + // Calculate scaled versions of the bounds of mHitRegion and mMaybeHitRegion // for quick access in FindPaintedLayerFor(). mScaledHitRegionBounds = aState->ScaleToOutsidePixels(mHitRegion.GetBounds()); @@ -3703,6 +3716,34 @@ ContainerState::ChooseAnimatedGeometryRoot(const nsDisplayList& aList, return false; } +nsRect +ContainerState::GetDisplayPortForAnimatedGeometryRoot(AnimatedGeometryRoot* aAnimatedGeometryRoot) +{ +// Faster version of the code from ComputeOpaqueRect (after bug 1331342). + if (mLastDisplayPortAGR == aAnimatedGeometryRoot) { + return mLastDisplayPortRect; + } + + nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot); + if (sf == nullptr) { + return nsRect(); + } + + mLastDisplayPortAGR = aAnimatedGeometryRoot; + nsRect& displayport = mLastDisplayPortRect; + + bool usingDisplayport = + nsLayoutUtils::GetDisplayPort((*aAnimatedGeometryRoot)->GetContent(), &displayport); + if (!usingDisplayport) { + // No async scrolling, so all that matters is that the layer contents + // cover the scrollport. + displayport = sf->GetScrollPortRect(); + } + nsIFrame* scrollFrame = do_QueryFrame(sf); + displayport += scrollFrame->GetOffsetToCrossDoc(mContainerReferenceFrame); + return displayport; +} + nsIntRegion ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem, AnimatedGeometryRoot* aAnimatedGeometryRoot, @@ -3740,6 +3781,7 @@ ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem, } opaquePixels = ScaleRegionToInsidePixels(opaqueClipped, snapOpaque); +#if(0) nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot); if (sf) { nsRect displayport; @@ -3756,6 +3798,17 @@ ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem, *aOpaqueForAnimatedGeometryRootParent = true; } } +#else +// backbugs from bug 1220466 and bug 1331342 + if (IsInInactiveLayer()) return opaquePixels; + + const nsRect& displayport = + GetDisplayPortForAnimatedGeometryRoot(aAnimatedGeometryRoot); + if (!displayport.IsEmpty() && + opaquePixels.Contains(ScaleRegionToNearestPixels(displayport))) { + *aOpaqueForAnimatedGeometryRootParent = true; + } +#endif // } return opaquePixels; } @@ -4041,11 +4094,11 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) // clip we care about is the overflow:hidden clip on the scrollbar. mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot->mParentAGR, clipPtr, - uniformColorPtr); + IsInInactiveLayer() ? nullptr : uniformColorPtr); } else if (prerenderedTransform) { mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot, clipPtr, - uniformColorPtr); + IsInInactiveLayer() ? nullptr : uniformColorPtr); } else { // Using itemVisibleRect here isn't perfect. itemVisibleRect can be // larger or smaller than the potential bounds of item's contents in @@ -4056,7 +4109,8 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) // Time will tell whether this is good enough, or whether we need to do // something more sophisticated here. mPaintedLayerDataTree.AddingOwnLayer(animatedGeometryRoot, - &itemVisibleRect, uniformColorPtr); + &itemVisibleRect, + IsInInactiveLayer() ? nullptr : uniformColorPtr); } mParameters.mBackgroundColor = uniformColor; diff --git a/layout/base/nsCounterManager.cpp b/layout/base/nsCounterManager.cpp index 6adf34d06..90a0268cd 100644 --- a/layout/base/nsCounterManager.cpp +++ b/layout/base/nsCounterManager.cpp @@ -186,11 +186,7 @@ nsCounterList::RecalcAll() { mDirty = false; - nsCounterNode *node = First(); - if (!node) - return; - - do { + for (nsCounterNode* node = First(); node; node = Next(node)) { SetScope(node); node->Calc(this); @@ -205,7 +201,7 @@ nsCounterList::RecalcAll() useNode->mText->SetData(text); } } - } while ((node = Next(node)) != First()); + } } nsCounterManager::nsCounterManager() @@ -291,21 +287,17 @@ nsCounterManager::SetAllCounterStylesDirty() { for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) { nsCounterList* list = iter.UserData(); - nsCounterNode* first = list->First(); - if (first) { - bool changed = false; - nsCounterNode* node = first; - do { - if (node->mType == nsCounterNode::USE) { - node->UseNode()->SetCounterStyleDirty(); - changed = true; - } - } while ((node = list->Next(node)) != first); - - if (changed) { - list->SetDirty(); + bool changed = false; + for (nsCounterNode* node = list->First(); node; node = list->Next(node)) { + if (node->mType == nsCounterNode::USE) { + node->UseNode()->SetCounterStyleDirty(); + changed = true; } } + + if (changed) { + list->SetDirty(); + } } } @@ -327,32 +319,29 @@ nsCounterManager::DestroyNodesFor(nsIFrame *aFrame) void nsCounterManager::Dump() { - printf("\n\nCounter Manager Lists:\n"); - for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) { - printf("Counter named \"%s\":\n", - NS_ConvertUTF16toUTF8(iter.Key()).get()); + printf("\n\nCounter Manager Lists:\n"); + for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) { + printf("Counter named \"%s\":\n", + NS_ConvertUTF16toUTF8(iter.Key()).get()); - nsCounterList* list = iter.UserData(); - nsCounterNode* node = list->First(); - if (node) { - int32_t i = 0; - do { - const char* types[] = { "RESET", "INCREMENT", "USE" }; - printf(" Node #%d @%p frame=%p index=%d type=%s valAfter=%d\n" - " scope-start=%p scope-prev=%p", - i++, (void*)node, (void*)node->mPseudoFrame, - node->mContentIndex, types[node->mType], - node->mValueAfter, (void*)node->mScopeStart, - (void*)node->mScopePrev); - if (node->mType == nsCounterNode::USE) { - nsAutoString text; - node->UseNode()->GetText(text); - printf(" text=%s", NS_ConvertUTF16toUTF8(text).get()); - } - printf("\n"); - } while ((node = list->Next(node)) != list->First()); - } + nsCounterList* list = iter.UserData(); + int32_t i = 0; + for (nsCounterNode* node = list->First(); node; node = list->Next(node)) { + const char* types[] = { "RESET", "INCREMENT", "USE" }; + printf(" Node #%d @%p frame=%p index=%d type=%s valAfter=%d\n" + " scope-start=%p scope-prev=%p", + i++, (void*)node, (void*)node->mPseudoFrame, + node->mContentIndex, types[node->mType], + node->mValueAfter, (void*)node->mScopeStart, + (void*)node->mScopePrev); + if (node->mType == nsCounterNode::USE) { + nsAutoString text; + node->UseNode()->GetText(text); + printf(" text=%s", NS_ConvertUTF16toUTF8(text).get()); + } + printf("\n"); } - printf("\n\n"); + } + printf("\n\n"); } #endif diff --git a/layout/base/nsCounterManager.h b/layout/base/nsCounterManager.h index 2eb0a8c03..e8ace6368 100644 --- a/layout/base/nsCounterManager.h +++ b/layout/base/nsCounterManager.h @@ -187,7 +187,7 @@ public: } nsCounterNode* First() { - return static_cast(mFirstNode); + return static_cast(mList.getFirst()); } static nsCounterNode* Next(nsCounterNode* aNode) { diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 40d279dae..23e5316d4 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -694,7 +694,7 @@ AnimatedGeometryRoot* nsDisplayListBuilder::WrapAGRForFrame(nsIFrame* aAnimatedGeometryRoot, AnimatedGeometryRoot* aParent /* = nullptr */) { - MOZ_ASSERT(IsAnimatedGeometryRoot(aAnimatedGeometryRoot)); + MOZ_ASSERT(IsAnimatedGeometryRoot(aAnimatedGeometryRoot) == AGR_YES); AnimatedGeometryRoot* result = nullptr; if (!mFrameToAnimatedGeometryRootMap.Get(aAnimatedGeometryRoot, &result)) { @@ -1079,66 +1079,91 @@ IsStickyFrameActive(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* return sf->IsScrollingActive(aBuilder) && sf->GetScrolledFrame() == cursor; } -bool +nsDisplayListBuilder::AGRState nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParent) { if (aFrame == mReferenceFrame) { - return true; + return AGR_YES; } if (!IsPaintingToWindow()) { if (aParent) { *aParent = nsLayoutUtils::GetCrossDocParentFrame(aFrame); } - return false; + return AGR_NO; } if (nsLayoutUtils::IsPopup(aFrame)) - return true; + return AGR_YES; if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(aFrame)) - return true; + // XXX: If we wanted to implement an AGRBudget, that's here. + // See bug 1247554, but it might not be needed since we have bug 1231818. + + return AGR_YES; if (!aFrame->GetParent() && nsLayoutUtils::ViewportHasDisplayPort(aFrame->PresContext())) { // Viewport frames in a display port need to be animated geometry roots // for background-attachment:fixed elements. - return true; + return AGR_YES; } if (aFrame->IsTransformed()) { - return true; + return AGR_YES; } nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame); if (!parent) - return true; + return AGR_YES; + + bool maybe = false; // Possible to transition from not being an AGR + // to being an AGR without a style change. nsIAtom* parentType = parent->GetType(); // Treat the slider thumb as being as an active scrolled root when it wants // its own layer so that it can move without repainting. - if (parentType == nsGkAtoms::sliderFrame && nsLayoutUtils::IsScrollbarThumbLayerized(aFrame)) { - return true; + if (parentType == nsGkAtoms::sliderFrame) { + if (nsLayoutUtils::IsScrollbarThumbLayerized(aFrame)) { + return AGR_YES; + } + maybe = true; } - if (aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY && - IsStickyFrameActive(this, aFrame, parent)) - { - return true; + if (aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY) { + if (IsStickyFrameActive(this, aFrame, parent)) { + return AGR_YES; + } + maybe = true; } if (parentType == nsGkAtoms::scrollFrame || parentType == nsGkAtoms::listControlFrame) { nsIScrollableFrame* sf = do_QueryFrame(parent); - if (sf->IsScrollingActive(this) && sf->GetScrolledFrame() == aFrame) { - return true; + if (sf->GetScrolledFrame() == aFrame) { + if (sf->IsScrollingActive(this)) { + return AGR_YES; + } + maybe = true; } } // Fixed-pos frames are parented by the viewport frame, which has no parent. if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame)) { - return true; + return AGR_YES; + } + + if ((aFrame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED) && + aFrame->IsFrameOfType(nsIFrame::eSVG)) { + // For SVG containers, they always have + // NS_FRAME_MAY_BE_TRANSFORMED bit. However, they would be + // affected by the fragement identifiers in the svgView form at + // runtime without a new style context. + // For example, layout/reftests/svg/fragmentIdentifier-01.xhtml + // + // see https://www.w3.org/TR/SVG/linking.html#SVGFragmentIdentifiers + maybe = true; } if (aParent) { *aParent = parent; } - return false; + return !maybe ? AGR_NO : AGR_MAYBE; } nsIFrame* @@ -1148,7 +1173,7 @@ nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame) nsIFrame* cursor = aFrame; while (cursor != RootReferenceFrame()) { nsIFrame* next; - if (IsAnimatedGeometryRoot(cursor, &next)) + if (IsAnimatedGeometryRoot(cursor, &next) == AGR_YES) return cursor; cursor = next; } @@ -1159,7 +1184,7 @@ void nsDisplayListBuilder::RecomputeCurrentAnimatedGeometryRoot() { if (*mCurrentAGR != mCurrentFrame && - IsAnimatedGeometryRoot(const_cast(mCurrentFrame))) { + IsAnimatedGeometryRoot(const_cast(mCurrentFrame)) == AGR_YES) { AnimatedGeometryRoot* oldAGR = mCurrentAGR; mCurrentAGR = WrapAGRForFrame(const_cast(mCurrentFrame), mCurrentAGR); diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index aa8a69b24..a770da648 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -160,6 +160,15 @@ class nsDisplayListBuilder { typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect; typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion; + /** + * A frame can be in one of three states of AGR. + * AGR_NO means the frame is not an AGR for now. + * AGR_YES means the frame is an AGR for now. + * AGR_MAYBE means the frame is not an AGR for now, but a transition + * to AGR_YES without restyling is possible. + */ + enum AGRState { AGR_NO, AGR_YES, AGR_MAYBE }; + public: typedef mozilla::FramePropertyDescriptor FramePropertyDescriptor; typedef mozilla::FrameLayerBuilder FrameLayerBuilder; @@ -636,8 +645,9 @@ public: aBuilder->FindReferenceFrameFor(aForChild, &aBuilder->mCurrentOffsetToReferenceFrame); } + mCurrentAGRState = aBuilder->IsAnimatedGeometryRoot(aForChild); if (aBuilder->mCurrentFrame == aForChild->GetParent()) { - if (aBuilder->IsAnimatedGeometryRoot(aForChild)) { + if (mCurrentAGRState == AGR_YES) { aBuilder->mCurrentAGR = aBuilder->WrapAGRForFrame(aForChild, aBuilder->mCurrentAGR); } } else if (aForChild != aBuilder->mCurrentFrame) { @@ -661,8 +671,10 @@ public: return mPrevAnimatedGeometryRoot; } bool IsAnimatedGeometryRoot() const { - return *mBuilder->mCurrentAGR == mBuilder->mCurrentFrame; - + return mCurrentAGRState == AGR_YES; + } + bool MaybeAnimatedGeometryRoot() const { + return mCurrentAGRState == AGR_MAYBE; } ~AutoBuildingDisplayList() { mBuilder->mCurrentFrame = mPrevFrame; @@ -676,6 +688,7 @@ public: } private: nsDisplayListBuilder* mBuilder; + AGRState mCurrentAGRState; const nsIFrame* mPrevFrame; const nsIFrame* mPrevReferenceFrame; nsIFrame* mPrevAnimatedGeometryRoot; @@ -938,7 +951,7 @@ private: * Returns whether a frame acts as an animated geometry root, optionally * returning the next ancestor to check. */ - bool IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParent = nullptr); + AGRState IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParent = nullptr); /** * Returns the nearest ancestor frame to aFrame that is considered to have diff --git a/layout/base/nsGenConList.cpp b/layout/base/nsGenConList.cpp index 06425bbb6..1842a1041 100644 --- a/layout/base/nsGenConList.cpp +++ b/layout/base/nsGenConList.cpp @@ -13,55 +13,39 @@ void nsGenConList::Clear() { - //Delete entire list - if (!mFirstNode) - return; - for (nsGenConNode *node = Next(mFirstNode); node != mFirstNode; - node = Next(mFirstNode)) - { - Remove(node); + // Delete entire list. + mNodes.Clear(); + while (nsGenConNode* node = mList.popFirst()) { delete node; } - delete mFirstNode; - - mFirstNode = nullptr; mSize = 0; + mLastInserted = nullptr; } bool nsGenConList::DestroyNodesFor(nsIFrame* aFrame) { - if (!mFirstNode) - return false; // list empty - nsGenConNode* node; - bool destroyed = false; - while (mFirstNode->mPseudoFrame == aFrame) { - destroyed = true; - node = Next(mFirstNode); - bool isLastNode = node == mFirstNode; // before they're dangling - Remove(mFirstNode); - delete mFirstNode; - if (isLastNode) { - mFirstNode = nullptr; - return true; - } - else { - mFirstNode = node; - } + // This algorithm relies on the invariant that nodes of a frame are + // put contiguously in the linked list. This is guaranteed because + // each frame is mapped to only one (nsIContent, pseudoType) pair, + // and the nodes in the linked list are put in the tree order based + // on that pair and offset inside frame. + nsGenConNode* node = mNodes.GetAndRemove(aFrame).valueOr(nullptr); + if (!node) { + return false; } - node = Next(mFirstNode); - while (node != mFirstNode) { - if (node->mPseudoFrame == aFrame) { - destroyed = true; - nsGenConNode *nextNode = Next(node); - Remove(node); - delete node; - node = nextNode; - } else { - node = Next(node); - } + MOZ_ASSERT(node->mPseudoFrame == aFrame); + + while (node && node->mPseudoFrame == aFrame) { + nsGenConNode* nextNode = Next(node); + Destroy(node); + node = nextNode; } - return destroyed; + + // Modification of the list invalidates the cached pointer. + mLastInserted = nullptr; + + return true; } /** @@ -118,62 +102,101 @@ nsGenConList::NodeAfter(const nsGenConNode* aNode1, const nsGenConNode* aNode2) // XXX Switch to the frame version of DoCompareTreePosition? int32_t cmp = nsLayoutUtils::DoCompareTreePosition(content1, content2, pseudoType1, -pseudoType2); - NS_ASSERTION(cmp != 0, "same content, different frames"); + MOZ_ASSERT(cmp != 0, "same content, different frames"); return cmp > 0; } void nsGenConList::Insert(nsGenConNode* aNode) { - if (mFirstNode) { - // Check for append. - if (NodeAfter(aNode, Prev(mFirstNode))) { - PR_INSERT_BEFORE(aNode, mFirstNode); - } - else { - // Binary search. + // Check for append. + if (mList.isEmpty() || NodeAfter(aNode, mList.getLast())) { + mList.insertBack(aNode); + } else if (mLastInserted && mLastInserted != mList.getLast() && + NodeAfter(aNode, mLastInserted) && + NodeAfter(Next(mLastInserted), aNode)) { + // Fast path for inserting many consecutive nodes in one place + mLastInserted->setNext(aNode); + } else { + // Binary search. - // the range of indices at which |aNode| could end up. - // (We already know it can't be at index mSize.) - uint32_t first = 0, last = mSize - 1; + // the range of indices at which |aNode| could end up. + // (We already know it can't be at index mSize.) + uint32_t first = 0, last = mSize - 1; - // A cursor to avoid walking more than the length of the list. - nsGenConNode *curNode = Prev(mFirstNode); - uint32_t curIndex = mSize - 1; + // A cursor to avoid walking more than the length of the list. + nsGenConNode* curNode = mList.getLast(); + uint32_t curIndex = mSize - 1; - while (first != last) { - uint32_t test = (first + last) / 2; - if (last == curIndex) { - for ( ; curIndex != test; --curIndex) - curNode = Prev(curNode); - } else { - for ( ; curIndex != test; ++curIndex) - curNode = Next(curNode); - } - - if (NodeAfter(aNode, curNode)) { - first = test + 1; - // if we exit the loop, we need curNode to be right - ++curIndex; + while (first != last) { + uint32_t test = (first + last) / 2; + if (last == curIndex) { + for ( ; curIndex != test; --curIndex) + curNode = Prev(curNode); + } else { + for ( ; curIndex != test; ++curIndex) curNode = Next(curNode); - } else { - last = test; - } } - PR_INSERT_BEFORE(aNode, curNode); - if (curNode == mFirstNode) { - mFirstNode = aNode; + + if (NodeAfter(aNode, curNode)) { + first = test + 1; + // if we exit the loop, we need curNode to be right + ++curIndex; + curNode = Next(curNode); + } else { + last = test; } } - } - else { - // initialize list with first node - PR_INIT_CLIST(aNode); - mFirstNode = aNode; + curNode->setPrevious(aNode); } ++mSize; - NS_ASSERTION(aNode == mFirstNode || NodeAfter(aNode, Prev(aNode)), + mLastInserted = aNode; + + // Set the mapping only if it is the first node of the frame. + // The DEBUG blocks below are for ensuring the invariant required by + // nsGenConList::DestroyNodesFor. See comment there. + if (IsFirst(aNode) || + Prev(aNode)->mPseudoFrame != aNode->mPseudoFrame) { +#ifdef DEBUG + if (nsGenConNode* oldFrameFirstNode = mNodes.Get(aNode->mPseudoFrame)) { + MOZ_ASSERT(Next(aNode) == oldFrameFirstNode, + "oldFrameFirstNode should now be immediately after " + "the newly-inserted one."); + } else { + // If the node is not the only node in the list. + if (!IsFirst(aNode) || !IsLast(aNode)) { + nsGenConNode* nextNode = Next(aNode); + MOZ_ASSERT(!nextNode || nextNode->mPseudoFrame != aNode->mPseudoFrame, + "There shouldn't exist any node for this frame."); + // If the node is neither the first nor the last node + if (!IsFirst(aNode) && !IsLast(aNode)) { + MOZ_ASSERT(Prev(aNode)->mPseudoFrame != nextNode->mPseudoFrame, + "New node should not break contiguity of nodes of " + "the same frame."); + } + } + } +#endif + mNodes.Put(aNode->mPseudoFrame, aNode); + } else { +#ifdef DEBUG + nsGenConNode* frameFirstNode = mNodes.Get(aNode->mPseudoFrame); + MOZ_ASSERT(frameFirstNode, "There should exist node map for the frame."); + for (nsGenConNode* curNode = Prev(aNode); + curNode != frameFirstNode; curNode = Prev(curNode)) { + MOZ_ASSERT(curNode->mPseudoFrame == aNode->mPseudoFrame, + "Every node between frameFirstNode and the new node inserted " + "should refer to the same frame."); + MOZ_ASSERT(!IsFirst(curNode), + "The newly-inserted node should be in a contiguous run after " + "frameFirstNode, thus frameFirstNode should be reached before " + "the first node of mList."); + } +#endif + } + + NS_ASSERTION(IsFirst(aNode) || NodeAfter(aNode, Prev(aNode)), "sorting error"); NS_ASSERTION(IsLast(aNode) || NodeAfter(Next(aNode), aNode), "sorting error"); diff --git a/layout/base/nsGenConList.h b/layout/base/nsGenConList.h index 2d0ad9f3e..58421d430 100644 --- a/layout/base/nsGenConList.h +++ b/layout/base/nsGenConList.h @@ -8,15 +8,15 @@ #ifndef nsGenConList_h___ #define nsGenConList_h___ +#include "mozilla/LinkedList.h" #include "nsIFrame.h" #include "nsStyleStruct.h" -#include "prclist.h" #include "nsCSSPseudoElements.h" #include "nsTextNode.h" class nsGenConList; -struct nsGenConNode : public PRCList { +struct nsGenConNode : public mozilla::LinkedListElement { // The wrapper frame for all of the pseudo-element's content. This // frame generally has useful style data and has the // NS_FRAME_GENERATED_CONTENT bit set (so we use it to track removal), @@ -51,7 +51,7 @@ struct nsGenConNode : public PRCList { * @return true iff this marked the list dirty */ virtual bool InitTextFrame(nsGenConList* aList, nsIFrame* aPseudoFrame, - nsIFrame* aTextFrame) + nsIFrame* aTextFrame) { mPseudoFrame = aPseudoFrame; CheckFrameAssertions(); @@ -82,28 +82,55 @@ protected: class nsGenConList { protected: - nsGenConNode* mFirstNode; + mozilla::LinkedList mList; uint32_t mSize; + public: - nsGenConList() : mFirstNode(nullptr), mSize(0) {} + nsGenConList() : mSize(0), mLastInserted(nullptr) {} ~nsGenConList() { Clear(); } void Clear(); static nsGenConNode* Next(nsGenConNode* aNode) { - return static_cast(PR_NEXT_LINK(aNode)); + MOZ_ASSERT(aNode, "aNode cannot be nullptr!"); + return aNode->getNext(); } static nsGenConNode* Prev(nsGenConNode* aNode) { - return static_cast(PR_PREV_LINK(aNode)); + MOZ_ASSERT(aNode, "aNode cannot be nullptr!"); + return aNode->getPrevious(); } void Insert(nsGenConNode* aNode); - // returns whether any nodes have been destroyed - bool DestroyNodesFor(nsIFrame* aFrame); //destroy all nodes with aFrame as parent + + // Destroy all nodes with aFrame as parent. Returns true if some nodes + // have been destroyed; otherwise false. + bool DestroyNodesFor(nsIFrame* aFrame); // Return true if |aNode1| is after |aNode2|. static bool NodeAfter(const nsGenConNode* aNode1, - const nsGenConNode* aNode2); + const nsGenConNode* aNode2); - void Remove(nsGenConNode* aNode) { PR_REMOVE_LINK(aNode); mSize--; } - bool IsLast(nsGenConNode* aNode) { return (Next(aNode) == mFirstNode); } + bool IsFirst(nsGenConNode* aNode) { + MOZ_ASSERT(aNode, "aNode cannot be nullptr!"); + return aNode == mList.getFirst(); + } + + bool IsLast(nsGenConNode* aNode) { + MOZ_ASSERT(aNode, "aNode cannot be nullptr!"); + return aNode == mList.getLast(); + } + +private: + void Destroy(nsGenConNode* aNode) + { + MOZ_ASSERT(aNode, "aNode cannot be nullptr!"); + delete aNode; + mSize--; + } + + // Map from frame to the first nsGenConNode of it in the list. + nsDataHashtable, nsGenConNode*> mNodes; + + // A weak pointer to the node most recently inserted, used to avoid repeated + // list traversals in Insert(). + nsGenConNode* mLastInserted; }; #endif /* nsGenConList_h___ */ diff --git a/layout/base/nsQuoteList.cpp b/layout/base/nsQuoteList.cpp index e11d72c60..0441fe485 100644 --- a/layout/base/nsQuoteList.cpp +++ b/layout/base/nsQuoteList.cpp @@ -73,20 +73,13 @@ nsQuoteList::Calc(nsQuoteNode* aNode) void nsQuoteList::RecalcAll() { - nsQuoteNode *node = FirstNode(); - if (!node) - return; - - do { + for (nsQuoteNode* node = FirstNode(); node; node = Next(node)) { int32_t oldDepth = node->mDepthBefore; Calc(node); if (node->mDepthBefore != oldDepth && node->mText && node->IsRealQuote()) node->mText->SetData(*node->Text()); - - // Next node - node = Next(node); - } while (node != FirstNode()); + } } #ifdef DEBUG @@ -94,11 +87,7 @@ void nsQuoteList::PrintChain() { printf("Chain: \n"); - if (!FirstNode()) { - return; - } - nsQuoteNode* node = FirstNode(); - do { + for (nsQuoteNode* node = FirstNode(); node; node = Next(node)) { printf(" %p %d - ", static_cast(node), node->mDepthBefore); switch(node->mType) { case (eStyleContentType_OpenQuote): @@ -123,7 +112,6 @@ nsQuoteList::PrintChain() printf(" \"%s\",", NS_ConvertUTF16toUTF8(data).get()); } printf("\n"); - node = Next(node); - } while (node != FirstNode()); + } } #endif diff --git a/layout/base/nsQuoteList.h b/layout/base/nsQuoteList.h index 12afc3928..19aa791ce 100644 --- a/layout/base/nsQuoteList.h +++ b/layout/base/nsQuoteList.h @@ -70,7 +70,7 @@ struct nsQuoteNode : public nsGenConNode { class nsQuoteList : public nsGenConList { private: - nsQuoteNode* FirstNode() { return static_cast(mFirstNode); } + nsQuoteNode* FirstNode() { return static_cast(mList.getFirst()); } public: // assign the correct |mDepthBefore| value to a node that has been inserted // Should be called immediately after calling |Insert|. diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index 521a0cbc0..7c9d54d94 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -262,22 +262,6 @@ nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f, !IsFixedMarginSize(margin->mMargin.GetIEnd(wm))) { return true; } - if (!wm.IsBidiLTR()) { - // Note that even if 'istart' is a length, our position can - // still depend on the containing block isze, because if - // 'iend' is also a length we will discard 'istart' and be - // positioned relative to the containing block iend edge. - // 'istart' length and 'iend' auto is the only combination - // we can be sure of. - if (!IsFixedOffset(pos->mOffset.GetIStart(wm)) || - pos->mOffset.GetIEndUnit(wm) != eStyleUnit_Auto) { - return true; - } - } else { - if (!IsFixedOffset(pos->mOffset.GetIStart(wm))) { - return true; - } - } } if (wm.IsVertical() ? aCBWidthChanged : aCBHeightChanged) { // See if f's block-size might have changed. @@ -302,10 +286,42 @@ nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f, !IsFixedMarginSize(margin->mMargin.GetBEnd(wm))) { return true; } - if (!IsFixedOffset(pos->mOffset.GetBStart(wm))) { + } + + // Since we store coordinates relative to top and left, the position + // of a frame depends on that of its container if it is fixed relative + // to the right or bottom, or if it is positioned using percentages + // relative to the left or top. Because of the dependency on the + // sides (left and top) that we use to store coordinates, these tests + // are easier to do using physical coordinates rather than logical. + if (aCBWidthChanged) { + if (!IsFixedOffset(pos->mOffset.GetLeft())) { + return true; + } + // Note that even if 'left' is a length, our position can still + // depend on the containing block width, because if our direction or + // writing-mode moves from right to left (in either block or inline + // progression) and 'right' is not 'auto', we will discard 'left' + // and be positioned relative to the containing block right edge. + // 'left' length and 'right' auto is the only combination we can be + // sure of. + if ((wm.GetInlineDir() == WritingMode::eInlineRTL || + wm.GetBlockDir() == WritingMode::eBlockRL) && + pos->mOffset.GetRightUnit() != eStyleUnit_Auto) { return true; } } + if (aCBHeightChanged) { + if (!IsFixedOffset(pos->mOffset.GetTop())) { + return true; + } + // See comment above for width changes. + if (wm.GetInlineDir() == WritingMode::eInlineBTT && + pos->mOffset.GetBottomUnit() != eStyleUnit_Auto) { + return true; + } + } + return false; } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 15ff42276..44116634e 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -870,6 +870,12 @@ nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { PresContext()->SetBidiEnabled(); } + + /* bug 1342009 has the following: + RemoveStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS | + NS_FRAME_SIMPLE_DISPLAYLIST); + */ + RemoveStateBits(NS_FRAME_SIMPLE_DISPLAYLIST); } // MSVC fails with link error "one or more multiply defined symbols found", @@ -1705,8 +1711,10 @@ ApplyClipPropClipping(nsDisplayListBuilder* aBuilder, * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping * for that overflow in aBuilder->ClipState() to clip all containing-block * descendants. + * + * Return true if clipping was applied. */ -static void +static bool ApplyOverflowClipping(nsDisplayListBuilder* aBuilder, const nsIFrame* aFrame, const nsStyleDisplay* aDisp, @@ -1718,7 +1726,7 @@ ApplyOverflowClipping(nsDisplayListBuilder* aBuilder, // is required by comboboxes which make their display text (an inline frame) // have clipping. if (!nsFrame::ShouldApplyOverflowClipping(aFrame, aDisp)) { - return; + return false; } nsRect clipRect; bool haveRadii = false; @@ -1734,6 +1742,7 @@ ApplyOverflowClipping(nsDisplayListBuilder* aBuilder, // XXX border-radius } aClipState.ClipContainingBlockDescendantsExtra(clipRect, haveRadii ? radii : nullptr); + return true; } #ifdef DEBUG @@ -2334,6 +2343,45 @@ WrapInWrapList(nsDisplayListBuilder* aBuilder, return item; } +/** + * Check if a frame should be visited for building display list. + */ +static bool +DescendIntoChild(nsDisplayListBuilder* aBuilder, nsIFrame *aChild, + const nsRect& aDirty) +{ + nsIFrame* child = aChild; + const nsRect& dirty = aDirty; + + if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { + // No need to descend into child to catch placeholders for visible + // positioned stuff. So see if we can short-circuit frame traversal here. + + // We can stop if child's frame subtree's intersection with the + // dirty area is empty. + // If the child is a scrollframe that we want to ignore, then we need + // to descend into it because its scrolled child may intersect the dirty + // area even if the scrollframe itself doesn't. + // There are cases where the "ignore scroll frame" on the builder is not set + // correctly, and so we additionally want to catch cases where the child is + // a root scrollframe and we are ignoring scrolling on the viewport. + nsIPresShell* shell = child->PresContext()->PresShell(); + bool keepDescending = child == aBuilder->GetIgnoreScrollFrame() || + (shell->IgnoringViewportScrolling() && child == shell->GetRootScrollFrame()); + if (!keepDescending) { + nsRect childDirty; + if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect())) + return false; + // Usually we could set dirty to childDirty now but there's no + // benefit, and it can be confusing. It can especially confuse + // situations where we're going to ignore a scrollframe's clipping; + // we wouldn't want to clip the dirty area to the scrollframe's + // bounds in that case. + } + } + return true; +} + void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsIFrame* aChild, @@ -2349,11 +2397,59 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return; + const bool doingShortcut = + (child->GetStateBits() & NS_FRAME_SIMPLE_DISPLAYLIST) && + aBuilder->IsPaintingToWindow() && + // This would be changed by the change of preference. + aBuilder->IsBuildingLayerEventRegions() && + // Animations may change the value of |HasOpacity()|. + !(child->GetContent() && + child->GetContent()->MayHaveAnimations()); + if (doingShortcut) { + // This is the shortcut for frames been handled along the common + // path, the most common one of THE COMMON CASE mentioned later. + MOZ_ASSERT(child->Type() != LayoutFrameType::Placeholder); + MOZ_ASSERT(!aBuilder->GetSelectedFramesOnly() && + !aBuilder->GetIncludeAllOutOfFlows(), + "It should be held for painting to window"); + + // dirty rect in child-relative coordinates + nsRect dirty = aDirtyRect - child->GetOffsetTo(this); + if (!DescendIntoChild(aBuilder, child, dirty)) { + return; + } + + nsDisplayListBuilder::AutoBuildingDisplayList + buildingForChild(aBuilder, child, dirty, false); + + CheckForApzAwareEventHandlers(aBuilder, child); + + nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions(); + if (eventRegions) { + eventRegions->AddFrame(aBuilder, child); + } + + child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty); + aBuilder->AdjustWindowDraggingRegion(child); + child->BuildDisplayList(aBuilder, dirty, aLists); + aBuilder->DisplayCaret(child, dirty, aLists.Content()); +#ifdef DEBUG +// DisplayDebugBorders(aBuilder, child, aLists); +#endif + return; + } + bool isSVG = (child->GetStateBits() & NS_FRAME_SVG_LAYOUT); + // It is raised if the control flow strays off the common path. + // The common path is the most common one of THE COMMON CASE + // mentioned later. + bool awayFromCommonPath = false; + // true if this is a real or pseudo stacking context bool pseudoStackingContext = (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0; + awayFromCommonPath |= pseudoStackingContext; if (!isSVG && (aFlags & DISPLAY_CHILD_INLINE) && !child->IsFrameOfType(eLineParticipant)) { @@ -2361,6 +2457,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // it acts like inline-block or inline-table. Therefore it is a // pseudo-stacking-context. pseudoStackingContext = true; + awayFromCommonPath = false; } // dirty rect in child-relative coordinates @@ -2408,6 +2505,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, dirty.SetEmpty(); } pseudoStackingContext = true; + awayFromCommonPath = true; } if (child->Preserves3D()) { nsRect* savedDirty = static_cast @@ -2430,31 +2528,9 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, if (aBuilder->GetIncludeAllOutOfFlows() && (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { dirty = child->GetVisualOverflowRect(); - } else if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { - // No need to descend into child to catch placeholders for visible - // positioned stuff. So see if we can short-circuit frame traversal here. - - // We can stop if child's frame subtree's intersection with the - // dirty area is empty. - // If the child is a scrollframe that we want to ignore, then we need - // to descend into it because its scrolled child may intersect the dirty - // area even if the scrollframe itself doesn't. - // There are cases where the "ignore scroll frame" on the builder is not set - // correctly, and so we additionally want to catch cases where the child is - // a root scrollframe and we are ignoring scrolling on the viewport. - nsIPresShell* shell = PresContext()->PresShell(); - bool keepDescending = child == aBuilder->GetIgnoreScrollFrame() || - (shell->IgnoringViewportScrolling() && child == shell->GetRootScrollFrame()); - if (!keepDescending) { - nsRect childDirty; - if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect())) - return; - // Usually we could set dirty to childDirty now but there's no - // benefit, and it can be confusing. It can especially confuse - // situations where we're going to ignore a scrollframe's clipping; - // we wouldn't want to clip the dirty area to the scrollframe's - // bounds in that case. - } + awayFromCommonPath = true; + } else if (!DescendIntoChild(aBuilder, child, dirty)) { + return; } // XXX need to have inline-block and inline-table set pseudoStackingContext @@ -2466,6 +2542,16 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance)) return; +#if(0) + // XXX: The backbug from bug 1342009 for tracking visibility at this + // point is only required if we implement bug 1261554 (then we also need + // bug 1284350). + if (aBuilder->IsPaintingToWindow() && child->TrackingVisibility()) { + child->PresContext()->PresShell()->EnsureFrameInApproximatelyVisibleList(child); + awayFromCommonPath = true; + } +#endif + // Child is composited if it's transformed, partially transparent, or has // SVG effects or a blend mode.. const nsStylePosition* pos = child->StylePosition(); @@ -2494,6 +2580,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { // If you change this, also change IsPseudoStackingContextFromStyle() pseudoStackingContext = true; + awayFromCommonPath = true; } NS_ASSERTION(!isStackingContext || pseudoStackingContext, "Stacking contexts must also be pseudo-stacking-contexts"); @@ -2506,8 +2593,31 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, if (savedOutOfFlowData) { clipState.SetClipForContainingBlockDescendants( &savedOutOfFlowData->mContainingBlockClip); + MOZ_ASSERT(awayFromCommonPath, "It is impossible when savedOutOfFlowData is true"); } +#if(0) + // XXX: there are some backbugs from bug 1342009 that should go here. + // These appear to be based on bug 1231538 and bug 1265237, so we + // probably don't need them since we don't have that particular regression. + + else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO && + isPlaceholder) { + NS_ASSERTION(dirty.IsEmpty(), "should have empty dirty rect"); + // Every item we build from now until we descent into an out of flow that + // does have saved out of flow data should be invisible. This state gets + // restored when AutoBuildingDisplayList gets out of scope. + aBuilder->SetBuildingInvisibleItems(true); + + // If we have nested out-of-flow frames and the outer one isn't visible + // then we won't have stored clip data for it. We can just clear the clip + // instead since we know we won't render anything, and the inner out-of-flow + // frame will setup the correct clip for itself. + clipState.SetClipChainForContainingBlockDescendants(nullptr); + awayFromCommonPath = true; + } +#endif + // Setup clipping for the parent's overflow:-moz-hidden-unscrollable, // or overflow:hidden on elements that don't support scrolling (and therefore // don't create nsHTML/XULScrollFrame). This clipping needs to not clip @@ -2519,7 +2629,9 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsIFrame* parent = child->GetParent(); const nsStyleDisplay* parentDisp = parent == this ? ourDisp : parent->StyleDisplay(); - ApplyOverflowClipping(aBuilder, parent, parentDisp, clipState); + if (ApplyOverflowClipping(aBuilder, parent, parentDisp, clipState)) { + awayFromCommonPath = true; + } nsDisplayList list; nsDisplayList extraPositionedDescendants; @@ -2538,26 +2650,43 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // clipRect is in builder-reference-frame coordinates, // dirty/clippedDirtyRect are in child coordinates dirty.IntersectRect(dirty, clipRect); + awayFromCommonPath = true; // XXX bug 1342009 } child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty); - // moved here from below + // moved here from below (bug 1220466) if (aBuilder->IsBuildingLayerEventRegions()) { // If this frame has a different animated geometry root than its parent, // make sure we accumulate event regions for its layer. - if (buildingForChild.IsAnimatedGeometryRoot()) { + if (buildingForChild.IsAnimatedGeometryRoot() || isPositioned) { nsDisplayLayerEventRegions* eventRegions = new (aBuilder) nsDisplayLayerEventRegions(aBuilder, child); eventRegions->AddFrame(aBuilder, child); aBuilder->SetLayerEventRegions(eventRegions); - aLists.BorderBackground()->AppendNewToTop(eventRegions); - } - } - nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions(); - if (eventRegions) { - eventRegions->AddFrame(aBuilder, child); + // various backbugs from 1342009 follow, including 1303408 and + // 1287142: + + if (isPositioned) { + // We need this nsDisplayLayerEventRegions to be sorted with the positioned + // elements as positioned elements will be sorted on top of normal elements + list.AppendNewToTop(eventRegions); + } else { + aLists.BorderBackground()->AppendNewToTop(eventRegions); + } + } else { + nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions(); + if (eventRegions) { + eventRegions->AddFrame(aBuilder, child); + } + if (!awayFromCommonPath && + aBuilder->IsPaintingToWindow() && + !buildingForChild.MaybeAnimatedGeometryRoot()) { + // The shortcut is available for the child for next time. + child->AddStateBits(NS_FRAME_SIMPLE_DISPLAYLIST); + } + } } if (!pseudoStackingContext) { diff --git a/layout/generic/nsFrameStateBits.h b/layout/generic/nsFrameStateBits.h index 3e721d6ef..422ad0da2 100644 --- a/layout/generic/nsFrameStateBits.h +++ b/layout/generic/nsFrameStateBits.h @@ -254,6 +254,14 @@ FRAME_STATE_BIT(Generic, 53, NS_FRAME_IS_NONDISPLAY) // Frame has a LayerActivityProperty property FRAME_STATE_BIT(Generic, 54, NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY) +// The display list of the frame can be handled by the shortcut for +// COMMON CASE. This is bug 1342009, but it uses bit 57. Since we might +// implement NS_FRAME_HAS_PROPERTIES (56) in the near future, we will use bit +// 55. XXX: Dump bit 57 and make it all work out -- we'd need the backout +// bits from bug 1250244 and then just not implement the replacement API. +// However, that's only worth doing if we're really short on bits later. +FRAME_STATE_BIT(Generic, 55, NS_FRAME_SIMPLE_DISPLAYLIST) + // Frame has VR content, and needs VR display items created FRAME_STATE_BIT(Generic, 57, NS_FRAME_HAS_VR_CONTENT) diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 4a9adc311..a7f85ddb5 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -5444,10 +5444,12 @@ nsTextFrame::ComputeSelectionUnderlineHeight( // the default font size, we should use the actual font size because the // computed value from the default font size can be too thick for the // current font size. - int32_t defaultFontSize = - aPresContext->AppUnitsToDevPixels(nsStyleFont(aPresContext).mFont.size); - gfxFloat fontSize = std::min(gfxFloat(defaultFontSize), - aFontMetrics.emHeight); + nscoord defaultFontSize = aPresContext->GetDefaultFont( + kPresContext_DefaultVariableFont_ID, nullptr)->size; + int32_t zoomedFontSize = aPresContext->AppUnitsToDevPixels( + nsStyleFont::ZoomText(aPresContext, defaultFontSize)); + gfxFloat fontSize = std::min(gfxFloat(zoomedFontSize), + aFontMetrics.emHeight); fontSize = std::max(fontSize, 1.0); return ceil(fontSize / 20); } diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 6a2c25b4d..b82a94317 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -1649,9 +1649,10 @@ nsRuleNode::ConvertChildrenToHash(int32_t aNumKids) sizeof(ChildrenHashEntry), aNumKids); for (nsRuleNode* curr = ChildrenList(); curr; curr = curr->mNextSibling) { + Key key = curr->GetKey(); // This will never fail because of the initial size we gave the table. auto entry = - static_cast(hash->Add(curr->mRule, fallible)); + static_cast(hash->Add(&key)); NS_ASSERTION(!entry->mRuleNode, "duplicate entries in list"); entry->mRuleNode = curr; } diff --git a/layout/xul/tree/nsTreeBodyFrame.cpp b/layout/xul/tree/nsTreeBodyFrame.cpp index 3cf7d3807..c8a1d5d78 100644 --- a/layout/xul/tree/nsTreeBodyFrame.cpp +++ b/layout/xul/tree/nsTreeBodyFrame.cpp @@ -909,6 +909,9 @@ nsTreeBodyFrame::CheckOverflow(const ScrollParts& aParts) } } + if (!horizontalOverflowChanged && !verticalOverflowChanged) + return; + nsWeakFrame weakFrame(this); RefPtr presContext = PresContext(); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 714e80acc..db8ac67a7 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1345,9 +1345,7 @@ pref("network.http.redirection-limit", 20); // NOTE: support for "compress" has been disabled per bug 196406. // NOTE: separate values with comma+space (", "): see bug 576033 pref("network.http.accept-encoding", "gzip, deflate"); -// pref("network.http.accept-encoding.secure", "gzip, deflate, br"); -// ESR-45 disable brotli due to library bugs fixed in 46 -pref("network.http.accept-encoding.secure", "gzip, deflate"); +pref("network.http.accept-encoding.secure", "gzip, deflate, br"); pref("network.http.pipelining" , false); pref("network.http.pipelining.ssl" , false); // disable pipelining over SSL diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index ad86c9524..23edde2dd 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -1836,6 +1836,13 @@ nsIOService::SpeculativeConnectInternal(nsIURI *aURI, nsIInterfaceRequestor *aCallbacks, bool aAnonymous) { + bool isHTTP, isHTTPS; + if (!(NS_SUCCEEDED(aURI->SchemeIs("http", &isHTTP)) && isHTTP) && + !(NS_SUCCEEDED(aURI->SchemeIs("https", &isHTTPS)) && isHTTPS)) { + // We don't speculatively connect to non-HTTP[S] URIs. + return NS_OK; + } + // Check for proxy information. If there is a proxy configured then a // speculative connect should not be performed because the potential // reward is slim with tcp peers closely located to the browser. diff --git a/parser/html/nsHtml5TreeOperation.h b/parser/html/nsHtml5TreeOperation.h index e4881b4d1..5958cfe36 100644 --- a/parser/html/nsHtml5TreeOperation.h +++ b/parser/html/nsHtml5TreeOperation.h @@ -110,7 +110,7 @@ class nsHtml5TreeOperation { } nsAutoString str; aAtom->ToString(str); - return do_GetAtom(str); + return NS_AtomizeMainThread(str); } static nsresult AppendTextToTextNode(const char16_t* aBuffer, diff --git a/toolkit/components/places/SQLFunctions.cpp b/toolkit/components/places/SQLFunctions.cpp index 295fee1c5..791bae43b 100644 --- a/toolkit/components/places/SQLFunctions.cpp +++ b/toolkit/components/places/SQLFunctions.cpp @@ -171,6 +171,17 @@ namespace { return false; } + static + MOZ_ALWAYS_INLINE nsDependentCString + getSharedString(mozIStorageValueArray* aValues, uint32_t aIndex) { + uint32_t len; + const char* str = aValues->AsSharedUTF8String(aIndex, &len); + if (!str) { + return nsDependentCString("", (uint32_t)0); + } + return nsDependentCString(str, len); + } + } // End anonymous namespace namespace mozilla { @@ -202,36 +213,40 @@ namespace places { } /* static */ - void - MatchAutoCompleteFunction::fixupURISpec(const nsCString &aURISpec, + nsDependentCSubstring + MatchAutoCompleteFunction::fixupURISpec(const nsACString &aURISpec, int32_t aMatchBehavior, - nsCString &_fixedSpec) + nsACString &aSpecBuf) { - nsCString unescapedSpec; - (void)NS_UnescapeURL(aURISpec, esc_SkipControl | esc_AlwaysCopy, - unescapedSpec); + nsDependentCSubstring fixedSpec; - // If this unescaped string is valid UTF-8, we'll use it. Otherwise, - // we will simply use our original string. - NS_ASSERTION(_fixedSpec.IsEmpty(), - "Passing a non-empty string as an out parameter!"); - if (IsUTF8(unescapedSpec)) - _fixedSpec.Assign(unescapedSpec); - else - _fixedSpec.Assign(aURISpec); + // Try to unescape the string. If that succeeds and yields a different + // string which is also valid UTF-8, we'll use it. + // Otherwise, we will simply use our original string. + bool unescaped = NS_UnescapeURL(aURISpec.BeginReading(), + aURISpec.Length(), esc_SkipControl, aSpecBuf); + if (unescaped && IsUTF8(aSpecBuf)) { + fixedSpec.Rebind(aSpecBuf, 0); + } else { + fixedSpec.Rebind(aURISpec, 0); + } if (aMatchBehavior == mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED) - return; + return fixedSpec; - if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("http://"))) - _fixedSpec.Cut(0, 7); - else if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("https://"))) - _fixedSpec.Cut(0, 8); - else if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("ftp://"))) - _fixedSpec.Cut(0, 6); + if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("http://"))) { + fixedSpec.Rebind(fixedSpec, 7); + } else if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("https://"))) { + fixedSpec.Rebind(fixedSpec, 8); + } else if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("ftp://"))) { + fixedSpec.Rebind(fixedSpec, 6); + } - if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("www."))) - _fixedSpec.Cut(0, 4); + if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("www."))) { + fixedSpec.Rebind(fixedSpec, 4); + } + + return fixedSpec; } /* static */ @@ -337,19 +352,19 @@ namespace places { #define HAS_BEHAVIOR(aBitName) \ (searchBehavior & mozIPlacesAutoComplete::BEHAVIOR_##aBitName) - nsAutoCString searchString; - (void)aArguments->GetUTF8String(kArgSearchString, searchString); - nsCString url; - (void)aArguments->GetUTF8String(kArgIndexURL, url); + nsDependentCString searchString = + getSharedString(aArguments, kArgSearchString); + nsDependentCString url = + getSharedString(aArguments, kArgIndexURL); int32_t matchBehavior = aArguments->AsInt32(kArgIndexMatchBehavior); // We only want to filter javascript: URLs if we are not supposed to search // for them, and the search does not start with "javascript:". if (matchBehavior != mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED && + StringBeginsWith(url, NS_LITERAL_CSTRING("javascript:")) && !HAS_BEHAVIOR(JAVASCRIPT) && - !StringBeginsWith(searchString, NS_LITERAL_CSTRING("javascript:")) && - StringBeginsWith(url, NS_LITERAL_CSTRING("javascript:"))) { + !StringBeginsWith(searchString, NS_LITERAL_CSTRING("javascript:"))) { NS_ADDREF(*_result = new IntegerVariant(0)); return NS_OK; } @@ -357,8 +372,7 @@ namespace places { int32_t visitCount = aArguments->AsInt32(kArgIndexVisitCount); bool typed = aArguments->AsInt32(kArgIndexTyped) ? true : false; bool bookmark = aArguments->AsInt32(kArgIndexBookmark) ? true : false; - nsAutoCString tags; - (void)aArguments->GetUTF8String(kArgIndexTags, tags); + nsDependentCString tags = getSharedString(aArguments, kArgIndexTags); int32_t openPageCount = aArguments->AsInt32(kArgIndexOpenPageCount); bool matches = false; if (HAS_BEHAVIOR(RESTRICT)) { @@ -388,11 +402,11 @@ namespace places { searchFunctionPtr searchFunction = getSearchFunction(matchBehavior); // Clean up our URI spec and prepare it for searching. - nsCString fixedURI; - fixupURISpec(url, matchBehavior, fixedURI); + nsCString fixedUrlBuf; + nsDependentCSubstring fixedURI = + fixupURISpec(url, matchBehavior, fixedUrlBuf); - nsAutoCString title; - (void)aArguments->GetUTF8String(kArgIndexTitle, title); + nsDependentCString title = getSharedString(aArguments, kArgIndexTitle); // Determine if every token matches either the bookmark title, tags, page // title, or page URL. diff --git a/toolkit/components/places/SQLFunctions.h b/toolkit/components/places/SQLFunctions.h index f7619bf63..f3485075e 100644 --- a/toolkit/components/places/SQLFunctions.h +++ b/toolkit/components/places/SQLFunctions.h @@ -166,11 +166,13 @@ private: * @param aMatchBehavior * The matching behavior to use defined by one of the * mozIPlacesAutoComplete::MATCH_* values. - * @param _fixedSpec - * An out parameter that is the fixed up string. + * @param aSpecBuf + * A string buffer that the returned slice can point into, if needed. + * @return the fixed up string. */ - static void fixupURISpec(const nsCString &aURISpec, int32_t aMatchBehavior, - nsCString &_fixedSpec); + static nsDependentCSubstring fixupURISpec(const nsACString &aURISpec, + int32_t aMatchBehavior, + nsACString &aSpecBuf); }; diff --git a/toolkit/components/places/UnifiedComplete.js b/toolkit/components/places/UnifiedComplete.js index c4296c30e..29aeb197a 100644 --- a/toolkit/components/places/UnifiedComplete.js +++ b/toolkit/components/places/UnifiedComplete.js @@ -112,6 +112,8 @@ const SQL_BOOKMARK_TAGS_FRAGMENT = // TODO bug 412736: in case of a frecency tie, we might break it with h.typed // and h.visit_count. That is slower though, so not doing it yet... +// NB: as a slight performance optimization, we only evaluate the "btitle" +// and "tags" queries for bookmarked entries. function defaultQuery(conditions = "") { let query = `SELECT :query_type, h.url, h.title, f.url, ${SQL_BOOKMARK_TAGS_FRAGMENT}, @@ -121,7 +123,12 @@ function defaultQuery(conditions = "") { LEFT JOIN moz_openpages_temp t ON t.url = h.url WHERE h.frecency <> 0 AND AUTOCOMPLETE_MATCH(:searchString, h.url, - IFNULL(btitle, h.title), tags, + CASE WHEN bookmarked THEN + IFNULL(btitle, h.title) + ELSE h.title END, + CASE WHEN bookmarked THEN + tags + ELSE '' END, h.visit_count, h.typed, bookmarked, t.open_count, :matchBehavior, :searchBehavior) diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 249bf9652..3084d70b5 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -481,14 +481,14 @@ "expires_in_version": "never", "kind": "enumerated", "n_values": 10, - "description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7, RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete)" + "description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7, RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10" }, "JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS": { "alert_emails": ["jdemooij@mozilla.com"], "expires_in_version": "never", "kind": "enumerated", "n_values": 10, - "description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7, RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete)" + "description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7, RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10" }, "XUL_CACHE_DISABLED": { "expires_in_version": "default", diff --git a/toolkit/content/widgets/autocomplete.xml b/toolkit/content/widgets/autocomplete.xml index c25a13701..fcf35d8b1 100644 --- a/toolkit/content/widgets/autocomplete.xml +++ b/toolkit/content/widgets/autocomplete.xml @@ -1035,12 +1035,17 @@ extends="chrome://global/content/bindings/popup.xml#popup"> this.maxRows) { + // when clearing the selection (val == -1, so selectedItem will be + // null), we want to scroll back to the top. see bug #406194 + this.richlistbox.ensureElementIsVisible( + this.richlistbox.selectedItem || this.richlistbox.firstChild); + } return val; ]]> diff --git a/xpcom/ds/nsAtomTable.cpp b/xpcom/ds/nsAtomTable.cpp index 6ca0e8001..55e9e3a22 100644 --- a/xpcom/ds/nsAtomTable.cpp +++ b/xpcom/ds/nsAtomTable.cpp @@ -298,6 +298,9 @@ static const PLDHashTableOps AtomTableOps = { AtomTableInitEntry }; +#define RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE 31 +static nsIAtom* + sRecentlyUsedMainThreadAtoms[RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE] = {}; static inline void @@ -651,6 +654,8 @@ NS_NewAtom(const char16_t* aUTF16String) return NS_NewAtom(nsDependentString(aUTF16String)); } +// Equivalent to current NS_Atomize. +// Left as such for legacy callers in our older 45-era codebase. already_AddRefed NS_NewAtom(const nsAString& aUTF16String) { @@ -671,6 +676,47 @@ NS_NewAtom(const nsAString& aUTF16String) return atom.forget(); } +// From bug 1351303, modified for Mozilla 45. +already_AddRefed +NS_AtomizeMainThread(const nsAString& aUTF16String) +{ +#if(0) + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr retVal; + uint32_t hash; + AtomTableKey key(aUTF16String.Data(), aUTF16String.Length(), &hash); + uint32_t index = hash % RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE; + nsIAtom* atom = sRecentlyUsedMainThreadAtoms[index]; + + if (atom && atom->GetUTF16String()) { // wallpaper + // This isn't ideal, but covers for the collision case, I guess. + // The atom names shouldn't be very long in any event. + uint32_t length = atom->GetLength(); + if (length == key.mLength && + (memcmp(atom->GetUTF16String(), + key.mUTF16String, length * sizeof(char16_t)) == 0)) { + retVal = atom; + return retVal.forget(); + } + } + + // Inline relevant parts of GetAtomHashEntry. + AtomTableEntry* he = static_cast(gAtomTable->Add(&key)); + if (he->mAtom) { + retVal = he->mAtom; + } else { + RefPtr atom = new AtomImpl(aUTF16String, hash); + he->mAtom = atom; + retVal = he->mAtom; // XXX? + } + + sRecentlyUsedMainThreadAtoms[index] = retVal; + return retVal.forget(); +#else + return NS_NewAtom(aUTF16String); +#endif +} + nsIAtom* NS_NewPermanentAtom(const nsAString& aUTF16String) { diff --git a/xpcom/ds/nsIAtom.idl b/xpcom/ds/nsIAtom.idl index da7d23792..f5f67ab23 100644 --- a/xpcom/ds/nsIAtom.idl +++ b/xpcom/ds/nsIAtom.idl @@ -127,6 +127,11 @@ extern nsIAtom* NS_NewPermanentAtom(const nsAString& aUTF16String); inline already_AddRefed do_GetAtom(const nsAString& aUTF16String) { return NS_NewAtom(aUTF16String); } +/** + * An optimized version of the method above for the main thread (bug 1351303). + */ +extern already_AddRefed NS_AtomizeMainThread(const nsAString& aUTF16String); + /** * Return a count of the total number of atoms currently * alive in the system. diff --git a/xpcom/glue/PLDHashTable.cpp b/xpcom/glue/PLDHashTable.cpp index 7433da17b..688403c23 100644 --- a/xpcom/glue/PLDHashTable.cpp +++ b/xpcom/glue/PLDHashTable.cpp @@ -395,6 +395,49 @@ PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash) // if Reason==ForAdd.) PLDHashEntryHdr* firstRemoved = nullptr; +#if(1) + // Speed up table searches a la bug 1352888, but we go one further by having + // separate for(;;) loops for ForAdd and everything else, which hoists the + // check up and eliminates up to several branches in the loop. + + if (Reason == ForAdd) { + for (;;) { + if (!firstRemoved) { + if (MOZ_UNLIKELY(EntryIsRemoved(entry))) { + firstRemoved = entry; + } else { + entry->mKeyHash |= kCollisionFlag; + } + } + + hash1 -= hash2; + hash1 &= sizeMask; + + entry = AddressEntry(hash1); + if (EntryIsFree(entry)) { + return (firstRemoved ? firstRemoved : entry); + } + + if (MatchEntryKeyhash(entry, aKeyHash) && + matchEntry(this, entry, aKey)) { + return entry; + } + } + } else for (;;) { + hash1 -= hash2; + hash1 &= sizeMask; + + entry = AddressEntry(hash1); + if (EntryIsFree(entry)) { + return nullptr; + } + + if (MatchEntryKeyhash(entry, aKeyHash) && + matchEntry(this, entry, aKey)) { + return entry; + } + } +#else for (;;) { if (Reason == ForAdd) { if (MOZ_UNLIKELY(EntryIsRemoved(entry))) { @@ -420,6 +463,7 @@ PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash) return entry; } } +#endif // NOTREACHED return nullptr; diff --git a/xpcom/glue/nsDataHashtable.h b/xpcom/glue/nsDataHashtable.h index 18aa210b7..19c0728b4 100644 --- a/xpcom/glue/nsDataHashtable.h +++ b/xpcom/glue/nsDataHashtable.h @@ -9,6 +9,7 @@ #include "nsHashKeys.h" #include "nsBaseHashtable.h" +#include "mozilla/Maybe.h" /** * templated hashtable class maps keys to simple datatypes. @@ -22,12 +23,36 @@ template class nsDataHashtable : public nsBaseHashtable { +private: + typedef nsBaseHashtable BaseClass; + public: + using typename BaseClass::KeyType; + using typename BaseClass::EntryType; + nsDataHashtable() {} explicit nsDataHashtable(uint32_t aInitLength) - : nsBaseHashtable(aInitLength) + : BaseClass(aInitLength) { } + + /** + * Retrieve the value for a key and remove the corresponding entry at + * the same time. + * + * @param aKey the key to retrieve and remove + * @return the found value, or Nothing if no entry was found with the + * given key. + */ + mozilla::Maybe GetAndRemove(KeyType aKey) + { + mozilla::Maybe value; + if (EntryType* ent = this->GetEntry(aKey)) { + value.emplace(mozilla::Move(ent->mData)); + this->RemoveEntry(ent); + } + return value; + } }; #endif // nsDataHashtable_h__ diff --git a/xpcom/io/nsEscape.cpp b/xpcom/io/nsEscape.cpp index f099a8ca1..25d851175 100644 --- a/xpcom/io/nsEscape.cpp +++ b/xpcom/io/nsEscape.cpp @@ -540,6 +540,9 @@ NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags, return false; } + MOZ_ASSERT(aResult.IsEmpty(), + "Passing a non-empty string as an out parameter!"); + if (aLen < 0) { aLen = strlen(aStr); } @@ -550,6 +553,10 @@ NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags, bool skipControl = !!(aFlags & esc_SkipControl); bool skipInvalidHostChar = !!(aFlags & esc_Host); + if (writing) { + aResult.SetCapacity(aLen); + } + const char* last = aStr; const char* p = aStr; @@ -563,7 +570,10 @@ NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags, ((c1 < '8' && !ignoreAscii) || (c1 >= '8' && !ignoreNonAscii)) && !(skipControl && (c1 < '2' || (c1 == '7' && (c2 == 'f' || c2 == 'F'))))) { - writing = true; + if (!writing) { + writing = true; + aResult.SetCapacity(aLen); + } if (p > last) { aResult.Append(last, p - last); last = p;