mirror of
https://github.com/Basilisk-Development-Team/UXP-mirror.git
synced 2026-11-04 17:00:06 -05:00
Compare commits
100
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6b9d9cbea | ||
|
|
b863bd2edc | ||
|
|
a8a397fbce | ||
|
|
a89597b5db | ||
|
|
074d194776 | ||
|
|
76d62cfc38 | ||
|
|
eefaee9064 | ||
|
|
1864bbec2a | ||
|
|
5771487106 | ||
|
|
887fface56 | ||
|
|
4782e8dfa2 | ||
|
|
bd061ac043 | ||
|
|
31a64578e2 | ||
|
|
85dc118aac | ||
|
|
2f3cf3c8cf | ||
|
|
241f06bc53 | ||
|
|
ecb827c5c4 | ||
|
|
fa5aba4c0f | ||
|
|
b6d909ad17 | ||
|
|
6bc5748b00 | ||
|
|
3abb8e28ad | ||
|
|
18df01eb54 | ||
|
|
8aa9355535 | ||
|
|
511ac54cab | ||
|
|
6d2e301d90 | ||
|
|
2d8013526a | ||
|
|
d86f49ba59 | ||
|
|
870fd86e13 | ||
|
|
13f505539c | ||
|
|
5262e01a4e | ||
|
|
4aeaea9195 | ||
|
|
5ef801fdc5 | ||
|
|
0ba1d49ae5 | ||
|
|
e8a6ee355f | ||
|
|
4694c60964 | ||
|
|
74ebcea409 | ||
|
|
c732425e1a | ||
|
|
51468e998c | ||
|
|
77746f1d90 | ||
|
|
cb0208163f | ||
|
|
19499014a3 | ||
|
|
78b3a722b4 | ||
|
|
2e07199197 | ||
|
|
d2b6975eb7 | ||
|
|
fd1b2dc2b1 | ||
|
|
cdf46e803b | ||
|
|
59511eb8dd | ||
|
|
ff355fe9ac | ||
|
|
1abc696f87 | ||
|
|
2f50f543a2 | ||
|
|
6f76f1cb36 | ||
|
|
383bc182e3 | ||
|
|
95e057e737 | ||
|
|
3d9bf5d7a3 | ||
|
|
65eac50e2d | ||
|
|
957f19d2bd | ||
|
|
bc450dab41 | ||
|
|
1396383aee | ||
|
|
eedfa63a1d | ||
|
|
24b835f0a9 | ||
|
|
24a5a7f7c9 | ||
|
|
ec13658ab9 | ||
|
|
0cdd39ff12 | ||
|
|
5bf19933d8 | ||
|
|
395ef79b91 | ||
|
|
bb4e9ad1c8 | ||
|
|
9b1406f183 | ||
|
|
d22717ff98 | ||
|
|
9ffc5e6c92 | ||
|
|
a2c26490b4 | ||
|
|
7c2bcc48c4 | ||
|
|
49765b53af | ||
|
|
6a4c3caa8e | ||
|
|
0daf842cf4 | ||
|
|
1950e886b9 | ||
|
|
6eba9263cc | ||
|
|
2cd89d5845 | ||
|
|
d5e5bef3ca | ||
|
|
28119b040d | ||
|
|
3b224b2b26 | ||
|
|
33a402947c | ||
|
|
3e59623312 | ||
|
|
e8ae76d473 | ||
|
|
5072843ee3 | ||
|
|
23a30020df | ||
|
|
95b4e5084b | ||
|
|
1fc996152b | ||
|
|
8e3832bacb | ||
|
|
87c4c77ad7 | ||
|
|
bda6f1a93f | ||
|
|
521f2b476c | ||
|
|
dadef50bdc | ||
|
|
8e18743ab8 | ||
|
|
15acac52b1 | ||
|
|
96736fce75 | ||
|
|
b8c604196b | ||
|
|
bc531bfbba | ||
|
|
fb086631d9 | ||
|
|
4e72f8ed03 | ||
|
|
a2ed139a44 |
No files matched your search
@@ -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
@@ -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).
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
# hardcoded milestones in the tree from these two files.
|
||||
#--------------------------------------------------------
|
||||
|
||||
4.6.0
|
||||
4.7.0
|
||||
@@ -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
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
@@ -94,7 +94,7 @@ exports.menuitems = [
|
||||
},
|
||||
key: {
|
||||
id: "browserToolbox",
|
||||
modifiers: "accel,alt,shift",
|
||||
modifiers: isMac ? "accel,alt" : "accel,shift",
|
||||
keytext: true
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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
|
||||
//*****************************************************************************
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -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 += [
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,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/. */
|
||||
|
||||
|
||||
@@ -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
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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',
|
||||
]
|
||||
@@ -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
|
||||
@@ -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',
|
||||
]
|
||||
@@ -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',
|
||||
]
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
Library('editline')
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
SOURCES += [
|
||||
'editline.c',
|
||||
'sysunix.c',
|
||||
]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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']
|
||||
@@ -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;
|
||||
|
||||
@@ -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*) \
|
||||
|
||||
@@ -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']
|
||||
@@ -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']
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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'
|
||||
]
|
||||
@@ -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
|
||||
@@ -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' ]
|
||||
@@ -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']
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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'
|
||||
]
|
||||
@@ -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',
|
||||
]
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
Imported using import-irregexp.py from:
|
||||
https://github.com/v8/v8/tree/560f2d8bb3f3a72d78e1a7d7654235d53fdcc83c/src/regexp
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
Reference in new issue
Block a user