Compare commits

...
Author SHA1 Message Date
Moonchild e6b9d9cbea Issue #1624 - Add missing error handling and bail on null specifier. 2020-11-28 10:17:43 +00:00
Moonchild b863bd2edc Issue #1684 - Move global user-agent override to networking component.
Since this is a http protocol networking feature, it belongs in the networking
branch of our preferences.
2020-11-27 12:58:02 +00:00
Matt A. Tobin a8a397fbce Issue #1676 - Follow-up: Put js/src (including vm), jit, and wasm back into unified sources.
Function template inflation in intermediate code. The linker problem we hit with the corrupt file was because there were too many function descriptors and relation in the .lib
2020-11-20 12:37:11 -05:00
Moonchild a89597b5db [webrtc] Only init WebRtcAec callbacks once 2020-11-19 22:32:24 +00:00
Moonchild 074d194776 Merge pull request 'Align a keybinding definition with the others' (#1687) from vannilla/UXP:align-key-definition into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/1687
2020-11-19 20:40:43 +00:00
Werner Lemberg 76d62cfc38 [sfnt] Fix heap buffer overflow.
This is CVE-2020-15999.

* src/sfnt/pngshim.c (Load_SBit_Png): Test bitmap size earlier.
2020-11-19 19:18:32 +00:00
Henri Sivonen eefaee9064 [dom] Remove attributes from descendants when setting sanitized style.
This avoids a number of problems with incomplete sanitation.
2020-11-19 19:06:03 +00:00
Moonchild 1864bbec2a [http] Use a copy of nsHttpConnectionInfo.
The root cause in this bug is that the connection info used by
'SpdyConnectTransaction' is the same instance as the connection info in
'nsHttpTransaction', so we should clone it and let 'SpdyConnectTransaction' use
the cloned one.
2020-11-19 18:19:29 +00:00
Moonchild 5771487106 Merge pull request 'Resolve various issues on Big Sur' (#1685) from dbsoft/UXP:master into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/1685
2020-11-19 18:03:34 +00:00
Moonchild 887fface56 [xpcom] Don't use realloc for shrinking nsTArrays when allowRealloc is false.
The original patch handled the grow case but not the shrink case. When the
current and new allocation sizes are in different size classes, jemalloc's
realloc will move the allocation when shrinking, not just truncate the existing
one.

Based on work by Jon Coppeard.
2020-11-19 17:48:59 +00:00
Henri Sivonen 4782e8dfa2 [parser] Make the refcount of nsHtml5OwningUTF16Buffer atomic. 2020-11-18 23:28:27 +00:00
David Major bd061ac043 [xpcom] Fix GetWindowsFolder storage 2020-11-18 22:21:22 +00:00
Moonchild 31a64578e2 [gfx][Skia] Hold mutex while accessing shared SkImage data. 2020-11-18 22:06:11 +00:00
Alessio Vanni 85dc118aac Issue #1686 - Align a keybinding definition with the others 2020-11-18 21:57:00 +01:00
Brian Smith 2f3cf3c8cf Issue #1667 - Part 3: Fix OpenGL load and runtime issues on Big Sur
This fix is included in NSPR 4.27 and Mozilla bug 1652330.
Also put a main thread check in the cocoa draw callback.
2020-11-17 06:52:06 -06:00
Matt A. Tobin 241f06bc53 Merge pull request 'Get rid of HPKP pinning mode leftovers' (#1680) from adesh/UXP:cleanup-hpkp-pinning-mode into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/1680
2020-11-16 17:54:58 +00:00
Matt A. Tobin ecb827c5c4 Issue #251 - Move chrome packaging options to ac configure
Also adds options for new functionality in #1683
2020-11-16 12:47:54 -05:00
Brian Smith fa5aba4c0f Issue #1667 - Part 2: Add MacOS 11.0 Big Sur widget compatibility
This involves refactoring the vibrancy and OpenGL/Pixel rendering changes contained
in the following Mozilla meta bugs: 1496823 and 1491445
Also add Big Sur to the features tests and update popup menu look and feel based
on Mozilla bug 1656301.
2020-11-16 07:23:44 -06:00
Brian Smith b6d909ad17 Issue #1667 - Part 1: Define _pthread_self if it is not already defined in jemalloc 2020-11-16 07:15:29 -06:00
Moonchild 6bc5748b00 [UA] Change default compatibility mode version to 68.0
Since several entities have started to ban .9 versions, even if they are valid
ESR versions.
2020-11-16 11:38:02 +00:00
Moonchild 3abb8e28ad Revert "Issue #1684 - Remove global UA override preference and supporting code."
This reverts commit 18df01eb54.

Backed out for devtools breakage.
2020-11-15 23:11:24 +00:00
Moonchild 18df01eb54 Issue #1684 - Remove global UA override preference and supporting code.
Resolves #1684
2020-11-15 16:24:47 +00:00
Moonchild 8aa9355535 Revert "[toolkit] Add optimized UI code format option"
This reverts commit 511ac54cab.
2020-11-15 12:49:30 +00:00
Moonchild 511ac54cab [toolkit] Add optimized UI code format option 2020-11-14 15:41:21 +00:00
Moonchild 6d2e301d90 [packager] Optimize archive accesses. 2020-11-14 01:03:27 +00:00
Moonchild 2d8013526a Issue #1683 - Update libjar module. 2020-11-13 16:00:06 +00:00
Moonchild d86f49ba59 Issue #1683 - Update Brotli lib to 1.0.9 2020-11-13 15:59:29 +00:00
Moonchild 870fd86e13 [libjar] Fix inflation logic.
This has been broken for 11 years. About time it's fixed.
Tag #1683
2020-11-13 15:57:56 +00:00
Moonchild 13f505539c Issue #1682 - Remove vibrator DOM interface and support code.
Resolves #1682
2020-11-13 13:33:08 +00:00
Moonchild 5262e01a4e Issue #1391 - Disable DOM Filesystem/dirpicker APIs by default.
There don't seem to be any drawbacks to this; tested for the past month disabled
and there have been no issues with any sites visited. Adoption seems very low.
2020-11-13 10:09:36 +00:00
Moonchild 4aeaea9195 Issue #1679 - Part 2: Some small issues to address buildability.
- Explicitly |this| the Asserts in Cell.as<>
- Unified deprot in regexp-error.h
- Convert RegExpShared* inner() to a reinterpret_cast because for SOME reason
  .as<> no longer works after the reimport (which is a static cast)
2020-11-12 11:58:39 +00:00
adesh 5ef801fdc5 Issue #1280 - Follow-up: Get rid of HPKP pinning mode.
This was a leftover from HPKP removal.

Also remove a couple of unused variables from security/manager/ssl/nsSiteSecurityService.cpp.
2020-11-10 23:07:22 -05:00
Matt A. Tobin 0ba1d49ae5 Issue #1679 - Part 1: First pass account for some of the refactoring differences in regexp-shim.h
This is the patch Moonchild committed on the aborted branch before the plan was revised.
2020-11-09 21:19:45 -05:00
Matt A. Tobin e8a6ee355f Bug 1466909 - Use AddLvalueReference for UniquePtr's operator*().
This is required for UniquePtr to accept <void>,
which is required for PseudoHandle = mozilla::UniquePtr<T, JS::FreePolicy>;
in turn for mozilla::SegmentedVector<PseudoHandle<void>> uniquePtrArena_;

Tag #1679
2020-11-09 21:12:46 -05:00
Matt A. Tobin 4694c60964 Bug 1328948 - Add is(), as() to Cell.
Tag #1679
2020-11-09 21:10:53 -05:00
Matt A. Tobin 74ebcea409 Issue #1677 - Part 7: Add unicode/uniset.h to check_spidermonkey_style.py for some reason 2020-11-09 20:48:11 -05:00
Matt A. Tobin c732425e1a Issue #1677 - Part 6: Implement NativeRegExpMacroAssembler for new regexp import based on irregexp/NativeRegExpMacroAssembler.cpp 2020-11-09 20:44:26 -05:00
Matt A. Tobin 51468e998c Issue #1677 - Part 5: "Simplify" regexp re-import process (and re-import from later revision)
I am going on record to say Mozilla are utter fucking assholes for pulling this as part of their progression.
2020-11-09 20:37:05 -05:00
Matt A. Tobin 77746f1d90 Issue #1677 - Part 4: Implement shim for regexp 2020-11-09 20:26:59 -05:00
Matt A. Tobin cb0208163f Issue #1677 - Part 3: Create shim definitions for V8-specific code in new regexp implementation 2020-11-09 20:20:18 -05:00
Matt A. Tobin 19499014a3 Issue #1677 - Part 2: Add build files 2020-11-04 20:28:06 -05:00
Matt A. Tobin 78b3a722b4 Issue #1677 - Part 1: Import new V8 regexp code with Mozilla's header modifications 2020-11-04 20:27:57 -05:00
Matt A. Tobin 2e07199197 Merge branch 'jsbuild-work' 2020-11-04 18:55:02 -05:00
Matt A. Tobin d2b6975eb7 Issue #1676 - Part 21: Use js-cxxflags.mozbuild in testing code and js shell 2020-11-04 16:24:38 -05:00
Matt A. Tobin fd1b2dc2b1 Issue #1676 - Part 20: Split vtune sources out of js/src/moz.build 2020-11-04 15:03:42 -05:00
Matt A. Tobin cdf46e803b Issue #1676 - Part 19: Split ctypes sources out of js/src/moz.build 2020-11-04 14:59:00 -05:00
Matt A. Tobin 59511eb8dd Issue #1676 - Part 18: Move and separate top level sources from vm sources in js/src/moz.build 2020-11-04 14:45:57 -05:00
Matt A. Tobin ff355fe9ac Issue #1676 - Part 17: Put remaining source files which have debug code ifdef'd behind MOZ_DEBUG 2020-11-04 14:38:12 -05:00
Matt A. Tobin 1abc696f87 Issue #1676 - Part 16: Split WASM sources out of js/src/moz.build
Also puts WasmBinaryIterator.cpp which is debug code ifdef'd behind MOZ_DEBUG
2020-11-04 14:31:48 -05:00
Matt A. Tobin 2f50f543a2 Issue #1676 - Part 15: Split threading sources out of js/src/moz.build 2020-11-04 14:11:22 -05:00
Matt A. Tobin 6f76f1cb36 Issue #1676 - Part 14: Split proxy sources out of js/src/moz.build 2020-11-04 13:59:16 -05:00
Matt A. Tobin 383bc182e3 Issue #1676 - Part 13: Split perf sources out of js/src/moz.build 2020-11-04 13:58:35 -05:00
Matt A. Tobin 95e057e737 Issue #1676 - Part 12: Split jit sources out of js/src/moz.build 2020-11-04 13:43:10 -05:00
Matt A. Tobin 3d9bf5d7a3 Issue #1676 - Part 11: Split irregexp sources out of js/src/moz.build 2020-11-04 13:06:41 -05:00
Matt A. Tobin 65eac50e2d Issue #1676 - Part 10: Split gc sources out of js/src/moz.build 2020-11-03 20:02:33 -05:00
Matt A. Tobin 957f19d2bd Issue #1676 - Part 9: Move DIRS down in js/src/moz.build 2020-11-03 19:08:56 -05:00
Matt A. Tobin bc450dab41 Issue #1676 - Part 8: Fix up include for selfhosted.out.h 2020-11-03 19:07:51 -05:00
Matt A. Tobin 1396383aee Issue #1676 - Part 7: Split frontend sources out of js/src/moz.build 2020-11-03 17:37:32 -05:00
Matt A. Tobin eedfa63a1d Issue #1676 - Part 6: Split ds sources out of js/src/moz.build 2020-11-03 17:33:42 -05:00
Matt A. Tobin 24b835f0a9 Issue #1676 - Part 5: Split devtools sources out of js/src/moz.build 2020-11-03 17:09:00 -05:00
Matt A. Tobin 24a5a7f7c9 Issue #1676 - Part 4: Split builtin sources out of js/src/moz.build 2020-11-03 16:57:05 -05:00
Matt A. Tobin ec13658ab9 Issue #80 - De-unify js/src
Three debug sources are producing link warnings and will be solved as Issue #1676 progresses.

This does not include JS Testing sources.
Specifically: js/src/gdb or js/src/jsapi-tests
2020-11-03 16:11:37 -05:00
Matt A. Tobin 0cdd39ff12 Issue #1676 - Part 3: Split DEFINES out of js/src/moz.build 2020-11-03 15:20:38 -05:00
Matt A. Tobin 5bf19933d8 Issue #1676 - Part 2: Split CFLAGS and CXXFLAGS directives out of js/src/moz.build 2020-11-03 14:20:26 -05:00
Matt A. Tobin 395ef79b91 Issue #1676 - Part 1: Split MozTesting directives out of js/src/moz.build 2020-11-03 14:19:03 -05:00
Moonchild bb4e9ad1c8 [devtools] More gracefully (than a crash) handle stack capture failures. 2020-10-31 17:31:38 +01:00
Moonchild 9b1406f183 Merge pull request 'Fix up -moz-tab-size and unprefix it.' (#1674) from athenian200/UXP:tab-size-length into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/1674
2020-10-30 22:36:49 +00:00
athenian200 d22717ff98 Issue #1673 - Part 5: Fix brace style and missed -moz-tab-size reference. 2020-10-29 13:16:53 -05:00
athenian200 9ffc5e6c92 Issue #1673 - Part 4: Unprefix -moz-tab-size.
While we do fail a couple of tests, the other mainstream browsers also fail them and I think our implementation of tab-size is good enough to be unprefixed at this point. Having this patch also makes testing easier.
2020-10-28 14:17:51 -05:00
athenian200 a2c26490b4 Issue #1673 - Part 3: Bring minimum tab advance up to spec.
This provides a clearer rule for the minimum tab advance that brings us to alignment with the spec and both major browsers.
2020-10-28 14:17:16 -05:00
athenian200 7c2bcc48c4 Issue #1673 - Part 2: Make tab-size animatable and fix typos.
There were a few typos in the previous patch and this patch also makes tab-size animatable which didn't really require much of a change at all.
2020-10-28 14:17:02 -05:00
athenian200 49765b53af Issue #1673 - Part 1: Allow tab-size to accept <length>.
Currently -moz-tab-size only accepts <number> values, and both Chrome and Firefox currently support <length> values and have for some time now. So with this you would be able to support sizes in px or em, for instance. This was implemented in Firefox 53 and was trivial to backport.
2020-10-28 14:16:42 -05:00
Moonchild 6a4c3caa8e Issue #1656 - Nuke the remaining vim lines in UXP
Closes #1656
2020-10-26 19:09:16 +00:00
Moonchild 0daf842cf4 Update docs for change of repository host.
Rename MCP back to MoonchildProductions.
2020-10-23 14:53:57 +00:00
Moonchild 1950e886b9 Update docs for change of repository host. 2020-10-23 12:31:21 +00:00
Moonchild 6eba9263cc [layout] Re-order rowgroups if reflowing.
This logic was missing for tfoot. See existing code in second hunk.
2020-10-23 11:10:13 +00:00
Moonchild 2cd89d5845 [layout] Avoid negative availSize.BSizes in paginated table reflow. 2020-10-23 10:36:09 +00:00
Moonchild d5e5bef3ca Bump platform version for added features. 2020-10-23 09:27:56 +00:00
Moonchild 28119b040d [netwerk] Make nsIncrementalStreamLoader's GetNumBytesRead threadsafe.
This prevents a potential race and simplifies the code a bit by keeping the
bytes read separate instead of using mData, which is modified from another
thread from OnDataAvailable.
Relaxed atomics are fine for these, since they don't guard any memory.
2020-10-23 08:10:01 +00:00
Moonchild 3b224b2b26 [DOM] When failing to create a channel and an image request, make sure to set
the image blocking status appropriately.

This is the same status as we do for known no-data protocols and ensures we
treat these two cases the same.
2020-10-22 20:43:08 +00:00
Moonchild 33a402947c [DOM Fetch] Detect broken pipes and propagate that write error to the caller. 2020-10-21 23:48:32 +00:00
Moonchild 3e59623312 [DOM Fetch] Improve stability of Fetch API. 2020-10-21 20:35:07 +00:00
Moonchild e8ae76d473 Merge branch 'master' of https://github.com/MoonchildProductions/UXP 2020-10-20 09:52:16 +00:00
Moonchild 5072843ee3 Issue #1671 - Unprefix ::-moz-selection
This actually keeps both pseudo-elements for now, since the prefixed version is
still used internally, but we need the unprefixed version for web compat.
Note: while unprefixing a non-spec-compliant pseudo here, it's exactly in line
with what other browsers do. Nobody is following the spec here and at least
we'll be doing what everyone else is with our unprefixed version.
2020-10-20 09:49:51 +00:00
Moonchild 23a30020df Merge pull request #1670 from athenian200/caret_color
Implement CSS caret-color
2020-10-20 09:47:00 +00:00
athenian200 95b4e5084b Merge branch 'master' of https://github.com/MoonchildProductions/UXP into caret_color 2020-10-18 10:45:55 -05:00
athenian200 1fc996152b Issue #1668 - Part 2: Visited color and auto support for caret-color property.
Mozilla's original implementation of this failed a couple of tests, but this seems to solve all the problems. Basically, the caret-color wasn't able to be set differently based on whether a link was visited, and the auto value implementation was incomplete. The only test we fail now is the one where you have grey text on a grey background and the caret is supposed to be visible, but I think that may have been removed from the spec. Even if it wasn't, no other browser supports it anyway.
2020-10-18 10:12:53 -05:00
athenian200 8e3832bacb Issue #1668 - Part 1: Implement support for caret-color property.
This CSS property allows input carets (that blinking input cursor you see in text fields), to be given a custom color. This was implemented in Firefox 53, and it was such a minor feature that no one ever missed it, but I don't see any harm in implementing this.

https://bugzilla.mozilla.org/show_bug.cgi?id=1063162
2020-10-18 10:04:12 -05:00
Moonchild 87c4c77ad7 Issue #1643 - Follow-up: Ensure we properly clear our pointers when the
Presentation of a document is destroyed.
2020-10-16 10:19:19 +00:00
Moonchild bda6f1a93f Issue #1606 - Add support for multi-monitor DPI awareness v2 (W10 1706+) 2020-10-06 09:23:59 +00:00
Moonchild 521f2b476c [devtools] Teach devtools about overflow-wrap: anywhere
Tag #1666
2020-10-03 15:05:53 +00:00
Moonchild dadef50bdc Issue #1666 - Implement overflow-wrap: anywhere
This aligns with the current spec regarding overflow-wrap: break-word and
overflow-wrap: anywhere in if it affects intrinsic sized due to considering
soft-wrap opportunities or not.
See CSS Text Module Level 3, Editor’s Draft, 1 October 2020, Section 5.5
2020-10-03 14:52:47 +00:00
Moonchild 8e18743ab8 Issue #1665 - Take overflow-wrap into account when calculating min-content intrinsic size. 2020-10-03 12:45:27 +00:00
New Tobin Paradigm 15acac52b1 Update CONTRIBUTING.md
Revise Commit message style and add Follow-up form
2020-10-02 05:13:43 -04:00
Moonchild 96736fce75 Merge pull request #1663 from athenian200/opacity_followup
Remove excessive VARIANT_OPACITY statements.
2020-10-01 09:54:11 +00:00
athenian200 b8c604196b Issue #1647 - Followup: Remove excessive VARIANT_OPACITY statements.
I got very anxious about making sure I included VARIANT_OPACITY in all the places VARIANT_NUMBER was included to make sure it couldn't possibly break unexpectedly, and that led to me accidentally breaking a mechanism that prevented percentages from serializing as numbers in other parts of the code. It was a total accident, and these additions were unnecessary. Basically, the situation is that there was one part of the code where it determines what's allowed for the flex statement (and possibly other statements) by checking whether it got stored as a "number", and basically only disallows percentages if it attempted to store/serialize them as percentages.

However, it only got to that part of the code because I accidentally allowed VARIANT_OPACITY as a valid way for certain tokens to parse where it wasn't necessary. If it tries to parse it that way under very specific circumstances... percentages will be marked valid and fed through the system as numbers rather than being rejected and not serialized at all, because the check to disallow percentages there relied on them being stored as percentages.

It's a really weird thing to have a problem with in a lot of ways, because if percentages aren't allowed in a field, you would think people wouldn't try to use them there, much less depend on the broken behavior that results from them not parsing as a related value.
2020-09-30 14:09:10 -05:00
Moonchild bc531bfbba Issue #1643 - Follow-up: Make sure things aren't changed while iterating.
This fixes some crashing scenarios.
2020-09-30 16:02:17 +00:00
Matt A. Tobin fb086631d9 Issue #1643 - Follow up: Add a null check for mOwner in ResizeObserverNotificationHelper::Unregister
A race condition seemed to exist between tab destruction and un-registering a ResizeObserver resulting in a null deref crash.
The original reporter in Forum Topic 25311 experienced this on msn.com so that was the functional test reference.
2020-09-29 15:03:13 -04:00
Moonchild 4e72f8ed03 Merge pull request #1660 from g4jc/1656
Issue #1656 - Fix broken comment from Part 1
2020-09-27 08:13:05 +00:00
Gaming4JC a2ed139a44 Issue #1656 - Fix broken comment from Part 1
Removing the vim line unintentionally broke the comment leading to build failure, this restores the comment.
2020-09-26 23:37:14 -04:00
332 changed files with 29035 additions and 5301 deletions

No files matched your search

+1 -1
View File
@@ -36,4 +36,4 @@ improvement patches upstream to its origin to mutually improve the platform and
a future for XUL.
## Looking for Pale Moon and Basilisk?
As of [`Issue #969`](https://github.com/MoonchildProductions/UXP/issues/969), release versions `28.9.0` and `2020.03.04` respectively, they have been split off into their own dedicated repositories located at [`MoonchildProductions/Pale-Moon`](https://github.com/MoonchildProductions/Pale-Moon) and [`MoonchildProductions/Basilisk`](https://github.com/MoonchildProductions/Basilisk).
As of [`Issue #969`](https://repo.palemoon.org/MoonchildProductions/UXP/issues/969), release versions `28.9.0` and `2020.03.04` respectively, they have been split off into their own dedicated repositories located at [`MoonchildProductions/Pale-Moon`](https://repo.palemoon.org/MoonchildProductions/Pale-Moon) and [`MoonchildProductions/Basilisk`](https://repo.palemoon.org/MoonchildProductions/Basilisk).
+1 -1
View File
@@ -23,7 +23,7 @@ Generally not security vulnerabilities:
## Reporting a Vulnerability
If you find an issue in UXP or the applications it builds on that could impact the security or safety of users please **do not**
make an issue on GitHub about it. GitHub does not support restricted viewability for security sensitive bugs.
make an issue on Gitea about it. Gitea does not support restricted viewability for security sensitive bugs.
If you want to report a security-sensitive issue then please go to the [forum](https://forum.palemoon.org) and report the issue
via a **private message** to **Moonchild** (the founder and prime responsible for security issues).
+3
View File
@@ -161,6 +161,8 @@ def old_configure_options(*options):
'--enable-b2g-camera',
'--enable-b2g-ril',
'--enable-bundled-fonts',
'--enable-chrome-compression',
'--enable-chrome-format',
'--enable-clang-plugin',
'--enable-content-sandbox',
'--enable-cookies',
@@ -262,6 +264,7 @@ def old_configure_options(*options):
'--with-app-name',
'--with-arch',
'--with-branding',
'--with-chrome-name',
'--with-crashreporter-enable-percent',
'--with-cross-lib',
'--with-debug-label',
+1
View File
@@ -86,6 +86,7 @@ included_inclnames_to_ignore = set([
'unicode/udat.h', # ICU
'unicode/udatpg.h', # ICU
'unicode/uenum.h', # ICU
'unicode/uniset.h', # ICU
'unicode/unorm.h', # ICU
'unicode/unum.h', # ICU
'unicode/unumsys.h', # ICU
+1 -1
View File
@@ -216,7 +216,7 @@ endif # NO_PROFILE_GUIDED_OPTIMIZE
MAKE_JARS_FLAGS = \
-t $(topsrcdir) \
-f $(MOZ_JAR_MAKER_FILE_FORMAT) \
-f $(MOZ_CHROME_FILE_FORMAT) \
$(NULL)
ifdef USE_EXTENSION_MANIFEST
+1 -1
View File
@@ -10,4 +10,4 @@
# hardcoded milestones in the tree from these two files.
#--------------------------------------------------------
4.6.0
4.7.0
-1
View File
@@ -526,7 +526,6 @@ hardware/lights.h
hardware/power.h
hardware_legacy/power.h
hardware_legacy/uevent.h
hardware_legacy/vibrator.h
#endif
HIToolbox/HIToolbox.h
hlink.h
+1 -1
View File
@@ -22,7 +22,7 @@ REF_MANIFEST = $(if $(USE_EXTENSION_MANIFEST),chrome.manifest,test.manifest)
check-%::
if test -d $(FINAL_TARGET); then rm -rf $(FINAL_TARGET); fi;
$(MAKE) realchrome MOZ_JAR_MAKER_FILE_FORMAT=$*
$(MAKE) realchrome MOZ_CHROME_FILE_FORMAT=$*
@echo 'Comparing manifests...'
@if ! sort $(MY_MANIFEST) | diff --text -U 0 $(srcdir)/../$(REF_MANIFEST).$* - ; then \
echo 'TEST-UNEXPECTED-FAIL | config/tests/$(REF_MANIFEST).$* | differing content in manifest!' ; \
@@ -29,7 +29,7 @@ function* testTopLeft(inspector, view) {
elementRulesNb: 4,
firstLineRulesNb: 2,
firstLetterRulesNb: 1,
selectionRulesNb: 0,
selectionRulesNb: 1,
afterRulesNb: 1,
beforeRulesNb: 2
}
@@ -166,7 +166,7 @@ function* testParagraph(inspector, view) {
elementRulesNb: 3,
firstLineRulesNb: 1,
firstLetterRulesNb: 1,
selectionRulesNb: 1,
selectionRulesNb: 2,
beforeRulesNb: 0,
afterRulesNb: 0
});
@@ -216,7 +216,7 @@ function* assertPseudoElementRulesNumbers(selector, inspector, view, ruleNbs) {
firstLetterRules: elementStyle.rules.filter(rule =>
rule.pseudoElement === ":first-letter"),
selectionRules: elementStyle.rules.filter(rule =>
rule.pseudoElement === ":-moz-selection"),
rule.pseudoElement === ":selection"),
beforeRules: elementStyle.rules.filter(rule =>
rule.pseudoElement === ":before"),
afterRules: elementStyle.rules.filter(rule =>
+1 -1
View File
@@ -94,7 +94,7 @@ exports.menuitems = [
},
key: {
id: "browserToolbox",
modifiers: "accel,alt,shift",
modifiers: isMac ? "accel,alt" : "accel,shift",
keytext: true
}
},
+45 -2
View File
@@ -1233,7 +1233,7 @@ exports.CSS_PROPERTIES = {
"-moz-tab-size": {
"isInherited": true,
"subproperties": [
"-moz-tab-size"
"tab-size"
],
"supports": [
7
@@ -2872,6 +2872,7 @@ exports.CSS_PROPERTIES = {
"box-shadow",
"box-sizing",
"caption-side",
"caret-color",
"clear",
"clip",
"clip-path",
@@ -3051,7 +3052,7 @@ exports.CSS_PROPERTIES = {
"stroke-opacity",
"stroke-width",
"-x-system-font",
"-moz-tab-size",
"tab-size",
"table-layout",
"text-align",
"text-align-last",
@@ -5277,6 +5278,28 @@ exports.CSS_PROPERTIES = {
"unset"
]
},
"caret-color": {
"isInherited": true,
"subproperties": [
"caret-color"
],
"supports": [
2
],
"values": [
"COLOR",
"auto",
"currentColor",
"hsl",
"hsla",
"inherit",
"initial",
"rgb",
"rgba",
"transparent",
"unset"
]
},
"clear": {
"isInherited": false,
"subproperties": [
@@ -7857,6 +7880,7 @@ exports.CSS_PROPERTIES = {
],
"supports": [],
"values": [
"anywhere",
"break-word",
"inherit",
"initial",
@@ -8626,6 +8650,23 @@ exports.CSS_PROPERTIES = {
"unset"
]
},
"tab-size": {
"isInherited": true,
"subproperties": [
"tab-size"
],
"supports": [
6,
7
],
"values": [
"-moz-calc",
"calc",
"inherit",
"initial",
"unset"
]
},
"table-layout": {
"isInherited": false,
"subproperties": [
@@ -9317,6 +9358,7 @@ exports.CSS_PROPERTIES = {
],
"supports": [],
"values": [
"anywhere",
"break-word",
"inherit",
"initial",
@@ -9373,6 +9415,7 @@ exports.PSEUDO_ELEMENTS = [
":backdrop",
":first-letter",
":first-line",
":selection",
":-moz-selection",
":-moz-focus-inner",
":-moz-focus-outer",
+7 -2
View File
@@ -93,15 +93,20 @@ With rare exception, it is advisable to use the following style for commit messa
This would directly apply to anyone making a pull request.
Single commits are where issues can be reasonably resolved in a single commit.
- Single Commit: `Issue #xxx - Cited issue title or appropriate direct description of changes`
- *`Issue #1083 - Deprecate FUEL extension helper javascript library`*
- Multi-Part Commits: `Issue #xxx - Part Number: Appropriate direct description of changes`
Multi-part commits would be used for complex issues. However, an exception exists for multi-part commits where the issue is anticipated or ends up being long term such as "Stop using unified compilation of sources". In these instances the multi-part form is not required.
- Multi-Part Form: `Issue #xxx - Part Number: Appropriate direct description of changes`
- *`Issue #492 - Part 1: Remove files`*
- *`Issue #492 - Part 2: Build system, Installer/Packaging`*
- *`Issue #492 - Part 3: nsUpdateService.js, updater.cpp, nsUpdateDriver.cpp`*
- *`Issue #492 - Part 4: Remove superfluous brackets in nsUpdateService.js and updater.cpp`*
An exception exists for multi-part commits where the issue is anticipated or ends up being long term such as "Stop using unified compilation of sources". In these instances the multi-part form is not required.
Occasionally a resolved (and shipped) issue requires further changes to fix bugs. However, If follow-ups are complex enough to be multi-part it should be considered a new issue.
- Follow-up Form: *`Issue #xxx - Follow-up: Appropriate direct description of changes`*
- *`Issue #1643 - Follow-up: Make sure things aren't changed while iterating.`*
### Commits with no issue
@@ -75,11 +75,6 @@ const gMozillaSpecificProperties = {
from: "ignore",
to: "stretch-to-fit"
},
"-moz-tab-size": {
// https://drafts.csswg.org/css-text-3/#propdef-tab-size
from: "1",
to: "5"
},
"-moz-text-size-adjust": {
// https://drafts.csswg.org/css-size-adjust/#propdef-text-size-adjust
from: "none",
+1 -192
View File
@@ -97,11 +97,6 @@
namespace mozilla {
namespace dom {
static bool sVibratorEnabled = false;
static uint32_t sMaxVibrateMS = 0;
static uint32_t sMaxVibrateListLen = 0;
static const char* kVibrationPermissionType = "vibration";
static void
AddPermission(nsIPrincipal* aPrincipal, const char* aType, uint32_t aPermission,
uint32_t aExpireType, int64_t aExpireTime)
@@ -152,12 +147,7 @@ GetPermission(nsIPrincipal* aPrincipal, const char* aType)
void
Navigator::Init()
{
Preferences::AddBoolVarCache(&sVibratorEnabled,
"dom.vibrator.enabled", true);
Preferences::AddUintVarCache(&sMaxVibrateMS,
"dom.vibrator.max_vibrate_ms", 10000);
Preferences::AddUintVarCache(&sMaxVibrateListLen,
"dom.vibrator.max_vibrate_list_len", 128);
// Add any Preferences::Add*VarCache(&sPref, "pref", default) here if needed.
}
Navigator::Navigator(nsPIDOMWindowInner* aWindow)
@@ -705,82 +695,6 @@ Navigator::RefreshMIMEArray()
}
}
namespace {
class VibrateWindowListener : public nsIDOMEventListener
{
public:
VibrateWindowListener(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument)
{
mWindow = do_GetWeakReference(aWindow);
mDocument = do_GetWeakReference(aDocument);
NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
aDocument->AddSystemEventListener(visibilitychange,
this, /* listener */
true, /* use capture */
false /* wants untrusted */);
}
void RemoveListener();
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
private:
virtual ~VibrateWindowListener()
{
}
nsWeakPtr mWindow;
nsWeakPtr mDocument;
};
NS_IMPL_ISUPPORTS(VibrateWindowListener, nsIDOMEventListener)
StaticRefPtr<VibrateWindowListener> gVibrateWindowListener;
static bool
MayVibrate(nsIDocument* doc) {
// Hidden documents cannot start or stop a vibration.
return (doc && !doc->Hidden());
}
NS_IMETHODIMP
VibrateWindowListener::HandleEvent(nsIDOMEvent* aEvent)
{
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
if (!MayVibrate(doc)) {
// It's important that we call CancelVibrate(), not Vibrate() with an
// empty list, because Vibrate() will fail if we're no longer focused, but
// CancelVibrate() will succeed, so long as nobody else has started a new
// vibration pattern.
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
hal::CancelVibrate(window);
RemoveListener();
gVibrateWindowListener = nullptr;
// Careful: The line above might have deleted |this|!
}
return NS_OK;
}
void
VibrateWindowListener::RemoveListener()
{
nsCOMPtr<EventTarget> target = do_QueryReferent(mDocument);
if (!target) {
return;
}
NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
target->RemoveSystemEventListener(visibilitychange, this,
true /* use capture */);
}
} // namespace
void
Navigator::AddIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
{
@@ -809,111 +723,6 @@ Navigator::RemoveIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
}
}
void
Navigator::SetVibrationPermission(bool aPermitted, bool aPersistent)
{
MOZ_ASSERT(NS_IsMainThread());
nsTArray<uint32_t> pattern;
pattern.SwapElements(mRequestedVibrationPattern);
if (!mWindow) {
return;
}
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
if (!MayVibrate(doc)) {
return;
}
if (aPermitted) {
// Add a listener to cancel the vibration if the document becomes hidden,
// and remove the old visibility listener, if there was one.
if (!gVibrateWindowListener) {
// If gVibrateWindowListener is null, this is the first time we've vibrated,
// and we need to register a listener to clear gVibrateWindowListener on
// shutdown.
ClearOnShutdown(&gVibrateWindowListener);
} else {
gVibrateWindowListener->RemoveListener();
}
gVibrateWindowListener = new VibrateWindowListener(mWindow, doc);
hal::Vibrate(pattern, mWindow);
}
if (aPersistent) {
AddPermission(doc->NodePrincipal(), kVibrationPermissionType,
aPermitted ? nsIPermissionManager::ALLOW_ACTION :
nsIPermissionManager::DENY_ACTION,
nsIPermissionManager::EXPIRE_SESSION, 0);
}
}
bool
Navigator::Vibrate(uint32_t aDuration)
{
AutoTArray<uint32_t, 1> pattern;
pattern.AppendElement(aDuration);
return Vibrate(pattern);
}
bool
Navigator::Vibrate(const nsTArray<uint32_t>& aPattern)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mWindow) {
return false;
}
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
if (!MayVibrate(doc)) {
return false;
}
nsTArray<uint32_t> pattern(aPattern);
if (pattern.Length() > sMaxVibrateListLen) {
pattern.SetLength(sMaxVibrateListLen);
}
for (size_t i = 0; i < pattern.Length(); ++i) {
pattern[i] = std::min(sMaxVibrateMS, pattern[i]);
}
// The spec says we check sVibratorEnabled after we've done the sanity
// checking on the pattern.
if (!sVibratorEnabled) {
return true;
}
mRequestedVibrationPattern.SwapElements(pattern);
uint32_t permission = GetPermission(mWindow, kVibrationPermissionType);
if (permission == nsIPermissionManager::ALLOW_ACTION ||
mRequestedVibrationPattern.IsEmpty() ||
(mRequestedVibrationPattern.Length() == 1 &&
mRequestedVibrationPattern[0] == 0)) {
// Always allow cancelling vibration and respect session permissions.
SetVibrationPermission(true /* permitted */, false /* persistent */);
return true;
}
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (!obs || permission == nsIPermissionManager::DENY_ACTION) {
// Abort without observer service or on denied session permission.
SetVibrationPermission(false /* permitted */, false /* persistent */);
return true;
}
// Request user permission.
obs->NotifyObservers(ToSupports(this), "Vibration:Request", nullptr);
return true;
}
//*****************************************************************************
// Pointer Events interface
//*****************************************************************************
-3
View File
@@ -149,9 +149,6 @@ public:
// NavigatorBinding::ClearCachedUserAgentValue(this);
void ClearUserAgentCache();
bool Vibrate(uint32_t aDuration);
bool Vibrate(const nsTArray<uint32_t>& aDuration);
void SetVibrationPermission(bool aPermitted, bool aPersistent);
uint32_t MaxTouchPoints();
void GetAppCodeName(nsString& aAppCodeName, ErrorResult& aRv)
{
+29 -11
View File
@@ -15,7 +15,7 @@ namespace dom {
void
ResizeObserverNotificationHelper::WillRefresh(TimeStamp aTime)
{
MOZ_ASSERT(mOwner, "Why is mOwner already dead when this RefreshObserver is still registered?");
MOZ_DIAGNOSTIC_ASSERT(mOwner, "RefreshObserver should have been de-registered on time, but isn't.");
if (mOwner) {
mOwner->Notify();
}
@@ -58,15 +58,19 @@ ResizeObserverNotificationHelper::Register()
void
ResizeObserverNotificationHelper::Unregister()
{
if (!mOwner) {
// We've outlived our owner, so there's nothing registered anymore.
mRegistered = false;
return;
}
if (!mRegistered) {
return;
}
nsRefreshDriver* refreshDriver = GetRefreshDriver();
if (!refreshDriver) {
// We can't access RefreshDriver now. Just abort the Unregister().
return;
}
MOZ_RELEASE_ASSERT(refreshDriver,
"We should not leave a dangling reference to the observer around");
refreshDriver->RemoveRefreshObserver(this, Flush_Display);
mRegistered = false;
@@ -75,9 +79,8 @@ ResizeObserverNotificationHelper::Unregister()
void
ResizeObserverNotificationHelper::Disconnect()
{
Unregister();
// Our owner is dying. Clear our pointer to it, in case we outlive it.
mOwner = nullptr;
MOZ_RELEASE_ASSERT(!mRegistered, "How can we die when registered?");
MOZ_RELEASE_ASSERT(!mOwner, "Forgot to clear weak pointer?");
}
ResizeObserverNotificationHelper::~ResizeObserverNotificationHelper()
@@ -105,6 +108,10 @@ ResizeObserverController::AddResizeObserver(ResizeObserver* aObserver)
mResizeObservers.AppendElement(aObserver);
}
void ResizeObserverController::DetachFromDocument() {
mResizeObserverNotificationHelper->Unregister();
}
void
ResizeObserverController::Notify()
{
@@ -112,6 +119,10 @@ ResizeObserverController::Notify()
return;
}
// Hold a strong reference to the document, because otherwise calling
// all active observers on it might yank it out from under us.
RefPtr<nsIDocument> document(mDocument);
uint32_t shallowestTargetDepth = 0;
GatherAllActiveObservations(shallowestTargetDepth);
@@ -146,7 +157,7 @@ ResizeObserverController::Notify()
nsEventStatus status = nsEventStatus_eIgnore;
nsCOMPtr<nsPIDOMWindowInner> window =
mDocument->GetWindow()->GetCurrentInnerWindow();
document->GetWindow()->GetCurrentInnerWindow();
if (window) {
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
@@ -178,7 +189,11 @@ ResizeObserverController::BroadcastAllActiveObservations()
{
uint32_t shallowestTargetDepth = UINT32_MAX;
for (auto observer : mResizeObservers) {
// Use a copy of the observers as this invokes the callbacks of the observers
// which could register/unregister observers at will.
nsTArray<RefPtr<ResizeObserver>> tempObservers(mResizeObservers);
for (auto observer : tempObservers) {
uint32_t targetDepth = observer->BroadcastActiveObservations();
@@ -226,7 +241,10 @@ ResizeObserverController::GetShell() const
ResizeObserverController::~ResizeObserverController()
{
mResizeObserverNotificationHelper->Disconnect();
MOZ_RELEASE_ASSERT(
!mResizeObserverNotificationHelper->IsRegistered(),
"Nothing else should keep a reference to our notification helper when we go away");
mResizeObserverNotificationHelper->DetachFromOwner();
}
} // namespace dom
+5
View File
@@ -41,6 +41,10 @@ public:
void Disconnect();
bool IsRegistered() const { return mRegistered; }
void DetachFromOwner() { mOwner = nullptr; }
protected:
virtual ~ResizeObserverNotificationHelper();
@@ -68,6 +72,7 @@ public:
void Traverse(nsCycleCollectionTraversalCallback& aCb);
void Unlink();
void DetachFromDocument();
void AddResizeObserver(ResizeObserver* aObserver);
/*
+6
View File
@@ -3644,6 +3644,12 @@ nsDocument::DeleteShell()
// objects for @font-face rules that came from the style set.
RebuildUserFontSet();
if (mResizeObserverController) {
// If the shell is going away, we need to remove any links to this document
// from the observer.
mResizeObserverController->DetachFromDocument();
}
nsIPresShell* oldShell = mPresShell;
mPresShell = nullptr;
UpdateFrameRequestCallbackSchedulingState(oldShell);
+5 -1
View File
@@ -932,8 +932,12 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
MOZ_ASSERT(!req, "Shouldn't have non-null request here");
// If we don't have a current URI, we might as well store this URI so people
// know what we tried (and failed) to load.
if (!mCurrentRequest)
if (!mCurrentRequest) {
mCurrentURI = aNewURI;
if (mImageBlockingStatus == nsIContentPolicy::ACCEPT) {
mImageBlockingStatus = nsIContentPolicy::REJECT_REQUEST;
}
}
FireEvent(NS_LITERAL_STRING("error"));
FireEvent(NS_LITERAL_STRING("loadend"));
+13
View File
@@ -1384,6 +1384,8 @@ nsTreeSanitizer::SanitizeChildren(nsINode* aRoot)
nsAutoString styleText;
nsContentUtils::GetNodeTextContent(node, false, styleText);
RemoveAllAttributesFromDescendants(elt);
nsAutoString sanitizedStyle;
nsCOMPtr<nsIURI> baseURI = node->GetBaseURI();
if (SanitizeStyleSheet(styleText,
@@ -1479,6 +1481,17 @@ nsTreeSanitizer::RemoveAllAttributes(nsIContent* aElement)
}
}
void nsTreeSanitizer::RemoveAllAttributesFromDescendants(mozilla::dom::Element* aElement) {
nsIContent* node = aElement->GetFirstChild();
while (node) {
if (node->IsElement()) {
mozilla::dom::Element* elt = node->AsElement();
RemoveAllAttributes(elt);
}
node = node->GetNextNode(aElement);
}
}
void
nsTreeSanitizer::InitializeStatics()
{
+6
View File
@@ -183,6 +183,12 @@ class MOZ_STACK_CLASS nsTreeSanitizer {
*/
void RemoveAllAttributes(nsIContent* aElement);
/**
* Removes all attributes from the descendants of an element but not from
* the element itself.
*/
void RemoveAllAttributesFromDescendants(mozilla::dom::Element* aElement);
/**
* The whitelist of HTML elements.
*/
+20 -4
View File
@@ -708,10 +708,12 @@ FetchDriver::OnDataAvailable(nsIRequest* aRequest,
// about races.
if (mObserver) {
// Need to keep mObserver alive.
RefPtr<FetchDriverObserver> observer = mObserver;
if (NS_IsMainThread()) {
mObserver->OnDataAvailable();
observer->OnDataAvailable();
} else {
RefPtr<Runnable> runnable = new DataAvailableRunnable(mObserver);
RefPtr<Runnable> runnable = new DataAvailableRunnable(observer);
nsresult rv = NS_DispatchToMainThread(runnable);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@@ -719,12 +721,15 @@ FetchDriver::OnDataAvailable(nsIRequest* aRequest,
}
}
uint32_t aRead;
// Explicitly initialized to 0 because in some cases nsStringInputStream may
// not write to aRead.
uint32_t aRead = 0;
MOZ_ASSERT(mResponse);
MOZ_ASSERT(mPipeOutputStream);
// From "Main Fetch" step 17: SRI-part2.
if (mResponse->Type() != ResponseType::Error &&
if (mResponse &&
mResponse->Type() != ResponseType::Error &&
!mRequest->GetIntegrity().IsEmpty()) {
MOZ_ASSERT(mSRIDataVerifier);
@@ -766,6 +771,17 @@ FetchDriver::OnDataAvailable(nsIRequest* aRequest,
nsresult rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
mPipeOutputStream,
aCount, &aRead);
// If no data was read, it's possible the output stream is closed but the
// ReadSegments call followed its contract of returning NS_OK despite write
// errors. Unfortunately, nsIOutputStream has an ill-conceived contract when
// taken together with ReadSegments' contract, because the pipe will just
// NS_OK if we try and invoke its Write* functions ourselves with a 0 count.
// So we must just assume the pipe is broken.
if (aRead == 0 && aCount != 0) {
return NS_BASE_STREAM_CLOSED;
}
return rv;
}
+37 -30
View File
@@ -599,38 +599,31 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest)
return rv;
}
static bool
CreateTypeError(JSContext* aCx, ModuleScript* aScript, const nsString& aMessage,
JS::MutableHandle<JS::Value> aError)
{
JS::Rooted<JSObject*> module(aCx, aScript->ModuleRecord());
JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(aCx, module));
JS::Rooted<JSString*> filename(aCx);
filename = JS_NewStringCopyZ(aCx, JS_GetScriptFilename(script));
if (!filename) {
return false;
}
JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyZ(aCx, aMessage.get()));
if (!message) {
return false;
}
return JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr,
message, aError);
}
static nsresult
HandleResolveFailure(JSContext* aCx, ModuleScript* aScript,
const nsAString& aSpecifier)
const nsAString& aSpecifier,
uint32_t aLineNumber, uint32_t aColumnNumber)
{
// TODO: How can we get the line number of the failed import?
nsAutoCString url;
aScript->BaseURL()->GetAsciiSpec(url);
JS::Rooted<JSString*> filename(aCx);
filename = JS_NewStringCopyZ(aCx, url.get());
if (!filename) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsAutoString message(NS_LITERAL_STRING("Error resolving module specifier: "));
message.Append(aSpecifier);
JS::Rooted<JSString*> string(aCx, JS_NewUCStringCopyZ(aCx, message.get()));
if (!string) {
return NS_ERROR_OUT_OF_MEMORY;
}
JS::Rooted<JS::Value> error(aCx);
if (!CreateTypeError(aCx, aScript, message, &error)) {
if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, aLineNumber,
aColumnNumber, nullptr, string, &error)) {
return NS_ERROR_OUT_OF_MEMORY;
}
@@ -685,28 +678,37 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>* aUrlsOu
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> moduleRecord(cx, ms->ModuleRecord());
JS::Rooted<JSObject*> specifiers(cx, JS::GetRequestedModules(cx, moduleRecord));
JS::Rooted<JSObject*> requestedModules(cx);
requestedModules = JS::GetRequestedModules(cx, moduleRecord);
MOZ_ASSERT(requestedModules);
uint32_t length;
if (!JS_GetArrayLength(cx, specifiers, &length)) {
if (!JS_GetArrayLength(cx, requestedModules, &length)) {
return NS_ERROR_FAILURE;
}
JS::Rooted<JS::Value> val(cx);
JS::Rooted<JS::Value> element(cx);
for (uint32_t i = 0; i < length; i++) {
if (!JS_GetElement(cx, specifiers, i, &val)) {
if (!JS_GetElement(cx, requestedModules, i, &element)) {
return NS_ERROR_FAILURE;
}
JS::Rooted<JSString*> str(cx, JS::GetRequestedModuleSpecifier(cx, element));
MOZ_ASSERT(str);
nsAutoJSString specifier;
if (!specifier.init(cx, val)) {
if (!specifier.init(cx, str)) {
return NS_ERROR_FAILURE;
}
// Let url be the result of resolving a module specifier given module script and requested.
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier);
if (!uri) {
nsresult rv = HandleResolveFailure(cx, ms, specifier);
uint32_t lineNumber = 0;
uint32_t columnNumber = 0;
JS::GetRequestedModuleSourcePos(cx, element, &lineNumber, &columnNumber);
nsresult rv = HandleResolveFailure(cx, ms, specifier, lineNumber, columnNumber);
NS_ENSURE_SUCCESS(rv, rv);
return NS_ERROR_FAILURE;
}
@@ -806,6 +808,11 @@ HostResolveImportedModule(JSContext* aCx, JS::Handle<JSObject*> aModule,
auto script = static_cast<ModuleScript*>(value.toPrivate());
MOZ_ASSERT(script->ModuleRecord() == aModule);
// FIXME: in-line module scripts apparently pass a null specifier?
// At least make sure that doesn't crash the browser.
if (!aSpecifier)
return nullptr;
// Let url be the result of resolving a module specifier given referencing
// module script and specifier.
nsAutoJSString string;
+1
View File
@@ -194,6 +194,7 @@ nsSMILCSSProperty::IsPropertyAnimatable(nsCSSPropertyID aPropID)
// writing-mode
switch (aPropID) {
case eCSSProperty_caret_color:
case eCSSProperty_clip:
case eCSSProperty_clip_rule:
case eCSSProperty_clip_path:
@@ -122,7 +122,6 @@ run-if = e10s
[test_storagePermissionsReject.html]
[test_storagePermissionsRejectForeign.html]
[test_stylesheetPI.html]
[test_vibrator.html]
[test_WebKitCSSMatrix.html]
[test_windowedhistoryframes.html]
[test_windowProperties.html]
@@ -1,93 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Vibrator</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<!-- Although we can't test that the vibrator works properly, we can test that
navigator.vibrate throws an exception where appropriate. -->
<script class="testbody" type="text/javascript;version=1.7">
SimpleTest.waitForExplicitFinish();
var result;
function expectFailure(param) {
result = navigator.vibrate(param);
is(result, false, 'vibrate(' + param + ') should have failed.');
}
function expectSuccess(param) {
result = navigator.vibrate(param);
is(result, true, 'vibrate(' + param + ') must succeed.');
}
function tests(aEnabled) {
// Some edge cases that the bindings should handle for us.
expectSuccess(null);
expectSuccess(undefined);
// -1 will be converted to the highest unsigned long then clamped.
expectSuccess(-1);
expectSuccess('a');
// -1 will be converted to the highest unsigned long then clamped.
expectSuccess([100, -1]);
expectSuccess([100, 'a']);
var maxVibrateMs = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_ms');
var maxVibrateListLen = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_list_len');
// If we pass a vibration pattern with a value higher than max_vibrate_ms or a
// pattern longer than max_vibrate_list_len, the call should succeed but the
// pattern should be modified to match the restrictions.
// Values will be clamped to dom.vibrator.max_vibrate_ms.
expectSuccess(maxVibrateMs + 1);
expectSuccess([maxVibrateMs + 1]);
var arr = [];
for (var i = 0; i < maxVibrateListLen + 1; i++) {
arr[i] = 0;
}
// The array will be truncated to have a length equal to dom.vibrator.max_vibrate_list_len.
expectSuccess(arr);
expectSuccess(0);
expectSuccess([]);
expectSuccess('1000');
expectSuccess(1000);
expectSuccess(1000.1);
expectSuccess([0, 0, 0]);
expectSuccess(['1000', 1000]);
expectSuccess([1000, 1000]);
expectSuccess([1000, 1000.1]);
// The following loop shouldn't cause us to crash. See bug 701716.
for (var i = 0; i < 10000; i++) {
navigator.vibrate([100, 100]);
}
ok(true, "Didn't crash after issuing a lot of vibrate() calls.");
if(!aEnabled)
SimpleTest.finish();
}
SpecialPowers.pushPermissions([
{type: 'vibration', allow: true, context: document}
], function() {
// Test with the vibrator pref enabled.
SpecialPowers.pushPrefEnv({"set": [['dom.vibrator.enabled', true]]}, function() {
tests(true);
SpecialPowers.pushPrefEnv({"set": [['dom.vibrator.enabled', false]]}, tests(false));
});
// Everything should be the same when the vibrator is disabled -- in
// particular, a disabled vibrator shouldn't eat failures we'd otherwise
// observe.
}
);
</script>
</body>
</html>
+3 -22
View File
@@ -13,9 +13,9 @@
* http://www.w3.org/TR/beacon/#sec-beacon-method
* https://html.spec.whatwg.org/#navigatorconcurrenthardware
*
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
* Opera Software ASA. You are granted a license to use, reproduce
* and create derivative works of this document.
* © Copyright 2004-2020 Apple Computer, Inc., Mozilla Foundation,
* Opera Software ASA and Moonchild Productions. You are granted a license to use,
* reproduce and create derivative works of this document.
*/
// http://www.whatwg.org/specs/web-apps/current-work/#the-navigator-object
@@ -124,14 +124,6 @@ interface NavigatorGeolocation {
};
Navigator implements NavigatorGeolocation;
// http://www.w3.org/TR/vibration/#vibration-interface
partial interface Navigator {
// We don't support sequences in unions yet
//boolean vibrate ((unsigned long or sequence<unsigned long>) pattern);
boolean vibrate(unsigned long duration);
boolean vibrate(sequence<unsigned long> pattern);
};
// http://www.w3.org/TR/pointerevents/#extensions-to-the-navigator-interface
partial interface Navigator {
[Pref="dom.w3c_pointer_events.enabled"]
@@ -140,17 +132,6 @@ partial interface Navigator {
// Mozilla-specific extensions
// Chrome-only interface for Vibration API permission handling.
partial interface Navigator {
/* Set permission state to device vibration.
* @param permitted permission state (true for allowing vibration)
* @param persistent make the permission session-persistent
*/
[ChromeOnly]
void setVibrationPermission(boolean permitted,
optional boolean persistent = true);
};
callback interface MozIdleObserver {
// Time is in seconds and is read only when idle observers are added
// and removed.
+30 -16
View File
@@ -212,7 +212,7 @@ VerifyRGBXCorners(uint8_t* aData, const IntSize &aSize, const int32_t aStride, S
#endif
static sk_sp<SkImage>
GetSkImageForSurface(SourceSurface* aSurface)
GetSkImageForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>* aLock)
{
if (!aSurface) {
gfxDebug() << "Creating null Skia image from null SourceSurface";
@@ -220,7 +220,7 @@ GetSkImageForSurface(SourceSurface* aSurface)
}
if (aSurface->GetType() == SurfaceType::SKIA) {
return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage();
return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock);
}
DataSourceSurface* surf = aSurface->GetDataSurface().take();
@@ -390,9 +390,9 @@ ExtractAlphaBitmap(sk_sp<SkImage> aImage, SkBitmap* aResultBitmap)
}
static sk_sp<SkImage>
ExtractAlphaForSurface(SourceSurface* aSurface)
ExtractAlphaForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>& aLock)
{
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &aLock);
if (!image) {
return nullptr;
}
@@ -411,7 +411,11 @@ ExtractAlphaForSurface(SourceSurface* aSurface)
}
static void
SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0, Point aOffset = Point(0, 0))
SetPaintPattern(SkPaint& aPaint,
const Pattern& aPattern,
Maybe<MutexAutoLock>& aLock,
Float aAlpha = 1.0,
Point aOffset = Point(0, 0))
{
switch (aPattern.GetType()) {
case PatternType::COLOR: {
@@ -473,7 +477,7 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0, Po
}
case PatternType::SURFACE: {
const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface);
sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface, &aLock);
if (!image) {
aPaint.setColor(SK_ColorTRANSPARENT);
break;
@@ -525,7 +529,7 @@ struct AutoPaintSetup {
: mNeedsRestore(false), mAlpha(1.0)
{
Init(aCanvas, aOptions, aMaskBounds, false);
SetPaintPattern(mPaint, aPattern, mAlpha, aOffset);
SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aOffset);
}
AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
@@ -579,6 +583,7 @@ struct AutoPaintSetup {
SkPaint mPaint;
bool mNeedsRestore;
SkCanvas* mCanvas;
Maybe<MutexAutoLock> mLock;
Float mAlpha;
};
@@ -601,7 +606,8 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
MarkChanged();
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
if (!image) {
return;
}
@@ -654,7 +660,8 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface,
MarkChanged();
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
if (!image) {
return;
}
@@ -1407,8 +1414,9 @@ DrawTargetSkia::Mask(const Pattern &aSource,
MarkChanged();
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource);
Maybe<MutexAutoLock> lock;
SkPaint maskPaint;
SetPaintPattern(maskPaint, aMask);
SetPaintPattern(maskPaint, aMask, lock);
SkLayerRasterizer::Builder builder;
builder.addLayer(maskPaint);
@@ -1427,7 +1435,8 @@ DrawTargetSkia::MaskSurface(const Pattern &aSource,
MarkChanged();
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource, nullptr, -aOffset);
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask);
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask, lock);
if (!alphaMask) {
gfxDebug() << *this << ": MaskSurface() failed to extract alpha for mask";
return;
@@ -1456,7 +1465,8 @@ DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& a
fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
// Read in the source data.
sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface);
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock);
if (!srcImage) {
return true;
}
@@ -1515,7 +1525,8 @@ DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x
MarkChanged();
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
if (!image) {
return true;
}
@@ -1599,7 +1610,8 @@ already_AddRefed<SourceSurface>
DrawTargetSkia::OptimizeGPUSourceSurface(SourceSurface *aSurface) const
{
// Check if the underlying SkImage already has an associated GrTexture.
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
if (!image || image->isTextureBacked()) {
RefPtr<SourceSurface> surface(aSurface);
return surface.forget();
@@ -1716,7 +1728,8 @@ DrawTargetSkia::CopySurface(SourceSurface *aSurface,
{
MarkChanged();
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
if (!image) {
return;
}
@@ -2061,7 +2074,8 @@ DrawTargetSkia::PopLayer()
paint.setColor(SK_ColorTRANSPARENT);
}
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask);
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask, lock);
if (!alphaMask) {
gfxDebug() << *this << ": PopLayer() failed to extract alpha for mask";
} else {
+19
View File
@@ -40,6 +40,25 @@ SourceSurfaceSkia::GetFormat() const
return mFormat;
}
sk_sp<SkImage>
SourceSurfaceSkia::GetImage(Maybe<MutexAutoLock>* aLock) {
// If we were provided a lock object, we can let the caller access
// a shared SkImage and we know it won't go away while the lock is held.
if (aLock) {
// We are locked, so now we can check mDrawTarget.
// If it's null, then we're not shared and we can unlock eagerly.
if (!mDrawTarget) {
aLock->reset();
}
} else {
// Otherwise we need to call DrawTargetWillChange to ensure we have our
// own copy of the SkImage.
DrawTargetWillChange();
}
sk_sp<SkImage> image = mImage;
return image;
}
static sk_sp<SkData>
MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride)
{
+5 -1
View File
@@ -10,6 +10,10 @@
#include <vector>
#include "skia/include/core/SkCanvas.h"
#include "skia/include/core/SkImage.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
typedef mozilla::MutexAutoLock MutexAutoLock;
namespace mozilla {
@@ -28,7 +32,7 @@ public:
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
sk_sp<SkImage>& GetImage() { return mImage; }
sk_sp<SkImage> GetImage(Maybe<MutexAutoLock>* aLock);
bool InitFromData(unsigned char* aData,
const IntSize &aSize,
+28
View File
@@ -1071,6 +1071,34 @@ gfxTextRun::GetAdvanceWidth(Range aRange, PropertyProvider *aProvider,
return result + GetAdvanceForGlyphs(ligatureRange);
}
gfxFloat
gfxTextRun::GetMinAdvanceWidth(Range aRange)
{
MOZ_ASSERT(aRange.end <= GetLength(), "Substring out of range");
Range ligatureRange = aRange;
ShrinkToLigatureBoundaries(&ligatureRange);
gfxFloat result = std::max(
ComputePartialLigatureWidth(Range(aRange.start, ligatureRange.start),
nullptr),
ComputePartialLigatureWidth(Range(ligatureRange.end, aRange.end),
nullptr));
// XXX Do we need to take spacing into account? When each grapheme cluster
// takes its own line, we shouldn't be adding spacings around them.
gfxFloat clusterAdvance = 0;
for (uint32_t i = ligatureRange.start; i < ligatureRange.end; ++i) {
clusterAdvance += GetAdvanceForGlyph(i);
if (i + 1 == ligatureRange.end || IsClusterStart(i + 1)) {
result = std::max(result, clusterAdvance);
clusterAdvance = 0;
}
}
return result;
}
bool
gfxTextRun::SetLineBreaks(Range aRange,
bool aLineBreakBefore, bool aLineBreakAfter,
+6
View File
@@ -297,6 +297,12 @@ public:
return GetAdvanceWidth(Range(this), nullptr);
}
/**
* Computes the minimum advance width for a substring assuming line
* breaking is allowed everywhere.
*/
gfxFloat GetMinAdvanceWidth(Range aRange);
/**
* Clear all stored line breaks for the given range (both before and after),
* and then set the line-break state before aRange.start to aBreakBefore and
-79
View File
@@ -94,87 +94,8 @@ WindowIsActive(nsPIDOMWindowInner* aWindow)
return !document->Hidden();
}
StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
void InitLastIDToVibrate()
{
gLastIDToVibrate = new WindowIdentifier::IDArrayType();
ClearOnShutdown(&gLastIDToVibrate);
}
} // namespace
void
Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window)
{
Vibrate(pattern, WindowIdentifier(window));
}
void
Vibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id)
{
AssertMainThread();
// Only active windows may start vibrations. If |id| hasn't gone
// through the IPC layer -- that is, if our caller is the outside
// world, not hal_proxy -- check whether the window is active. If
// |id| has gone through IPC, don't check the window's visibility;
// only the window corresponding to the bottommost process has its
// visibility state set correctly.
if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");
return;
}
if (!InSandbox()) {
if (!gLastIDToVibrate) {
InitLastIDToVibrate();
}
*gLastIDToVibrate = id.AsArray();
}
// Don't forward our ID if we are not in the sandbox, because hal_impl
// doesn't need it, and we don't want it to be tempted to read it. The
// empty identifier will assert if it's used.
PROXY_IF_SANDBOXED(Vibrate(pattern, InSandbox() ? id : WindowIdentifier()));
}
void
CancelVibrate(nsPIDOMWindowInner* window)
{
CancelVibrate(WindowIdentifier(window));
}
void
CancelVibrate(const WindowIdentifier &id)
{
AssertMainThread();
// Although only active windows may start vibrations, a window may
// cancel its own vibration even if it's no longer active.
//
// After a window is marked as inactive, it sends a CancelVibrate
// request. We want this request to cancel a playing vibration
// started by that window, so we certainly don't want to reject the
// cancellation request because the window is now inactive.
//
// But it could be the case that, after this window became inactive,
// some other window came along and started a vibration. We don't
// want this window's cancellation request to cancel that window's
// actively-playing vibration!
//
// To solve this problem, we keep track of the id of the last window
// to start a vibration, and only accepts cancellation requests from
// the same window. All other cancellation requests are ignored.
if (InSandbox() || (gLastIDToVibrate && *gLastIDToVibrate == id.AsArray())) {
// Don't forward our ID if we are not in the sandbox, because hal_impl
// doesn't need it, and we don't want it to be tempted to read it. The
// empty identifier will assert if it's used.
PROXY_IF_SANDBOXED(CancelVibrate(InSandbox() ? id : WindowIdentifier()));
}
}
template <class InfoType>
class ObserversManager
{
-35
View File
@@ -50,41 +50,6 @@ typedef Observer<SystemTimezoneChangeInformation> SystemTimezoneChangeObserver;
namespace MOZ_HAL_NAMESPACE {
/**
* Turn the default vibrator device on/off per the pattern specified
* by |pattern|. Each element in the pattern is the number of
* milliseconds to turn the vibrator on or off. The first element in
* |pattern| is an "on" element, the next is "off", and so on.
*
* If |pattern| is empty, any in-progress vibration is canceled.
*
* Only an active window within an active tab may call Vibrate; calls
* from inactive windows and windows on inactive tabs do nothing.
*
* If you're calling hal::Vibrate from the outside world, pass an
* nsIDOMWindow* in place of the WindowIdentifier parameter.
* The method with WindowIdentifier will be called automatically.
*/
void Vibrate(const nsTArray<uint32_t>& pattern,
nsPIDOMWindowInner* aWindow);
void Vibrate(const nsTArray<uint32_t>& pattern,
const hal::WindowIdentifier &id);
/**
* Cancel a vibration started by the content window identified by
* WindowIdentifier.
*
* If the window was the last window to start a vibration, the
* cancellation request will go through even if the window is not
* active.
*
* As with hal::Vibrate(), if you're calling hal::CancelVibrate from the outside
* world, pass an nsIDOMWindow*. The method with WindowIdentifier will be called
* automatically.
*/
void CancelVibrate(nsPIDOMWindowInner* aWindow);
void CancelVibrate(const hal::WindowIdentifier &id);
/**
* Determine whether the device's screen is currently enabled.
*/
-22
View File
@@ -1,22 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Hal.h"
using mozilla::hal::WindowIdentifier;
namespace mozilla {
namespace hal_impl {
void
Vibrate(const nsTArray<uint32_t>& pattern, const hal::WindowIdentifier &)
{}
void
CancelVibrate(const hal::WindowIdentifier &)
{}
} // namespace hal_impl
} // namespace mozilla
-5
View File
@@ -29,7 +29,6 @@ if CONFIG['OS_TARGET'] == 'Linux':
'fallback/FallbackAlarm.cpp',
'fallback/FallbackScreenConfiguration.cpp',
'fallback/FallbackSensor.cpp',
'fallback/FallbackVibration.cpp',
'linux/LinuxMemory.cpp',
'linux/LinuxPower.cpp',
]
@@ -39,7 +38,6 @@ elif CONFIG['OS_TARGET'] == 'WINNT':
'fallback/FallbackMemory.cpp',
'fallback/FallbackPower.cpp',
'fallback/FallbackScreenConfiguration.cpp',
'fallback/FallbackVibration.cpp',
'windows/WindowsSensor.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
@@ -48,7 +46,6 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
'fallback/FallbackMemory.cpp',
'fallback/FallbackPower.cpp',
'fallback/FallbackScreenConfiguration.cpp',
'fallback/FallbackVibration.cpp',
]
elif CONFIG['OS_TARGET'] in ('OpenBSD', 'NetBSD', 'FreeBSD', 'DragonFly'):
UNIFIED_SOURCES += [
@@ -57,7 +54,6 @@ elif CONFIG['OS_TARGET'] in ('OpenBSD', 'NetBSD', 'FreeBSD', 'DragonFly'):
'fallback/FallbackPower.cpp',
'fallback/FallbackScreenConfiguration.cpp',
'fallback/FallbackSensor.cpp',
'fallback/FallbackVibration.cpp',
]
else:
UNIFIED_SOURCES += [
@@ -66,7 +62,6 @@ else:
'fallback/FallbackPower.cpp',
'fallback/FallbackScreenConfiguration.cpp',
'fallback/FallbackSensor.cpp',
'fallback/FallbackVibration.cpp',
]
UNIFIED_SOURCES += [
-3
View File
@@ -69,9 +69,6 @@ child:
async NotifySystemTimezoneChange(SystemTimezoneChangeInformation aSystemTimezoneChangeInfo);
parent:
async Vibrate(uint32_t[] pattern, uint64_t[] id, PBrowser browser);
async CancelVibrate(uint64_t[] id, PBrowser browser);
async EnableNetworkNotifications();
async DisableNetworkNotifications();
sync GetCurrentNetworkInformation()
-52
View File
@@ -45,28 +45,6 @@ Hal()
return sHal;
}
void
Vibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id)
{
HAL_LOG("Vibrate: Sending to parent process.");
AutoTArray<uint32_t, 8> p(pattern);
WindowIdentifier newID(id);
newID.AppendProcessID();
Hal()->SendVibrate(p, newID.AsArray(), TabChild::GetFrom(newID.GetWindow()));
}
void
CancelVibrate(const WindowIdentifier &id)
{
HAL_LOG("CancelVibrate: Sending to parent process.");
WindowIdentifier newID(id);
newID.AppendProcessID();
Hal()->SendCancelVibrate(newID.AsArray(), TabChild::GetFrom(newID.GetWindow()));
}
void
EnableNetworkNotifications()
{
@@ -379,36 +357,6 @@ public:
hal::UnregisterSystemTimezoneChangeObserver(this);
}
virtual bool
RecvVibrate(InfallibleTArray<unsigned int>&& pattern,
InfallibleTArray<uint64_t>&& id,
PBrowserParent *browserParent) override
{
// We give all content vibration permission.
// TabParent *tabParent = TabParent::GetFrom(browserParent);
/* xxxkhuey wtf
nsCOMPtr<nsIDOMWindow> window =
do_QueryInterface(tabParent->GetBrowserDOMWindow());
*/
WindowIdentifier newID(id, nullptr);
hal::Vibrate(pattern, newID);
return true;
}
virtual bool
RecvCancelVibrate(InfallibleTArray<uint64_t> &&id,
PBrowserParent *browserParent) override
{
//TabParent *tabParent = TabParent::GetFrom(browserParent);
/* XXXkhuey wtf
nsCOMPtr<nsIDOMWindow> window =
tabParent->GetBrowserDOMWindow();
*/
WindowIdentifier newID(id, nullptr);
hal::CancelVibrate(newID);
return true;
}
virtual bool
RecvEnableNetworkNotifications() override {
// We give all content access to this network-status information.
+1
View File
@@ -29,6 +29,7 @@
<ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
</ms_asmv3:windowsSettings>
</ms_asmv3:application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+1 -1
View File
@@ -1,5 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
* This Source Code Form is subject to the terms of the Mozilla Public
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+13
View File
@@ -246,3 +246,16 @@ with only_when('--enable-compile-environment'):
set_config('LIBFUZZER', enable_libfuzzer)
set_define('LIBFUZZER', enable_libfuzzer)
# Initial support for new regexp engine
# ==================================================
js_option('--enable-new-regexp', default=False, help='Enable new regexp engine')
@depends('--enable-new-regexp')
def enable_new_regexp(value):
if value:
return True
set_config('JS_NEW_REGEXP', enable_new_regexp)
set_define('JS_NEW_REGEXP', enable_new_regexp)
+1 -1
View File
@@ -778,7 +778,7 @@ struct JSClass {
// application.
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
#define JSCLASS_GLOBAL_SLOT_COUNT \
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45)
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \
+2 -2
View File
@@ -363,7 +363,7 @@ function InnerModuleInstantiation(module, stack, index)
// Step 9
let requestedModules = module.requestedModules;
for (let i = 0; i < requestedModules.length; i++) {
let required = requestedModules[i];
let required = requestedModules[i].moduleSpecifier;
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_UNINSTANTIATED);
index = InnerModuleInstantiation(requiredModule, stack, index);
@@ -591,7 +591,7 @@ function InnerModuleEvaluation(module, stack, index)
// Step 10
let requestedModules = module.requestedModules;
for (let i = 0; i < requestedModules.length; i++) {
let required = requestedModules[i];
let required = requestedModules[i].moduleSpecifier;
let requiredModule =
CallModuleResolveHook(module, required, MODULE_STATUS_INSTANTIATED);
+98 -30
View File
@@ -125,7 +125,7 @@ GlobalObject::initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
return false;
global->setReservedSlot(IMPORT_ENTRY_PROTO, ObjectValue(*proto));
global->initReservedSlot(IMPORT_ENTRY_PROTO, ObjectValue(*proto));
return true;
}
@@ -203,7 +203,7 @@ GlobalObject::initExportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
return false;
global->setReservedSlot(EXPORT_ENTRY_PROTO, ObjectValue(*proto));
global->initReservedSlot(EXPORT_ENTRY_PROTO, ObjectValue(*proto));
return true;
}
@@ -240,6 +240,71 @@ ExportEntryObject::create(ExclusiveContext* cx,
return self;
}
///////////////////////////////////////////////////////////////////////////
// RequestedModuleObject
/* static */ const Class
RequestedModuleObject::class_ = {
"RequestedModule",
JSCLASS_HAS_RESERVED_SLOTS(RequestedModuleObject::SlotCount) |
JSCLASS_IS_ANONYMOUS
};
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, moduleSpecifier, ModuleSpecifierSlot)
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, lineNumber, LineNumberSlot)
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, columnNumber, ColumnNumberSlot)
DEFINE_ATOM_ACCESSOR_METHOD(RequestedModuleObject, moduleSpecifier)
DEFINE_UINT32_ACCESSOR_METHOD(RequestedModuleObject, lineNumber)
DEFINE_UINT32_ACCESSOR_METHOD(RequestedModuleObject, columnNumber)
/* static */ bool
RequestedModuleObject::isInstance(HandleValue value)
{
return value.isObject() && value.toObject().is<RequestedModuleObject>();
}
/* static */ bool
GlobalObject::initRequestedModuleProto(JSContext* cx, Handle<GlobalObject*> global)
{
static const JSPropertySpec protoAccessors[] = {
JS_PSG("moduleSpecifier", RequestedModuleObject_moduleSpecifierGetter, 0),
JS_PSG("lineNumber", RequestedModuleObject_lineNumberGetter, 0),
JS_PSG("columnNumber", RequestedModuleObject_columnNumberGetter, 0),
JS_PS_END
};
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return false;
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
return false;
global->initReservedSlot(REQUESTED_MODULE_PROTO, ObjectValue(*proto));
return true;
}
/* static */ RequestedModuleObject*
RequestedModuleObject::create(JSContext* cx,
HandleAtom moduleSpecifier,
uint32_t lineNumber,
uint32_t columnNumber)
{
MOZ_ASSERT(lineNumber > 0);
RootedObject proto(cx, cx->global()->getRequestedModulePrototype());
RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
if (!obj)
return nullptr;
RootedRequestedModuleObject self(cx, &obj->as<RequestedModuleObject>());
self->initReservedSlot(ModuleSpecifierSlot, StringValue(moduleSpecifier));
self->initReservedSlot(LineNumberSlot, Int32Value(lineNumber));
self->initReservedSlot(ColumnNumberSlot, Int32Value(columnNumber));
return self;
}
///////////////////////////////////////////////////////////////////////////
// IndirectBindingMap
@@ -1074,7 +1139,8 @@ ModuleBuilder::ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
: cx_(cx),
module_(cx, module),
tokenStream_(tokenStream),
requestedModules_(cx, AtomVector(cx)),
requestedModuleSpecifiers_(cx, AtomSet(cx)),
requestedModules_(cx, RequestedModuleVector(cx)),
importedBoundNames_(cx, AtomVector(cx)),
importEntries_(cx, ImportEntryVector(cx)),
exportEntries_(cx, ExportEntryVector(cx)),
@@ -1083,6 +1149,12 @@ ModuleBuilder::ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
starExportEntries_(cx, ExportEntryVector(cx))
{}
bool
ModuleBuilder::init()
{
return requestedModuleSpecifiers_.init();
}
bool
ModuleBuilder::buildTables()
{
@@ -1130,24 +1202,23 @@ ModuleBuilder::buildTables()
bool
ModuleBuilder::initModule()
{
RootedArrayObject requestedModules(cx_, createArray<JSAtom*>(requestedModules_));
RootedArrayObject requestedModules(cx_, createArray(requestedModules_));
if (!requestedModules)
return false;
RootedArrayObject importEntries(cx_, createArray<ImportEntryObject*>(importEntries_));
RootedArrayObject importEntries(cx_, createArray(importEntries_));
if (!importEntries)
return false;
RootedArrayObject localExportEntries(cx_, createArray<ExportEntryObject*>(localExportEntries_));
RootedArrayObject localExportEntries(cx_, createArray(localExportEntries_));
if (!localExportEntries)
return false;
RootedArrayObject indirectExportEntries(cx_);
indirectExportEntries = createArray<ExportEntryObject*>(indirectExportEntries_);
RootedArrayObject indirectExportEntries(cx_, createArray(indirectExportEntries_));
if (!indirectExportEntries)
return false;
RootedArrayObject starExportEntries(cx_, createArray<ExportEntryObject*>(starExportEntries_));
RootedArrayObject starExportEntries(cx_, createArray(starExportEntries_));
if (!starExportEntries)
return false;
@@ -1169,7 +1240,7 @@ ModuleBuilder::processImport(frontend::ParseNode* pn)
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
RootedAtom module(cx_, pn->pn_right->pn_atom);
if (!maybeAppendRequestedModule(module))
if (!maybeAppendRequestedModule(module, pn->pn_right))
return false;
for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
@@ -1277,7 +1348,7 @@ ModuleBuilder::processExportFrom(frontend::ParseNode* pn)
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
RootedAtom module(cx_, pn->pn_right->pn_atom);
if (!maybeAppendRequestedModule(module))
if (!maybeAppendRequestedModule(module, pn->pn_right))
return false;
for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
@@ -1346,29 +1417,26 @@ ModuleBuilder::appendExportFromEntry(HandleAtom exportName, HandleAtom moduleReq
}
bool
ModuleBuilder::maybeAppendRequestedModule(HandleAtom module)
ModuleBuilder::maybeAppendRequestedModule(HandleAtom specifier, ParseNode* node)
{
for (auto m : requestedModules_) {
if (m == module)
return true;
if (requestedModuleSpecifiers_.has(specifier)) {
return true;
}
return requestedModules_.append(module);
}
static Value
MakeElementValue(JSString *string)
{
return StringValue(string);
}
static Value
MakeElementValue(JSObject *object)
{
return ObjectValue(*object);
uint32_t line;
uint32_t column;
tokenStream_.srcCoords.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column);
JSContext* cx = reinterpret_cast<JSContext*>(cx_);
RootedRequestedModuleObject req(cx, RequestedModuleObject::create(cx, specifier, line, column));
if (!req) {
return false;
}
return FreezeObject(cx, req) &&
requestedModules_.append(req) &&
requestedModuleSpecifiers_.put(specifier);
}
template <typename T>
ArrayObject* ModuleBuilder::createArray(const GCVector<T>& vector)
ArrayObject* ModuleBuilder::createArray(const JS::Rooted<GCVector<T>>& vector)
{
uint32_t length = vector.length();
RootedArrayObject array(cx_, NewDenseFullyAllocatedArray(cx_, length));
@@ -1377,7 +1445,7 @@ ArrayObject* ModuleBuilder::createArray(const GCVector<T>& vector)
array->setDenseInitializedLength(length);
for (uint32_t i = 0; i < length; i++)
array->initDenseElement(i, MakeElementValue(vector[i]));
array->initDenseElement(i, ObjectValue(*vector[i]));
return array;
}
+37 -4
View File
@@ -100,6 +100,32 @@ class ExportEntryObject : public NativeObject
typedef Rooted<ExportEntryObject*> RootedExportEntryObject;
typedef Handle<ExportEntryObject*> HandleExportEntryObject;
class RequestedModuleObject : public NativeObject
{
public:
enum
{
ModuleSpecifierSlot = 0,
LineNumberSlot,
ColumnNumberSlot,
SlotCount
};
static const Class class_;
static JSObject* initClass(JSContext* cx, HandleObject obj);
static bool isInstance(HandleValue value);
static RequestedModuleObject* create(JSContext* cx,
HandleAtom moduleSpecifier,
uint32_t lineNumber,
uint32_t columnNumber);
JSAtom* moduleSpecifier() const;
uint32_t lineNumber() const;
uint32_t columnNumber() const;
};
typedef Rooted<RequestedModuleObject*> RootedRequestedModuleObject;
typedef Handle<RequestedModuleObject*> HandleRequestedModuleObject;
class IndirectBindingMap
{
public:
@@ -318,6 +344,8 @@ class MOZ_STACK_CLASS ModuleBuilder
explicit ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
const frontend::TokenStream& tokenStream);
bool init();
bool processImport(frontend::ParseNode* pn);
bool processExport(frontend::ParseNode* pn);
bool processExportFrom(frontend::ParseNode* pn);
@@ -334,15 +362,20 @@ class MOZ_STACK_CLASS ModuleBuilder
private:
using AtomVector = GCVector<JSAtom*>;
using RootedAtomVector = JS::Rooted<AtomVector>;
using ImportEntryVector = GCVector<ImportEntryObject*>;
using RequestedModuleVector = GCVector<RequestedModuleObject*>;
using AtomSet = JS::GCHashSet<JSAtom*>;
using RootedAtomVector = JS::Rooted<AtomVector>;
using RootedImportEntryVector = JS::Rooted<ImportEntryVector>;
using RootedExportEntryVector = JS::Rooted<ExportEntryVector>;
using RootedRequestedModuleVector = JS::Rooted<RequestedModuleVector>;
using RootedAtomSet = JS::Rooted<AtomSet>;
ExclusiveContext* cx_;
RootedModuleObject module_;
const frontend::TokenStream& tokenStream_;
RootedAtomVector requestedModules_;
RootedAtomSet requestedModuleSpecifiers_;
RootedRequestedModuleVector requestedModules_;
RootedAtomVector importedBoundNames_;
RootedImportEntryVector importEntries_;
RootedExportEntryVector exportEntries_;
@@ -357,10 +390,10 @@ class MOZ_STACK_CLASS ModuleBuilder
bool appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest,
HandleAtom importName, frontend::ParseNode* node);
bool maybeAppendRequestedModule(HandleAtom module);
bool maybeAppendRequestedModule(HandleAtom specifier, frontend::ParseNode* node);
template <typename T>
ArrayObject* createArray(const GCVector<T>& vector);
ArrayObject* createArray(const JS::Rooted<GCVector<T>>& vector);
};
} // namespace js
+3
View File
@@ -3741,6 +3741,9 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
return false;
ModuleBuilder builder(cx, module, parser.tokenStream);
if (!builder.init())
return false;
ModuleSharedContext modulesc(cx, module, &cx->global()->emptyGlobalScope(), builder);
pn = parser.moduleBody(&modulesc);
if (!pn)
+73
View File
@@ -0,0 +1,73 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
SOURCES += [
'AtomicsObject.cpp',
'Eval.cpp',
'Intl.cpp',
'MapObject.cpp',
'ModuleObject.cpp',
'Object.cpp',
'Profilers.cpp',
'Promise.cpp',
'Reflect.cpp',
'ReflectParse.cpp',
'RegExp.cpp',
'SIMD.cpp',
'SymbolObject.cpp',
'TestingFunctions.cpp',
'TypedObject.cpp',
'WeakMapObject.cpp',
'WeakSetObject.cpp',
]
if CONFIG['_MSC_VER']:
if CONFIG['CPU_ARCH'] == 'x86':
SOURCES['RegExp.cpp'].no_pgo = True # Bug 772303
# Prepare self-hosted JS code for embedding
GENERATED_FILES += [('selfhosted.out.h', 'selfhosted.js')]
selfhosted = GENERATED_FILES[('selfhosted.out.h', 'selfhosted.js')]
selfhosted.script = 'embedjs.py:generate_selfhosted'
selfhosted.inputs = [
'../js.msg',
'TypedObjectConstants.h',
'SelfHostingDefines.h',
'Utilities.js',
'Array.js',
'AsyncIteration.js',
'Classes.js',
'Date.js',
'Error.js',
'Function.js',
'Generator.js',
'Intl.js',
'IntlData.js',
'Iterator.js',
'Map.js',
'Module.js',
'Number.js',
'Object.js',
'Promise.js',
'Reflect.js',
'RegExp.js',
'RegExpGlobalReplaceOpt.h.js',
'RegExpLocalReplaceOpt.h.js',
'String.js',
'Set.js',
'Sorting.js',
'TypedArray.js',
'TypedObject.js',
'WeakMap.js',
'WeakSet.js',
]
+26
View File
@@ -0,0 +1,26 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
SOURCES += [
'CTypes.cpp',
'Library.cpp',
]
if not CONFIG['MOZ_SYSTEM_FFI']:
LOCAL_INCLUDES += [
'!libffi/include',
'libffi/src/%s' % CONFIG['FFI_TARGET_DIR'],
]
if CONFIG['_MSC_VER'] and CONFIG['CPU_ARCH'] == 'x86_64':
SOURCES['CTypes.cpp'].no_pgo = True # Bug 810661
+25
View File
@@ -0,0 +1,25 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
CONFIGURE_SUBST_FILES += [
'rootAnalysis/Makefile',
]
SOURCES += [
'sharkctl.cpp',
]
if CONFIG['MOZ_INSTRUMENTS']:
SOURCES += [
'Instruments.cpp',
]
+17
View File
@@ -0,0 +1,17 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
SOURCES += [
'LifoAlloc.cpp',
'MemoryProtectionExceptionHandler.cpp',
]
+1 -1
View File
@@ -5,7 +5,7 @@
Library('editline')
UNIFIED_SOURCES += [
SOURCES += [
'editline.c',
'sysunix.c',
]
+3
View File
@@ -405,6 +405,9 @@ BytecodeCompiler::compileModule()
module->init(script);
ModuleBuilder builder(cx, module, parser->tokenStream);
if (!builder.init())
return nullptr;
ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
ParseNode* pn = parser->moduleBody(&modulesc);
if (!pn)
+27
View File
@@ -0,0 +1,27 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
SOURCES += [
'BytecodeCompiler.cpp',
'BytecodeEmitter.cpp',
'FoldConstants.cpp',
'NameFunctions.cpp',
'ParseNode.cpp',
'Parser.cpp',
'TokenStream.cpp',
]
GENERATED_FILES += ['ReservedWordsGenerated.h']
ReservedWordsGenerated = GENERATED_FILES['ReservedWordsGenerated.h']
ReservedWordsGenerated.script = 'GenerateReservedWords.py'
ReservedWordsGenerated.inputs += ['ReservedWords.h']
+18
View File
@@ -28,6 +28,7 @@
#include "js/HeapAPI.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
#include "js/TraceKind.h"
struct JSRuntime;
@@ -272,6 +273,23 @@ struct Cell
static MOZ_ALWAYS_INLINE bool needWriteBarrierPre(JS::Zone* zone);
template <class T>
inline bool is() const {
return getTraceKind() == JS::MapTypeToTraceKind<T>::kind;
}
template<class T>
inline T* as() {
MOZ_ASSERT(this->is<T>());
return static_cast<T*>(this);
}
template <class T>
inline const T* as() const {
MOZ_ASSERT(this->is<T>());
return static_cast<const T*>(this);
}
#ifdef DEBUG
inline bool isAligned() const;
void dump(FILE* fp) const;
+2
View File
@@ -46,6 +46,7 @@ class RegExpObject;
class SavedFrame;
class Scope;
class EnvironmentObject;
class RequestedModuleObject;
class ScriptSourceObject;
class Shape;
class SharedArrayBufferObject;
@@ -84,6 +85,7 @@ class JitCode;
D(js::PlainObject*) \
D(js::PropertyName*) \
D(js::RegExpObject*) \
D(js::RequestedModuleObject*) \
D(js::SavedFrame*) \
D(js::Scope*) \
D(js::ScriptSourceObject*) \
+31
View File
@@ -0,0 +1,31 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
SOURCES += [
'Allocator.cpp',
'Barrier.cpp',
'GCTrace.cpp',
'Iteration.cpp',
'Marking.cpp',
'Memory.cpp',
'MemoryProfiler.cpp',
'Nursery.cpp',
'RootMarking.cpp',
'Statistics.cpp',
'StoreBuffer.cpp',
'Tracer.cpp',
'Zone.cpp',
]
if CONFIG['MOZ_DEBUG']:
SOURCES += ['Verifier.cpp']
+2 -7
View File
@@ -5,6 +5,8 @@
GeckoProgram('gdb-tests', linkage=None)
include('../js-cxxflags.mozbuild')
UNIFIED_SOURCES += [
'gdb-tests.cpp',
'tests/test-asmjs.cpp',
@@ -40,13 +42,6 @@ if CONFIG['MOZ_ICU_DATA_ARCHIVE']:
OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-shadow', '-fno-strict-aliasing']
# This is intended as a temporary workaround to enable VS2015.
if CONFIG['_MSC_VER']:
CXXFLAGS += ['-wd4312']
DEFINES['topsrcdir'] = '%s/js/src' % TOPSRCDIR
FINAL_TARGET_PP_FILES += ['gdb-tests-gdb.py.in']
OBJDIR_FILES.js.src.gdb += ['!/dist/bin/gdb-tests-gdb.py']
+23
View File
@@ -0,0 +1,23 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
SOURCES += [
'NativeRegExpMacroAssembler.cpp',
'RegExpAST.cpp',
'RegExpCharacters.cpp',
'RegExpEngine.cpp',
'RegExpInterpreter.cpp',
'RegExpMacroAssembler.cpp',
'RegExpParser.cpp',
'RegExpStack.cpp',
]
@@ -5,7 +5,7 @@ function testRequestedModules(source, expected) {
var actual = module.requestedModules;
assertEq(actual.length, expected.length);
for (var i = 0; i < actual.length; i++) {
assertEq(actual[i], expected[i]);
assertEq(actual[i].moduleSpecifier, expected[i]);
}
}
+233
View File
@@ -0,0 +1,233 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
UNIFIED_SOURCES += [
'AliasAnalysis.cpp',
'AliasAnalysisShared.cpp',
'AlignmentMaskAnalysis.cpp',
'BacktrackingAllocator.cpp',
'Bailouts.cpp',
'BaselineBailouts.cpp',
'BaselineCacheIR.cpp',
'BaselineCompiler.cpp',
'BaselineDebugModeOSR.cpp',
'BaselineFrame.cpp',
'BaselineFrameInfo.cpp',
'BaselineIC.cpp',
'BaselineInspector.cpp',
'BaselineJIT.cpp',
'BitSet.cpp',
'BytecodeAnalysis.cpp',
'C1Spewer.cpp',
'CacheIR.cpp',
'CodeGenerator.cpp',
'CompileWrappers.cpp',
'EagerSimdUnbox.cpp',
'EdgeCaseAnalysis.cpp',
'EffectiveAddressAnalysis.cpp',
'ExecutableAllocator.cpp',
'FlowAliasAnalysis.cpp',
'FoldLinearArithConstants.cpp',
'InstructionReordering.cpp',
'Ion.cpp',
'IonAnalysis.cpp',
'IonBuilder.cpp',
'IonCaches.cpp',
'IonOptimizationLevels.cpp',
'JitcodeMap.cpp',
'JitFrames.cpp',
'JitOptions.cpp',
'JitSpewer.cpp',
'JSONSpewer.cpp',
'LICM.cpp',
'Linker.cpp',
'LIR.cpp',
'LoopUnroller.cpp',
'Lowering.cpp',
'MacroAssembler.cpp',
'MCallOptimize.cpp',
'MIR.cpp',
'MIRGraph.cpp',
'MoveResolver.cpp',
'OptimizationTracking.cpp',
'PerfSpewer.cpp',
'ProcessExecutableMemory.cpp',
'RangeAnalysis.cpp',
'Recover.cpp',
'RegisterAllocator.cpp',
'RematerializedFrame.cpp',
'Safepoints.cpp',
'ScalarReplacement.cpp',
'shared/BaselineCompiler-shared.cpp',
'shared/CodeGenerator-shared.cpp',
'shared/Lowering-shared.cpp',
'SharedIC.cpp',
'Sink.cpp',
'Snapshots.cpp',
'StupidAllocator.cpp',
'TypedObjectPrediction.cpp',
'TypePolicy.cpp',
'ValueNumbering.cpp',
'VMFunctions.cpp',
'WasmBCE.cpp',
]
if CONFIG['MOZ_DEBUG']:
UNIFIED_SOURCES += ['Disassembler.cpp']
if not CONFIG['ENABLE_ION']:
UNIFIED_SOURCES += [
'none/Trampoline-none.cpp'
]
elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
UNIFIED_SOURCES += [
'x86-shared/Architecture-x86-shared.cpp',
'x86-shared/Assembler-x86-shared.cpp',
'x86-shared/AssemblerBuffer-x86-shared.cpp',
'x86-shared/BaselineCompiler-x86-shared.cpp',
'x86-shared/BaselineIC-x86-shared.cpp',
'x86-shared/CodeGenerator-x86-shared.cpp',
'x86-shared/Lowering-x86-shared.cpp',
'x86-shared/MacroAssembler-x86-shared.cpp',
'x86-shared/MoveEmitter-x86-shared.cpp',
]
UNIFIED_SOURCES += [
'x86-shared/Disassembler-x86-shared.cpp', # using namespace js::jit::X86Encoding;
]
if CONFIG['JS_CODEGEN_X64']:
UNIFIED_SOURCES += [
'x64/Assembler-x64.cpp',
'x64/Bailouts-x64.cpp',
'x64/BaselineCompiler-x64.cpp',
'x64/BaselineIC-x64.cpp',
'x64/CodeGenerator-x64.cpp',
'x64/Lowering-x64.cpp',
'x64/MacroAssembler-x64.cpp',
'x64/SharedIC-x64.cpp',
'x64/Trampoline-x64.cpp',
]
else:
UNIFIED_SOURCES += [
'x86/Assembler-x86.cpp',
'x86/Bailouts-x86.cpp',
'x86/BaselineCompiler-x86.cpp',
'x86/BaselineIC-x86.cpp',
'x86/CodeGenerator-x86.cpp',
'x86/Lowering-x86.cpp',
'x86/MacroAssembler-x86.cpp',
'x86/SharedIC-x86.cpp',
'x86/Trampoline-x86.cpp',
]
elif CONFIG['JS_CODEGEN_ARM']:
UNIFIED_SOURCES += [
'arm/Architecture-arm.cpp',
'arm/Assembler-arm.cpp',
'arm/Bailouts-arm.cpp',
'arm/BaselineCompiler-arm.cpp',
'arm/BaselineIC-arm.cpp',
'arm/CodeGenerator-arm.cpp',
'arm/disasm/Constants-arm.cpp',
'arm/disasm/Disasm-arm.cpp',
'arm/Lowering-arm.cpp',
'arm/MacroAssembler-arm.cpp',
'arm/MoveEmitter-arm.cpp',
'arm/SharedIC-arm.cpp',
'arm/Trampoline-arm.cpp',
]
if CONFIG['JS_SIMULATOR_ARM']:
UNIFIED_SOURCES += [
'arm/Simulator-arm.cpp'
]
elif CONFIG['OS_ARCH'] == 'Darwin':
UNIFIED_SOURCES += [
'arm/llvm-compiler-rt/arm/aeabi_idivmod.S',
'arm/llvm-compiler-rt/arm/aeabi_uidivmod.S',
]
elif CONFIG['JS_CODEGEN_ARM64']:
UNIFIED_SOURCES += [
'arm64/Architecture-arm64.cpp',
'arm64/Assembler-arm64.cpp',
'arm64/Bailouts-arm64.cpp',
'arm64/BaselineIC-arm64.cpp',
'arm64/CodeGenerator-arm64.cpp',
'arm64/Lowering-arm64.cpp',
'arm64/MacroAssembler-arm64.cpp',
'arm64/MoveEmitter-arm64.cpp',
'arm64/SharedIC-arm64.cpp',
'arm64/Trampoline-arm64.cpp',
'arm64/vixl/Assembler-vixl.cpp',
'arm64/vixl/Cpu-vixl.cpp',
'arm64/vixl/Decoder-vixl.cpp',
'arm64/vixl/Disasm-vixl.cpp',
'arm64/vixl/Instructions-vixl.cpp',
'arm64/vixl/Instrument-vixl.cpp',
'arm64/vixl/MacroAssembler-vixl.cpp',
'arm64/vixl/MozAssembler-vixl.cpp',
'arm64/vixl/MozInstructions-vixl.cpp',
'arm64/vixl/Utils-vixl.cpp'
]
if CONFIG['JS_SIMULATOR_ARM64']:
UNIFIED_SOURCES += [
'arm64/vixl/Debugger-vixl.cpp',
'arm64/vixl/Logic-vixl.cpp',
'arm64/vixl/MozSimulator-vixl.cpp',
'arm64/vixl/Simulator-vixl.cpp'
]
elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
UNIFIED_SOURCES += [
'mips-shared/Architecture-mips-shared.cpp',
'mips-shared/Assembler-mips-shared.cpp',
'mips-shared/Bailouts-mips-shared.cpp',
'mips-shared/BaselineCompiler-mips-shared.cpp',
'mips-shared/BaselineIC-mips-shared.cpp',
'mips-shared/CodeGenerator-mips-shared.cpp',
'mips-shared/Lowering-mips-shared.cpp',
'mips-shared/MacroAssembler-mips-shared.cpp',
'mips-shared/MoveEmitter-mips-shared.cpp',
]
if CONFIG['JS_CODEGEN_MIPS32']:
UNIFIED_SOURCES += [
'mips32/Architecture-mips32.cpp',
'mips32/Assembler-mips32.cpp',
'mips32/Bailouts-mips32.cpp',
'mips32/BaselineCompiler-mips32.cpp',
'mips32/BaselineIC-mips32.cpp',
'mips32/CodeGenerator-mips32.cpp',
'mips32/Lowering-mips32.cpp',
'mips32/MacroAssembler-mips32.cpp',
'mips32/MoveEmitter-mips32.cpp',
'mips32/SharedIC-mips32.cpp',
'mips32/Trampoline-mips32.cpp',
]
if CONFIG['JS_SIMULATOR_MIPS32']:
UNIFIED_SOURCES += [
'mips32/Simulator-mips32.cpp'
]
elif CONFIG['JS_CODEGEN_MIPS64']:
UNIFIED_SOURCES += [
'mips64/Architecture-mips64.cpp',
'mips64/Assembler-mips64.cpp',
'mips64/Bailouts-mips64.cpp',
'mips64/BaselineCompiler-mips64.cpp',
'mips64/BaselineIC-mips64.cpp',
'mips64/CodeGenerator-mips64.cpp',
'mips64/Lowering-mips64.cpp',
'mips64/MacroAssembler-mips64.cpp',
'mips64/MoveEmitter-mips64.cpp',
'mips64/SharedIC-mips64.cpp',
'mips64/Trampoline-mips64.cpp',
]
if CONFIG['JS_SIMULATOR_MIPS64']:
UNIFIED_SOURCES += [
'mips64/Simulator-mips64.cpp'
]
+34
View File
@@ -0,0 +1,34 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
FILES_PER_UNIFIED_FILE = 6
# Also set in shell/moz.build
DEFINES['ENABLE_SHARED_ARRAY_BUFFER'] = True
DEFINES['EXPORT_JS_API'] = True
if CONFIG['NIGHTLY_BUILD']:
DEFINES['ENABLE_BINARYDATA'] = True
if CONFIG['NIGHTLY_BUILD']:
DEFINES['ENABLE_SIMD'] = True
if CONFIG['JS_HAS_CTYPES']:
DEFINES['JS_HAS_CTYPES'] = True
for var in ('DLL_PREFIX', 'DLL_SUFFIX'):
DEFINES[var] = '"%s"' % CONFIG[var]
if CONFIG['MOZ_LINKER']:
DEFINES['MOZ_LINKER'] = True
if CONFIG['JS_HAS_CTYPES']:
if not CONFIG['MOZ_SYSTEM_FFI']:
# Windows needs this to be linked with a static library.
DEFINES['FFI_BUILDING'] = True
if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['_CRT_RAND_S'] = True
DEFINES['NOMINMAX'] = True
+42
View File
@@ -0,0 +1,42 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
CFLAGS += CONFIG['MOZ_ICU_CFLAGS']
CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
if CONFIG['_MSC_VER']:
# Prevent floating point errors caused by VC++ optimizations
CFLAGS += ['-fp:precise']
CXXFLAGS += ['-fp:precise']
# C4805 warns mixing bool with other integral types in computation.
# But given the conversion from bool is specified, and this is a
# pattern widely used in code in js/src, suppress this warning here.
CXXFLAGS += ['-wd4805']
# C4661 ("no suitable definition provided for explicit template
# instantiation request") is emitted for all Parser methods that
# have a Parser<FullParseHandler> definition but no
# Parser<SyntaxParseHandler> definition, see bug 1167030.
CXXFLAGS += ['-wd4661', '-we4067', '-we4258', '-we4275']
CXXFLAGS += ['-wd4146'] # FIXME: unary minus operator applied to unsigned type (bug 1229189)
# This is intended as a temporary hack to support building with VS2015.
# 'noexcept' used with no exception handling mode specified;
# termination on exception is not guaranteed. Specify /EHsc
CXXFLAGS += ['-wd4577', '-wd4312']
if CONFIG['GNU_CXX']:
# Disable strict-aliasing for GCC, which is enabled by default
# starting with version 7.1, see Mozilla bug 1363009.
CXXFLAGS += ['-Wno-shadow', '-Werror=format', '-fno-strict-aliasing']
if CONFIG['JS_HAS_CTYPES'] and CONFIG['MOZ_SYSTEM_FFI']:
CXXFLAGS += CONFIG['MOZ_FFI_CFLAGS']
if CONFIG['JS_CODEGEN_ARM'] and CONFIG['JS_SIMULATOR_ARM']:
# Configuration used only for testing.
if CONFIG['OS_ARCH'] == 'Linux' or CONFIG['OS_ARCH'] == 'Darwin':
CXXFLAGS += [ '-msse2', '-mfpmath=sse' ]
+48
View File
@@ -0,0 +1,48 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
component_engine = ('Core', 'JavaScript Engine')
component_gc = ('Core', 'JavaScript: GC')
component_intl = ('Core', 'JavaScript: Internationalization API')
component_jit = ('Core', 'JavaScript Engine: JIT')
component_stl = ('Core', 'JavaScript: Standard Library')
with Files('../public/**'):
BUG_COMPONENT = component_engine
with Files('*'):
BUG_COMPONENT = component_engine
with Files('wasm/**'):
BUG_COMPONENT = component_jit
with Files('builtin/**'):
BUG_COMPONENT = component_stl
with Files('ctypes/**'):
BUG_COMPONENT = ('Core', 'js-ctypes')
with Files('gc/**'):
BUG_COMPONENT = component_gc
with Files('jit/**'):
BUG_COMPONENT = component_jit
# File-specific metadata
for gcfile in ['jsgc*', 'devtools/rootAnalysis', 'devtools/gc-ubench', 'devtools/gctrace']:
with Files(gcfile):
BUG_COMPONENT = component_gc
for header in ('GCAnnotations.h', 'GCAPI.h', 'HeapAPI.h', 'RootingAPI.h', 'SliceBudget.h', 'SweepingAPI.h', 'TraceKind.h', 'TracingAPI.h', 'WeakMapPtr.h'):
with Files('../public/' + header):
BUG_COMPONENT = component_gc
for stlfile in ['jsarray.*', 'jsbool*', 'jsdate.*', 'jsnum.*', 'json.*', 'jsstr.*']:
with Files(stlfile):
BUG_COMPONENT = component_stl
with Files('builtin/Intl*'):
BUG_COMPONENT = component_intl
with Files('builtin/make_intl_data.py'):
BUG_COMPONENT = component_intl
with Files('../public/TrackedOptimizationInfo.h'):
BUG_COMPONENT = component_jit
TEST_DIRS += ['jsapi-tests', 'tests', 'gdb']
+2 -7
View File
@@ -3,6 +3,8 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include(../js-cxxflags.mozbuild)
GeckoProgram('jsapi-tests', linkage=None)
UNIFIED_SOURCES += [
@@ -142,12 +144,5 @@ USE_LIBS += [
OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-shadow', '-Werror=format', '-fno-strict-aliasing']
# This is intended as a temporary workaround to enable VS2015.
if CONFIG['_MSC_VER']:
CXXFLAGS += ['-wd4312']
DEFINES['topsrcdir'] = '%s/js/src' % TOPSRCDIR
OBJDIR_PP_FILES.js.src['jsapi-tests'] += ['jsapi-tests-gdb.py.in']
+22 -5
View File
@@ -4729,13 +4729,28 @@ JS::GetRequestedModules(JSContext* cx, JS::HandleObject moduleArg)
return &moduleArg->as<ModuleObject>().requestedModules();
}
JS_PUBLIC_API(JSScript*)
JS::GetModuleScript(JSContext* cx, JS::HandleObject moduleArg)
JS_PUBLIC_API(JSString*)
JS::GetRequestedModuleSpecifier(JSContext* cx, JS::HandleValue value)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, moduleArg);
return moduleArg->as<ModuleObject>().script();
assertSameCompartment(cx, value);
JSObject* obj = &value.toObject();
return obj->as<RequestedModuleObject>().moduleSpecifier();
}
JS_PUBLIC_API(void)
JS::GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue value,
uint32_t* lineNumber, uint32_t* columnNumber)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, value);
MOZ_ASSERT(lineNumber);
MOZ_ASSERT(columnNumber);
auto& requested = value.toObject().as<RequestedModuleObject>();
*lineNumber = requested.lineNumber();
*columnNumber = requested.columnNumber();
}
JS_PUBLIC_API(JSObject*)
@@ -7039,7 +7054,9 @@ JS::CaptureCurrentStack(JSContext* cx, JS::MutableHandleObject stackp,
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
MOZ_RELEASE_ASSERT(cx->compartment());
if (!cx->compartment()) {
return false;
}
JSCompartment* compartment = cx->compartment();
Rooted<SavedFrame*> frame(cx);
+17 -8
View File
@@ -4386,18 +4386,27 @@ ModuleEvaluate(JSContext* cx, JS::HandleObject moduleRecord);
* Get a list of the module specifiers used by a source text module
* record to request importation of modules.
*
* The result is a JavaScript array of string values. To extract the individual
* values use only JS_GetArrayLength and JS_GetElement with indices 0 to
* length - 1.
* The result is a JavaScript array of object values. To extract the individual
* values use only JS_GetArrayLength and JS_GetElement with indices 0 to length
* - 1.
*
* The element values are objects with the following properties:
* - moduleSpecifier: the module specifier string
* - lineNumber: the line number of the import in the source text
* - columnNumber: the column number of the import in the source text
*
* These property values can be extracted with GetRequestedModuleSpecifier() and
* GetRequestedModuleSourcePos()
*/
extern JS_PUBLIC_API(JSObject*)
GetRequestedModules(JSContext* cx, JS::HandleObject moduleRecord);
/*
* Get the script associated with a module.
*/
extern JS_PUBLIC_API(JSScript*)
GetModuleScript(JSContext* cx, JS::HandleObject moduleRecord);
extern JS_PUBLIC_API(JSString*)
GetRequestedModuleSpecifier(JSContext* cx, JS::HandleValue requestedModuleObject);
extern JS_PUBLIC_API(void)
GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue requestedModuleObject,
uint32_t* lineNumber, uint32_t* columnNumber);
} /* namespace JS */
+55 -541
View File
@@ -3,65 +3,15 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# Directory metadata
component_engine = ('Core', 'JavaScript Engine')
component_gc = ('Core', 'JavaScript: GC')
component_intl = ('Core', 'JavaScript: Internationalization API')
component_jit = ('Core', 'JavaScript Engine: JIT')
component_stl = ('Core', 'JavaScript: Standard Library')
FILES_PER_UNIFIED_FILE = 6
with Files('../public/**'):
BUG_COMPONENT = component_engine
with Files('*'):
BUG_COMPONENT = component_engine
with Files('wasm/**'):
BUG_COMPONENT = component_jit
with Files('builtin/**'):
BUG_COMPONENT = component_stl
with Files('ctypes/**'):
BUG_COMPONENT = ('Core', 'js-ctypes')
with Files('gc/**'):
BUG_COMPONENT = component_gc
with Files('jit/**'):
BUG_COMPONENT = component_jit
# File-specific metadata
for gcfile in ['jsgc*', 'devtools/rootAnalysis', 'devtools/gc-ubench', 'devtools/gctrace']:
with Files(gcfile):
BUG_COMPONENT = component_gc
for header in ('GCAnnotations.h', 'GCAPI.h', 'HeapAPI.h', 'RootingAPI.h', 'SliceBudget.h', 'SweepingAPI.h', 'TraceKind.h', 'TracingAPI.h', 'WeakMapPtr.h'):
with Files('../public/' + header):
BUG_COMPONENT = component_gc
for stlfile in ['jsarray.*', 'jsbool*', 'jsdate.*', 'jsnum.*', 'json.*', 'jsstr.*']:
with Files(stlfile):
BUG_COMPONENT = component_stl
with Files('builtin/Intl*'):
BUG_COMPONENT = component_intl
with Files('builtin/make_intl_data.py'):
BUG_COMPONENT = component_intl
with Files('../public/TrackedOptimizationInfo.h'):
BUG_COMPONENT = component_jit
if CONFIG['JS_BUNDLED_EDITLINE']:
DIRS += ['editline']
if not CONFIG['JS_DISABLE_SHELL']:
DIRS += ['shell']
TEST_DIRS += ['jsapi-tests', 'tests', 'gdb']
include('js-config.mozbuild')
include('js-cxxflags.mozbuild')
include('js-testing.mozbuild')
CONFIGURE_SUBST_FILES += [
'devtools/rootAnalysis/Makefile',
'js-config',
'js.pc',
]
CONFIGURE_DEFINE_FILES += [
'js-confdefs.h',
]
@@ -80,7 +30,6 @@ CONFIGURE_DEFINE_FILES += [
if CONFIG['HAVE_DTRACE']:
GENERATED_FILES += ['javascript-trace.h']
EXPORTS += ['!javascript-trace.h']
# Changes to internal header files, used externally, massively slow down
@@ -101,7 +50,6 @@ EXPORTS += [
'jstypes.h',
'jsversion.h',
'jswrapper.h',
'perf/jsperf.h',
]
# If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so
@@ -154,122 +102,36 @@ EXPORTS.js += [
'../public/WeakMapPtr.h',
]
DIRS += [
'builtin',
'devtools',
'ds',
'frontend',
'gc',
'irregexp',
'jit',
'perf',
'proxy',
'threading',
'wasm',
]
if CONFIG['JS_HAS_CTYPES']:
DIRS += ['ctypes']
if CONFIG['JS_BUNDLED_EDITLINE']:
DIRS += ['editline']
if CONFIG['JS_NEW_REGEXP']:
DIRS += ['regexp']
if not CONFIG['JS_DISABLE_SHELL']:
DIRS += ['shell']
if CONFIG['MOZ_VTUNE']:
DIRS += ['vtune']
UNIFIED_SOURCES += [
'builtin/AtomicsObject.cpp',
'builtin/Eval.cpp',
'builtin/Intl.cpp',
'builtin/MapObject.cpp',
'builtin/ModuleObject.cpp',
'builtin/Object.cpp',
'builtin/Profilers.cpp',
'builtin/Promise.cpp',
'builtin/Reflect.cpp',
'builtin/ReflectParse.cpp',
'builtin/SIMD.cpp',
'builtin/SymbolObject.cpp',
'builtin/TestingFunctions.cpp',
'builtin/TypedObject.cpp',
'builtin/WeakMapObject.cpp',
'builtin/WeakSetObject.cpp',
'devtools/sharkctl.cpp',
'ds/LifoAlloc.cpp',
'ds/MemoryProtectionExceptionHandler.cpp',
'frontend/BytecodeCompiler.cpp',
'frontend/BytecodeEmitter.cpp',
'frontend/FoldConstants.cpp',
'frontend/NameFunctions.cpp',
'frontend/ParseNode.cpp',
'frontend/TokenStream.cpp',
'gc/Allocator.cpp',
'gc/Barrier.cpp',
'gc/GCTrace.cpp',
'gc/Iteration.cpp',
'gc/Marking.cpp',
'gc/Memory.cpp',
'gc/MemoryProfiler.cpp',
'gc/Nursery.cpp',
'gc/RootMarking.cpp',
'gc/Statistics.cpp',
'gc/Tracer.cpp',
'gc/Verifier.cpp',
'gc/Zone.cpp',
'irregexp/NativeRegExpMacroAssembler.cpp',
'irregexp/RegExpAST.cpp',
'irregexp/RegExpCharacters.cpp',
'irregexp/RegExpEngine.cpp',
'irregexp/RegExpInterpreter.cpp',
'irregexp/RegExpMacroAssembler.cpp',
'irregexp/RegExpParser.cpp',
'irregexp/RegExpStack.cpp',
'jit/AliasAnalysis.cpp',
'jit/AliasAnalysisShared.cpp',
'jit/AlignmentMaskAnalysis.cpp',
'jit/BacktrackingAllocator.cpp',
'jit/Bailouts.cpp',
'jit/BaselineBailouts.cpp',
'jit/BaselineCacheIR.cpp',
'jit/BaselineCompiler.cpp',
'jit/BaselineDebugModeOSR.cpp',
'jit/BaselineFrame.cpp',
'jit/BaselineFrameInfo.cpp',
'jit/BaselineIC.cpp',
'jit/BaselineInspector.cpp',
'jit/BaselineJIT.cpp',
'jit/BitSet.cpp',
'jit/BytecodeAnalysis.cpp',
'jit/C1Spewer.cpp',
'jit/CacheIR.cpp',
'jit/CodeGenerator.cpp',
'jit/CompileWrappers.cpp',
'jit/Disassembler.cpp',
'jit/EagerSimdUnbox.cpp',
'jit/EdgeCaseAnalysis.cpp',
'jit/EffectiveAddressAnalysis.cpp',
'jit/ExecutableAllocator.cpp',
'jit/FlowAliasAnalysis.cpp',
'jit/FoldLinearArithConstants.cpp',
'jit/InstructionReordering.cpp',
'jit/Ion.cpp',
'jit/IonAnalysis.cpp',
'jit/IonBuilder.cpp',
'jit/IonCaches.cpp',
'jit/IonOptimizationLevels.cpp',
'jit/JitcodeMap.cpp',
'jit/JitFrames.cpp',
'jit/JitOptions.cpp',
'jit/JitSpewer.cpp',
'jit/JSONSpewer.cpp',
'jit/LICM.cpp',
'jit/Linker.cpp',
'jit/LIR.cpp',
'jit/LoopUnroller.cpp',
'jit/Lowering.cpp',
'jit/MacroAssembler.cpp',
'jit/MCallOptimize.cpp',
'jit/MIR.cpp',
'jit/MIRGraph.cpp',
'jit/MoveResolver.cpp',
'jit/OptimizationTracking.cpp',
'jit/PerfSpewer.cpp',
'jit/ProcessExecutableMemory.cpp',
'jit/RangeAnalysis.cpp',
'jit/Recover.cpp',
'jit/RegisterAllocator.cpp',
'jit/RematerializedFrame.cpp',
'jit/Safepoints.cpp',
'jit/ScalarReplacement.cpp',
'jit/shared/BaselineCompiler-shared.cpp',
'jit/shared/CodeGenerator-shared.cpp',
'jit/shared/Lowering-shared.cpp',
'jit/SharedIC.cpp',
'jit/Sink.cpp',
'jit/Snapshots.cpp',
'jit/StupidAllocator.cpp',
'jit/TypedObjectPrediction.cpp',
'jit/TypePolicy.cpp',
'jit/ValueNumbering.cpp',
'jit/VMFunctions.cpp',
'jit/WasmBCE.cpp',
'jsalloc.cpp',
'jsapi.cpp',
'jsbool.cpp',
@@ -291,16 +153,23 @@ UNIFIED_SOURCES += [
'jsscript.cpp',
'jsstr.cpp',
'jsweakmap.cpp',
'perf/jsperf.cpp',
'proxy/BaseProxyHandler.cpp',
'proxy/CrossCompartmentWrapper.cpp',
'proxy/DeadObjectProxy.cpp',
'proxy/OpaqueCrossCompartmentWrapper.cpp',
'proxy/Proxy.cpp',
'proxy/ScriptedProxyHandler.cpp',
'proxy/SecurityWrapper.cpp',
'proxy/Wrapper.cpp',
'threading/Mutex.cpp',
]
# Can't be built unified
SOURCES += [
'jsarray.cpp',
'jsatom.cpp',
'jsdtoa.cpp',
'jsmath.cpp',
'jsutil.cpp',
]
# Suppress warnings in third-party code.
# We are keeping this in the main moz.build because it is file specific
if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']:
SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough']
UNIFIED_SOURCES += [
'vm/ArgumentsObject.cpp',
'vm/ArrayBufferObject.cpp',
'vm/AsyncFunction.cpp',
@@ -359,54 +228,10 @@ UNIFIED_SOURCES += [
'vm/Value.cpp',
'vm/WeakMapPtr.cpp',
'vm/Xdr.cpp',
'wasm/AsmJS.cpp',
'wasm/WasmBaselineCompile.cpp',
'wasm/WasmBinaryFormat.cpp',
'wasm/WasmBinaryIterator.cpp',
'wasm/WasmBinaryToAST.cpp',
'wasm/WasmBinaryToExperimentalText.cpp',
'wasm/WasmBinaryToText.cpp',
'wasm/WasmCode.cpp',
'wasm/WasmCompartment.cpp',
'wasm/WasmCompile.cpp',
'wasm/WasmFrameIterator.cpp',
'wasm/WasmGenerator.cpp',
'wasm/WasmInstance.cpp',
'wasm/WasmIonCompile.cpp',
'wasm/WasmJS.cpp',
'wasm/WasmModule.cpp',
'wasm/WasmSignalHandlers.cpp',
'wasm/WasmStubs.cpp',
'wasm/WasmTable.cpp',
'wasm/WasmTextToBinary.cpp',
'wasm/WasmTextUtils.cpp',
'wasm/WasmTypes.cpp'
]
# jsarray.cpp and jsatom.cpp cannot be built in unified mode because
# xpcshell is broken during packaging when compiled with gcc-4.8.2
# builtin/RegExp.cpp cannot be built in unified mode because it is built
# without PGO
# frontend/Parser.cpp cannot be built in unified mode because of explicit
# template instantiations.
# jsdtoa.cpp cannot be built in unified mode because we want to suppress
# compiler warnings in third-party dtoa.c.
# jsmath.cpp cannot be built in unified mode because it needs to pull rand_s
# from <stdlib.h> on Windows through a preprocessor define.
# jsutil.cpp cannot be built in unified mode because it is needed for
# check-vanilla-allocations.
# StoreBuffer.cpp cannot be built in unified because its template
# instantiations may or may not be needed depending on what it gets bundled
# with.
# Can't be built unified
SOURCES += [
'builtin/RegExp.cpp',
'frontend/Parser.cpp',
'gc/StoreBuffer.cpp',
'jsarray.cpp',
'jsatom.cpp',
'jsdtoa.cpp',
'jsmath.cpp',
'jsutil.cpp',
'vm/Initialization.cpp',
]
@@ -415,217 +240,14 @@ if CONFIG['JS_POSIX_NSPR']:
'vm/PosixNSPR.cpp',
]
if CONFIG['MOZ_INSTRUMENTS']:
SOURCES += [
'devtools/Instruments.cpp',
]
if CONFIG['ENABLE_TRACE_LOGGING']:
# Can't be built unified
SOURCES += [
'vm/TraceLogging.cpp',
'vm/TraceLoggingGraph.cpp',
'vm/TraceLoggingTypes.cpp',
]
if not CONFIG['ENABLE_ION']:
UNIFIED_SOURCES += [
'jit/none/Trampoline-none.cpp'
]
elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
UNIFIED_SOURCES += [
'jit/x86-shared/Architecture-x86-shared.cpp',
'jit/x86-shared/Assembler-x86-shared.cpp',
'jit/x86-shared/AssemblerBuffer-x86-shared.cpp',
'jit/x86-shared/BaselineCompiler-x86-shared.cpp',
'jit/x86-shared/BaselineIC-x86-shared.cpp',
'jit/x86-shared/CodeGenerator-x86-shared.cpp',
'jit/x86-shared/Lowering-x86-shared.cpp',
'jit/x86-shared/MacroAssembler-x86-shared.cpp',
'jit/x86-shared/MoveEmitter-x86-shared.cpp',
]
SOURCES += [
'jit/x86-shared/Disassembler-x86-shared.cpp', # using namespace js::jit::X86Encoding;
]
if CONFIG['JS_CODEGEN_X64']:
UNIFIED_SOURCES += [
'jit/x64/Assembler-x64.cpp',
'jit/x64/Bailouts-x64.cpp',
'jit/x64/BaselineCompiler-x64.cpp',
'jit/x64/BaselineIC-x64.cpp',
'jit/x64/CodeGenerator-x64.cpp',
'jit/x64/Lowering-x64.cpp',
'jit/x64/MacroAssembler-x64.cpp',
'jit/x64/SharedIC-x64.cpp',
'jit/x64/Trampoline-x64.cpp',
]
else:
UNIFIED_SOURCES += [
'jit/x86/Assembler-x86.cpp',
'jit/x86/Bailouts-x86.cpp',
'jit/x86/BaselineCompiler-x86.cpp',
'jit/x86/BaselineIC-x86.cpp',
'jit/x86/CodeGenerator-x86.cpp',
'jit/x86/Lowering-x86.cpp',
'jit/x86/MacroAssembler-x86.cpp',
'jit/x86/SharedIC-x86.cpp',
'jit/x86/Trampoline-x86.cpp',
]
elif CONFIG['JS_CODEGEN_ARM']:
UNIFIED_SOURCES += [
'jit/arm/Architecture-arm.cpp',
'jit/arm/Assembler-arm.cpp',
'jit/arm/Bailouts-arm.cpp',
'jit/arm/BaselineCompiler-arm.cpp',
'jit/arm/BaselineIC-arm.cpp',
'jit/arm/CodeGenerator-arm.cpp',
'jit/arm/disasm/Constants-arm.cpp',
'jit/arm/disasm/Disasm-arm.cpp',
'jit/arm/Lowering-arm.cpp',
'jit/arm/MacroAssembler-arm.cpp',
'jit/arm/MoveEmitter-arm.cpp',
'jit/arm/SharedIC-arm.cpp',
'jit/arm/Trampoline-arm.cpp',
]
if CONFIG['JS_SIMULATOR_ARM']:
UNIFIED_SOURCES += [
'jit/arm/Simulator-arm.cpp'
]
# Configuration used only for testing.
if CONFIG['OS_ARCH'] == 'Linux' or CONFIG['OS_ARCH'] == 'Darwin':
CXXFLAGS += [ '-msse2', '-mfpmath=sse' ]
elif CONFIG['OS_ARCH'] == 'Darwin':
SOURCES += [
'jit/arm/llvm-compiler-rt/arm/aeabi_idivmod.S',
'jit/arm/llvm-compiler-rt/arm/aeabi_uidivmod.S',
]
elif CONFIG['JS_CODEGEN_ARM64']:
UNIFIED_SOURCES += [
'jit/arm64/Architecture-arm64.cpp',
'jit/arm64/Assembler-arm64.cpp',
'jit/arm64/Bailouts-arm64.cpp',
'jit/arm64/BaselineIC-arm64.cpp',
'jit/arm64/CodeGenerator-arm64.cpp',
'jit/arm64/Lowering-arm64.cpp',
'jit/arm64/MacroAssembler-arm64.cpp',
'jit/arm64/MoveEmitter-arm64.cpp',
'jit/arm64/SharedIC-arm64.cpp',
'jit/arm64/Trampoline-arm64.cpp',
'jit/arm64/vixl/Assembler-vixl.cpp',
'jit/arm64/vixl/Cpu-vixl.cpp',
'jit/arm64/vixl/Decoder-vixl.cpp',
'jit/arm64/vixl/Disasm-vixl.cpp',
'jit/arm64/vixl/Instructions-vixl.cpp',
'jit/arm64/vixl/Instrument-vixl.cpp',
'jit/arm64/vixl/MacroAssembler-vixl.cpp',
'jit/arm64/vixl/MozAssembler-vixl.cpp',
'jit/arm64/vixl/MozInstructions-vixl.cpp',
'jit/arm64/vixl/Utils-vixl.cpp'
]
if CONFIG['JS_SIMULATOR_ARM64']:
UNIFIED_SOURCES += [
'jit/arm64/vixl/Debugger-vixl.cpp',
'jit/arm64/vixl/Logic-vixl.cpp',
'jit/arm64/vixl/MozSimulator-vixl.cpp',
'jit/arm64/vixl/Simulator-vixl.cpp'
]
elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
UNIFIED_SOURCES += [
'jit/mips-shared/Architecture-mips-shared.cpp',
'jit/mips-shared/Assembler-mips-shared.cpp',
'jit/mips-shared/Bailouts-mips-shared.cpp',
'jit/mips-shared/BaselineCompiler-mips-shared.cpp',
'jit/mips-shared/BaselineIC-mips-shared.cpp',
'jit/mips-shared/CodeGenerator-mips-shared.cpp',
'jit/mips-shared/Lowering-mips-shared.cpp',
'jit/mips-shared/MacroAssembler-mips-shared.cpp',
'jit/mips-shared/MoveEmitter-mips-shared.cpp',
]
if CONFIG['JS_CODEGEN_MIPS32']:
UNIFIED_SOURCES += [
'jit/mips32/Architecture-mips32.cpp',
'jit/mips32/Assembler-mips32.cpp',
'jit/mips32/Bailouts-mips32.cpp',
'jit/mips32/BaselineCompiler-mips32.cpp',
'jit/mips32/BaselineIC-mips32.cpp',
'jit/mips32/CodeGenerator-mips32.cpp',
'jit/mips32/Lowering-mips32.cpp',
'jit/mips32/MacroAssembler-mips32.cpp',
'jit/mips32/MoveEmitter-mips32.cpp',
'jit/mips32/SharedIC-mips32.cpp',
'jit/mips32/Trampoline-mips32.cpp',
]
if CONFIG['JS_SIMULATOR_MIPS32']:
UNIFIED_SOURCES += [
'jit/mips32/Simulator-mips32.cpp'
]
elif CONFIG['JS_CODEGEN_MIPS64']:
UNIFIED_SOURCES += [
'jit/mips64/Architecture-mips64.cpp',
'jit/mips64/Assembler-mips64.cpp',
'jit/mips64/Bailouts-mips64.cpp',
'jit/mips64/BaselineCompiler-mips64.cpp',
'jit/mips64/BaselineIC-mips64.cpp',
'jit/mips64/CodeGenerator-mips64.cpp',
'jit/mips64/Lowering-mips64.cpp',
'jit/mips64/MacroAssembler-mips64.cpp',
'jit/mips64/MoveEmitter-mips64.cpp',
'jit/mips64/SharedIC-mips64.cpp',
'jit/mips64/Trampoline-mips64.cpp',
]
if CONFIG['JS_SIMULATOR_MIPS64']:
UNIFIED_SOURCES += [
'jit/mips64/Simulator-mips64.cpp'
]
if CONFIG['OS_ARCH'] == 'WINNT':
SOURCES += [
'threading/windows/ConditionVariable.cpp',
'threading/windows/MutexImpl.cpp',
'threading/windows/Thread.cpp',
]
# _CRT_RAND_S must be #defined before #including stdlib.h to get rand_s()
DEFINES['_CRT_RAND_S'] = True
else:
SOURCES += [
'threading/posix/ConditionVariable.cpp',
'threading/posix/MutexImpl.cpp',
'threading/posix/Thread.cpp',
]
if CONFIG['JS_HAS_CTYPES']:
SOURCES += [
'ctypes/CTypes.cpp',
'ctypes/Library.cpp',
]
if not CONFIG['MOZ_SYSTEM_FFI']:
LOCAL_INCLUDES += [
'!ctypes/libffi/include',
'ctypes/libffi/src/%s' % CONFIG['FFI_TARGET_DIR'],
]
if CONFIG['MOZ_VTUNE']:
SOURCES += [
'vtune/jitprofiling.c'
]
if CONFIG['HAVE_LINUX_PERF_EVENT_H']:
SOURCES += [
'perf/pm_linux.cpp'
]
if CONFIG['LINUX_HEADERS_INCLUDES']:
SOURCES['perf/pm_linux.cpp'].flags += [CONFIG['LINUX_HEADERS_INCLUDES']]
else:
SOURCES += [
'perf/pm_stub.cpp'
]
GENERATED_FILES += ['frontend/ReservedWordsGenerated.h']
ReservedWordsGenerated = GENERATED_FILES['frontend/ReservedWordsGenerated.h']
ReservedWordsGenerated.script = 'frontend/GenerateReservedWords.py'
ReservedWordsGenerated.inputs += [
'frontend/ReservedWords.h'
]
# JavaScript must be built shared, even for static builds, as it is used by
# other modules which are always built shared. Failure to do so results in
# the js code getting copied into xpinstall and jsd as well as mozilla-bin,
@@ -661,65 +283,14 @@ USE_LIBS += [
'zlib',
]
if CONFIG['NIGHTLY_BUILD']:
DEFINES['ENABLE_BINARYDATA'] = True
if CONFIG['NIGHTLY_BUILD']:
DEFINES['ENABLE_SIMD'] = True
# Also set in shell/moz.build
DEFINES['ENABLE_SHARED_ARRAY_BUFFER'] = True
DEFINES['EXPORT_JS_API'] = True
if CONFIG['JS_HAS_CTYPES']:
DEFINES['JS_HAS_CTYPES'] = True
for var in ('DLL_PREFIX', 'DLL_SUFFIX'):
DEFINES[var] = '"%s"' % CONFIG[var]
if CONFIG['MOZ_LINKER']:
DEFINES['MOZ_LINKER'] = True
if CONFIG['_MSC_VER']:
if CONFIG['CPU_ARCH'] == 'x86':
SOURCES['builtin/RegExp.cpp'].no_pgo = True # Bug 772303
elif CONFIG['CPU_ARCH'] == 'x86_64' and CONFIG['JS_HAS_CTYPES']:
SOURCES['ctypes/CTypes.cpp'].no_pgo = True # Bug 810661
# Prevent floating point errors caused by VC++ optimizations
CFLAGS += ['-fp:precise']
CXXFLAGS += ['-fp:precise']
# C4805 warns mixing bool with other integral types in computation.
# But given the conversion from bool is specified, and this is a
# pattern widely used in code in js/src, suppress this warning here.
CXXFLAGS += ['-wd4805']
# C4661 ("no suitable definition provided for explicit template
# instantiation request") is emitted for all Parser methods that
# have a Parser<FullParseHandler> definition but no
# Parser<SyntaxParseHandler> definition, see bug 1167030.
CXXFLAGS += ['-wd4661']
CXXFLAGS += ['-we4067', '-we4258', '-we4275']
CXXFLAGS += ['-wd4146'] # FIXME: unary minus operator applied to unsigned type (bug 1229189)
# This is intended as a temporary hack to support building with VS2015.
# 'noexcept' used with no exception handling mode specified;
# termination on exception is not guaranteed. Specify /EHsc
CXXFLAGS += ['-wd4577']
CXXFLAGS += ['-wd4312']
if CONFIG['OS_ARCH'] not in ('WINNT'):
OS_LIBS += [
'm',
]
OS_LIBS += ['m']
if CONFIG['OS_ARCH'] == 'FreeBSD':
OS_LIBS += [
'-pthread',
]
OS_LIBS += ['-pthread']
if CONFIG['OS_ARCH'] == 'Linux':
OS_LIBS += [
'dl',
]
OS_LIBS += ['dl']
if CONFIG['OS_ARCH'] == 'SunOS':
OS_LIBS += [
@@ -731,65 +302,8 @@ if CONFIG['OS_ARCH'] == 'SunOS':
OS_LIBS += CONFIG['REALTIME_LIBS']
CFLAGS += CONFIG['MOZ_ICU_CFLAGS']
CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
NO_EXPAND_LIBS = True
DIST_INSTALL = True
# Prepare self-hosted JS code for embedding
GENERATED_FILES += [('selfhosted.out.h', 'selfhosted.js')]
selfhosted = GENERATED_FILES[('selfhosted.out.h', 'selfhosted.js')]
selfhosted.script = 'builtin/embedjs.py:generate_selfhosted'
selfhosted.inputs = [
'js.msg',
'builtin/TypedObjectConstants.h',
'builtin/SelfHostingDefines.h',
'builtin/Utilities.js',
'builtin/Array.js',
'builtin/AsyncIteration.js',
'builtin/Classes.js',
'builtin/Date.js',
'builtin/Error.js',
'builtin/Function.js',
'builtin/Generator.js',
'builtin/Intl.js',
'builtin/IntlData.js',
'builtin/Iterator.js',
'builtin/Map.js',
'builtin/Module.js',
'builtin/Number.js',
'builtin/Object.js',
'builtin/Promise.js',
'builtin/Reflect.js',
'builtin/RegExp.js',
'builtin/RegExpGlobalReplaceOpt.h.js',
'builtin/RegExpLocalReplaceOpt.h.js',
'builtin/String.js',
'builtin/Set.js',
'builtin/Sorting.js',
'builtin/TypedArray.js',
'builtin/TypedObject.js',
'builtin/WeakMap.js',
'builtin/WeakSet.js'
]
if CONFIG['JS_HAS_CTYPES']:
if CONFIG['MOZ_SYSTEM_FFI']:
CXXFLAGS += CONFIG['MOZ_FFI_CFLAGS']
else:
# Windows needs this to be linked with a static library.
DEFINES['FFI_BUILDING'] = True
if CONFIG['GNU_CXX']:
# Disable strict-aliasing for GCC, which is enabled by default
# starting with version 7.1, see Mozilla bug 1363009.
CXXFLAGS += ['-Wno-shadow', '-Werror=format', '-fno-strict-aliasing']
# Suppress warnings in third-party code.
if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']:
SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough']
if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['NOMINMAX'] = True
+29
View File
@@ -0,0 +1,29 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
EXPORTS += ['jsperf.h']
SOURCES += [
'jsperf.cpp',
]
if CONFIG['HAVE_LINUX_PERF_EVENT_H']:
SOURCES += [
'pm_linux.cpp'
]
if CONFIG['LINUX_HEADERS_INCLUDES']:
SOURCES['pm_linux.cpp'].flags += [CONFIG['LINUX_HEADERS_INCLUDES']]
else:
SOURCES += [
'pm_stub.cpp'
]
+23
View File
@@ -0,0 +1,23 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
SOURCES += [
'BaseProxyHandler.cpp',
'CrossCompartmentWrapper.cpp',
'DeadObjectProxy.cpp',
'OpaqueCrossCompartmentWrapper.cpp',
'Proxy.cpp',
'ScriptedProxyHandler.cpp',
'SecurityWrapper.cpp',
'Wrapper.cpp',
]
+51
View File
@@ -0,0 +1,51 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This file forward-defines Irregexp classes that need to be visible
// to the rest of Spidermonkey and re-exports them into js::irregexp.
#ifndef regexp_RegExpTypes_h
#define regexp_RegExpTypes_h
namespace js {
class MatchPairs;
}
namespace v8 {
namespace internal {
struct InputOutputData {
const void* inputStart;
const void* inputEnd;
// Index into inputStart (in chars) at which to begin matching.
size_t startIndex;
js::MatchPairs* matches;
template <typename CharT>
InputOutputData(const CharT* inputStart, const CharT* inputEnd,
size_t startIndex, js::MatchPairs* matches)
: inputStart(inputStart),
inputEnd(inputEnd),
startIndex(startIndex),
matches(matches)
{}
};
} // namespace internal
} // namespace v8
namespace js {
namespace irregexp {
using InputOutputData = v8::internal::InputOutputData;
} // namespace irregexp
} // namespace js
#endif // regexp_RegExpTypes_h
+2
View File
@@ -0,0 +1,2 @@
Imported using import-irregexp.py from:
https://github.com/v8/v8/tree/560f2d8bb3f3a72d78e1a7d7654235d53fdcc83c/src/regexp
+165
View File
@@ -0,0 +1,165 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include "regexp/special-case.h"
namespace v8 {
namespace internal {
static const uc32 kSurrogateStart = 0xd800;
static const uc32 kSurrogateEnd = 0xdfff;
static const uc32 kNonBmpStart = 0x10000;
// The following code generates "src/regexp/special-case.cc".
void PrintSet(std::ofstream& out, const char* name,
const icu::UnicodeSet& set) {
out << "icu::UnicodeSet Build" << name << "() {\n"
<< " icu::UnicodeSet set;\n";
for (int32_t i = 0; i < set.getRangeCount(); i++) {
if (set.getRangeStart(i) == set.getRangeEnd(i)) {
out << " set.add(0x" << set.getRangeStart(i) << ");\n";
} else {
out << " set.add(0x" << set.getRangeStart(i) << ", 0x"
<< set.getRangeEnd(i) << ");\n";
}
}
out << " set.freeze();\n"
<< " return set;\n"
<< "}\n\n";
out << "struct " << name << "Data {\n"
<< " " << name << "Data() : set(Build" << name << "()) {}\n"
<< " const icu::UnicodeSet set;\n"
<< "};\n\n";
out << "//static\n"
<< "const icu::UnicodeSet& RegExpCaseFolding::" << name << "() {\n"
<< " static base::LazyInstance<" << name << "Data>::type set =\n"
<< " LAZY_INSTANCE_INITIALIZER;\n"
<< " return set.Pointer()->set;\n"
<< "}\n\n";
}
void PrintSpecial(std::ofstream& out) {
icu::UnicodeSet current;
icu::UnicodeSet special_add;
icu::UnicodeSet ignore;
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeSet upper("[\\p{Lu}]", status);
CHECK(U_SUCCESS(status));
// Iterate through all chars in BMP except surrogates.
for (UChar32 i = 0; i < kNonBmpStart; i++) {
if (i >= kSurrogateStart && i <= kSurrogateEnd) {
continue; // Ignore surrogate range
}
current.set(i, i);
current.closeOver(USET_CASE_INSENSITIVE);
// Check to see if all characters in the case-folding equivalence
// class as defined by UnicodeSet::closeOver all map to the same
// canonical value.
UChar32 canonical = RegExpCaseFolding::Canonicalize(i);
bool class_has_matching_canonical_char = false;
bool class_has_non_matching_canonical_char = false;
for (int32_t j = 0; j < current.getRangeCount(); j++) {
for (UChar32 c = current.getRangeStart(j); c <= current.getRangeEnd(j);
c++) {
if (c == i) {
continue;
}
UChar32 other_canonical = RegExpCaseFolding::Canonicalize(c);
if (canonical == other_canonical) {
class_has_matching_canonical_char = true;
} else {
class_has_non_matching_canonical_char = true;
}
}
}
// If any other character in i's equivalence class has a
// different canonical value, then i needs special handling. If
// no other character shares a canonical value with i, we can
// ignore i when adding alternatives for case-independent
// comparison. If at least one other character shares a
// canonical value, then i needs special handling.
if (class_has_non_matching_canonical_char) {
if (class_has_matching_canonical_char) {
special_add.add(i);
} else {
ignore.add(i);
}
}
}
// Verify that no Unicode equivalence class contains two non-trivial
// JS equivalence classes. Every character in SpecialAddSet has the
// same canonical value as every other non-IgnoreSet character in
// its Unicode equivalence class. Therefore, if we call closeOver on
// a set containing no IgnoreSet characters, the only characters
// that must be removed from the result are in IgnoreSet. This fact
// is used in CharacterRange::AddCaseEquivalents.
for (int32_t i = 0; i < special_add.getRangeCount(); i++) {
for (UChar32 c = special_add.getRangeStart(i);
c <= special_add.getRangeEnd(i); c++) {
UChar32 canonical = RegExpCaseFolding::Canonicalize(c);
current.set(c, c);
current.closeOver(USET_CASE_INSENSITIVE);
current.removeAll(ignore);
for (int32_t j = 0; j < current.getRangeCount(); j++) {
for (UChar32 c2 = current.getRangeStart(j);
c2 <= current.getRangeEnd(j); c2++) {
CHECK_EQ(canonical, RegExpCaseFolding::Canonicalize(c2));
}
}
}
}
PrintSet(out, "IgnoreSet", ignore);
PrintSet(out, "SpecialAddSet", special_add);
}
void WriteHeader(const char* header_filename) {
std::ofstream out(header_filename);
out << std::hex << std::setfill('0') << std::setw(4);
out << "// Copyright 2020 the V8 project authors. All rights reserved.\n"
<< "// Use of this source code is governed by a BSD-style license that\n"
<< "// can be found in the LICENSE file.\n\n"
<< "// Automatically generated by regexp/gen-regexp-special-case.cc\n\n"
<< "// The following functions are used to build UnicodeSets\n"
<< "// for special cases where the case-folding algorithm used by\n"
<< "// UnicodeSet::closeOver(USET_CASE_INSENSITIVE) does not match\n"
<< "// the algorithm defined in ECMAScript 2020 21.2.2.8.2 (Runtime\n"
<< "// Semantics: Canonicalize) step 3.\n\n"
<< "#ifdef V8_INTL_SUPPORT\n"
<< "#include \"src/base/lazy-instance.h\"\n\n"
<< "#include \"src/regexp/special-case.h\"\n\n"
<< "#include \"unicode/uniset.h\"\n"
<< "namespace v8 {\n"
<< "namespace internal {\n\n";
PrintSpecial(out);
out << "\n"
<< "} // namespace internal\n"
<< "} // namespace v8\n"
<< "#endif // V8_INTL_SUPPORT\n";
}
} // namespace internal
} // namespace v8
int main(int argc, const char** argv) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <output filename>\n";
std::exit(1);
}
v8::internal::WriteHeader(argv[1]);
return 0;
}
+143
View File
@@ -0,0 +1,143 @@
#!/usr/bin/env python3
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
# This script handles all the mechanical steps of importing irregexp from v8:
#
# 1. Acquire the source: either from github, or optionally from a local copy of v8.
# 2. Copy the contents of v8/src/regexp into js/src/regexp
# - Exclude files that we have chosen not to import.
# 3. While doing so, update #includes:
# - Change "src/regexp/*" to "regexp/*".
# - Remove other v8-specific headers completely.
# 4. Add '#include "regexp/regexp-shim.h" in the necessary places.
# 5. Update the VERSION file to include the correct git hash.
#
# Usage:
# cd path/to/js/src/regexp
# ./import-irregexp.py --path path/to/v8/src/regexp
#
# Alternatively, without the --path argument, import-irregexp.py will
# clone v8 from github into a temporary directory.
#
# After running this script, changes to the shim code may be necessary
# to account for changes in upstream irregexp.
import os
import re
import subprocess
import sys
from pathlib import Path
def get_hash(path):
# Get the hash for the current git revision
cwd = os.getcwd()
os.chdir(path)
command = ['git', 'rev-parse', 'HEAD']
result = subprocess.check_output(command, encoding='utf-8')
os.chdir(cwd)
return result.rstrip()
def copy_and_update_includes(src_path, dst_path):
# List of header files that need to include the shim header
need_shim = ['property-sequences.h',
'regexp-ast.h',
'regexp-bytecode-peephole.h',
'regexp-bytecodes.h',
'regexp-dotprinter.h',
'regexp.h',
'regexp-macro-assembler.h',
'regexp-stack.h',
'special-case.h']
src = open(str(src_path), 'r')
dst = open(str(dst_path), 'w')
# 1. Rewrite includes of V8 regexp headers:
regexp_include = re.compile('#include "src/regexp')
regexp_include_new = '#include "regexp'
# 2. Remove includes of other V8 headers
other_include = re.compile('#include "src/')
# 3. If needed, add '#include "regexp/regexp-shim.h"'.
# Note: We get a little fancy to ensure that header files are
# in alphabetic order. `need_to_add_shim` is true if we still
# have to add the shim header in this file. `adding_shim_now`
# is true if we have found a '#include "src/*' and we are just
# waiting to find something alphabetically smaller (or an empty
# line) so that we can insert the shim header in the right place.
need_to_add_shim = src_path.name in need_shim
adding_shim_now = False
for line in src:
if adding_shim_now:
if (line == '\n' or line > '#include "src/regexp/regexp-shim.h"'):
dst.write('#include "regexp/regexp-shim.h"\n')
need_to_add_shim = False
adding_shim_now = False
if regexp_include.search(line):
dst.write(re.sub(regexp_include, regexp_include_new, line))
elif other_include.search(line):
if need_to_add_shim:
adding_shim_now = True
else:
dst.write(line)
def import_from(srcdir, dstdir):
excluded = ['OWNERS',
'regexp.cc',
'regexp-utils.cc',
'regexp-utils.h',
'regexp-macro-assembler-arch.h']
for file in srcdir.iterdir():
if file.is_dir():
continue
if str(file.name) in excluded:
continue
copy_and_update_includes(file, dstdir / file.name)
# Update VERSION file
hash = get_hash(srcdir)
version_file = open(str(dstdir / 'VERSION'), 'w')
version_file.write('Imported using import-irregexp.py from:\n')
version_file.write('https://github.com/v8/v8/tree/%s/src/regexp\n' % hash)
if __name__ == '__main__':
import argparse
import tempfile
# This script should be run from js/src/regexp to work correctly.
current_path = Path(os.getcwd())
expected_path = 'js/src/regexp'
if not current_path.match(expected_path):
raise RuntimeError('%s must be run from %s' % (sys.argv[0],
expected_path))
parser = argparse.ArgumentParser(description='Import irregexp from v8')
parser.add_argument('-p', '--path', help='path to v8/src/regexp')
args = parser.parse_args()
if args.path:
src_path = Path(args.path)
if not (src_path / 'regexp.h').exists():
print('Usage:\n import-irregexp.py --path <path/to/v8/src/regexp>')
sys.exit(1)
import_from(src_path, current_path)
sys.exit(0)
with tempfile.TemporaryDirectory() as tempdir:
v8_git = 'https://github.com/v8/v8.git'
clone = 'git clone --depth 1 %s %s' % (v8_git, tempdir)
os.system(clone)
src_path = Path(tempdir) / 'src/regexp'
import_from(src_path, current_path)
+37
View File
@@ -0,0 +1,37 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('../js-config.mozbuild')
include('../js-cxxflags.mozbuild')
FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
SOURCES += [
'regexp-ast.cc',
'regexp-bytecode-generator.cc',
'regexp-bytecode-peephole.cc',
'regexp-bytecodes.cc',
'regexp-compiler-tonode.cc',
'regexp-compiler.cc',
'regexp-dotprinter.cc',
'regexp-interpreter.cc',
'regexp-macro-assembler-tracer.cc',
'regexp-macro-assembler.cc',
'regexp-native-macro-assembler.cc',
'regexp-parser.cc',
'regexp-shim.cc',
'regexp-stack.cc',
'util/unicode.cc'
]
if CONFIG['ENABLE_INTL_API']:
CXXFLAGS += ['-DV8_INTL_SUPPORT']
SOURCES += [
'property-sequences.cc',
'special-case.cc'
]
File diff suppressed because it is too large. Load diff
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_PROPERTY_SEQUENCES_H_
#define V8_REGEXP_PROPERTY_SEQUENCES_H_
#ifdef V8_INTL_SUPPORT
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
class UnicodePropertySequences : public AllStatic {
public:
static const uc32 kEmojiFlagSequences[];
static const uc32 kEmojiTagSequences[];
static const uc32 kEmojiZWJSequences[];
};
} // namespace internal
} // namespace v8
#endif // V8_INTL_SUPPORT
#endif // V8_REGEXP_PROPERTY_SEQUENCES_H_
+342
View File
@@ -0,0 +1,342 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "regexp/regexp-ast.h"
namespace v8 {
namespace internal {
#define MAKE_ACCEPT(Name) \
void* RegExp##Name::Accept(RegExpVisitor* visitor, void* data) { \
return visitor->Visit##Name(this, data); \
}
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_ACCEPT)
#undef MAKE_ACCEPT
#define MAKE_TYPE_CASE(Name) \
RegExp##Name* RegExpTree::As##Name() { return nullptr; } \
bool RegExpTree::Is##Name() { return false; }
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE)
#undef MAKE_TYPE_CASE
#define MAKE_TYPE_CASE(Name) \
RegExp##Name* RegExp##Name::As##Name() { return this; } \
bool RegExp##Name::Is##Name() { return true; }
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE)
#undef MAKE_TYPE_CASE
static Interval ListCaptureRegisters(ZoneList<RegExpTree*>* children) {
Interval result = Interval::Empty();
for (int i = 0; i < children->length(); i++)
result = result.Union(children->at(i)->CaptureRegisters());
return result;
}
Interval RegExpAlternative::CaptureRegisters() {
return ListCaptureRegisters(nodes());
}
Interval RegExpDisjunction::CaptureRegisters() {
return ListCaptureRegisters(alternatives());
}
Interval RegExpLookaround::CaptureRegisters() {
return body()->CaptureRegisters();
}
Interval RegExpCapture::CaptureRegisters() {
Interval self(StartRegister(index()), EndRegister(index()));
return self.Union(body()->CaptureRegisters());
}
Interval RegExpQuantifier::CaptureRegisters() {
return body()->CaptureRegisters();
}
bool RegExpAssertion::IsAnchoredAtStart() {
return assertion_type() == RegExpAssertion::START_OF_INPUT;
}
bool RegExpAssertion::IsAnchoredAtEnd() {
return assertion_type() == RegExpAssertion::END_OF_INPUT;
}
bool RegExpAlternative::IsAnchoredAtStart() {
ZoneList<RegExpTree*>* nodes = this->nodes();
for (int i = 0; i < nodes->length(); i++) {
RegExpTree* node = nodes->at(i);
if (node->IsAnchoredAtStart()) {
return true;
}
if (node->max_match() > 0) {
return false;
}
}
return false;
}
bool RegExpAlternative::IsAnchoredAtEnd() {
ZoneList<RegExpTree*>* nodes = this->nodes();
for (int i = nodes->length() - 1; i >= 0; i--) {
RegExpTree* node = nodes->at(i);
if (node->IsAnchoredAtEnd()) {
return true;
}
if (node->max_match() > 0) {
return false;
}
}
return false;
}
bool RegExpDisjunction::IsAnchoredAtStart() {
ZoneList<RegExpTree*>* alternatives = this->alternatives();
for (int i = 0; i < alternatives->length(); i++) {
if (!alternatives->at(i)->IsAnchoredAtStart()) return false;
}
return true;
}
bool RegExpDisjunction::IsAnchoredAtEnd() {
ZoneList<RegExpTree*>* alternatives = this->alternatives();
for (int i = 0; i < alternatives->length(); i++) {
if (!alternatives->at(i)->IsAnchoredAtEnd()) return false;
}
return true;
}
bool RegExpLookaround::IsAnchoredAtStart() {
return is_positive() && type() == LOOKAHEAD && body()->IsAnchoredAtStart();
}
bool RegExpCapture::IsAnchoredAtStart() { return body()->IsAnchoredAtStart(); }
bool RegExpCapture::IsAnchoredAtEnd() { return body()->IsAnchoredAtEnd(); }
// Convert regular expression trees to a simple sexp representation.
// This representation should be different from the input grammar
// in as many cases as possible, to make it more difficult for incorrect
// parses to look as correct ones which is likely if the input and
// output formats are alike.
class RegExpUnparser final : public RegExpVisitor {
public:
RegExpUnparser(std::ostream& os, Zone* zone) : os_(os), zone_(zone) {}
void VisitCharacterRange(CharacterRange that);
#define MAKE_CASE(Name) void* Visit##Name(RegExp##Name*, void* data) override;
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
#undef MAKE_CASE
private:
std::ostream& os_;
Zone* zone_;
};
void* RegExpUnparser::VisitDisjunction(RegExpDisjunction* that, void* data) {
os_ << "(|";
for (int i = 0; i < that->alternatives()->length(); i++) {
os_ << " ";
that->alternatives()->at(i)->Accept(this, data);
}
os_ << ")";
return nullptr;
}
void* RegExpUnparser::VisitAlternative(RegExpAlternative* that, void* data) {
os_ << "(:";
for (int i = 0; i < that->nodes()->length(); i++) {
os_ << " ";
that->nodes()->at(i)->Accept(this, data);
}
os_ << ")";
return nullptr;
}
void RegExpUnparser::VisitCharacterRange(CharacterRange that) {
os_ << AsUC32(that.from());
if (!that.IsSingleton()) {
os_ << "-" << AsUC32(that.to());
}
}
void* RegExpUnparser::VisitCharacterClass(RegExpCharacterClass* that,
void* data) {
if (that->is_negated()) os_ << "^";
os_ << "[";
for (int i = 0; i < that->ranges(zone_)->length(); i++) {
if (i > 0) os_ << " ";
VisitCharacterRange(that->ranges(zone_)->at(i));
}
os_ << "]";
return nullptr;
}
void* RegExpUnparser::VisitAssertion(RegExpAssertion* that, void* data) {
switch (that->assertion_type()) {
case RegExpAssertion::START_OF_INPUT:
os_ << "@^i";
break;
case RegExpAssertion::END_OF_INPUT:
os_ << "@$i";
break;
case RegExpAssertion::START_OF_LINE:
os_ << "@^l";
break;
case RegExpAssertion::END_OF_LINE:
os_ << "@$l";
break;
case RegExpAssertion::BOUNDARY:
os_ << "@b";
break;
case RegExpAssertion::NON_BOUNDARY:
os_ << "@B";
break;
}
return nullptr;
}
void* RegExpUnparser::VisitAtom(RegExpAtom* that, void* data) {
os_ << "'";
Vector<const uc16> chardata = that->data();
for (int i = 0; i < chardata.length(); i++) {
os_ << AsUC16(chardata[i]);
}
os_ << "'";
return nullptr;
}
void* RegExpUnparser::VisitText(RegExpText* that, void* data) {
if (that->elements()->length() == 1) {
that->elements()->at(0).tree()->Accept(this, data);
} else {
os_ << "(!";
for (int i = 0; i < that->elements()->length(); i++) {
os_ << " ";
that->elements()->at(i).tree()->Accept(this, data);
}
os_ << ")";
}
return nullptr;
}
void* RegExpUnparser::VisitQuantifier(RegExpQuantifier* that, void* data) {
os_ << "(# " << that->min() << " ";
if (that->max() == RegExpTree::kInfinity) {
os_ << "- ";
} else {
os_ << that->max() << " ";
}
os_ << (that->is_greedy() ? "g " : that->is_possessive() ? "p " : "n ");
that->body()->Accept(this, data);
os_ << ")";
return nullptr;
}
void* RegExpUnparser::VisitCapture(RegExpCapture* that, void* data) {
os_ << "(^ ";
that->body()->Accept(this, data);
os_ << ")";
return nullptr;
}
void* RegExpUnparser::VisitGroup(RegExpGroup* that, void* data) {
os_ << "(?: ";
that->body()->Accept(this, data);
os_ << ")";
return nullptr;
}
void* RegExpUnparser::VisitLookaround(RegExpLookaround* that, void* data) {
os_ << "(";
os_ << (that->type() == RegExpLookaround::LOOKAHEAD ? "->" : "<-");
os_ << (that->is_positive() ? " + " : " - ");
that->body()->Accept(this, data);
os_ << ")";
return nullptr;
}
void* RegExpUnparser::VisitBackReference(RegExpBackReference* that,
void* data) {
os_ << "(<- " << that->index() << ")";
return nullptr;
}
void* RegExpUnparser::VisitEmpty(RegExpEmpty* that, void* data) {
os_ << '%';
return nullptr;
}
std::ostream& RegExpTree::Print(std::ostream& os, Zone* zone) { // NOLINT
RegExpUnparser unparser(os, zone);
Accept(&unparser, nullptr);
return os;
}
RegExpDisjunction::RegExpDisjunction(ZoneList<RegExpTree*>* alternatives)
: alternatives_(alternatives) {
DCHECK_LT(1, alternatives->length());
RegExpTree* first_alternative = alternatives->at(0);
min_match_ = first_alternative->min_match();
max_match_ = first_alternative->max_match();
for (int i = 1; i < alternatives->length(); i++) {
RegExpTree* alternative = alternatives->at(i);
min_match_ = Min(min_match_, alternative->min_match());
max_match_ = Max(max_match_, alternative->max_match());
}
}
static int IncreaseBy(int previous, int increase) {
if (RegExpTree::kInfinity - previous < increase) {
return RegExpTree::kInfinity;
} else {
return previous + increase;
}
}
RegExpAlternative::RegExpAlternative(ZoneList<RegExpTree*>* nodes)
: nodes_(nodes) {
DCHECK_LT(1, nodes->length());
min_match_ = 0;
max_match_ = 0;
for (int i = 0; i < nodes->length(); i++) {
RegExpTree* node = nodes->at(i);
int node_min_match = node->min_match();
min_match_ = IncreaseBy(min_match_, node_min_match);
int node_max_match = node->max_match();
max_match_ = IncreaseBy(max_match_, node_max_match);
}
}
} // namespace internal
} // namespace v8
+615
View File
@@ -0,0 +1,615 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_AST_H_
#define V8_REGEXP_REGEXP_AST_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
#define FOR_EACH_REG_EXP_TREE_TYPE(VISIT) \
VISIT(Disjunction) \
VISIT(Alternative) \
VISIT(Assertion) \
VISIT(CharacterClass) \
VISIT(Atom) \
VISIT(Quantifier) \
VISIT(Capture) \
VISIT(Group) \
VISIT(Lookaround) \
VISIT(BackReference) \
VISIT(Empty) \
VISIT(Text)
#define FORWARD_DECLARE(Name) class RegExp##Name;
FOR_EACH_REG_EXP_TREE_TYPE(FORWARD_DECLARE)
#undef FORWARD_DECLARE
class RegExpCompiler;
class RegExpNode;
class RegExpTree;
class RegExpVisitor {
public:
virtual ~RegExpVisitor() = default;
#define MAKE_CASE(Name) \
virtual void* Visit##Name(RegExp##Name*, void* data) = 0;
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
#undef MAKE_CASE
};
// A simple closed interval.
class Interval {
public:
Interval() : from_(kNone), to_(kNone - 1) {} // '- 1' for branchless size().
Interval(int from, int to) : from_(from), to_(to) {}
Interval Union(Interval that) {
if (that.from_ == kNone)
return *this;
else if (from_ == kNone)
return that;
else
return Interval(Min(from_, that.from_), Max(to_, that.to_));
}
bool Contains(int value) { return (from_ <= value) && (value <= to_); }
bool is_empty() { return from_ == kNone; }
int from() const { return from_; }
int to() const { return to_; }
int size() const { return to_ - from_ + 1; }
static Interval Empty() { return Interval(); }
static constexpr int kNone = -1;
private:
int from_;
int to_;
};
// Represents code units in the range from from_ to to_, both ends are
// inclusive.
class CharacterRange {
public:
CharacterRange() : from_(0), to_(0) {}
// For compatibility with the CHECK_OK macro
CharacterRange(void* null) { DCHECK_NULL(null); } // NOLINT
V8_EXPORT_PRIVATE static void AddClassEscape(char type,
ZoneList<CharacterRange>* ranges,
Zone* zone);
// Add class escapes. Add case equivalent closure for \w and \W if necessary.
V8_EXPORT_PRIVATE static void AddClassEscape(
char type, ZoneList<CharacterRange>* ranges,
bool add_unicode_case_equivalents, Zone* zone);
static Vector<const int> GetWordBounds();
static inline CharacterRange Singleton(uc32 value) {
return CharacterRange(value, value);
}
static inline CharacterRange Range(uc32 from, uc32 to) {
DCHECK(0 <= from && to <= String::kMaxCodePoint);
DCHECK(static_cast<uint32_t>(from) <= static_cast<uint32_t>(to));
return CharacterRange(from, to);
}
static inline CharacterRange Everything() {
return CharacterRange(0, String::kMaxCodePoint);
}
static inline ZoneList<CharacterRange>* List(Zone* zone,
CharacterRange range) {
ZoneList<CharacterRange>* list =
new (zone) ZoneList<CharacterRange>(1, zone);
list->Add(range, zone);
return list;
}
bool Contains(uc32 i) { return from_ <= i && i <= to_; }
uc32 from() const { return from_; }
void set_from(uc32 value) { from_ = value; }
uc32 to() const { return to_; }
void set_to(uc32 value) { to_ = value; }
bool is_valid() { return from_ <= to_; }
bool IsEverything(uc32 max) { return from_ == 0 && to_ >= max; }
bool IsSingleton() { return (from_ == to_); }
V8_EXPORT_PRIVATE static void AddCaseEquivalents(
Isolate* isolate, Zone* zone, ZoneList<CharacterRange>* ranges,
bool is_one_byte);
// Whether a range list is in canonical form: Ranges ordered by from value,
// and ranges non-overlapping and non-adjacent.
V8_EXPORT_PRIVATE static bool IsCanonical(ZoneList<CharacterRange>* ranges);
// Convert range list to canonical form. The characters covered by the ranges
// will still be the same, but no character is in more than one range, and
// adjacent ranges are merged. The resulting list may be shorter than the
// original, but cannot be longer.
static void Canonicalize(ZoneList<CharacterRange>* ranges);
// Negate the contents of a character range in canonical form.
static void Negate(ZoneList<CharacterRange>* src,
ZoneList<CharacterRange>* dst, Zone* zone);
static const int kStartMarker = (1 << 24);
static const int kPayloadMask = (1 << 24) - 1;
private:
CharacterRange(uc32 from, uc32 to) : from_(from), to_(to) {}
uc32 from_;
uc32 to_;
};
class CharacterSet final {
public:
explicit CharacterSet(uc16 standard_set_type)
: ranges_(nullptr), standard_set_type_(standard_set_type) {}
explicit CharacterSet(ZoneList<CharacterRange>* ranges)
: ranges_(ranges), standard_set_type_(0) {}
ZoneList<CharacterRange>* ranges(Zone* zone);
uc16 standard_set_type() const { return standard_set_type_; }
void set_standard_set_type(uc16 special_set_type) {
standard_set_type_ = special_set_type;
}
bool is_standard() { return standard_set_type_ != 0; }
V8_EXPORT_PRIVATE void Canonicalize();
private:
ZoneList<CharacterRange>* ranges_;
// If non-zero, the value represents a standard set (e.g., all whitespace
// characters) without having to expand the ranges.
uc16 standard_set_type_;
};
class TextElement final {
public:
enum TextType { ATOM, CHAR_CLASS };
static TextElement Atom(RegExpAtom* atom);
static TextElement CharClass(RegExpCharacterClass* char_class);
int cp_offset() const { return cp_offset_; }
void set_cp_offset(int cp_offset) { cp_offset_ = cp_offset; }
int length() const;
TextType text_type() const { return text_type_; }
RegExpTree* tree() const { return tree_; }
RegExpAtom* atom() const {
DCHECK(text_type() == ATOM);
return reinterpret_cast<RegExpAtom*>(tree());
}
RegExpCharacterClass* char_class() const {
DCHECK(text_type() == CHAR_CLASS);
return reinterpret_cast<RegExpCharacterClass*>(tree());
}
private:
TextElement(TextType text_type, RegExpTree* tree)
: cp_offset_(-1), text_type_(text_type), tree_(tree) {}
int cp_offset_;
TextType text_type_;
RegExpTree* tree_;
};
class RegExpTree : public ZoneObject {
public:
static const int kInfinity = kMaxInt;
virtual ~RegExpTree() = default;
virtual void* Accept(RegExpVisitor* visitor, void* data) = 0;
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
RegExpNode* on_success) = 0;
virtual bool IsTextElement() { return false; }
virtual bool IsAnchoredAtStart() { return false; }
virtual bool IsAnchoredAtEnd() { return false; }
virtual int min_match() = 0;
virtual int max_match() = 0;
// Returns the interval of registers used for captures within this
// expression.
virtual Interval CaptureRegisters() { return Interval::Empty(); }
virtual void AppendToText(RegExpText* text, Zone* zone);
V8_EXPORT_PRIVATE std::ostream& Print(std::ostream& os,
Zone* zone); // NOLINT
#define MAKE_ASTYPE(Name) \
virtual RegExp##Name* As##Name(); \
virtual bool Is##Name();
FOR_EACH_REG_EXP_TREE_TYPE(MAKE_ASTYPE)
#undef MAKE_ASTYPE
};
class RegExpDisjunction final : public RegExpTree {
public:
explicit RegExpDisjunction(ZoneList<RegExpTree*>* alternatives);
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
RegExpDisjunction* AsDisjunction() override;
Interval CaptureRegisters() override;
bool IsDisjunction() override;
bool IsAnchoredAtStart() override;
bool IsAnchoredAtEnd() override;
int min_match() override { return min_match_; }
int max_match() override { return max_match_; }
ZoneList<RegExpTree*>* alternatives() { return alternatives_; }
private:
bool SortConsecutiveAtoms(RegExpCompiler* compiler);
void RationalizeConsecutiveAtoms(RegExpCompiler* compiler);
void FixSingleCharacterDisjunctions(RegExpCompiler* compiler);
ZoneList<RegExpTree*>* alternatives_;
int min_match_;
int max_match_;
};
class RegExpAlternative final : public RegExpTree {
public:
explicit RegExpAlternative(ZoneList<RegExpTree*>* nodes);
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
RegExpAlternative* AsAlternative() override;
Interval CaptureRegisters() override;
bool IsAlternative() override;
bool IsAnchoredAtStart() override;
bool IsAnchoredAtEnd() override;
int min_match() override { return min_match_; }
int max_match() override { return max_match_; }
ZoneList<RegExpTree*>* nodes() { return nodes_; }
private:
ZoneList<RegExpTree*>* nodes_;
int min_match_;
int max_match_;
};
class RegExpAssertion final : public RegExpTree {
public:
enum AssertionType {
START_OF_LINE = 0,
START_OF_INPUT = 1,
END_OF_LINE = 2,
END_OF_INPUT = 3,
BOUNDARY = 4,
NON_BOUNDARY = 5,
LAST_TYPE = NON_BOUNDARY,
};
RegExpAssertion(AssertionType type, JSRegExp::Flags flags)
: assertion_type_(type), flags_(flags) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
RegExpAssertion* AsAssertion() override;
bool IsAssertion() override;
bool IsAnchoredAtStart() override;
bool IsAnchoredAtEnd() override;
int min_match() override { return 0; }
int max_match() override { return 0; }
AssertionType assertion_type() const { return assertion_type_; }
JSRegExp::Flags flags() const { return flags_; }
private:
const AssertionType assertion_type_;
const JSRegExp::Flags flags_;
};
class RegExpCharacterClass final : public RegExpTree {
public:
// NEGATED: The character class is negated and should match everything but
// the specified ranges.
// CONTAINS_SPLIT_SURROGATE: The character class contains part of a split
// surrogate and should not be unicode-desugared (crbug.com/641091).
enum Flag {
NEGATED = 1 << 0,
CONTAINS_SPLIT_SURROGATE = 1 << 1,
};
using CharacterClassFlags = base::Flags<Flag>;
RegExpCharacterClass(
Zone* zone, ZoneList<CharacterRange>* ranges, JSRegExp::Flags flags,
CharacterClassFlags character_class_flags = CharacterClassFlags())
: set_(ranges),
flags_(flags),
character_class_flags_(character_class_flags) {
// Convert the empty set of ranges to the negated Everything() range.
if (ranges->is_empty()) {
ranges->Add(CharacterRange::Everything(), zone);
character_class_flags_ ^= NEGATED;
}
}
RegExpCharacterClass(uc16 type, JSRegExp::Flags flags)
: set_(type),
flags_(flags),
character_class_flags_(CharacterClassFlags()) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
RegExpCharacterClass* AsCharacterClass() override;
bool IsCharacterClass() override;
bool IsTextElement() override { return true; }
int min_match() override { return 1; }
// The character class may match two code units for unicode regexps.
// TODO(yangguo): we should split this class for usage in TextElement, and
// make max_match() dependent on the character class content.
int max_match() override { return 2; }
void AppendToText(RegExpText* text, Zone* zone) override;
CharacterSet character_set() { return set_; }
// TODO(lrn): Remove need for complex version if is_standard that
// recognizes a mangled standard set and just do { return set_.is_special(); }
bool is_standard(Zone* zone);
// Returns a value representing the standard character set if is_standard()
// returns true.
// Currently used values are:
// s : unicode whitespace
// S : unicode non-whitespace
// w : ASCII word character (digit, letter, underscore)
// W : non-ASCII word character
// d : ASCII digit
// D : non-ASCII digit
// . : non-newline
// * : All characters, for advancing unanchored regexp
uc16 standard_type() const { return set_.standard_set_type(); }
ZoneList<CharacterRange>* ranges(Zone* zone) { return set_.ranges(zone); }
bool is_negated() const { return (character_class_flags_ & NEGATED) != 0; }
JSRegExp::Flags flags() const { return flags_; }
bool contains_split_surrogate() const {
return (character_class_flags_ & CONTAINS_SPLIT_SURROGATE) != 0;
}
private:
CharacterSet set_;
const JSRegExp::Flags flags_;
CharacterClassFlags character_class_flags_;
};
class RegExpAtom final : public RegExpTree {
public:
explicit RegExpAtom(Vector<const uc16> data, JSRegExp::Flags flags)
: data_(data), flags_(flags) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
RegExpAtom* AsAtom() override;
bool IsAtom() override;
bool IsTextElement() override { return true; }
int min_match() override { return data_.length(); }
int max_match() override { return data_.length(); }
void AppendToText(RegExpText* text, Zone* zone) override;
Vector<const uc16> data() { return data_; }
int length() { return data_.length(); }
JSRegExp::Flags flags() const { return flags_; }
bool ignore_case() const { return (flags_ & JSRegExp::kIgnoreCase) != 0; }
private:
Vector<const uc16> data_;
const JSRegExp::Flags flags_;
};
class RegExpText final : public RegExpTree {
public:
explicit RegExpText(Zone* zone) : elements_(2, zone), length_(0) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
RegExpText* AsText() override;
bool IsText() override;
bool IsTextElement() override { return true; }
int min_match() override { return length_; }
int max_match() override { return length_; }
void AppendToText(RegExpText* text, Zone* zone) override;
void AddElement(TextElement elm, Zone* zone) {
elements_.Add(elm, zone);
length_ += elm.length();
}
ZoneList<TextElement>* elements() { return &elements_; }
private:
ZoneList<TextElement> elements_;
int length_;
};
class RegExpQuantifier final : public RegExpTree {
public:
enum QuantifierType { GREEDY, NON_GREEDY, POSSESSIVE };
RegExpQuantifier(int min, int max, QuantifierType type, RegExpTree* body)
: body_(body),
min_(min),
max_(max),
quantifier_type_(type) {
if (min > 0 && body->min_match() > kInfinity / min) {
min_match_ = kInfinity;
} else {
min_match_ = min * body->min_match();
}
if (max > 0 && body->max_match() > kInfinity / max) {
max_match_ = kInfinity;
} else {
max_match_ = max * body->max_match();
}
}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
static RegExpNode* ToNode(int min, int max, bool is_greedy, RegExpTree* body,
RegExpCompiler* compiler, RegExpNode* on_success,
bool not_at_start = false);
RegExpQuantifier* AsQuantifier() override;
Interval CaptureRegisters() override;
bool IsQuantifier() override;
int min_match() override { return min_match_; }
int max_match() override { return max_match_; }
int min() { return min_; }
int max() { return max_; }
bool is_possessive() { return quantifier_type_ == POSSESSIVE; }
bool is_non_greedy() { return quantifier_type_ == NON_GREEDY; }
bool is_greedy() { return quantifier_type_ == GREEDY; }
RegExpTree* body() { return body_; }
private:
RegExpTree* body_;
int min_;
int max_;
int min_match_;
int max_match_;
QuantifierType quantifier_type_;
};
class RegExpCapture final : public RegExpTree {
public:
explicit RegExpCapture(int index)
: body_(nullptr),
index_(index),
min_match_(0),
max_match_(0),
name_(nullptr) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
static RegExpNode* ToNode(RegExpTree* body, int index,
RegExpCompiler* compiler, RegExpNode* on_success);
RegExpCapture* AsCapture() override;
bool IsAnchoredAtStart() override;
bool IsAnchoredAtEnd() override;
Interval CaptureRegisters() override;
bool IsCapture() override;
int min_match() override { return min_match_; }
int max_match() override { return max_match_; }
RegExpTree* body() { return body_; }
void set_body(RegExpTree* body) {
body_ = body;
min_match_ = body->min_match();
max_match_ = body->max_match();
}
int index() const { return index_; }
const ZoneVector<uc16>* name() const { return name_; }
void set_name(const ZoneVector<uc16>* name) { name_ = name; }
static int StartRegister(int index) { return index * 2; }
static int EndRegister(int index) { return index * 2 + 1; }
private:
RegExpTree* body_;
int index_;
int min_match_;
int max_match_;
const ZoneVector<uc16>* name_;
};
class RegExpGroup final : public RegExpTree {
public:
explicit RegExpGroup(RegExpTree* body)
: body_(body),
min_match_(body->min_match()),
max_match_(body->max_match()) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler,
RegExpNode* on_success) override {
return body_->ToNode(compiler, on_success);
}
RegExpGroup* AsGroup() override;
bool IsAnchoredAtStart() override { return body_->IsAnchoredAtStart(); }
bool IsAnchoredAtEnd() override { return body_->IsAnchoredAtEnd(); }
bool IsGroup() override;
int min_match() override { return min_match_; }
int max_match() override { return max_match_; }
Interval CaptureRegisters() override { return body_->CaptureRegisters(); }
RegExpTree* body() { return body_; }
private:
RegExpTree* body_;
int min_match_;
int max_match_;
};
class RegExpLookaround final : public RegExpTree {
public:
enum Type { LOOKAHEAD, LOOKBEHIND };
RegExpLookaround(RegExpTree* body, bool is_positive, int capture_count,
int capture_from, Type type)
: body_(body),
is_positive_(is_positive),
capture_count_(capture_count),
capture_from_(capture_from),
type_(type) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
RegExpLookaround* AsLookaround() override;
Interval CaptureRegisters() override;
bool IsLookaround() override;
bool IsAnchoredAtStart() override;
int min_match() override { return 0; }
int max_match() override { return 0; }
RegExpTree* body() { return body_; }
bool is_positive() { return is_positive_; }
int capture_count() { return capture_count_; }
int capture_from() { return capture_from_; }
Type type() { return type_; }
class Builder {
public:
Builder(bool is_positive, RegExpNode* on_success,
int stack_pointer_register, int position_register,
int capture_register_count = 0, int capture_register_start = 0);
RegExpNode* on_match_success() { return on_match_success_; }
RegExpNode* ForMatch(RegExpNode* match);
private:
bool is_positive_;
RegExpNode* on_match_success_;
RegExpNode* on_success_;
int stack_pointer_register_;
int position_register_;
};
private:
RegExpTree* body_;
bool is_positive_;
int capture_count_;
int capture_from_;
Type type_;
};
class RegExpBackReference final : public RegExpTree {
public:
explicit RegExpBackReference(JSRegExp::Flags flags)
: capture_(nullptr), name_(nullptr), flags_(flags) {}
RegExpBackReference(RegExpCapture* capture, JSRegExp::Flags flags)
: capture_(capture), name_(nullptr), flags_(flags) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
RegExpBackReference* AsBackReference() override;
bool IsBackReference() override;
int min_match() override { return 0; }
// The back reference may be recursive, e.g. /(\2)(\1)/. To avoid infinite
// recursion, we give up. Ignorance is bliss.
int max_match() override { return kInfinity; }
int index() { return capture_->index(); }
RegExpCapture* capture() { return capture_; }
void set_capture(RegExpCapture* capture) { capture_ = capture; }
const ZoneVector<uc16>* name() const { return name_; }
void set_name(const ZoneVector<uc16>* name) { name_ = name; }
private:
RegExpCapture* capture_;
const ZoneVector<uc16>* name_;
const JSRegExp::Flags flags_;
};
class RegExpEmpty final : public RegExpTree {
public:
RegExpEmpty() = default;
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
RegExpEmpty* AsEmpty() override;
bool IsEmpty() override;
int min_match() override { return 0; }
int max_match() override { return 0; }
};
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_AST_H_
@@ -0,0 +1,55 @@
// Copyright 2008-2009 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_BYTECODE_GENERATOR_INL_H_
#define V8_REGEXP_REGEXP_BYTECODE_GENERATOR_INL_H_
#include "regexp/regexp-bytecode-generator.h"
#include "regexp/regexp-bytecodes.h"
namespace v8 {
namespace internal {
void RegExpBytecodeGenerator::Emit(uint32_t byte, uint32_t twenty_four_bits) {
uint32_t word = ((twenty_four_bits << BYTECODE_SHIFT) | byte);
DCHECK(pc_ <= buffer_.length());
if (pc_ + 3 >= buffer_.length()) {
Expand();
}
*reinterpret_cast<uint32_t*>(buffer_.begin() + pc_) = word;
pc_ += 4;
}
void RegExpBytecodeGenerator::Emit16(uint32_t word) {
DCHECK(pc_ <= buffer_.length());
if (pc_ + 1 >= buffer_.length()) {
Expand();
}
*reinterpret_cast<uint16_t*>(buffer_.begin() + pc_) = word;
pc_ += 2;
}
void RegExpBytecodeGenerator::Emit8(uint32_t word) {
DCHECK(pc_ <= buffer_.length());
if (pc_ == buffer_.length()) {
Expand();
}
*reinterpret_cast<unsigned char*>(buffer_.begin() + pc_) = word;
pc_ += 1;
}
void RegExpBytecodeGenerator::Emit32(uint32_t word) {
DCHECK(pc_ <= buffer_.length());
if (pc_ + 3 >= buffer_.length()) {
Expand();
}
*reinterpret_cast<uint32_t*>(buffer_.begin() + pc_) = word;
pc_ += 4;
}
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_BYTECODE_GENERATOR_INL_H_
+395
View File
@@ -0,0 +1,395 @@
// Copyright 2008-2009 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "regexp/regexp-bytecode-generator.h"
#include "regexp/regexp-bytecode-generator-inl.h"
#include "regexp/regexp-bytecode-peephole.h"
#include "regexp/regexp-bytecodes.h"
#include "regexp/regexp-macro-assembler.h"
namespace v8 {
namespace internal {
RegExpBytecodeGenerator::RegExpBytecodeGenerator(Isolate* isolate, Zone* zone)
: RegExpMacroAssembler(isolate, zone),
buffer_(Vector<byte>::New(1024)),
pc_(0),
advance_current_end_(kInvalidPC),
jump_edges_(zone),
isolate_(isolate) {}
RegExpBytecodeGenerator::~RegExpBytecodeGenerator() {
if (backtrack_.is_linked()) backtrack_.Unuse();
buffer_.Dispose();
}
RegExpBytecodeGenerator::IrregexpImplementation
RegExpBytecodeGenerator::Implementation() {
return kBytecodeImplementation;
}
void RegExpBytecodeGenerator::Bind(Label* l) {
advance_current_end_ = kInvalidPC;
DCHECK(!l->is_bound());
if (l->is_linked()) {
int pos = l->pos();
while (pos != 0) {
int fixup = pos;
pos = *reinterpret_cast<int32_t*>(buffer_.begin() + fixup);
*reinterpret_cast<uint32_t*>(buffer_.begin() + fixup) = pc_;
jump_edges_.emplace(fixup, pc_);
}
}
l->bind_to(pc_);
}
void RegExpBytecodeGenerator::EmitOrLink(Label* l) {
if (l == nullptr) l = &backtrack_;
int pos = 0;
if (l->is_bound()) {
pos = l->pos();
jump_edges_.emplace(pc_, pos);
} else {
if (l->is_linked()) {
pos = l->pos();
}
l->link_to(pc_);
}
Emit32(pos);
}
void RegExpBytecodeGenerator::PopRegister(int register_index) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_POP_REGISTER, register_index);
}
void RegExpBytecodeGenerator::PushRegister(int register_index,
StackCheckFlag check_stack_limit) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_PUSH_REGISTER, register_index);
}
void RegExpBytecodeGenerator::WriteCurrentPositionToRegister(int register_index,
int cp_offset) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_SET_REGISTER_TO_CP, register_index);
Emit32(cp_offset); // Current position offset.
}
void RegExpBytecodeGenerator::ClearRegisters(int reg_from, int reg_to) {
DCHECK(reg_from <= reg_to);
for (int reg = reg_from; reg <= reg_to; reg++) {
SetRegister(reg, -1);
}
}
void RegExpBytecodeGenerator::ReadCurrentPositionFromRegister(
int register_index) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_SET_CP_TO_REGISTER, register_index);
}
void RegExpBytecodeGenerator::WriteStackPointerToRegister(int register_index) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_SET_REGISTER_TO_SP, register_index);
}
void RegExpBytecodeGenerator::ReadStackPointerFromRegister(int register_index) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_SET_SP_TO_REGISTER, register_index);
}
void RegExpBytecodeGenerator::SetCurrentPositionFromEnd(int by) {
DCHECK(is_uint24(by));
Emit(BC_SET_CURRENT_POSITION_FROM_END, by);
}
void RegExpBytecodeGenerator::SetRegister(int register_index, int to) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_SET_REGISTER, register_index);
Emit32(to);
}
void RegExpBytecodeGenerator::AdvanceRegister(int register_index, int by) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_ADVANCE_REGISTER, register_index);
Emit32(by);
}
void RegExpBytecodeGenerator::PopCurrentPosition() { Emit(BC_POP_CP, 0); }
void RegExpBytecodeGenerator::PushCurrentPosition() { Emit(BC_PUSH_CP, 0); }
void RegExpBytecodeGenerator::Backtrack() { Emit(BC_POP_BT, 0); }
void RegExpBytecodeGenerator::GoTo(Label* l) {
if (advance_current_end_ == pc_) {
// Combine advance current and goto.
pc_ = advance_current_start_;
Emit(BC_ADVANCE_CP_AND_GOTO, advance_current_offset_);
EmitOrLink(l);
advance_current_end_ = kInvalidPC;
} else {
// Regular goto.
Emit(BC_GOTO, 0);
EmitOrLink(l);
}
}
void RegExpBytecodeGenerator::PushBacktrack(Label* l) {
Emit(BC_PUSH_BT, 0);
EmitOrLink(l);
}
bool RegExpBytecodeGenerator::Succeed() {
Emit(BC_SUCCEED, 0);
return false; // Restart matching for global regexp not supported.
}
void RegExpBytecodeGenerator::Fail() { Emit(BC_FAIL, 0); }
void RegExpBytecodeGenerator::AdvanceCurrentPosition(int by) {
DCHECK_LE(kMinCPOffset, by);
DCHECK_GE(kMaxCPOffset, by);
advance_current_start_ = pc_;
advance_current_offset_ = by;
Emit(BC_ADVANCE_CP, by);
advance_current_end_ = pc_;
}
void RegExpBytecodeGenerator::CheckGreedyLoop(
Label* on_tos_equals_current_position) {
Emit(BC_CHECK_GREEDY, 0);
EmitOrLink(on_tos_equals_current_position);
}
void RegExpBytecodeGenerator::LoadCurrentCharacterImpl(int cp_offset,
Label* on_failure,
bool check_bounds,
int characters,
int eats_at_least) {
DCHECK_GE(eats_at_least, characters);
if (eats_at_least > characters && check_bounds) {
DCHECK(is_uint24(cp_offset + eats_at_least));
Emit(BC_CHECK_CURRENT_POSITION, cp_offset + eats_at_least);
EmitOrLink(on_failure);
check_bounds = false; // Load below doesn't need to check.
}
DCHECK_LE(kMinCPOffset, cp_offset);
DCHECK_GE(kMaxCPOffset, cp_offset);
int bytecode;
if (check_bounds) {
if (characters == 4) {
bytecode = BC_LOAD_4_CURRENT_CHARS;
} else if (characters == 2) {
bytecode = BC_LOAD_2_CURRENT_CHARS;
} else {
DCHECK_EQ(1, characters);
bytecode = BC_LOAD_CURRENT_CHAR;
}
} else {
if (characters == 4) {
bytecode = BC_LOAD_4_CURRENT_CHARS_UNCHECKED;
} else if (characters == 2) {
bytecode = BC_LOAD_2_CURRENT_CHARS_UNCHECKED;
} else {
DCHECK_EQ(1, characters);
bytecode = BC_LOAD_CURRENT_CHAR_UNCHECKED;
}
}
Emit(bytecode, cp_offset);
if (check_bounds) EmitOrLink(on_failure);
}
void RegExpBytecodeGenerator::CheckCharacterLT(uc16 limit, Label* on_less) {
Emit(BC_CHECK_LT, limit);
EmitOrLink(on_less);
}
void RegExpBytecodeGenerator::CheckCharacterGT(uc16 limit, Label* on_greater) {
Emit(BC_CHECK_GT, limit);
EmitOrLink(on_greater);
}
void RegExpBytecodeGenerator::CheckCharacter(uint32_t c, Label* on_equal) {
if (c > MAX_FIRST_ARG) {
Emit(BC_CHECK_4_CHARS, 0);
Emit32(c);
} else {
Emit(BC_CHECK_CHAR, c);
}
EmitOrLink(on_equal);
}
void RegExpBytecodeGenerator::CheckAtStart(int cp_offset, Label* on_at_start) {
Emit(BC_CHECK_AT_START, cp_offset);
EmitOrLink(on_at_start);
}
void RegExpBytecodeGenerator::CheckNotAtStart(int cp_offset,
Label* on_not_at_start) {
Emit(BC_CHECK_NOT_AT_START, cp_offset);
EmitOrLink(on_not_at_start);
}
void RegExpBytecodeGenerator::CheckNotCharacter(uint32_t c,
Label* on_not_equal) {
if (c > MAX_FIRST_ARG) {
Emit(BC_CHECK_NOT_4_CHARS, 0);
Emit32(c);
} else {
Emit(BC_CHECK_NOT_CHAR, c);
}
EmitOrLink(on_not_equal);
}
void RegExpBytecodeGenerator::CheckCharacterAfterAnd(uint32_t c, uint32_t mask,
Label* on_equal) {
if (c > MAX_FIRST_ARG) {
Emit(BC_AND_CHECK_4_CHARS, 0);
Emit32(c);
} else {
Emit(BC_AND_CHECK_CHAR, c);
}
Emit32(mask);
EmitOrLink(on_equal);
}
void RegExpBytecodeGenerator::CheckNotCharacterAfterAnd(uint32_t c,
uint32_t mask,
Label* on_not_equal) {
if (c > MAX_FIRST_ARG) {
Emit(BC_AND_CHECK_NOT_4_CHARS, 0);
Emit32(c);
} else {
Emit(BC_AND_CHECK_NOT_CHAR, c);
}
Emit32(mask);
EmitOrLink(on_not_equal);
}
void RegExpBytecodeGenerator::CheckNotCharacterAfterMinusAnd(
uc16 c, uc16 minus, uc16 mask, Label* on_not_equal) {
Emit(BC_MINUS_AND_CHECK_NOT_CHAR, c);
Emit16(minus);
Emit16(mask);
EmitOrLink(on_not_equal);
}
void RegExpBytecodeGenerator::CheckCharacterInRange(uc16 from, uc16 to,
Label* on_in_range) {
Emit(BC_CHECK_CHAR_IN_RANGE, 0);
Emit16(from);
Emit16(to);
EmitOrLink(on_in_range);
}
void RegExpBytecodeGenerator::CheckCharacterNotInRange(uc16 from, uc16 to,
Label* on_not_in_range) {
Emit(BC_CHECK_CHAR_NOT_IN_RANGE, 0);
Emit16(from);
Emit16(to);
EmitOrLink(on_not_in_range);
}
void RegExpBytecodeGenerator::CheckBitInTable(Handle<ByteArray> table,
Label* on_bit_set) {
Emit(BC_CHECK_BIT_IN_TABLE, 0);
EmitOrLink(on_bit_set);
for (int i = 0; i < kTableSize; i += kBitsPerByte) {
int byte = 0;
for (int j = 0; j < kBitsPerByte; j++) {
if (table->get(i + j) != 0) byte |= 1 << j;
}
Emit8(byte);
}
}
void RegExpBytecodeGenerator::CheckNotBackReference(int start_reg,
bool read_backward,
Label* on_not_equal) {
DCHECK_LE(0, start_reg);
DCHECK_GE(kMaxRegister, start_reg);
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_BACKWARD : BC_CHECK_NOT_BACK_REF,
start_reg);
EmitOrLink(on_not_equal);
}
void RegExpBytecodeGenerator::CheckNotBackReferenceIgnoreCase(
int start_reg, bool read_backward, Label* on_not_equal) {
DCHECK_LE(0, start_reg);
DCHECK_GE(kMaxRegister, start_reg);
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD
: BC_CHECK_NOT_BACK_REF_NO_CASE,
start_reg);
EmitOrLink(on_not_equal);
}
void RegExpBytecodeGenerator::IfRegisterLT(int register_index, int comparand,
Label* on_less_than) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_CHECK_REGISTER_LT, register_index);
Emit32(comparand);
EmitOrLink(on_less_than);
}
void RegExpBytecodeGenerator::IfRegisterGE(int register_index, int comparand,
Label* on_greater_or_equal) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_CHECK_REGISTER_GE, register_index);
Emit32(comparand);
EmitOrLink(on_greater_or_equal);
}
void RegExpBytecodeGenerator::IfRegisterEqPos(int register_index,
Label* on_eq) {
DCHECK_LE(0, register_index);
DCHECK_GE(kMaxRegister, register_index);
Emit(BC_CHECK_REGISTER_EQ_POS, register_index);
EmitOrLink(on_eq);
}
Handle<HeapObject> RegExpBytecodeGenerator::GetCode(Handle<String> source) {
Bind(&backtrack_);
Emit(BC_POP_BT, 0);
Handle<ByteArray> array;
if (FLAG_regexp_peephole_optimization) {
array = RegExpBytecodePeepholeOptimization::OptimizeBytecode(
isolate_, zone(), source, buffer_.begin(), length(), jump_edges_);
} else {
array = isolate_->factory()->NewByteArray(length());
Copy(array->GetDataStartAddress());
}
return array;
}
int RegExpBytecodeGenerator::length() { return pc_; }
void RegExpBytecodeGenerator::Copy(byte* a) {
MemCopy(a, buffer_.begin(), length());
}
void RegExpBytecodeGenerator::Expand() {
Vector<byte> old_buffer = buffer_;
buffer_ = Vector<byte>::New(old_buffer.length() * 2);
MemCopy(buffer_.begin(), old_buffer.begin(), old_buffer.length());
old_buffer.Dispose();
}
} // namespace internal
} // namespace v8
+119
View File
@@ -0,0 +1,119 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_BYTECODE_GENERATOR_H_
#define V8_REGEXP_REGEXP_BYTECODE_GENERATOR_H_
#include "regexp/regexp-macro-assembler.h"
namespace v8 {
namespace internal {
// An assembler/generator for the Irregexp byte code.
class V8_EXPORT_PRIVATE RegExpBytecodeGenerator : public RegExpMacroAssembler {
public:
// Create an assembler. Instructions and relocation information are emitted
// into a buffer, with the instructions starting from the beginning and the
// relocation information starting from the end of the buffer. See CodeDesc
// for a detailed comment on the layout (globals.h).
//
// The assembler allocates and grows its own buffer, and buffer_size
// determines the initial buffer size. The buffer is owned by the assembler
// and deallocated upon destruction of the assembler.
RegExpBytecodeGenerator(Isolate* isolate, Zone* zone);
virtual ~RegExpBytecodeGenerator();
// The byte-code interpreter checks on each push anyway.
virtual int stack_limit_slack() { return 1; }
virtual bool CanReadUnaligned() { return false; }
virtual void Bind(Label* label);
virtual void AdvanceCurrentPosition(int by); // Signed cp change.
virtual void PopCurrentPosition();
virtual void PushCurrentPosition();
virtual void Backtrack();
virtual void GoTo(Label* label);
virtual void PushBacktrack(Label* label);
virtual bool Succeed();
virtual void Fail();
virtual void PopRegister(int register_index);
virtual void PushRegister(int register_index,
StackCheckFlag check_stack_limit);
virtual void AdvanceRegister(int reg, int by); // r[reg] += by.
virtual void SetCurrentPositionFromEnd(int by);
virtual void SetRegister(int register_index, int to);
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
virtual void ClearRegisters(int reg_from, int reg_to);
virtual void ReadCurrentPositionFromRegister(int reg);
virtual void WriteStackPointerToRegister(int reg);
virtual void ReadStackPointerFromRegister(int reg);
virtual void LoadCurrentCharacterImpl(int cp_offset, Label* on_end_of_input,
bool check_bounds, int characters,
int eats_at_least);
virtual void CheckCharacter(unsigned c, Label* on_equal);
virtual void CheckCharacterAfterAnd(unsigned c, unsigned mask,
Label* on_equal);
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
virtual void CheckAtStart(int cp_offset, Label* on_at_start);
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
virtual void CheckNotCharacterAfterAnd(unsigned c, unsigned mask,
Label* on_not_equal);
virtual void CheckNotCharacterAfterMinusAnd(uc16 c, uc16 minus, uc16 mask,
Label* on_not_equal);
virtual void CheckCharacterInRange(uc16 from, uc16 to, Label* on_in_range);
virtual void CheckCharacterNotInRange(uc16 from, uc16 to,
Label* on_not_in_range);
virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
virtual void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match);
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
bool read_backward,
Label* on_no_match);
virtual void IfRegisterLT(int register_index, int comparand, Label* if_lt);
virtual void IfRegisterGE(int register_index, int comparand, Label* if_ge);
virtual void IfRegisterEqPos(int register_index, Label* if_eq);
virtual IrregexpImplementation Implementation();
virtual Handle<HeapObject> GetCode(Handle<String> source);
private:
void Expand();
// Code and bitmap emission.
inline void EmitOrLink(Label* label);
inline void Emit32(uint32_t x);
inline void Emit16(uint32_t x);
inline void Emit8(uint32_t x);
inline void Emit(uint32_t bc, uint32_t arg);
// Bytecode buffer.
int length();
void Copy(byte* a);
// The buffer into which code and relocation info are generated.
Vector<byte> buffer_;
// The program counter.
int pc_;
Label backtrack_;
int advance_current_start_;
int advance_current_offset_;
int advance_current_end_;
// Stores jump edges emitted for the bytecode (used by
// RegExpBytecodePeepholeOptimization).
// Key: jump source (offset in buffer_ where jump destination is stored).
// Value: jump destination (offset in buffer_ to jump to).
ZoneUnorderedMap<int, int> jump_edges_;
Isolate* isolate_;
static const int kInvalidPC = -1;
DISALLOW_IMPLICIT_CONSTRUCTORS(RegExpBytecodeGenerator);
};
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_BYTECODE_GENERATOR_H_
File diff suppressed because it is too large. Load diff
+30
View File
@@ -0,0 +1,30 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
#define V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
class ByteArray;
// Peephole optimization for regexp interpreter bytecode.
// Pre-defined bytecode sequences occuring in the bytecode generated by the
// RegExpBytecodeGenerator can be optimized into a single bytecode.
class RegExpBytecodePeepholeOptimization : public AllStatic {
public:
// Performs peephole optimization on the given bytecode and returns the
// optimized bytecode.
static Handle<ByteArray> OptimizeBytecode(
Isolate* isolate, Zone* zone, Handle<String> source, const byte* bytecode,
int length, const ZoneUnorderedMap<int, int>& jump_edges);
};
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
+45
View File
@@ -0,0 +1,45 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "regexp/regexp-bytecodes.h"
#include <cctype>
namespace v8 {
namespace internal {
void RegExpBytecodeDisassembleSingle(const byte* code_base, const byte* pc) {
PrintF("%s", RegExpBytecodeName(*pc));
// Args and the bytecode as hex.
for (int i = 0; i < RegExpBytecodeLength(*pc); i++) {
PrintF(", %02x", pc[i]);
}
PrintF(" ");
// Args as ascii.
for (int i = 1; i < RegExpBytecodeLength(*pc); i++) {
unsigned char b = pc[i];
PrintF("%c", std::isprint(b) ? b : '.');
}
PrintF("\n");
}
void RegExpBytecodeDisassemble(const byte* code_base, int length,
const char* pattern) {
PrintF("[generated bytecode for regexp pattern: '%s']\n", pattern);
ptrdiff_t offset = 0;
while (offset < length) {
const byte* const pc = code_base + offset;
PrintF("%p %4" V8PRIxPTRDIFF " ", pc, offset);
RegExpBytecodeDisassembleSingle(code_base, pc);
offset += RegExpBytecodeLength(*pc);
}
}
} // namespace internal
} // namespace v8
+251
View File
@@ -0,0 +1,251 @@
// Copyright 2011 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_BYTECODES_H_
#define V8_REGEXP_REGEXP_BYTECODES_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
// Maximum number of bytecodes that will be used (next power of 2 of actually
// defined bytecodes).
// All slots between the last actually defined bytecode and maximum id will be
// filled with BREAKs, indicating an invalid operation. This way using
// BYTECODE_MASK guarantees no OOB access to the dispatch table.
constexpr int kRegExpPaddedBytecodeCount = 1 << 6;
constexpr int BYTECODE_MASK = kRegExpPaddedBytecodeCount - 1;
// The first argument is packed in with the byte code in one word, but so it
// has 24 bits, but it can be positive and negative so only use 23 bits for
// positive values.
const unsigned int MAX_FIRST_ARG = 0x7fffffu;
const int BYTECODE_SHIFT = 8;
STATIC_ASSERT(1 << BYTECODE_SHIFT > BYTECODE_MASK);
// TODO(pthier): Argument offsets of bytecodes should be easily accessible by
// name or at least by position.
#define BYTECODE_ITERATOR(V) \
V(BREAK, 0, 4) /* bc8 */ \
V(PUSH_CP, 1, 4) /* bc8 pad24 */ \
V(PUSH_BT, 2, 8) /* bc8 pad24 offset32 */ \
V(PUSH_REGISTER, 3, 4) /* bc8 reg_idx24 */ \
V(SET_REGISTER_TO_CP, 4, 8) /* bc8 reg_idx24 offset32 */ \
V(SET_CP_TO_REGISTER, 5, 4) /* bc8 reg_idx24 */ \
V(SET_REGISTER_TO_SP, 6, 4) /* bc8 reg_idx24 */ \
V(SET_SP_TO_REGISTER, 7, 4) /* bc8 reg_idx24 */ \
V(SET_REGISTER, 8, 8) /* bc8 reg_idx24 value32 */ \
V(ADVANCE_REGISTER, 9, 8) /* bc8 reg_idx24 value32 */ \
V(POP_CP, 10, 4) /* bc8 pad24 */ \
V(POP_BT, 11, 4) /* bc8 pad24 */ \
V(POP_REGISTER, 12, 4) /* bc8 reg_idx24 */ \
V(FAIL, 13, 4) /* bc8 pad24 */ \
V(SUCCEED, 14, 4) /* bc8 pad24 */ \
V(ADVANCE_CP, 15, 4) /* bc8 offset24 */ \
/* Jump to another bytecode given its offset. */ \
/* Bit Layout: */ \
/* 0x00 - 0x07: 0x10 (fixed) Bytecode */ \
/* 0x08 - 0x1F: 0x00 (unused) Padding */ \
/* 0x20 - 0x3F: Address of bytecode to jump to */ \
V(GOTO, 16, 8) /* bc8 pad24 addr32 */ \
/* Check if offset is in range and load character at given offset. */ \
/* Bit Layout: */ \
/* 0x00 - 0x07: 0x11 (fixed) Bytecode */ \
/* 0x08 - 0x1F: Offset from current position */ \
/* 0x20 - 0x3F: Address of bytecode when load is out of range */ \
V(LOAD_CURRENT_CHAR, 17, 8) /* bc8 offset24 addr32 */ \
/* Load character at given offset without range checks. */ \
/* Bit Layout: */ \
/* 0x00 - 0x07: 0x12 (fixed) Bytecode */ \
/* 0x08 - 0x1F: Offset from current position */ \
V(LOAD_CURRENT_CHAR_UNCHECKED, 18, 4) /* bc8 offset24 */ \
V(LOAD_2_CURRENT_CHARS, 19, 8) /* bc8 offset24 addr32 */ \
V(LOAD_2_CURRENT_CHARS_UNCHECKED, 20, 4) /* bc8 offset24 */ \
V(LOAD_4_CURRENT_CHARS, 21, 8) /* bc8 offset24 addr32 */ \
V(LOAD_4_CURRENT_CHARS_UNCHECKED, 22, 4) /* bc8 offset24 */ \
V(CHECK_4_CHARS, 23, 12) /* bc8 pad24 uint32 addr32 */ \
/* Check if current character is equal to a given character */ \
/* Bit Layout: */ \
/* 0x00 - 0x07: 0x19 (fixed) Bytecode */ \
/* 0x08 - 0x0F: 0x00 (unused) Padding */ \
/* 0x10 - 0x1F: Character to check */ \
/* 0x20 - 0x3F: Address of bytecode when matched */ \
V(CHECK_CHAR, 24, 8) /* bc8 pad8 uint16 addr32 */ \
V(CHECK_NOT_4_CHARS, 25, 12) /* bc8 pad24 uint32 addr32 */ \
V(CHECK_NOT_CHAR, 26, 8) /* bc8 pad8 uint16 addr32 */ \
V(AND_CHECK_4_CHARS, 27, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
/* Checks if the current character combined with mask (bitwise and) */ \
/* matches a character (e.g. used when two characters in a disjunction */ \
/* differ by only a single bit */ \
/* Bit Layout: */ \
/* 0x00 - 0x07: 0x1c (fixed) Bytecode */ \
/* 0x08 - 0x0F: 0x00 (unused) Padding */ \
/* 0x10 - 0x1F: Character to match against (after mask aplied) */ \
/* 0x20 - 0x3F: Bitmask bitwise and combined with current character */ \
/* 0x40 - 0x5F: Address of bytecode when matched */ \
V(AND_CHECK_CHAR, 28, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
V(AND_CHECK_NOT_4_CHARS, 29, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
V(AND_CHECK_NOT_CHAR, 30, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
V(MINUS_AND_CHECK_NOT_CHAR, 31, 12) /* bc8 pad8 uc16 uc16 uc16 addr32 */ \
V(CHECK_CHAR_IN_RANGE, 32, 12) /* bc8 pad24 uc16 uc16 addr32 */ \
V(CHECK_CHAR_NOT_IN_RANGE, 33, 12) /* bc8 pad24 uc16 uc16 addr32 */ \
/* Checks if the current character matches any of the characters encoded */ \
/* in a bit table. Similar to/inspired by boyer moore string search */ \
/* Bit Layout: */ \
/* 0x00 - 0x07: 0x22 (fixed) Bytecode */ \
/* 0x08 - 0x1F: 0x00 (unused) Padding */ \
/* 0x20 - 0x3F: Address of bytecode when bit is set */ \
/* 0x40 - 0xBF: Bit table */ \
V(CHECK_BIT_IN_TABLE, 34, 24) /* bc8 pad24 addr32 bits128 */ \
V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \
V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 39, 8) /* UNUSED */ \
V(CHECK_NOT_BACK_REF_BACKWARD, 40, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD, 41, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD, 42, 8) /* UNUSED */ \
V(CHECK_NOT_REGS_EQUAL, 43, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
V(CHECK_REGISTER_LT, 44, 12) /* bc8 reg_idx24 value32 addr32 */ \
V(CHECK_REGISTER_GE, 45, 12) /* bc8 reg_idx24 value32 addr32 */ \
V(CHECK_REGISTER_EQ_POS, 46, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_AT_START, 47, 8) /* bc8 pad24 addr32 */ \
V(CHECK_NOT_AT_START, 48, 8) /* bc8 offset24 addr32 */ \
/* Checks if the current position matches top of backtrack stack */ \
/* Bit Layout: */ \
/* 0x00 - 0x07: 0x31 (fixed) Bytecode */ \
/* 0x08 - 0x1F: 0x00 (unused) Padding */ \
/* 0x20 - 0x3F: Address of bytecode when current matches tos */ \
V(CHECK_GREEDY, 49, 8) /* bc8 pad24 addr32 */ \
/* Advance character pointer by given offset and jump to another bytecode.*/ \
/* Bit Layout: */ \
/* 0x00 - 0x07: 0x32 (fixed) Bytecode */ \
/* 0x08 - 0x1F: Number of characters to advance */ \
/* 0x20 - 0x3F: Address of bytecode to jump to */ \
V(ADVANCE_CP_AND_GOTO, 50, 8) /* bc8 offset24 addr32 */ \
V(SET_CURRENT_POSITION_FROM_END, 51, 4) /* bc8 idx24 */ \
/* Checks if current position + given offset is in range. */ \
/* Bit Layout: */ \
/* 0x00 - 0x07: 0x34 (fixed) Bytecode */ \
/* 0x08 - 0x1F: Offset from current position */ \
/* 0x20 - 0x3F: Address of bytecode when position is out of range */ \
V(CHECK_CURRENT_POSITION, 52, 8) /* bc8 idx24 addr32 */ \
/* Combination of: */ \
/* LOAD_CURRENT_CHAR, CHECK_BIT_IN_TABLE and ADVANCE_CP_AND_GOTO */ \
/* Emitted by RegExpBytecodePeepholeOptimization. */ \
/* Bit Layout: */ \
/* 0x00 - 0x07 0x35 (fixed) Bytecode */ \
/* 0x08 - 0x1F Load character offset from current position */ \
/* 0x20 - 0x3F Number of characters to advance */ \
/* 0x40 - 0xBF Bit Table */ \
/* 0xC0 - 0xDF Address of bytecode when character is matched */ \
/* 0xE0 - 0xFF Address of bytecode when no match */ \
V(SKIP_UNTIL_BIT_IN_TABLE, 53, 32) \
/* Combination of: */ \
/* CHECK_CURRENT_POSITION, LOAD_CURRENT_CHAR_UNCHECKED, AND_CHECK_CHAR */ \
/* and ADVANCE_CP_AND_GOTO */ \
/* Emitted by RegExpBytecodePeepholeOptimization. */ \
/* Bit Layout: */ \
/* 0x00 - 0x07 0x36 (fixed) Bytecode */ \
/* 0x08 - 0x1F Load character offset from current position */ \
/* 0x20 - 0x2F Number of characters to advance */ \
/* 0x30 - 0x3F Character to match against (after mask applied) */ \
/* 0x40 - 0x5F: Bitmask bitwise and combined with current character */ \
/* 0x60 - 0x7F Minimum number of characters this pattern consumes */ \
/* 0x80 - 0x9F Address of bytecode when character is matched */ \
/* 0xA0 - 0xBF Address of bytecode when no match */ \
V(SKIP_UNTIL_CHAR_AND, 54, 24) \
/* Combination of: */ \
/* LOAD_CURRENT_CHAR, CHECK_CHAR and ADVANCE_CP_AND_GOTO */ \
/* Emitted by RegExpBytecodePeepholeOptimization. */ \
/* Bit Layout: */ \
/* 0x00 - 0x07 0x37 (fixed) Bytecode */ \
/* 0x08 - 0x1F Load character offset from current position */ \
/* 0x20 - 0x2F Number of characters to advance */ \
/* 0x30 - 0x3F Character to match */ \
/* 0x40 - 0x5F Address of bytecode when character is matched */ \
/* 0x60 - 0x7F Address of bytecode when no match */ \
V(SKIP_UNTIL_CHAR, 55, 16) \
/* Combination of: */ \
/* CHECK_CURRENT_POSITION, LOAD_CURRENT_CHAR_UNCHECKED, CHECK_CHAR */ \
/* and ADVANCE_CP_AND_GOTO */ \
/* Emitted by RegExpBytecodePeepholeOptimization. */ \
/* Bit Layout: */ \
/* 0x00 - 0x07 0x38 (fixed) Bytecode */ \
/* 0x08 - 0x1F Load character offset from current position */ \
/* 0x20 - 0x2F Number of characters to advance */ \
/* 0x30 - 0x3F Character to match */ \
/* 0x40 - 0x5F Minimum number of characters this pattern consumes */ \
/* 0x60 - 0x7F Address of bytecode when character is matched */ \
/* 0x80 - 0x9F Address of bytecode when no match */ \
V(SKIP_UNTIL_CHAR_POS_CHECKED, 56, 20) \
/* Combination of: */ \
/* LOAD_CURRENT_CHAR, CHECK_CHAR, CHECK_CHAR and ADVANCE_CP_AND_GOTO */ \
/* Emitted by RegExpBytecodePeepholeOptimization. */ \
/* Bit Layout: */ \
/* 0x00 - 0x07 0x39 (fixed) Bytecode */ \
/* 0x08 - 0x1F Load character offset from current position */ \
/* 0x20 - 0x3F Number of characters to advance */ \
/* 0x40 - 0x4F Character to match */ \
/* 0x50 - 0x5F Other Character to match */ \
/* 0x60 - 0x7F Address of bytecode when either character is matched */ \
/* 0x80 - 0x9F Address of bytecode when no match */ \
V(SKIP_UNTIL_CHAR_OR_CHAR, 57, 20) \
/* Combination of: */ \
/* LOAD_CURRENT_CHAR, CHECK_GT, CHECK_BIT_IN_TABLE, GOTO and */ \
/* and ADVANCE_CP_AND_GOTO */ \
/* Emitted by RegExpBytecodePeepholeOptimization. */ \
/* Bit Layout: */ \
/* 0x00 - 0x07 0x3A (fixed) Bytecode */ \
/* 0x08 - 0x1F Load character offset from current position */ \
/* 0x20 - 0x2F Number of characters to advance */ \
/* 0x30 - 0x3F Character to check if it is less than current char */ \
/* 0x40 - 0xBF Bit Table */ \
/* 0xC0 - 0xDF Address of bytecode when character is matched */ \
/* 0xE0 - 0xFF Address of bytecode when no match */ \
V(SKIP_UNTIL_GT_OR_NOT_BIT_IN_TABLE, 58, 32)
#define COUNT(...) +1
static constexpr int kRegExpBytecodeCount = BYTECODE_ITERATOR(COUNT);
#undef COUNT
// Just making sure we assigned values above properly. They should be
// contiguous, strictly increasing, and start at 0.
// TODO(jgruber): Do not explicitly assign values, instead generate them
// implicitly from the list order.
STATIC_ASSERT(kRegExpBytecodeCount == 59);
#define DECLARE_BYTECODES(name, code, length) \
static constexpr int BC_##name = code;
BYTECODE_ITERATOR(DECLARE_BYTECODES)
#undef DECLARE_BYTECODES
static constexpr int kRegExpBytecodeLengths[] = {
#define DECLARE_BYTECODE_LENGTH(name, code, length) length,
BYTECODE_ITERATOR(DECLARE_BYTECODE_LENGTH)
#undef DECLARE_BYTECODE_LENGTH
};
inline constexpr int RegExpBytecodeLength(int bytecode) {
return kRegExpBytecodeLengths[bytecode];
}
static const char* const kRegExpBytecodeNames[] = {
#define DECLARE_BYTECODE_NAME(name, ...) #name,
BYTECODE_ITERATOR(DECLARE_BYTECODE_NAME)
#undef DECLARE_BYTECODE_NAME
};
inline const char* RegExpBytecodeName(int bytecode) {
return kRegExpBytecodeNames[bytecode];
}
void RegExpBytecodeDisassembleSingle(const byte* code_base, const byte* pc);
void RegExpBytecodeDisassemble(const byte* code_base, int length,
const char* pattern);
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_BYTECODES_H_
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+621
View File
@@ -0,0 +1,621 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_COMPILER_H_
#define V8_REGEXP_REGEXP_COMPILER_H_
#include <bitset>
#include "regexp/regexp-nodes.h"
namespace v8 {
namespace internal {
class DynamicBitSet;
class Isolate;
namespace regexp_compiler_constants {
// The '2' variant is has inclusive from and exclusive to.
// This covers \s as defined in ECMA-262 5.1, 15.10.2.12,
// which include WhiteSpace (7.2) or LineTerminator (7.3) values.
constexpr uc32 kRangeEndMarker = 0x110000;
constexpr int kSpaceRanges[] = {
'\t', '\r' + 1, ' ', ' ' + 1, 0x00A0, 0x00A1, 0x1680,
0x1681, 0x2000, 0x200B, 0x2028, 0x202A, 0x202F, 0x2030,
0x205F, 0x2060, 0x3000, 0x3001, 0xFEFF, 0xFF00, kRangeEndMarker};
constexpr int kSpaceRangeCount = arraysize(kSpaceRanges);
constexpr int kWordRanges[] = {'0', '9' + 1, 'A', 'Z' + 1, '_',
'_' + 1, 'a', 'z' + 1, kRangeEndMarker};
constexpr int kWordRangeCount = arraysize(kWordRanges);
constexpr int kDigitRanges[] = {'0', '9' + 1, kRangeEndMarker};
constexpr int kDigitRangeCount = arraysize(kDigitRanges);
constexpr int kSurrogateRanges[] = {kLeadSurrogateStart,
kLeadSurrogateStart + 1, kRangeEndMarker};
constexpr int kSurrogateRangeCount = arraysize(kSurrogateRanges);
constexpr int kLineTerminatorRanges[] = {0x000A, 0x000B, 0x000D, 0x000E,
0x2028, 0x202A, kRangeEndMarker};
constexpr int kLineTerminatorRangeCount = arraysize(kLineTerminatorRanges);
// More makes code generation slower, less makes V8 benchmark score lower.
constexpr int kMaxLookaheadForBoyerMoore = 8;
// In a 3-character pattern you can maximally step forwards 3 characters
// at a time, which is not always enough to pay for the extra logic.
constexpr int kPatternTooShortForBoyerMoore = 2;
} // namespace regexp_compiler_constants
inline bool IgnoreCase(JSRegExp::Flags flags) {
return (flags & JSRegExp::kIgnoreCase) != 0;
}
inline bool IsUnicode(JSRegExp::Flags flags) {
return (flags & JSRegExp::kUnicode) != 0;
}
inline bool IsSticky(JSRegExp::Flags flags) {
return (flags & JSRegExp::kSticky) != 0;
}
inline bool IsGlobal(JSRegExp::Flags flags) {
return (flags & JSRegExp::kGlobal) != 0;
}
inline bool DotAll(JSRegExp::Flags flags) {
return (flags & JSRegExp::kDotAll) != 0;
}
inline bool Multiline(JSRegExp::Flags flags) {
return (flags & JSRegExp::kMultiline) != 0;
}
inline bool NeedsUnicodeCaseEquivalents(JSRegExp::Flags flags) {
// Both unicode and ignore_case flags are set. We need to use ICU to find
// the closure over case equivalents.
return IsUnicode(flags) && IgnoreCase(flags);
}
// Details of a quick mask-compare check that can look ahead in the
// input stream.
class QuickCheckDetails {
public:
QuickCheckDetails()
: characters_(0), mask_(0), value_(0), cannot_match_(false) {}
explicit QuickCheckDetails(int characters)
: characters_(characters), mask_(0), value_(0), cannot_match_(false) {}
bool Rationalize(bool one_byte);
// Merge in the information from another branch of an alternation.
void Merge(QuickCheckDetails* other, int from_index);
// Advance the current position by some amount.
void Advance(int by, bool one_byte);
void Clear();
bool cannot_match() { return cannot_match_; }
void set_cannot_match() { cannot_match_ = true; }
struct Position {
Position() : mask(0), value(0), determines_perfectly(false) {}
uc16 mask;
uc16 value;
bool determines_perfectly;
};
int characters() { return characters_; }
void set_characters(int characters) { characters_ = characters; }
Position* positions(int index) {
DCHECK_LE(0, index);
DCHECK_GT(characters_, index);
return positions_ + index;
}
uint32_t mask() { return mask_; }
uint32_t value() { return value_; }
private:
// How many characters do we have quick check information from. This is
// the same for all branches of a choice node.
int characters_;
Position positions_[4];
// These values are the condensate of the above array after Rationalize().
uint32_t mask_;
uint32_t value_;
// If set to true, there is no way this quick check can match at all.
// E.g., if it requires to be at the start of the input, and isn't.
bool cannot_match_;
};
// Improve the speed that we scan for an initial point where a non-anchored
// regexp can match by using a Boyer-Moore-like table. This is done by
// identifying non-greedy non-capturing loops in the nodes that eat any
// character one at a time. For example in the middle of the regexp
// /foo[\s\S]*?bar/ we find such a loop. There is also such a loop implicitly
// inserted at the start of any non-anchored regexp.
//
// When we have found such a loop we look ahead in the nodes to find the set of
// characters that can come at given distances. For example for the regexp
// /.?foo/ we know that there are at least 3 characters ahead of us, and the
// sets of characters that can occur are [any, [f, o], [o]]. We find a range in
// the lookahead info where the set of characters is reasonably constrained. In
// our example this is from index 1 to 2 (0 is not constrained). We can now
// look 3 characters ahead and if we don't find one of [f, o] (the union of
// [f, o] and [o]) then we can skip forwards by the range size (in this case 2).
//
// For Unicode input strings we do the same, but modulo 128.
//
// We also look at the first string fed to the regexp and use that to get a hint
// of the character frequencies in the inputs. This affects the assessment of
// whether the set of characters is 'reasonably constrained'.
//
// We also have another lookahead mechanism (called quick check in the code),
// which uses a wide load of multiple characters followed by a mask and compare
// to determine whether a match is possible at this point.
enum ContainedInLattice {
kNotYet = 0,
kLatticeIn = 1,
kLatticeOut = 2,
kLatticeUnknown = 3 // Can also mean both in and out.
};
inline ContainedInLattice Combine(ContainedInLattice a, ContainedInLattice b) {
return static_cast<ContainedInLattice>(a | b);
}
class BoyerMoorePositionInfo : public ZoneObject {
public:
bool at(int i) const { return map_[i]; }
static constexpr int kMapSize = 128;
static constexpr int kMask = kMapSize - 1;
int map_count() const { return map_count_; }
void Set(int character);
void SetInterval(const Interval& interval);
void SetAll();
bool is_non_word() { return w_ == kLatticeOut; }
bool is_word() { return w_ == kLatticeIn; }
using Bitset = std::bitset<kMapSize>;
Bitset raw_bitset() const { return map_; }
private:
Bitset map_;
int map_count_ = 0; // Number of set bits in the map.
ContainedInLattice w_ = kNotYet; // The \w character class.
};
class BoyerMooreLookahead : public ZoneObject {
public:
BoyerMooreLookahead(int length, RegExpCompiler* compiler, Zone* zone);
int length() { return length_; }
int max_char() { return max_char_; }
RegExpCompiler* compiler() { return compiler_; }
int Count(int map_number) { return bitmaps_->at(map_number)->map_count(); }
BoyerMoorePositionInfo* at(int i) { return bitmaps_->at(i); }
void Set(int map_number, int character) {
if (character > max_char_) return;
BoyerMoorePositionInfo* info = bitmaps_->at(map_number);
info->Set(character);
}
void SetInterval(int map_number, const Interval& interval) {
if (interval.from() > max_char_) return;
BoyerMoorePositionInfo* info = bitmaps_->at(map_number);
if (interval.to() > max_char_) {
info->SetInterval(Interval(interval.from(), max_char_));
} else {
info->SetInterval(interval);
}
}
void SetAll(int map_number) { bitmaps_->at(map_number)->SetAll(); }
void SetRest(int from_map) {
for (int i = from_map; i < length_; i++) SetAll(i);
}
void EmitSkipInstructions(RegExpMacroAssembler* masm);
private:
// This is the value obtained by EatsAtLeast. If we do not have at least this
// many characters left in the sample string then the match is bound to fail.
// Therefore it is OK to read a character this far ahead of the current match
// point.
int length_;
RegExpCompiler* compiler_;
// 0xff for Latin1, 0xffff for UTF-16.
int max_char_;
ZoneList<BoyerMoorePositionInfo*>* bitmaps_;
int GetSkipTable(int min_lookahead, int max_lookahead,
Handle<ByteArray> boolean_skip_table);
bool FindWorthwhileInterval(int* from, int* to);
int FindBestInterval(int max_number_of_chars, int old_biggest_points,
int* from, int* to);
};
// There are many ways to generate code for a node. This class encapsulates
// the current way we should be generating. In other words it encapsulates
// the current state of the code generator. The effect of this is that we
// generate code for paths that the matcher can take through the regular
// expression. A given node in the regexp can be code-generated several times
// as it can be part of several traces. For example for the regexp:
// /foo(bar|ip)baz/ the code to match baz will be generated twice, once as part
// of the foo-bar-baz trace and once as part of the foo-ip-baz trace. The code
// to match foo is generated only once (the traces have a common prefix). The
// code to store the capture is deferred and generated (twice) after the places
// where baz has been matched.
class Trace {
public:
// A value for a property that is either known to be true, know to be false,
// or not known.
enum TriBool { UNKNOWN = -1, FALSE_VALUE = 0, TRUE_VALUE = 1 };
class DeferredAction {
public:
DeferredAction(ActionNode::ActionType action_type, int reg)
: action_type_(action_type), reg_(reg), next_(nullptr) {}
DeferredAction* next() { return next_; }
bool Mentions(int reg);
int reg() { return reg_; }
ActionNode::ActionType action_type() { return action_type_; }
private:
ActionNode::ActionType action_type_;
int reg_;
DeferredAction* next_;
friend class Trace;
};
class DeferredCapture : public DeferredAction {
public:
DeferredCapture(int reg, bool is_capture, Trace* trace)
: DeferredAction(ActionNode::STORE_POSITION, reg),
cp_offset_(trace->cp_offset()),
is_capture_(is_capture) {}
int cp_offset() { return cp_offset_; }
bool is_capture() { return is_capture_; }
private:
int cp_offset_;
bool is_capture_;
void set_cp_offset(int cp_offset) { cp_offset_ = cp_offset; }
};
class DeferredSetRegisterForLoop : public DeferredAction {
public:
DeferredSetRegisterForLoop(int reg, int value)
: DeferredAction(ActionNode::SET_REGISTER_FOR_LOOP, reg),
value_(value) {}
int value() { return value_; }
private:
int value_;
};
class DeferredClearCaptures : public DeferredAction {
public:
explicit DeferredClearCaptures(Interval range)
: DeferredAction(ActionNode::CLEAR_CAPTURES, -1), range_(range) {}
Interval range() { return range_; }
private:
Interval range_;
};
class DeferredIncrementRegister : public DeferredAction {
public:
explicit DeferredIncrementRegister(int reg)
: DeferredAction(ActionNode::INCREMENT_REGISTER, reg) {}
};
Trace()
: cp_offset_(0),
actions_(nullptr),
backtrack_(nullptr),
stop_node_(nullptr),
loop_label_(nullptr),
characters_preloaded_(0),
bound_checked_up_to_(0),
flush_budget_(100),
at_start_(UNKNOWN) {}
// End the trace. This involves flushing the deferred actions in the trace
// and pushing a backtrack location onto the backtrack stack. Once this is
// done we can start a new trace or go to one that has already been
// generated.
void Flush(RegExpCompiler* compiler, RegExpNode* successor);
int cp_offset() { return cp_offset_; }
DeferredAction* actions() { return actions_; }
// A trivial trace is one that has no deferred actions or other state that
// affects the assumptions used when generating code. There is no recorded
// backtrack location in a trivial trace, so with a trivial trace we will
// generate code that, on a failure to match, gets the backtrack location
// from the backtrack stack rather than using a direct jump instruction. We
// always start code generation with a trivial trace and non-trivial traces
// are created as we emit code for nodes or add to the list of deferred
// actions in the trace. The location of the code generated for a node using
// a trivial trace is recorded in a label in the node so that gotos can be
// generated to that code.
bool is_trivial() {
return backtrack_ == nullptr && actions_ == nullptr && cp_offset_ == 0 &&
characters_preloaded_ == 0 && bound_checked_up_to_ == 0 &&
quick_check_performed_.characters() == 0 && at_start_ == UNKNOWN;
}
TriBool at_start() { return at_start_; }
void set_at_start(TriBool at_start) { at_start_ = at_start; }
Label* backtrack() { return backtrack_; }
Label* loop_label() { return loop_label_; }
RegExpNode* stop_node() { return stop_node_; }
int characters_preloaded() { return characters_preloaded_; }
int bound_checked_up_to() { return bound_checked_up_to_; }
int flush_budget() { return flush_budget_; }
QuickCheckDetails* quick_check_performed() { return &quick_check_performed_; }
bool mentions_reg(int reg);
// Returns true if a deferred position store exists to the specified
// register and stores the offset in the out-parameter. Otherwise
// returns false.
bool GetStoredPosition(int reg, int* cp_offset);
// These set methods and AdvanceCurrentPositionInTrace should be used only on
// new traces - the intention is that traces are immutable after creation.
void add_action(DeferredAction* new_action) {
DCHECK(new_action->next_ == nullptr);
new_action->next_ = actions_;
actions_ = new_action;
}
void set_backtrack(Label* backtrack) { backtrack_ = backtrack; }
void set_stop_node(RegExpNode* node) { stop_node_ = node; }
void set_loop_label(Label* label) { loop_label_ = label; }
void set_characters_preloaded(int count) { characters_preloaded_ = count; }
void set_bound_checked_up_to(int to) { bound_checked_up_to_ = to; }
void set_flush_budget(int to) { flush_budget_ = to; }
void set_quick_check_performed(QuickCheckDetails* d) {
quick_check_performed_ = *d;
}
void InvalidateCurrentCharacter();
void AdvanceCurrentPositionInTrace(int by, RegExpCompiler* compiler);
private:
int FindAffectedRegisters(DynamicBitSet* affected_registers, Zone* zone);
void PerformDeferredActions(RegExpMacroAssembler* macro, int max_register,
const DynamicBitSet& affected_registers,
DynamicBitSet* registers_to_pop,
DynamicBitSet* registers_to_clear, Zone* zone);
void RestoreAffectedRegisters(RegExpMacroAssembler* macro, int max_register,
const DynamicBitSet& registers_to_pop,
const DynamicBitSet& registers_to_clear);
int cp_offset_;
DeferredAction* actions_;
Label* backtrack_;
RegExpNode* stop_node_;
Label* loop_label_;
int characters_preloaded_;
int bound_checked_up_to_;
QuickCheckDetails quick_check_performed_;
int flush_budget_;
TriBool at_start_;
};
class GreedyLoopState {
public:
explicit GreedyLoopState(bool not_at_start);
Label* label() { return &label_; }
Trace* counter_backtrack_trace() { return &counter_backtrack_trace_; }
private:
Label label_;
Trace counter_backtrack_trace_;
};
struct PreloadState {
static const int kEatsAtLeastNotYetInitialized = -1;
bool preload_is_current_;
bool preload_has_checked_bounds_;
int preload_characters_;
int eats_at_least_;
void init() { eats_at_least_ = kEatsAtLeastNotYetInitialized; }
};
// Analysis performs assertion propagation and computes eats_at_least_ values.
// See the comments on AssertionPropagator and EatsAtLeastPropagator for more
// details.
RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
class FrequencyCollator {
public:
FrequencyCollator() : total_samples_(0) {
for (int i = 0; i < RegExpMacroAssembler::kTableSize; i++) {
frequencies_[i] = CharacterFrequency(i);
}
}
void CountCharacter(int character) {
int index = (character & RegExpMacroAssembler::kTableMask);
frequencies_[index].Increment();
total_samples_++;
}
// Does not measure in percent, but rather per-128 (the table size from the
// regexp macro assembler).
int Frequency(int in_character) {
DCHECK((in_character & RegExpMacroAssembler::kTableMask) == in_character);
if (total_samples_ < 1) return 1; // Division by zero.
int freq_in_per128 =
(frequencies_[in_character].counter() * 128) / total_samples_;
return freq_in_per128;
}
private:
class CharacterFrequency {
public:
CharacterFrequency() : counter_(0), character_(-1) {}
explicit CharacterFrequency(int character)
: counter_(0), character_(character) {}
void Increment() { counter_++; }
int counter() { return counter_; }
int character() { return character_; }
private:
int counter_;
int character_;
};
private:
CharacterFrequency frequencies_[RegExpMacroAssembler::kTableSize];
int total_samples_;
};
class RegExpCompiler {
public:
RegExpCompiler(Isolate* isolate, Zone* zone, int capture_count,
bool is_one_byte);
int AllocateRegister() {
if (next_register_ >= RegExpMacroAssembler::kMaxRegister) {
reg_exp_too_big_ = true;
return next_register_;
}
return next_register_++;
}
// Lookarounds to match lone surrogates for unicode character class matches
// are never nested. We can therefore reuse registers.
int UnicodeLookaroundStackRegister() {
if (unicode_lookaround_stack_register_ == kNoRegister) {
unicode_lookaround_stack_register_ = AllocateRegister();
}
return unicode_lookaround_stack_register_;
}
int UnicodeLookaroundPositionRegister() {
if (unicode_lookaround_position_register_ == kNoRegister) {
unicode_lookaround_position_register_ = AllocateRegister();
}
return unicode_lookaround_position_register_;
}
struct CompilationResult final {
explicit CompilationResult(RegExpError err) : error(err) {}
CompilationResult(Object code, int registers)
: code(code), num_registers(registers) {}
static CompilationResult RegExpTooBig() {
return CompilationResult(RegExpError::kTooLarge);
}
bool Succeeded() const { return error == RegExpError::kNone; }
const RegExpError error = RegExpError::kNone;
Object code;
int num_registers = 0;
};
CompilationResult Assemble(Isolate* isolate, RegExpMacroAssembler* assembler,
RegExpNode* start, int capture_count,
Handle<String> pattern);
// If the regexp matching starts within a surrogate pair, step back to the
// lead surrogate and start matching from there.
static RegExpNode* OptionallyStepBackToLeadSurrogate(RegExpCompiler* compiler,
RegExpNode* on_success,
JSRegExp::Flags flags);
inline void AddWork(RegExpNode* node) {
if (!node->on_work_list() && !node->label()->is_bound()) {
node->set_on_work_list(true);
work_list_->push_back(node);
}
}
static const int kImplementationOffset = 0;
static const int kNumberOfRegistersOffset = 0;
static const int kCodeOffset = 1;
RegExpMacroAssembler* macro_assembler() { return macro_assembler_; }
EndNode* accept() { return accept_; }
static const int kMaxRecursion = 100;
inline int recursion_depth() { return recursion_depth_; }
inline void IncrementRecursionDepth() { recursion_depth_++; }
inline void DecrementRecursionDepth() { recursion_depth_--; }
void SetRegExpTooBig() { reg_exp_too_big_ = true; }
inline bool one_byte() { return one_byte_; }
inline bool optimize() { return optimize_; }
inline void set_optimize(bool value) { optimize_ = value; }
inline bool limiting_recursion() { return limiting_recursion_; }
inline void set_limiting_recursion(bool value) {
limiting_recursion_ = value;
}
bool read_backward() { return read_backward_; }
void set_read_backward(bool value) { read_backward_ = value; }
FrequencyCollator* frequency_collator() { return &frequency_collator_; }
int current_expansion_factor() { return current_expansion_factor_; }
void set_current_expansion_factor(int value) {
current_expansion_factor_ = value;
}
Isolate* isolate() const { return isolate_; }
Zone* zone() const { return zone_; }
static const int kNoRegister = -1;
private:
EndNode* accept_;
int next_register_;
int unicode_lookaround_stack_register_;
int unicode_lookaround_position_register_;
ZoneVector<RegExpNode*>* work_list_;
int recursion_depth_;
RegExpMacroAssembler* macro_assembler_;
bool one_byte_;
bool reg_exp_too_big_;
bool limiting_recursion_;
bool optimize_;
bool read_backward_;
int current_expansion_factor_;
FrequencyCollator frequency_collator_;
Isolate* isolate_;
Zone* zone_;
};
// Categorizes character ranges into BMP, non-BMP, lead, and trail surrogates.
class UnicodeRangeSplitter {
public:
V8_EXPORT_PRIVATE UnicodeRangeSplitter(ZoneList<CharacterRange>* base);
static constexpr int kInitialSize = 8;
using CharacterRangeVector = base::SmallVector<CharacterRange, kInitialSize>;
const CharacterRangeVector* bmp() const { return &bmp_; }
const CharacterRangeVector* lead_surrogates() const {
return &lead_surrogates_;
}
const CharacterRangeVector* trail_surrogates() const {
return &trail_surrogates_;
}
const CharacterRangeVector* non_bmp() const { return &non_bmp_; }
private:
void AddRange(CharacterRange range);
CharacterRangeVector bmp_;
CharacterRangeVector lead_surrogates_;
CharacterRangeVector trail_surrogates_;
CharacterRangeVector non_bmp_;
};
// We need to check for the following characters: 0x39C 0x3BC 0x178.
// TODO(jgruber): Move to CharacterRange.
bool RangeContainsLatin1Equivalents(CharacterRange range);
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_COMPILER_H_
+252
View File
@@ -0,0 +1,252 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "regexp/regexp-dotprinter.h"
#include "regexp/regexp-compiler.h"
namespace v8 {
namespace internal {
// -------------------------------------------------------------------
// Dot/dotty output
#ifdef DEBUG
class DotPrinterImpl : public NodeVisitor {
public:
explicit DotPrinterImpl(std::ostream& os) : os_(os) {}
void PrintNode(const char* label, RegExpNode* node);
void Visit(RegExpNode* node);
void PrintAttributes(RegExpNode* from);
void PrintOnFailure(RegExpNode* from, RegExpNode* to);
#define DECLARE_VISIT(Type) virtual void Visit##Type(Type##Node* that);
FOR_EACH_NODE_TYPE(DECLARE_VISIT)
#undef DECLARE_VISIT
private:
std::ostream& os_;
};
void DotPrinterImpl::PrintNode(const char* label, RegExpNode* node) {
os_ << "digraph G {\n graph [label=\"";
for (int i = 0; label[i]; i++) {
switch (label[i]) {
case '\\':
os_ << "\\\\";
break;
case '"':
os_ << "\"";
break;
default:
os_ << label[i];
break;
}
}
os_ << "\"];\n";
Visit(node);
os_ << "}" << std::endl;
}
void DotPrinterImpl::Visit(RegExpNode* node) {
if (node->info()->visited) return;
node->info()->visited = true;
node->Accept(this);
}
void DotPrinterImpl::PrintOnFailure(RegExpNode* from, RegExpNode* on_failure) {
os_ << " n" << from << " -> n" << on_failure << " [style=dotted];\n";
Visit(on_failure);
}
class AttributePrinter {
public:
explicit AttributePrinter(std::ostream& os) // NOLINT
: os_(os), first_(true) {}
void PrintSeparator() {
if (first_) {
first_ = false;
} else {
os_ << "|";
}
}
void PrintBit(const char* name, bool value) {
if (!value) return;
PrintSeparator();
os_ << "{" << name << "}";
}
void PrintPositive(const char* name, int value) {
if (value < 0) return;
PrintSeparator();
os_ << "{" << name << "|" << value << "}";
}
private:
std::ostream& os_;
bool first_;
};
void DotPrinterImpl::PrintAttributes(RegExpNode* that) {
os_ << " a" << that << " [shape=Mrecord, color=grey, fontcolor=grey, "
<< "margin=0.1, fontsize=10, label=\"{";
AttributePrinter printer(os_);
NodeInfo* info = that->info();
printer.PrintBit("NI", info->follows_newline_interest);
printer.PrintBit("WI", info->follows_word_interest);
printer.PrintBit("SI", info->follows_start_interest);
Label* label = that->label();
if (label->is_bound()) printer.PrintPositive("@", label->pos());
os_ << "}\"];\n"
<< " a" << that << " -> n" << that
<< " [style=dashed, color=grey, arrowhead=none];\n";
}
void DotPrinterImpl::VisitChoice(ChoiceNode* that) {
os_ << " n" << that << " [shape=Mrecord, label=\"?\"];\n";
for (int i = 0; i < that->alternatives()->length(); i++) {
GuardedAlternative alt = that->alternatives()->at(i);
os_ << " n" << that << " -> n" << alt.node();
}
for (int i = 0; i < that->alternatives()->length(); i++) {
GuardedAlternative alt = that->alternatives()->at(i);
alt.node()->Accept(this);
}
}
void DotPrinterImpl::VisitLoopChoice(LoopChoiceNode* that) {
VisitChoice(that);
}
void DotPrinterImpl::VisitNegativeLookaroundChoice(
NegativeLookaroundChoiceNode* that) {
VisitChoice(that);
}
void DotPrinterImpl::VisitText(TextNode* that) {
Zone* zone = that->zone();
os_ << " n" << that << " [label=\"";
for (int i = 0; i < that->elements()->length(); i++) {
if (i > 0) os_ << " ";
TextElement elm = that->elements()->at(i);
switch (elm.text_type()) {
case TextElement::ATOM: {
Vector<const uc16> data = elm.atom()->data();
for (int i = 0; i < data.length(); i++) {
os_ << static_cast<char>(data[i]);
}
break;
}
case TextElement::CHAR_CLASS: {
RegExpCharacterClass* node = elm.char_class();
os_ << "[";
if (node->is_negated()) os_ << "^";
for (int j = 0; j < node->ranges(zone)->length(); j++) {
CharacterRange range = node->ranges(zone)->at(j);
os_ << AsUC16(range.from()) << "-" << AsUC16(range.to());
}
os_ << "]";
break;
}
default:
UNREACHABLE();
}
}
os_ << "\", shape=box, peripheries=2];\n";
PrintAttributes(that);
os_ << " n" << that << " -> n" << that->on_success() << ";\n";
Visit(that->on_success());
}
void DotPrinterImpl::VisitBackReference(BackReferenceNode* that) {
os_ << " n" << that << " [label=\"$" << that->start_register() << "..$"
<< that->end_register() << "\", shape=doubleoctagon];\n";
PrintAttributes(that);
os_ << " n" << that << " -> n" << that->on_success() << ";\n";
Visit(that->on_success());
}
void DotPrinterImpl::VisitEnd(EndNode* that) {
os_ << " n" << that << " [style=bold, shape=point];\n";
PrintAttributes(that);
}
void DotPrinterImpl::VisitAssertion(AssertionNode* that) {
os_ << " n" << that << " [";
switch (that->assertion_type()) {
case AssertionNode::AT_END:
os_ << "label=\"$\", shape=septagon";
break;
case AssertionNode::AT_START:
os_ << "label=\"^\", shape=septagon";
break;
case AssertionNode::AT_BOUNDARY:
os_ << "label=\"\\b\", shape=septagon";
break;
case AssertionNode::AT_NON_BOUNDARY:
os_ << "label=\"\\B\", shape=septagon";
break;
case AssertionNode::AFTER_NEWLINE:
os_ << "label=\"(?<=\\n)\", shape=septagon";
break;
}
os_ << "];\n";
PrintAttributes(that);
RegExpNode* successor = that->on_success();
os_ << " n" << that << " -> n" << successor << ";\n";
Visit(successor);
}
void DotPrinterImpl::VisitAction(ActionNode* that) {
os_ << " n" << that << " [";
switch (that->action_type_) {
case ActionNode::SET_REGISTER_FOR_LOOP:
os_ << "label=\"$" << that->data_.u_store_register.reg
<< ":=" << that->data_.u_store_register.value << "\", shape=octagon";
break;
case ActionNode::INCREMENT_REGISTER:
os_ << "label=\"$" << that->data_.u_increment_register.reg
<< "++\", shape=octagon";
break;
case ActionNode::STORE_POSITION:
os_ << "label=\"$" << that->data_.u_position_register.reg
<< ":=$pos\", shape=octagon";
break;
case ActionNode::BEGIN_SUBMATCH:
os_ << "label=\"$" << that->data_.u_submatch.current_position_register
<< ":=$pos,begin\", shape=septagon";
break;
case ActionNode::POSITIVE_SUBMATCH_SUCCESS:
os_ << "label=\"escape\", shape=septagon";
break;
case ActionNode::EMPTY_MATCH_CHECK:
os_ << "label=\"$" << that->data_.u_empty_match_check.start_register
<< "=$pos?,$" << that->data_.u_empty_match_check.repetition_register
<< "<" << that->data_.u_empty_match_check.repetition_limit
<< "?\", shape=septagon";
break;
case ActionNode::CLEAR_CAPTURES: {
os_ << "label=\"clear $" << that->data_.u_clear_captures.range_from
<< " to $" << that->data_.u_clear_captures.range_to
<< "\", shape=septagon";
break;
}
}
os_ << "];\n";
PrintAttributes(that);
RegExpNode* successor = that->on_success();
os_ << " n" << that << " -> n" << successor << ";\n";
Visit(successor);
}
#endif // DEBUG
void DotPrinter::DotPrint(const char* label, RegExpNode* node) {
#ifdef DEBUG
StdoutStream os;
DotPrinterImpl printer(os);
printer.PrintNode(label, node);
#endif // DEBUG
}
} // namespace internal
} // namespace v8
+23
View File
@@ -0,0 +1,23 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_DOTPRINTER_H_
#define V8_REGEXP_REGEXP_DOTPRINTER_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
class RegExpNode;
class DotPrinter final : public AllStatic {
public:
static void DotPrint(const char* label, RegExpNode* node);
};
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_DOTPRINTER_H_
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "regexp/regexp-error.h"
namespace v8 {
namespace internal {
const char* kRegExpErrorStrings[] = {
#define TEMPLATE(NAME, STRING) STRING,
REGEXP_ERROR_MESSAGES(TEMPLATE)
#undef TEMPLATE
};
const char* RegExpErrorString(RegExpError error) {
DCHECK_LT(error, RegExpError::NumErrors);
return kRegExpErrorStrings[static_cast<int>(error)];
}
} // namespace internal
} // namespace v8
+57
View File
@@ -0,0 +1,57 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_ERROR_H_
#define V8_REGEXP_REGEXP_ERROR_H_
#include "regexp-shim.h"
namespace v8 {
namespace internal {
#define REGEXP_ERROR_MESSAGES(T) \
T(None, "") \
T(StackOverflow, "Maximum call stack size exceeded") \
T(AnalysisStackOverflow, "Stack overflow") \
T(TooLarge, "Regular expression too large") \
T(UnterminatedGroup, "Unterminated group") \
T(UnmatchedParen, "Unmatched ')'") \
T(EscapeAtEndOfPattern, "\\ at end of pattern") \
T(InvalidPropertyName, "Invalid property name") \
T(InvalidEscape, "Invalid escape") \
T(InvalidDecimalEscape, "Invalid decimal escape") \
T(InvalidUnicodeEscape, "Invalid Unicode escape") \
T(NothingToRepeat, "Nothing to repeat") \
T(LoneQuantifierBrackets, "Lone quantifier brackets") \
T(RangeOutOfOrder, "numbers out of order in {} quantifier") \
T(IncompleteQuantifier, "Incomplete quantifier") \
T(InvalidQuantifier, "Invalid quantifier") \
T(InvalidGroup, "Invalid group") \
T(MultipleFlagDashes, "Multiple dashes in flag group") \
T(RepeatedFlag, "Repeated flag in flag group") \
T(InvalidFlagGroup, "Invalid flag group") \
T(TooManyCaptures, "Too many captures") \
T(InvalidCaptureGroupName, "Invalid capture group name") \
T(DuplicateCaptureGroupName, "Duplicate capture group name") \
T(InvalidNamedReference, "Invalid named reference") \
T(InvalidNamedCaptureReference, "Invalid named capture referenced") \
T(InvalidClassEscape, "Invalid class escape") \
T(InvalidClassPropertyName, "Invalid property name in character class") \
T(InvalidCharacterClass, "Invalid character class") \
T(UnterminatedCharacterClass, "Unterminated character class") \
T(OutOfOrderCharacterClass, "Range out of order in character class")
enum class RegExpError : uint32_t {
#define TEMPLATE(NAME, STRING) k##NAME,
REGEXP_ERROR_MESSAGES(TEMPLATE)
#undef TEMPLATE
NumErrors
};
V8_EXPORT_PRIVATE const char* RegExpErrorString(RegExpError error);
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_ERROR_H_
File diff suppressed because it is too large. Load diff
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2011 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// A simple interpreter for the Irregexp byte code.
#ifndef V8_REGEXP_REGEXP_INTERPRETER_H_
#define V8_REGEXP_REGEXP_INTERPRETER_H_
#include "regexp/regexp.h"
namespace v8 {
namespace internal {
class V8_EXPORT_PRIVATE IrregexpInterpreter : public AllStatic {
public:
enum Result {
FAILURE = RegExp::kInternalRegExpFailure,
SUCCESS = RegExp::kInternalRegExpSuccess,
EXCEPTION = RegExp::kInternalRegExpException,
RETRY = RegExp::kInternalRegExpRetry,
};
// In case a StackOverflow occurs, a StackOverflowException is created and
// EXCEPTION is returned.
static Result MatchForCallFromRuntime(Isolate* isolate,
Handle<JSRegExp> regexp,
Handle<String> subject_string,
int* registers, int registers_length,
int start_position);
// In case a StackOverflow occurs, EXCEPTION is returned. The caller is
// responsible for creating the exception.
// RETRY is returned if a retry through the runtime is needed (e.g. when
// interrupts have been scheduled or the regexp is marked for tier-up).
// Arguments input_start, input_end and backtrack_stack are
// unused. They are only passed to match the signature of the native irregex
// code.
static Result MatchForCallFromJs(Address subject, int32_t start_position,
Address input_start, Address input_end,
int* registers, int32_t registers_length,
Address backtrack_stack,
RegExp::CallOrigin call_origin,
Isolate* isolate, Address regexp);
static Result MatchInternal(Isolate* isolate, ByteArray code_array,
String subject_string, int* registers,
int registers_length, int start_position,
RegExp::CallOrigin call_origin,
uint32_t backtrack_limit);
private:
static Result Match(Isolate* isolate, JSRegExp regexp, String subject_string,
int* registers, int registers_length, int start_position,
RegExp::CallOrigin call_origin);
};
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_INTERPRETER_H_
+291
View File
@@ -0,0 +1,291 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file implements the NativeRegExpMacroAssembler interface for
// SpiderMonkey. It provides the same interface as each of V8's
// architecture-specific implementations.
#ifndef RegexpMacroAssemblerArch_h
#define RegexpMacroAssemblerArch_h
#include "jit/MacroAssembler.h"
#include "regexp/regexp-macro-assembler.h"
namespace v8 {
namespace internal {
struct FrameData {
// Character position at the start of the input, stored as a
// negative offset from the end of the string (input_end_pointer_).
size_t inputStart;
// The backtrack_stack_pointer_ register points to the top of the stack.
// This points to the bottom of the backtrack stack.
void* backtrackStackBase;
// Copy of the input MatchPairs.
int32_t* matches; // pointer to capture array
int32_t numMatches; // size of capture array
};
class SMRegExpMacroAssembler final : public NativeRegExpMacroAssembler {
public:
SMRegExpMacroAssembler(JSContext* cx, Isolate* isolate,
js::jit::StackMacroAssembler& masm, Zone* zone,
Mode mode, uint32_t num_capture_registers);
virtual ~SMRegExpMacroAssembler() {} // Nothing to do here
virtual int stack_limit_slack();
virtual IrregexpImplementation Implementation();
virtual bool Succeed();
virtual void Fail();
virtual void AdvanceCurrentPosition(int by);
virtual void PopCurrentPosition();
virtual void PushCurrentPosition();
virtual void SetCurrentPositionFromEnd(int by);
virtual void Backtrack();
virtual void Bind(Label* label);
virtual void GoTo(Label* label);
virtual void PushBacktrack(Label* label);
virtual void CheckCharacter(uint32_t c, Label* on_equal);
virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
virtual void CheckCharacterAfterAnd(uint32_t c, uint32_t mask,
Label* on_equal);
virtual void CheckNotCharacterAfterAnd(uint32_t c, uint32_t mask,
Label* on_not_equal);
virtual void CheckNotCharacterAfterMinusAnd(uc16 c, uc16 minus, uc16 mask,
Label* on_not_equal);
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
virtual void CheckCharacterInRange(uc16 from, uc16 to, Label* on_in_range);
virtual void CheckCharacterNotInRange(uc16 from, uc16 to,
Label* on_not_in_range);
virtual void CheckAtStart(int cp_offset, Label* on_at_start);
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
virtual void CheckPosition(int cp_offset, Label* on_outside_input);
virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match);
virtual void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match);
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
bool read_backward,
Label* on_no_match);
virtual void LoadCurrentCharacterImpl(int cp_offset, Label* on_end_of_input,
bool check_bounds, int characters,
int eats_at_least);
virtual void AdvanceRegister(int reg, int by);
virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
virtual void IfRegisterEqPos(int reg, Label* if_eq);
virtual void PopRegister(int register_index);
virtual void PushRegister(int register_index,
StackCheckFlag check_stack_limit);
virtual void ReadCurrentPositionFromRegister(int reg);
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
virtual void ReadStackPointerFromRegister(int reg);
virtual void WriteStackPointerToRegister(int reg);
virtual void SetRegister(int register_index, int to);
virtual void ClearRegisters(int reg_from, int reg_to);
virtual Handle<HeapObject> GetCode(Handle<String> source);
private:
size_t frameSize_ = 0;
void createStackFrame();
void initFrameAndRegs();
void successHandler();
void exitHandler();
void backtrackHandler();
void stackOverflowHandler();
// Push a register on the backtrack stack.
void Push(js::jit::Register value);
// Pop a value from the backtrack stack.
void Pop(js::jit::Register target);
void CheckAtStartImpl(int cp_offset, Label* on_cond,
js::jit::Assembler::Condition cond);
void CheckCharacterImpl(js::jit::Imm32 c, Label* on_cond,
js::jit::Assembler::Condition cond);
void CheckCharacterAfterAndImpl(uint32_t c, uint32_t and_with, Label* on_cond,
bool negate);
void CheckCharacterInRangeImpl(uc16 from, uc16 to, Label* on_cond,
js::jit::Assembler::Condition cond);
void CheckNotBackReferenceImpl(int start_reg, bool read_backward,
Label* on_no_match, bool ignore_case);
void LoadCurrentCharacterUnchecked(int cp_offset, int characters);
void JumpOrBacktrack(Label* to);
// MacroAssembler methods that take a Label can be called with a
// null label, which means that we should backtrack if we would jump
// to that label. This is a helper to avoid writing out the same
// logic a dozen times.
inline js::jit::Label* LabelOrBacktrack(Label* to) {
return to ? to->inner() : &backtrack_label_;
}
void CheckBacktrackStackLimit();
static bool GrowBacktrackStack(RegExpStack* regexp_stack);
static uint32_t CaseInsensitiveCompareStrings(const char16_t* substring1,
const char16_t* substring2,
size_t byteLength);
static uint32_t CaseInsensitiveCompareUCStrings(const char16_t* substring1,
const char16_t* substring2,
size_t byteLength);
inline int char_size() { return static_cast<int>(mode_); }
inline js::jit::Scale factor() {
return mode_ == UC16 ? js::jit::TimesTwo : js::jit::TimesOne;
}
js::jit::Address inputStart() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, inputStart));
}
js::jit::Address backtrackStackBase() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, backtrackStackBase));
}
js::jit::Address matches() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, matches));
}
js::jit::Address numMatches() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, numMatches));
}
// The stack-pointer-relative location of a regexp register.
js::jit::Address register_location(int register_index) {
return js::jit::Address(masm_.getStackPointer(),
register_offset(register_index));
}
int32_t register_offset(int register_index) {
MOZ_ASSERT(register_index >= 0 && register_index <= kMaxRegister);
if (num_registers_ <= register_index) {
num_registers_ = register_index + 1;
}
static_assert(alignof(uintptr_t) <= alignof(FrameData));
return sizeof(FrameData) + register_index * sizeof(uintptr_t*);
}
JSContext* cx_;
js::jit::StackMacroAssembler& masm_;
/*
* This assembler uses the following registers:
*
* - current_character_:
* Contains the character (or characters) currently being examined.
* Must be loaded using LoadCurrentCharacter before using any of the
* dispatch methods. After a matching pass for a global regexp,
* temporarily stores the index of capture start.
* - current_position_:
* Current position in input *as negative byte offset from end of string*.
* - input_end_pointer_:
* Points to byte after last character in the input. current_position_ is
* relative to this.
* - backtrack_stack_pointer_:
* Points to tip of the (heap-allocated) backtrack stack. The stack grows
* downward (like the native stack).
* - temp0_, temp1_, temp2_:
* Scratch registers.
*
* The native stack pointer is used to access arguments (InputOutputData),
* local variables (FrameData), and irregexp's internal virtual registers
* (see register_location).
*/
js::jit::Register current_character_;
js::jit::Register current_position_;
js::jit::Register input_end_pointer_;
js::jit::Register backtrack_stack_pointer_;
js::jit::Register temp0_, temp1_, temp2_;
js::jit::Label entry_label_;
js::jit::Label start_label_;
js::jit::Label backtrack_label_;
js::jit::Label success_label_;
js::jit::Label exit_label_;
js::jit::Label stack_overflow_label_;
js::jit::Label exit_with_exception_label_;
// When we generate the code to push a backtrack label's address
// onto the backtrack stack, we don't know its final address. We
// have to patch it after linking. This is slightly delicate, as the
// Label itself (which is allocated on the stack) may not exist by
// the time we link. The approach is as follows:
//
// 1. When we push a label on the backtrack stack (PushBacktrack),
// we bind the label's patchOffset_ field to the offset within
// the code that should be overwritten. This works because each
// label is only pushed by a single instruction.
//
// 2. When we bind a label (Bind), we check to see if it has a
// bound patchOffset_. If it does, we create a LabelPatch mapping
// its patch offset to the offset of the label itself.
//
// 3. While linking the code, we walk the list of label patches
// and patch the code accordingly.
class LabelPatch {
public:
LabelPatch(js::jit::CodeOffset patchOffset, size_t labelOffset)
: patchOffset_(patchOffset), labelOffset_(labelOffset) {}
js::jit::CodeOffset patchOffset_;
size_t labelOffset_ = 0;
};
js::Vector<LabelPatch, 4, js::SystemAllocPolicy> labelPatches_;
void AddLabelPatch(js::jit::CodeOffset patchOffset, size_t labelOffset) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
if (!labelPatches_.emplaceBack(patchOffset, labelOffset)) {
oomUnsafe.crash("Irregexp label patch");
}
}
Mode mode_;
int num_registers_;
int num_capture_registers_;
js::jit::LiveGeneralRegisterSet savedRegisters_;
public:
using TableVector =
js::Vector<PseudoHandle<ByteArrayData>, 4, js::SystemAllocPolicy>;
TableVector& tables() { return tables_; }
private:
TableVector tables_;
void AddTable(PseudoHandle<ByteArrayData> table) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
if (!tables_.append(std::move(table))) {
oomUnsafe.crash("Irregexp table append");
}
}
};
} // namespace internal
} // namespace v8
#endif // RegexpMacroAssemblerArch_h
@@ -0,0 +1,418 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "regexp/regexp-macro-assembler-tracer.h"
namespace v8 {
namespace internal {
RegExpMacroAssemblerTracer::RegExpMacroAssemblerTracer(
Isolate* isolate, RegExpMacroAssembler* assembler)
: RegExpMacroAssembler(isolate, assembler->zone()), assembler_(assembler) {
IrregexpImplementation type = assembler->Implementation();
DCHECK_LT(type, 9);
const char* impl_names[] = {"IA32", "ARM", "ARM64", "MIPS", "S390",
"PPC", "X64", "X87", "Bytecode"};
PrintF("RegExpMacroAssembler%s();\n", impl_names[type]);
}
RegExpMacroAssemblerTracer::~RegExpMacroAssemblerTracer() = default;
void RegExpMacroAssemblerTracer::AbortedCodeGeneration() {
PrintF(" AbortedCodeGeneration\n");
assembler_->AbortedCodeGeneration();
}
// This is used for printing out debugging information. It makes an integer
// that is closely related to the address of an object.
static int LabelToInt(Label* label) {
return static_cast<int>(reinterpret_cast<intptr_t>(label));
}
void RegExpMacroAssemblerTracer::Bind(Label* label) {
PrintF("label[%08x]: (Bind)\n", LabelToInt(label));
assembler_->Bind(label);
}
void RegExpMacroAssemblerTracer::AdvanceCurrentPosition(int by) {
PrintF(" AdvanceCurrentPosition(by=%d);\n", by);
assembler_->AdvanceCurrentPosition(by);
}
void RegExpMacroAssemblerTracer::CheckGreedyLoop(Label* label) {
PrintF(" CheckGreedyLoop(label[%08x]);\n\n", LabelToInt(label));
assembler_->CheckGreedyLoop(label);
}
void RegExpMacroAssemblerTracer::PopCurrentPosition() {
PrintF(" PopCurrentPosition();\n");
assembler_->PopCurrentPosition();
}
void RegExpMacroAssemblerTracer::PushCurrentPosition() {
PrintF(" PushCurrentPosition();\n");
assembler_->PushCurrentPosition();
}
void RegExpMacroAssemblerTracer::Backtrack() {
PrintF(" Backtrack();\n");
assembler_->Backtrack();
}
void RegExpMacroAssemblerTracer::GoTo(Label* label) {
PrintF(" GoTo(label[%08x]);\n\n", LabelToInt(label));
assembler_->GoTo(label);
}
void RegExpMacroAssemblerTracer::PushBacktrack(Label* label) {
PrintF(" PushBacktrack(label[%08x]);\n", LabelToInt(label));
assembler_->PushBacktrack(label);
}
bool RegExpMacroAssemblerTracer::Succeed() {
bool restart = assembler_->Succeed();
PrintF(" Succeed();%s\n", restart ? " [restart for global match]" : "");
return restart;
}
void RegExpMacroAssemblerTracer::Fail() {
PrintF(" Fail();");
assembler_->Fail();
}
void RegExpMacroAssemblerTracer::PopRegister(int register_index) {
PrintF(" PopRegister(register=%d);\n", register_index);
assembler_->PopRegister(register_index);
}
void RegExpMacroAssemblerTracer::PushRegister(
int register_index,
StackCheckFlag check_stack_limit) {
PrintF(" PushRegister(register=%d, %s);\n",
register_index,
check_stack_limit ? "check stack limit" : "");
assembler_->PushRegister(register_index, check_stack_limit);
}
void RegExpMacroAssemblerTracer::AdvanceRegister(int reg, int by) {
PrintF(" AdvanceRegister(register=%d, by=%d);\n", reg, by);
assembler_->AdvanceRegister(reg, by);
}
void RegExpMacroAssemblerTracer::SetCurrentPositionFromEnd(int by) {
PrintF(" SetCurrentPositionFromEnd(by=%d);\n", by);
assembler_->SetCurrentPositionFromEnd(by);
}
void RegExpMacroAssemblerTracer::SetRegister(int register_index, int to) {
PrintF(" SetRegister(register=%d, to=%d);\n", register_index, to);
assembler_->SetRegister(register_index, to);
}
void RegExpMacroAssemblerTracer::WriteCurrentPositionToRegister(int reg,
int cp_offset) {
PrintF(" WriteCurrentPositionToRegister(register=%d,cp_offset=%d);\n",
reg,
cp_offset);
assembler_->WriteCurrentPositionToRegister(reg, cp_offset);
}
void RegExpMacroAssemblerTracer::ClearRegisters(int reg_from, int reg_to) {
PrintF(" ClearRegister(from=%d, to=%d);\n", reg_from, reg_to);
assembler_->ClearRegisters(reg_from, reg_to);
}
void RegExpMacroAssemblerTracer::ReadCurrentPositionFromRegister(int reg) {
PrintF(" ReadCurrentPositionFromRegister(register=%d);\n", reg);
assembler_->ReadCurrentPositionFromRegister(reg);
}
void RegExpMacroAssemblerTracer::WriteStackPointerToRegister(int reg) {
PrintF(" WriteStackPointerToRegister(register=%d);\n", reg);
assembler_->WriteStackPointerToRegister(reg);
}
void RegExpMacroAssemblerTracer::ReadStackPointerFromRegister(int reg) {
PrintF(" ReadStackPointerFromRegister(register=%d);\n", reg);
assembler_->ReadStackPointerFromRegister(reg);
}
void RegExpMacroAssemblerTracer::LoadCurrentCharacterImpl(
int cp_offset, Label* on_end_of_input, bool check_bounds, int characters,
int eats_at_least) {
const char* check_msg = check_bounds ? "" : " (unchecked)";
PrintF(
" LoadCurrentCharacter(cp_offset=%d, label[%08x]%s (%d chars) (eats at "
"least %d));\n",
cp_offset, LabelToInt(on_end_of_input), check_msg, characters,
eats_at_least);
assembler_->LoadCurrentCharacter(cp_offset, on_end_of_input, check_bounds,
characters, eats_at_least);
}
class PrintablePrinter {
public:
explicit PrintablePrinter(uc16 character) : character_(character) { }
const char* operator*() {
if (character_ >= ' ' && character_ <= '~') {
buffer_[0] = '(';
buffer_[1] = static_cast<char>(character_);
buffer_[2] = ')';
buffer_[3] = '\0';
} else {
buffer_[0] = '\0';
}
return &buffer_[0];
}
private:
uc16 character_;
char buffer_[4];
};
void RegExpMacroAssemblerTracer::CheckCharacterLT(uc16 limit, Label* on_less) {
PrintablePrinter printable(limit);
PrintF(" CheckCharacterLT(c=0x%04x%s, label[%08x]);\n",
limit,
*printable,
LabelToInt(on_less));
assembler_->CheckCharacterLT(limit, on_less);
}
void RegExpMacroAssemblerTracer::CheckCharacterGT(uc16 limit,
Label* on_greater) {
PrintablePrinter printable(limit);
PrintF(" CheckCharacterGT(c=0x%04x%s, label[%08x]);\n",
limit,
*printable,
LabelToInt(on_greater));
assembler_->CheckCharacterGT(limit, on_greater);
}
void RegExpMacroAssemblerTracer::CheckCharacter(unsigned c, Label* on_equal) {
PrintablePrinter printable(c);
PrintF(" CheckCharacter(c=0x%04x%s, label[%08x]);\n",
c,
*printable,
LabelToInt(on_equal));
assembler_->CheckCharacter(c, on_equal);
}
void RegExpMacroAssemblerTracer::CheckAtStart(int cp_offset,
Label* on_at_start) {
PrintF(" CheckAtStart(cp_offset=%d, label[%08x]);\n", cp_offset,
LabelToInt(on_at_start));
assembler_->CheckAtStart(cp_offset, on_at_start);
}
void RegExpMacroAssemblerTracer::CheckNotAtStart(int cp_offset,
Label* on_not_at_start) {
PrintF(" CheckNotAtStart(cp_offset=%d, label[%08x]);\n", cp_offset,
LabelToInt(on_not_at_start));
assembler_->CheckNotAtStart(cp_offset, on_not_at_start);
}
void RegExpMacroAssemblerTracer::CheckNotCharacter(unsigned c,
Label* on_not_equal) {
PrintablePrinter printable(c);
PrintF(" CheckNotCharacter(c=0x%04x%s, label[%08x]);\n",
c,
*printable,
LabelToInt(on_not_equal));
assembler_->CheckNotCharacter(c, on_not_equal);
}
void RegExpMacroAssemblerTracer::CheckCharacterAfterAnd(
unsigned c,
unsigned mask,
Label* on_equal) {
PrintablePrinter printable(c);
PrintF(" CheckCharacterAfterAnd(c=0x%04x%s, mask=0x%04x, label[%08x]);\n",
c,
*printable,
mask,
LabelToInt(on_equal));
assembler_->CheckCharacterAfterAnd(c, mask, on_equal);
}
void RegExpMacroAssemblerTracer::CheckNotCharacterAfterAnd(
unsigned c,
unsigned mask,
Label* on_not_equal) {
PrintablePrinter printable(c);
PrintF(" CheckNotCharacterAfterAnd(c=0x%04x%s, mask=0x%04x, label[%08x]);\n",
c,
*printable,
mask,
LabelToInt(on_not_equal));
assembler_->CheckNotCharacterAfterAnd(c, mask, on_not_equal);
}
void RegExpMacroAssemblerTracer::CheckNotCharacterAfterMinusAnd(
uc16 c,
uc16 minus,
uc16 mask,
Label* on_not_equal) {
PrintF(" CheckNotCharacterAfterMinusAnd(c=0x%04x, minus=%04x, mask=0x%04x, "
"label[%08x]);\n",
c,
minus,
mask,
LabelToInt(on_not_equal));
assembler_->CheckNotCharacterAfterMinusAnd(c, minus, mask, on_not_equal);
}
void RegExpMacroAssemblerTracer::CheckCharacterInRange(
uc16 from,
uc16 to,
Label* on_not_in_range) {
PrintablePrinter printable_from(from);
PrintablePrinter printable_to(to);
PrintF(" CheckCharacterInRange(from=0x%04x%s, to=0x%04x%s, label[%08x]);\n",
from,
*printable_from,
to,
*printable_to,
LabelToInt(on_not_in_range));
assembler_->CheckCharacterInRange(from, to, on_not_in_range);
}
void RegExpMacroAssemblerTracer::CheckCharacterNotInRange(
uc16 from,
uc16 to,
Label* on_in_range) {
PrintablePrinter printable_from(from);
PrintablePrinter printable_to(to);
PrintF(
" CheckCharacterNotInRange(from=0x%04x%s," " to=%04x%s, label[%08x]);\n",
from,
*printable_from,
to,
*printable_to,
LabelToInt(on_in_range));
assembler_->CheckCharacterNotInRange(from, to, on_in_range);
}
void RegExpMacroAssemblerTracer::CheckBitInTable(
Handle<ByteArray> table, Label* on_bit_set) {
PrintF(" CheckBitInTable(label[%08x] ", LabelToInt(on_bit_set));
for (int i = 0; i < kTableSize; i++) {
PrintF("%c", table->get(i) != 0 ? 'X' : '.');
if (i % 32 == 31 && i != kTableMask) {
PrintF("\n ");
}
}
PrintF(");\n");
assembler_->CheckBitInTable(table, on_bit_set);
}
void RegExpMacroAssemblerTracer::CheckNotBackReference(int start_reg,
bool read_backward,
Label* on_no_match) {
PrintF(" CheckNotBackReference(register=%d, %s, label[%08x]);\n", start_reg,
read_backward ? "backward" : "forward", LabelToInt(on_no_match));
assembler_->CheckNotBackReference(start_reg, read_backward, on_no_match);
}
void RegExpMacroAssemblerTracer::CheckNotBackReferenceIgnoreCase(
int start_reg, bool read_backward, Label* on_no_match) {
PrintF(" CheckNotBackReferenceIgnoreCase(register=%d, %s, label[%08x]);\n",
start_reg, read_backward ? "backward" : "forward",
LabelToInt(on_no_match));
assembler_->CheckNotBackReferenceIgnoreCase(start_reg, read_backward,
on_no_match);
}
void RegExpMacroAssemblerTracer::CheckPosition(int cp_offset,
Label* on_outside_input) {
PrintF(" CheckPosition(cp_offset=%d, label[%08x]);\n", cp_offset,
LabelToInt(on_outside_input));
assembler_->CheckPosition(cp_offset, on_outside_input);
}
bool RegExpMacroAssemblerTracer::CheckSpecialCharacterClass(
uc16 type,
Label* on_no_match) {
bool supported = assembler_->CheckSpecialCharacterClass(type,
on_no_match);
PrintF(" CheckSpecialCharacterClass(type='%c', label[%08x]): %s;\n",
type,
LabelToInt(on_no_match),
supported ? "true" : "false");
return supported;
}
void RegExpMacroAssemblerTracer::IfRegisterLT(int register_index,
int comparand, Label* if_lt) {
PrintF(" IfRegisterLT(register=%d, number=%d, label[%08x]);\n",
register_index, comparand, LabelToInt(if_lt));
assembler_->IfRegisterLT(register_index, comparand, if_lt);
}
void RegExpMacroAssemblerTracer::IfRegisterEqPos(int register_index,
Label* if_eq) {
PrintF(" IfRegisterEqPos(register=%d, label[%08x]);\n",
register_index, LabelToInt(if_eq));
assembler_->IfRegisterEqPos(register_index, if_eq);
}
void RegExpMacroAssemblerTracer::IfRegisterGE(int register_index,
int comparand, Label* if_ge) {
PrintF(" IfRegisterGE(register=%d, number=%d, label[%08x]);\n",
register_index, comparand, LabelToInt(if_ge));
assembler_->IfRegisterGE(register_index, comparand, if_ge);
}
RegExpMacroAssembler::IrregexpImplementation
RegExpMacroAssemblerTracer::Implementation() {
return assembler_->Implementation();
}
Handle<HeapObject> RegExpMacroAssemblerTracer::GetCode(Handle<String> source) {
PrintF(" GetCode(%s);\n", source->ToCString().get());
return assembler_->GetCode(source);
}
} // namespace internal
} // namespace v8
@@ -0,0 +1,80 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_MACRO_ASSEMBLER_TRACER_H_
#define V8_REGEXP_REGEXP_MACRO_ASSEMBLER_TRACER_H_
#include "regexp/regexp-macro-assembler.h"
namespace v8 {
namespace internal {
// Decorator on a RegExpMacroAssembler that write all calls.
class RegExpMacroAssemblerTracer: public RegExpMacroAssembler {
public:
RegExpMacroAssemblerTracer(Isolate* isolate, RegExpMacroAssembler* assembler);
~RegExpMacroAssemblerTracer() override;
void AbortedCodeGeneration() override;
int stack_limit_slack() override { return assembler_->stack_limit_slack(); }
bool CanReadUnaligned() override { return assembler_->CanReadUnaligned(); }
void AdvanceCurrentPosition(int by) override; // Signed cp change.
void AdvanceRegister(int reg, int by) override; // r[reg] += by.
void Backtrack() override;
void Bind(Label* label) override;
void CheckCharacter(unsigned c, Label* on_equal) override;
void CheckCharacterAfterAnd(unsigned c, unsigned and_with,
Label* on_equal) override;
void CheckCharacterGT(uc16 limit, Label* on_greater) override;
void CheckCharacterLT(uc16 limit, Label* on_less) override;
void CheckGreedyLoop(Label* on_tos_equals_current_position) override;
void CheckAtStart(int cp_offset, Label* on_at_start) override;
void CheckNotAtStart(int cp_offset, Label* on_not_at_start) override;
void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match) override;
void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
Label* on_no_match) override;
void CheckNotCharacter(unsigned c, Label* on_not_equal) override;
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with,
Label* on_not_equal) override;
void CheckNotCharacterAfterMinusAnd(uc16 c, uc16 minus, uc16 and_with,
Label* on_not_equal) override;
void CheckCharacterInRange(uc16 from, uc16 to, Label* on_in_range) override;
void CheckCharacterNotInRange(uc16 from, uc16 to,
Label* on_not_in_range) override;
void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set) override;
void CheckPosition(int cp_offset, Label* on_outside_input) override;
bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match) override;
void Fail() override;
Handle<HeapObject> GetCode(Handle<String> source) override;
void GoTo(Label* label) override;
void IfRegisterGE(int reg, int comparand, Label* if_ge) override;
void IfRegisterLT(int reg, int comparand, Label* if_lt) override;
void IfRegisterEqPos(int reg, Label* if_eq) override;
IrregexpImplementation Implementation() override;
void LoadCurrentCharacterImpl(int cp_offset, Label* on_end_of_input,
bool check_bounds, int characters,
int eats_at_least) override;
void PopCurrentPosition() override;
void PopRegister(int register_index) override;
void PushBacktrack(Label* label) override;
void PushCurrentPosition() override;
void PushRegister(int register_index,
StackCheckFlag check_stack_limit) override;
void ReadCurrentPositionFromRegister(int reg) override;
void ReadStackPointerFromRegister(int reg) override;
void SetCurrentPositionFromEnd(int by) override;
void SetRegister(int register_index, int to) override;
bool Succeed() override;
void WriteCurrentPositionToRegister(int reg, int cp_offset) override;
void ClearRegisters(int reg_from, int reg_to) override;
void WriteStackPointerToRegister(int reg) override;
private:
RegExpMacroAssembler* assembler_;
};
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_MACRO_ASSEMBLER_TRACER_H_
+344
View File
@@ -0,0 +1,344 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "regexp/regexp-macro-assembler.h"
#include "regexp/regexp-stack.h"
#ifdef V8_INTL_SUPPORT
#include "unicode/uchar.h"
#include "unicode/unistr.h"
#endif // V8_INTL_SUPPORT
namespace v8 {
namespace internal {
RegExpMacroAssembler::RegExpMacroAssembler(Isolate* isolate, Zone* zone)
: slow_safe_compiler_(false),
global_mode_(NOT_GLOBAL),
isolate_(isolate),
zone_(zone) {}
RegExpMacroAssembler::~RegExpMacroAssembler() = default;
int RegExpMacroAssembler::CaseInsensitiveCompareUC16(Address byte_offset1,
Address byte_offset2,
size_t byte_length,
Isolate* isolate) {
// This function is not allowed to cause a garbage collection.
// A GC might move the calling generated code and invalidate the
// return address on the stack.
DCHECK_EQ(0, byte_length % 2);
#ifdef V8_INTL_SUPPORT
int32_t length = (int32_t)(byte_length >> 1);
icu::UnicodeString uni_str_1(reinterpret_cast<const char16_t*>(byte_offset1),
length);
return uni_str_1.caseCompare(reinterpret_cast<const char16_t*>(byte_offset2),
length, U_FOLD_CASE_DEFAULT) == 0;
#else
uc16* substring1 = reinterpret_cast<uc16*>(byte_offset1);
uc16* substring2 = reinterpret_cast<uc16*>(byte_offset2);
size_t length = byte_length >> 1;
DCHECK_NOT_NULL(isolate);
unibrow::Mapping<unibrow::Ecma262Canonicalize>* canonicalize =
isolate->regexp_macro_assembler_canonicalize();
for (size_t i = 0; i < length; i++) {
unibrow::uchar c1 = substring1[i];
unibrow::uchar c2 = substring2[i];
if (c1 != c2) {
unibrow::uchar s1[1] = {c1};
canonicalize->get(c1, '\0', s1);
if (s1[0] != c2) {
unibrow::uchar s2[1] = {c2};
canonicalize->get(c2, '\0', s2);
if (s1[0] != s2[0]) {
return 0;
}
}
}
}
return 1;
#endif // V8_INTL_SUPPORT
}
void RegExpMacroAssembler::CheckNotInSurrogatePair(int cp_offset,
Label* on_failure) {
Label ok;
// Check that current character is not a trail surrogate.
LoadCurrentCharacter(cp_offset, &ok);
CheckCharacterNotInRange(kTrailSurrogateStart, kTrailSurrogateEnd, &ok);
// Check that previous character is not a lead surrogate.
LoadCurrentCharacter(cp_offset - 1, &ok);
CheckCharacterInRange(kLeadSurrogateStart, kLeadSurrogateEnd, on_failure);
Bind(&ok);
}
void RegExpMacroAssembler::CheckPosition(int cp_offset,
Label* on_outside_input) {
LoadCurrentCharacter(cp_offset, on_outside_input, true);
}
void RegExpMacroAssembler::LoadCurrentCharacter(int cp_offset,
Label* on_end_of_input,
bool check_bounds,
int characters,
int eats_at_least) {
// By default, eats_at_least = characters.
if (eats_at_least == kUseCharactersValue) {
eats_at_least = characters;
}
LoadCurrentCharacterImpl(cp_offset, on_end_of_input, check_bounds, characters,
eats_at_least);
}
bool RegExpMacroAssembler::CheckSpecialCharacterClass(uc16 type,
Label* on_no_match) {
return false;
}
NativeRegExpMacroAssembler::NativeRegExpMacroAssembler(Isolate* isolate,
Zone* zone)
: RegExpMacroAssembler(isolate, zone) {}
NativeRegExpMacroAssembler::~NativeRegExpMacroAssembler() = default;
bool NativeRegExpMacroAssembler::CanReadUnaligned() {
return FLAG_enable_regexp_unaligned_accesses && !slow_safe();
}
#ifndef COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
// This method may only be called after an interrupt.
int NativeRegExpMacroAssembler::CheckStackGuardState(
Isolate* isolate, int start_index, RegExp::CallOrigin call_origin,
Address* return_address, Code re_code, Address* subject,
const byte** input_start, const byte** input_end) {
DisallowHeapAllocation no_gc;
Address old_pc = PointerAuthentication::AuthenticatePC(return_address, 0);
DCHECK_LE(re_code.raw_instruction_start(), old_pc);
DCHECK_LE(old_pc, re_code.raw_instruction_end());
StackLimitCheck check(isolate);
bool js_has_overflowed = check.JsHasOverflowed();
if (call_origin == RegExp::CallOrigin::kFromJs) {
// Direct calls from JavaScript can be interrupted in two ways:
// 1. A real stack overflow, in which case we let the caller throw the
// exception.
// 2. The stack guard was used to interrupt execution for another purpose,
// forcing the call through the runtime system.
// Bug(v8:9540) Investigate why this method is called from JS although no
// stackoverflow or interrupt is pending on ARM64. We return 0 in this case
// to continue execution normally.
if (js_has_overflowed) {
return EXCEPTION;
} else if (check.InterruptRequested()) {
return RETRY;
} else {
return 0;
}
}
DCHECK(call_origin == RegExp::CallOrigin::kFromRuntime);
// Prepare for possible GC.
HandleScope handles(isolate);
Handle<Code> code_handle(re_code, isolate);
Handle<String> subject_handle(String::cast(Object(*subject)), isolate);
bool is_one_byte = String::IsOneByteRepresentationUnderneath(*subject_handle);
int return_value = 0;
if (js_has_overflowed) {
AllowHeapAllocation yes_gc;
isolate->StackOverflow();
return_value = EXCEPTION;
} else if (check.InterruptRequested()) {
AllowHeapAllocation yes_gc;
Object result = isolate->stack_guard()->HandleInterrupts();
if (result.IsException(isolate)) return_value = EXCEPTION;
}
if (*code_handle != re_code) { // Return address no longer valid
// Overwrite the return address on the stack.
intptr_t delta = code_handle->address() - re_code.address();
Address new_pc = old_pc + delta;
// TODO(v8:10026): avoid replacing a signed pointer.
PointerAuthentication::ReplacePC(return_address, new_pc, 0);
}
// If we continue, we need to update the subject string addresses.
if (return_value == 0) {
// String encoding might have changed.
if (String::IsOneByteRepresentationUnderneath(*subject_handle) !=
is_one_byte) {
// If we changed between an LATIN1 and an UC16 string, the specialized
// code cannot be used, and we need to restart regexp matching from
// scratch (including, potentially, compiling a new version of the code).
return_value = RETRY;
} else {
*subject = subject_handle->ptr();
intptr_t byte_length = *input_end - *input_start;
*input_start = subject_handle->AddressOfCharacterAt(start_index, no_gc);
*input_end = *input_start + byte_length;
}
}
return return_value;
}
// Returns a {Result} sentinel, or the number of successful matches.
int NativeRegExpMacroAssembler::Match(Handle<JSRegExp> regexp,
Handle<String> subject,
int* offsets_vector,
int offsets_vector_length,
int previous_index, Isolate* isolate) {
DCHECK(subject->IsFlat());
DCHECK_LE(0, previous_index);
DCHECK_LE(previous_index, subject->length());
// No allocations before calling the regexp, but we can't use
// DisallowHeapAllocation, since regexps might be preempted, and another
// thread might do allocation anyway.
String subject_ptr = *subject;
// Character offsets into string.
int start_offset = previous_index;
int char_length = subject_ptr.length() - start_offset;
int slice_offset = 0;
// The string has been flattened, so if it is a cons string it contains the
// full string in the first part.
if (StringShape(subject_ptr).IsCons()) {
DCHECK_EQ(0, ConsString::cast(subject_ptr).second().length());
subject_ptr = ConsString::cast(subject_ptr).first();
} else if (StringShape(subject_ptr).IsSliced()) {
SlicedString slice = SlicedString::cast(subject_ptr);
subject_ptr = slice.parent();
slice_offset = slice.offset();
}
if (StringShape(subject_ptr).IsThin()) {
subject_ptr = ThinString::cast(subject_ptr).actual();
}
// Ensure that an underlying string has the same representation.
bool is_one_byte = subject_ptr.IsOneByteRepresentation();
DCHECK(subject_ptr.IsExternalString() || subject_ptr.IsSeqString());
// String is now either Sequential or External
int char_size_shift = is_one_byte ? 0 : 1;
DisallowHeapAllocation no_gc;
const byte* input_start =
subject_ptr.AddressOfCharacterAt(start_offset + slice_offset, no_gc);
int byte_length = char_length << char_size_shift;
const byte* input_end = input_start + byte_length;
return Execute(*subject, start_offset, input_start, input_end, offsets_vector,
offsets_vector_length, isolate, *regexp);
}
// Returns a {Result} sentinel, or the number of successful matches.
// TODO(pthier): The JSRegExp object is passed to native irregexp code to match
// the signature of the interpreter. We should get rid of JS objects passed to
// internal methods.
int NativeRegExpMacroAssembler::Execute(
String input, // This needs to be the unpacked (sliced, cons) string.
int start_offset, const byte* input_start, const byte* input_end,
int* output, int output_size, Isolate* isolate, JSRegExp regexp) {
// Ensure that the minimum stack has been allocated.
RegExpStackScope stack_scope(isolate);
Address stack_base = stack_scope.stack()->stack_base();
bool is_one_byte = String::IsOneByteRepresentationUnderneath(input);
Code code = Code::cast(regexp.Code(is_one_byte));
RegExp::CallOrigin call_origin = RegExp::CallOrigin::kFromRuntime;
using RegexpMatcherSig = int(
Address input_string, int start_offset, // NOLINT(readability/casting)
const byte* input_start, const byte* input_end, int* output,
int output_size, Address stack_base, int call_origin, Isolate* isolate,
Address regexp);
auto fn = GeneratedCode<RegexpMatcherSig>::FromCode(code);
int result =
fn.Call(input.ptr(), start_offset, input_start, input_end, output,
output_size, stack_base, call_origin, isolate, regexp.ptr());
DCHECK(result >= RETRY);
if (result == EXCEPTION && !isolate->has_pending_exception()) {
// We detected a stack overflow (on the backtrack stack) in RegExp code,
// but haven't created the exception yet. Additionally, we allow heap
// allocation because even though it invalidates {input_start} and
// {input_end}, we are about to return anyway.
AllowHeapAllocation allow_allocation;
isolate->StackOverflow();
}
return result;
}
#endif // !COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
// clang-format off
const byte NativeRegExpMacroAssembler::word_character_map[] = {
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // '0' - '7'
0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, // '8' - '9'
0x00u, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'A' - 'G'
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'H' - 'O'
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'P' - 'W'
0xFFu, 0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x00u, 0xFFu, // 'X' - 'Z', '_'
0x00u, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'a' - 'g'
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'h' - 'o'
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'p' - 'w'
0xFFu, 0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, // 'x' - 'z'
// Latin-1 range
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
};
// clang-format on
Address NativeRegExpMacroAssembler::GrowStack(Address stack_pointer,
Address* stack_base,
Isolate* isolate) {
RegExpStack* regexp_stack = isolate->regexp_stack();
size_t size = regexp_stack->stack_capacity();
Address old_stack_base = regexp_stack->stack_base();
DCHECK(old_stack_base == *stack_base);
DCHECK(stack_pointer <= old_stack_base);
DCHECK(static_cast<size_t>(old_stack_base - stack_pointer) <= size);
Address new_stack_base = regexp_stack->EnsureCapacity(size * 2);
if (new_stack_base == kNullAddress) {
return kNullAddress;
}
*stack_base = new_stack_base;
intptr_t stack_content_size = old_stack_base - stack_pointer;
return new_stack_base - stack_content_size;
}
} // namespace internal
} // namespace v8
+280
View File
@@ -0,0 +1,280 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_MACRO_ASSEMBLER_H_
#define V8_REGEXP_REGEXP_MACRO_ASSEMBLER_H_
#include "regexp/regexp-ast.h"
#include "regexp/regexp-shim.h"
#include "regexp/regexp.h"
namespace v8 {
namespace internal {
static const uc32 kLeadSurrogateStart = 0xd800;
static const uc32 kLeadSurrogateEnd = 0xdbff;
static const uc32 kTrailSurrogateStart = 0xdc00;
static const uc32 kTrailSurrogateEnd = 0xdfff;
static const uc32 kNonBmpStart = 0x10000;
static const uc32 kNonBmpEnd = 0x10ffff;
struct DisjunctDecisionRow {
RegExpCharacterClass cc;
Label* on_match;
};
class RegExpMacroAssembler {
public:
// The implementation must be able to handle at least:
static const int kMaxRegister = (1 << 16) - 1;
static const int kMaxCPOffset = (1 << 15) - 1;
static const int kMinCPOffset = -(1 << 15);
static const int kTableSizeBits = 7;
static const int kTableSize = 1 << kTableSizeBits;
static const int kTableMask = kTableSize - 1;
static constexpr int kUseCharactersValue = -1;
enum IrregexpImplementation {
kIA32Implementation,
kARMImplementation,
kARM64Implementation,
kMIPSImplementation,
kS390Implementation,
kPPCImplementation,
kX64Implementation,
kX87Implementation,
kBytecodeImplementation
};
enum StackCheckFlag {
kNoStackLimitCheck = false,
kCheckStackLimit = true
};
RegExpMacroAssembler(Isolate* isolate, Zone* zone);
virtual ~RegExpMacroAssembler();
// This function is called when code generation is aborted, so that
// the assembler could clean up internal data structures.
virtual void AbortedCodeGeneration() {}
// The maximal number of pushes between stack checks. Users must supply
// kCheckStackLimit flag to push operations (instead of kNoStackLimitCheck)
// at least once for every stack_limit() pushes that are executed.
virtual int stack_limit_slack() = 0;
virtual bool CanReadUnaligned() = 0;
virtual void AdvanceCurrentPosition(int by) = 0; // Signed cp change.
virtual void AdvanceRegister(int reg, int by) = 0; // r[reg] += by.
// Continues execution from the position pushed on the top of the backtrack
// stack by an earlier PushBacktrack(Label*).
virtual void Backtrack() = 0;
virtual void Bind(Label* label) = 0;
// Dispatch after looking the current character up in a 2-bits-per-entry
// map. The destinations vector has up to 4 labels.
virtual void CheckCharacter(unsigned c, Label* on_equal) = 0;
// Bitwise and the current character with the given constant and then
// check for a match with c.
virtual void CheckCharacterAfterAnd(unsigned c,
unsigned and_with,
Label* on_equal) = 0;
virtual void CheckCharacterGT(uc16 limit, Label* on_greater) = 0;
virtual void CheckCharacterLT(uc16 limit, Label* on_less) = 0;
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position) = 0;
virtual void CheckAtStart(int cp_offset, Label* on_at_start) = 0;
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start) = 0;
virtual void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match) = 0;
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
bool read_backward,
Label* on_no_match) = 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
// matches. If the label is nullptr then we should pop a backtrack address
// off the stack and go to that.
virtual void CheckNotCharacter(unsigned c, Label* on_not_equal) = 0;
virtual void CheckNotCharacterAfterAnd(unsigned c,
unsigned and_with,
Label* on_not_equal) = 0;
// Subtract a constant from the current character, then and with the given
// constant and then check for a match with c.
virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
uc16 minus,
uc16 and_with,
Label* on_not_equal) = 0;
virtual void CheckCharacterInRange(uc16 from,
uc16 to, // Both inclusive.
Label* on_in_range) = 0;
virtual void CheckCharacterNotInRange(uc16 from,
uc16 to, // Both inclusive.
Label* on_not_in_range) = 0;
// The current character (modulus the kTableSize) is looked up in the byte
// array, and if the found byte is non-zero, we jump to the on_bit_set label.
virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set) = 0;
// Checks whether the given offset from the current position is before
// the end of the string. May overwrite the current character.
virtual void CheckPosition(int cp_offset, Label* on_outside_input);
// Check whether a standard/default character class matches the current
// character. Returns false if the type of special character class does
// not have custom support.
// May clobber the current loaded character.
virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match);
// Control-flow integrity:
// Define a jump target and bind a label.
virtual void BindJumpTarget(Label* label) { Bind(label); }
virtual void Fail() = 0;
virtual Handle<HeapObject> GetCode(Handle<String> source) = 0;
virtual void GoTo(Label* label) = 0;
// Check whether a register is >= a given constant and go to a label if it
// is. Backtracks instead if the label is nullptr.
virtual void IfRegisterGE(int reg, int comparand, Label* if_ge) = 0;
// Check whether a register is < a given constant and go to a label if it is.
// Backtracks instead if the label is nullptr.
virtual void IfRegisterLT(int reg, int comparand, Label* if_lt) = 0;
// Check whether a register is == to the current position and go to a
// label if it is.
virtual void IfRegisterEqPos(int reg, Label* if_eq) = 0;
virtual IrregexpImplementation Implementation() = 0;
V8_EXPORT_PRIVATE void LoadCurrentCharacter(
int cp_offset, Label* on_end_of_input, bool check_bounds = true,
int characters = 1, int eats_at_least = kUseCharactersValue);
virtual void LoadCurrentCharacterImpl(int cp_offset, Label* on_end_of_input,
bool check_bounds, int characters,
int eats_at_least) = 0;
virtual void PopCurrentPosition() = 0;
virtual void PopRegister(int register_index) = 0;
// Pushes the label on the backtrack stack, so that a following Backtrack
// will go to this label. Always checks the backtrack stack limit.
virtual void PushBacktrack(Label* label) = 0;
virtual void PushCurrentPosition() = 0;
virtual void PushRegister(int register_index,
StackCheckFlag check_stack_limit) = 0;
virtual void ReadCurrentPositionFromRegister(int reg) = 0;
virtual void ReadStackPointerFromRegister(int reg) = 0;
virtual void SetCurrentPositionFromEnd(int by) = 0;
virtual void SetRegister(int register_index, int to) = 0;
// Return whether the matching (with a global regexp) will be restarted.
virtual bool Succeed() = 0;
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset) = 0;
virtual void ClearRegisters(int reg_from, int reg_to) = 0;
virtual void WriteStackPointerToRegister(int reg) = 0;
// Compares two-byte strings case insensitively.
// Called from generated RegExp code.
static int CaseInsensitiveCompareUC16(Address byte_offset1,
Address byte_offset2,
size_t byte_length, Isolate* isolate);
// Check that we are not in the middle of a surrogate pair.
void CheckNotInSurrogatePair(int cp_offset, Label* on_failure);
// Controls the generation of large inlined constants in the code.
void set_slow_safe(bool ssc) { slow_safe_compiler_ = ssc; }
bool slow_safe() { return slow_safe_compiler_; }
void set_backtrack_limit(uint32_t backtrack_limit) {
backtrack_limit_ = backtrack_limit;
}
enum GlobalMode {
NOT_GLOBAL,
GLOBAL_NO_ZERO_LENGTH_CHECK,
GLOBAL,
GLOBAL_UNICODE
};
// Set whether the regular expression has the global flag. Exiting due to
// a failure in a global regexp may still mean success overall.
inline void set_global_mode(GlobalMode mode) { global_mode_ = mode; }
inline bool global() { return global_mode_ != NOT_GLOBAL; }
inline bool global_with_zero_length_check() {
return global_mode_ == GLOBAL || global_mode_ == GLOBAL_UNICODE;
}
inline bool global_unicode() { return global_mode_ == GLOBAL_UNICODE; }
Isolate* isolate() const { return isolate_; }
Zone* zone() const { return zone_; }
protected:
bool has_backtrack_limit() const {
return backtrack_limit_ != JSRegExp::kNoBacktrackLimit;
}
uint32_t backtrack_limit() const { return backtrack_limit_; }
private:
bool slow_safe_compiler_;
uint32_t backtrack_limit_ = JSRegExp::kNoBacktrackLimit;
GlobalMode global_mode_;
Isolate* isolate_;
Zone* zone_;
};
class NativeRegExpMacroAssembler: public RegExpMacroAssembler {
public:
// Type of input string to generate code for.
enum Mode { LATIN1 = 1, UC16 = 2 };
// Result of calling generated native RegExp code.
// RETRY: Something significant changed during execution, and the matching
// should be retried from scratch.
// EXCEPTION: Something failed during execution. If no exception has been
// thrown, it's an internal out-of-memory, and the caller should
// throw the exception.
// FAILURE: Matching failed.
// SUCCESS: Matching succeeded, and the output array has been filled with
// capture positions.
enum Result {
FAILURE = RegExp::kInternalRegExpFailure,
SUCCESS = RegExp::kInternalRegExpSuccess,
EXCEPTION = RegExp::kInternalRegExpException,
RETRY = RegExp::kInternalRegExpRetry,
};
NativeRegExpMacroAssembler(Isolate* isolate, Zone* zone);
~NativeRegExpMacroAssembler() override;
bool CanReadUnaligned() override;
// Returns a {Result} sentinel, or the number of successful matches.
static int Match(Handle<JSRegExp> regexp, Handle<String> subject,
int* offsets_vector, int offsets_vector_length,
int previous_index, Isolate* isolate);
// Called from RegExp if the backtrack stack limit is hit.
// Tries to expand the stack. Returns the new stack-pointer if
// successful, and updates the stack_top address, or returns 0 if unable
// to grow the stack.
// This function must not trigger a garbage collection.
static Address GrowStack(Address stack_pointer, Address* stack_top,
Isolate* isolate);
static int CheckStackGuardState(Isolate* isolate, int start_index,
RegExp::CallOrigin call_origin,
Address* return_address, Code re_code,
Address* subject, const byte** input_start,
const byte** input_end);
// Byte map of one byte characters with a 0xff if the character is a word
// character (digit, letter or underscore) and 0x00 otherwise.
// Used by generated RegExp code.
static const byte word_character_map[256];
static Address word_character_map_address() {
return reinterpret_cast<Address>(&word_character_map[0]);
}
// Returns a {Result} sentinel, or the number of successful matches.
V8_EXPORT_PRIVATE static int Execute(String input, int start_offset,
const byte* input_start,
const byte* input_end, int* output,
int output_size, Isolate* isolate,
JSRegExp regexp);
};
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_MACRO_ASSEMBLER_H_
File diff suppressed because it is too large. Load diff
Loaded 100 of 332 files, more files were not shown because too many files have changed in this diff. Show more