[JSC] SamplingProfiler should recognize RegExp execution
https://bugs.webkit.org/show_bug.cgi?id=201702
Reviewed by Saam Barati.
JSTests:
* stress/sampling-profiler-regexp.js: Added.
(platformSupportsSamplingProfiler.getText):
(platformSupportsSamplingProfiler.test):
(platformSupportsSamplingProfiler.baz):
(platformSupportsSamplingProfiler):
* stress/sampling-profiler/samplingProfiler.js: Extend samplingProfiler to show better error information when VERBOSE = true.
(doesTreeHaveStackTrace):
Source/JavaScriptCore:
This patch extends SamplingProfiler to recognize RegExp execution. We record
executing RegExp in VM via MatchingContextHolder so that SamplingProfiler can detect it.
We use MatchingContextHolder even if the RegExp is interpreter mode so that we can still
catch non-JIT RegExp in SamplingProfiler.
The example output is the following.
Sampling rate: 1000.000000 microseconds. Total samples: 1830
Top functions as <numSamples 'functionName#hash:sourceID'>
466 'Exec#<nil>:4'
225 '/(^|[^\\])\"\\\/Qngr\((-?[0-9]+)\)\\\/\"/g#<nil>:-1'
173 'runBlock1#<nil>:4'
88 '/NQ_VQ/g#<nil>:-1'
83 '/d2/g#<nil>:-1'
78 '/d1/g#<nil>:-1'
70 '/\s?;\s?/#<nil>:-1'
68 'replace#<nil>:3'
50 '/(((\w+):\/\/)([^\/:]*)(:(\d+))?)?([^#?]*)(\?([^#]*))?(#(.*))?/#<nil>:-1'
49 'runBlock0#<nil>:4'
46 '#<nil>:-1'
24 '/^\s*|\s*$/g#<nil>:-1'
Sampling rate: 1000.000000 microseconds. Total samples: 1830
Tier breakdown:
-----------------------------------
LLInt: 13 (0.710383%)
Baseline: 54 (2.950820%)
DFG: 187 (10.218579%)
FTL: 612 (33.442623%)
js builtin: 73 (3.989071%)
Wasm: 0 (0.000000%)
Host: 0 (0.000000%)
RegExp: 907 (49.562842%)
C/C++: 0 (0.000000%)
Unknown Executable: 57 (3.114754%)
Hottest bytecodes as <numSamples 'functionName#hash:JITType:bytecodeIndex'>
378 'Exec#<nil>:FTL:bc#27'
225 '/(^|[^\\])\"\\\/Qngr\((-?[0-9]+)\)\\\/\"/g#<nil>:RegExp:<nil>'
88 '/NQ_VQ/g#<nil>:RegExp:<nil>'
83 '/d2/g#<nil>:RegExp:<nil>'
78 '/d1/g#<nil>:RegExp:<nil>'
70 '/\s?;\s?/#<nil>:RegExp:<nil>'
62 'replace#<nil>:FTL:bc#63'
53 'Exec#<nil>:DFG:bc#27'
50 '/(((\w+):\/\/)([^\/:]*)(:(\d+))?)?([^#?]*)(\?([^#]*))?(#(.*))?/#<nil>:RegExp:<nil>'
46 '#<nil>:None:<nil>'
42 'runBlock1#<nil>:FTL:bc#1795'
29 'runBlock1#<nil>:FTL:bc#1849'
29 'runBlock1#<nil>:FTL:bc#1741'
24 '/^\s*|\s*$/g#<nil>:RegExp:<nil>'
17 'runBlock1#<nil>:DFG:bc#1741'
17 'runBlock1#<nil>:DFG:bc#1795'
16 '/\b\w+\b/g#<nil>:RegExp:<nil>'
14 '/TNQP=([^;]*)/i#<nil>:RegExp:<nil>'
13 '/%\w?$/#<nil>:RegExp:<nil>'
12 '/^ba/#<nil>:RegExp:<nil>'
11 '/^\s*(\S*(\s+\S+)*)\s*$/#<nil>:RegExp:<nil>'
10 'runBlock0#<nil>:FTL:bc#400'
10 'runBlock1#<nil>:DFG:bc#1849'
10 'Exec#<nil>:DFG:bc#16'
9 '/(?:ZFVR.(\d+\.\d+))|(?:(?:Sversbk|TenaCnenqvfb|Vprjrnfry).(\d+\.\d+))|(?:Bcren.(\d+\.\d+))|(?:NccyrJroXvg.(\d+(?:\.\d+)?))/#<nil>:RegExp:<nil>'
8 '/##yv18##/gi#<nil>:RegExp:<nil>'
8 '/([a-zA-Z]|\s)+/#<nil>:RegExp:<nil>'
7 '/JroXvg\/(\S+)/#<nil>:RegExp:<nil>'
7 'runBlock0#<nil>:FTL:bc#2671'
7 '/^([#.]?)((?:[\w\u0128-\uffff*_-]|\\.)*)/#<nil>:RegExp:<nil>'
6 '/##oe##/gi#<nil>:RegExp:<nil>'
6 'runBlock1#<nil>:FTL:bc#124'
6 '/[+, ]/#<nil>:RegExp:<nil>'
6 'runBlock1#<nil>:FTL:bc#1903'
6 'replace#<nil>:DFG:bc#63'
5 'runBlock0#<nil>:FTL:bc#3378'
5 '/'/g#<nil>:RegExp:<nil>'
5 '/##yv20##/gi#<nil>:RegExp:<nil>'
4 '/\?[\w\W]*(sevraqvq|punaaryvq|tebhcvq)=([^\&\?#]*)/i#<nil>:RegExp:<nil>'
4 'load#<nil>:None:<nil>'
* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* runtime/RegExp.cpp:
(JSC::RegExp::matchCompareWithInterpreter):
(JSC::RegExp::dumpToStream):
(JSC::appendLineTerminatorEscape<LChar>):
(JSC::appendLineTerminatorEscape<UChar>):
(JSC::escapePattern):
(JSC::RegExp::escapedPattern const): Moved from RegExpPrototype.cpp to implement RegExp::toSourceString and RegExp::escapedPattern.
(JSC::RegExp::toSourceString const):
(JSC::regexpToSourceString): Deleted.
* runtime/RegExp.h:
* runtime/RegExpInlines.h:
(JSC::RegExp::matchInline):
* runtime/RegExpPrototype.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
(JSC::appendLineTerminatorEscape<LChar>): Deleted.
(JSC::appendLineTerminatorEscape<UChar>): Deleted.
(JSC::regExpProtoGetterSourceInternal): Deleted.
* runtime/SamplingProfiler.cpp:
(JSC::SamplingProfiler::takeSample):
(JSC::SamplingProfiler::processUnverifiedStackTraces):
(JSC::SamplingProfiler::StackFrame::nameFromCallee):
(JSC::SamplingProfiler::StackFrame::displayName):
(JSC::SamplingProfiler::StackFrame::displayNameForJSONTests):
(JSC::SamplingProfiler::StackFrame::functionStartLine):
(JSC::SamplingProfiler::StackFrame::functionStartColumn):
(JSC::SamplingProfiler::StackFrame::sourceID):
(JSC::SamplingProfiler::StackFrame::url):
(JSC::SamplingProfiler::reportTopBytecodes):
(WTF::printInternal):
* runtime/SamplingProfiler.h:
* runtime/VM.h:
* yarr/YarrJIT.cpp:
(JSC::Yarr::MatchingContextHolder::MatchingContextHolder): Deleted.
(JSC::Yarr::MatchingContextHolder::~MatchingContextHolder): Deleted.
* yarr/YarrJIT.h:
(JSC::Yarr::MatchingContextHolder::offsetOfStackLimit): Deleted.
(JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBuffer): Deleted.
(JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBufferSize): Deleted.
* yarr/YarrMatchingContextHolder.h: Added.
(JSC::Yarr::MatchingContextHolder::offsetOfStackLimit):
(JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBuffer):
(JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBufferSize):
(JSC::Yarr::MatchingContextHolder::MatchingContextHolder):
(JSC::Yarr::MatchingContextHolder::~MatchingContextHolder):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@279976 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog
index caed5c3..b49817f 100644
--- a/JSTests/ChangeLog
+++ b/JSTests/ChangeLog
@@ -1,5 +1,20 @@
2021-07-15 Yusuke Suzuki <ysuzuki@apple.com>
+ [JSC] SamplingProfiler should recognize RegExp execution
+ https://bugs.webkit.org/show_bug.cgi?id=201702
+
+ Reviewed by Saam Barati.
+
+ * stress/sampling-profiler-regexp.js: Added.
+ (platformSupportsSamplingProfiler.getText):
+ (platformSupportsSamplingProfiler.test):
+ (platformSupportsSamplingProfiler.baz):
+ (platformSupportsSamplingProfiler):
+ * stress/sampling-profiler/samplingProfiler.js: Extend samplingProfiler to show better error information when VERBOSE = true.
+ (doesTreeHaveStackTrace):
+
+2021-07-15 Yusuke Suzuki <ysuzuki@apple.com>
+
[JSC] Harden defaultTimeZone retrieval
https://bugs.webkit.org/show_bug.cgi?id=227996
diff --git a/JSTests/stress/sampling-profiler-regexp.js b/JSTests/stress/sampling-profiler-regexp.js
new file mode 100644
index 0000000..966a6e1
--- /dev/null
+++ b/JSTests/stress/sampling-profiler-regexp.js
@@ -0,0 +1,24 @@
+if (platformSupportsSamplingProfiler()) {
+ load("./sampling-profiler/samplingProfiler.js");
+
+ var text = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`.repeat(1000);
+ function getText()
+ {
+ return text;
+ }
+ noInline(getText);
+
+ function test(regexp)
+ {
+ return getText().match(regexp);
+ }
+ noInline(test);
+
+ function baz() {
+ var regexp = /(.+)/gi;
+ for (var i = 0; i < 1e1; ++i)
+ test(regexp);
+ }
+
+ runTest(baz, ["/(.+)/gi", "", "test", "baz"]);
+}
diff --git a/JSTests/stress/sampling-profiler/samplingProfiler.js b/JSTests/stress/sampling-profiler/samplingProfiler.js
index 3767a23..bc236ae 100644
--- a/JSTests/stress/sampling-profiler/samplingProfiler.js
+++ b/JSTests/stress/sampling-profiler/samplingProfiler.js
@@ -34,7 +34,9 @@
}
}
-function doesTreeHaveStackTrace(tree, stackTrace, isRunFromRunTest = true, verbose = false) {
+const VERBOSE = false;
+
+function doesTreeHaveStackTrace(tree, stackTrace, isRunFromRunTest = true) {
// stack trace should be top-down array with the deepest
// call frame at index 0.
if (isRunFromRunTest)
@@ -46,8 +48,10 @@
for (let i = stackTrace.length; i--; ) {
node = node.children[stackTrace[i]];
if (!node) {
- if (verbose)
+ if (VERBOSE) {
print("failing on " + i + " : " + stackTrace[i]);
+ print(JSON.stringify(tree));
+ }
return false;
}
}
@@ -60,7 +64,6 @@
return root;
}
-const VERBOSE = false;
// This test suite assumes that "runTest" is being called
// from the global scope.
function runTest(func, stackTrace) {
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index 8737928..9877e0c 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -1160,6 +1160,7 @@
yarr/YarrFlags.h
yarr/YarrInterpreter.h
yarr/YarrJIT.h
+ yarr/YarrMatchingContextHolder.h
yarr/YarrParser.h
yarr/YarrPattern.h
yarr/YarrUnicodeProperties.h
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 1145572..d379c1c 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,5 +1,140 @@
2021-07-15 Yusuke Suzuki <ysuzuki@apple.com>
+ [JSC] SamplingProfiler should recognize RegExp execution
+ https://bugs.webkit.org/show_bug.cgi?id=201702
+
+ Reviewed by Saam Barati.
+
+ This patch extends SamplingProfiler to recognize RegExp execution. We record
+ executing RegExp in VM via MatchingContextHolder so that SamplingProfiler can detect it.
+ We use MatchingContextHolder even if the RegExp is interpreter mode so that we can still
+ catch non-JIT RegExp in SamplingProfiler.
+
+ The example output is the following.
+
+ Sampling rate: 1000.000000 microseconds. Total samples: 1830
+ Top functions as <numSamples 'functionName#hash:sourceID'>
+ 466 'Exec#<nil>:4'
+ 225 '/(^|[^\\])\"\\\/Qngr\((-?[0-9]+)\)\\\/\"/g#<nil>:-1'
+ 173 'runBlock1#<nil>:4'
+ 88 '/NQ_VQ/g#<nil>:-1'
+ 83 '/d2/g#<nil>:-1'
+ 78 '/d1/g#<nil>:-1'
+ 70 '/\s?;\s?/#<nil>:-1'
+ 68 'replace#<nil>:3'
+ 50 '/(((\w+):\/\/)([^\/:]*)(:(\d+))?)?([^#?]*)(\?([^#]*))?(#(.*))?/#<nil>:-1'
+ 49 'runBlock0#<nil>:4'
+ 46 '#<nil>:-1'
+ 24 '/^\s*|\s*$/g#<nil>:-1'
+
+
+ Sampling rate: 1000.000000 microseconds. Total samples: 1830
+
+ Tier breakdown:
+ -----------------------------------
+ LLInt: 13 (0.710383%)
+ Baseline: 54 (2.950820%)
+ DFG: 187 (10.218579%)
+ FTL: 612 (33.442623%)
+ js builtin: 73 (3.989071%)
+ Wasm: 0 (0.000000%)
+ Host: 0 (0.000000%)
+ RegExp: 907 (49.562842%)
+ C/C++: 0 (0.000000%)
+ Unknown Executable: 57 (3.114754%)
+
+
+ Hottest bytecodes as <numSamples 'functionName#hash:JITType:bytecodeIndex'>
+ 378 'Exec#<nil>:FTL:bc#27'
+ 225 '/(^|[^\\])\"\\\/Qngr\((-?[0-9]+)\)\\\/\"/g#<nil>:RegExp:<nil>'
+ 88 '/NQ_VQ/g#<nil>:RegExp:<nil>'
+ 83 '/d2/g#<nil>:RegExp:<nil>'
+ 78 '/d1/g#<nil>:RegExp:<nil>'
+ 70 '/\s?;\s?/#<nil>:RegExp:<nil>'
+ 62 'replace#<nil>:FTL:bc#63'
+ 53 'Exec#<nil>:DFG:bc#27'
+ 50 '/(((\w+):\/\/)([^\/:]*)(:(\d+))?)?([^#?]*)(\?([^#]*))?(#(.*))?/#<nil>:RegExp:<nil>'
+ 46 '#<nil>:None:<nil>'
+ 42 'runBlock1#<nil>:FTL:bc#1795'
+ 29 'runBlock1#<nil>:FTL:bc#1849'
+ 29 'runBlock1#<nil>:FTL:bc#1741'
+ 24 '/^\s*|\s*$/g#<nil>:RegExp:<nil>'
+ 17 'runBlock1#<nil>:DFG:bc#1741'
+ 17 'runBlock1#<nil>:DFG:bc#1795'
+ 16 '/\b\w+\b/g#<nil>:RegExp:<nil>'
+ 14 '/TNQP=([^;]*)/i#<nil>:RegExp:<nil>'
+ 13 '/%\w?$/#<nil>:RegExp:<nil>'
+ 12 '/^ba/#<nil>:RegExp:<nil>'
+ 11 '/^\s*(\S*(\s+\S+)*)\s*$/#<nil>:RegExp:<nil>'
+ 10 'runBlock0#<nil>:FTL:bc#400'
+ 10 'runBlock1#<nil>:DFG:bc#1849'
+ 10 'Exec#<nil>:DFG:bc#16'
+ 9 '/(?:ZFVR.(\d+\.\d+))|(?:(?:Sversbk|TenaCnenqvfb|Vprjrnfry).(\d+\.\d+))|(?:Bcren.(\d+\.\d+))|(?:NccyrJroXvg.(\d+(?:\.\d+)?))/#<nil>:RegExp:<nil>'
+ 8 '/##yv18##/gi#<nil>:RegExp:<nil>'
+ 8 '/([a-zA-Z]|\s)+/#<nil>:RegExp:<nil>'
+ 7 '/JroXvg\/(\S+)/#<nil>:RegExp:<nil>'
+ 7 'runBlock0#<nil>:FTL:bc#2671'
+ 7 '/^([#.]?)((?:[\w\u0128-\uffff*_-]|\\.)*)/#<nil>:RegExp:<nil>'
+ 6 '/##oe##/gi#<nil>:RegExp:<nil>'
+ 6 'runBlock1#<nil>:FTL:bc#124'
+ 6 '/[+, ]/#<nil>:RegExp:<nil>'
+ 6 'runBlock1#<nil>:FTL:bc#1903'
+ 6 'replace#<nil>:DFG:bc#63'
+ 5 'runBlock0#<nil>:FTL:bc#3378'
+ 5 '/'/g#<nil>:RegExp:<nil>'
+ 5 '/##yv20##/gi#<nil>:RegExp:<nil>'
+ 4 '/\?[\w\W]*(sevraqvq|punaaryvq|tebhcvq)=([^\&\?#]*)/i#<nil>:RegExp:<nil>'
+ 4 'load#<nil>:None:<nil>'
+
+ * CMakeLists.txt:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * runtime/RegExp.cpp:
+ (JSC::RegExp::matchCompareWithInterpreter):
+ (JSC::RegExp::dumpToStream):
+ (JSC::appendLineTerminatorEscape<LChar>):
+ (JSC::appendLineTerminatorEscape<UChar>):
+ (JSC::escapePattern):
+ (JSC::RegExp::escapedPattern const): Moved from RegExpPrototype.cpp to implement RegExp::toSourceString and RegExp::escapedPattern.
+ (JSC::RegExp::toSourceString const):
+ (JSC::regexpToSourceString): Deleted.
+ * runtime/RegExp.h:
+ * runtime/RegExpInlines.h:
+ (JSC::RegExp::matchInline):
+ * runtime/RegExpPrototype.cpp:
+ (JSC::JSC_DEFINE_HOST_FUNCTION):
+ (JSC::appendLineTerminatorEscape<LChar>): Deleted.
+ (JSC::appendLineTerminatorEscape<UChar>): Deleted.
+ (JSC::regExpProtoGetterSourceInternal): Deleted.
+ * runtime/SamplingProfiler.cpp:
+ (JSC::SamplingProfiler::takeSample):
+ (JSC::SamplingProfiler::processUnverifiedStackTraces):
+ (JSC::SamplingProfiler::StackFrame::nameFromCallee):
+ (JSC::SamplingProfiler::StackFrame::displayName):
+ (JSC::SamplingProfiler::StackFrame::displayNameForJSONTests):
+ (JSC::SamplingProfiler::StackFrame::functionStartLine):
+ (JSC::SamplingProfiler::StackFrame::functionStartColumn):
+ (JSC::SamplingProfiler::StackFrame::sourceID):
+ (JSC::SamplingProfiler::StackFrame::url):
+ (JSC::SamplingProfiler::reportTopBytecodes):
+ (WTF::printInternal):
+ * runtime/SamplingProfiler.h:
+ * runtime/VM.h:
+ * yarr/YarrJIT.cpp:
+ (JSC::Yarr::MatchingContextHolder::MatchingContextHolder): Deleted.
+ (JSC::Yarr::MatchingContextHolder::~MatchingContextHolder): Deleted.
+ * yarr/YarrJIT.h:
+ (JSC::Yarr::MatchingContextHolder::offsetOfStackLimit): Deleted.
+ (JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBuffer): Deleted.
+ (JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBufferSize): Deleted.
+ * yarr/YarrMatchingContextHolder.h: Added.
+ (JSC::Yarr::MatchingContextHolder::offsetOfStackLimit):
+ (JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBuffer):
+ (JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBufferSize):
+ (JSC::Yarr::MatchingContextHolder::MatchingContextHolder):
+ (JSC::Yarr::MatchingContextHolder::~MatchingContextHolder):
+
+2021-07-15 Yusuke Suzuki <ysuzuki@apple.com>
+
[JSC] Harden defaultTimeZone retrieval
https://bugs.webkit.org/show_bug.cgi?id=227996
rdar://80540300
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index b0ca1cb..0d1cb32 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -1929,6 +1929,7 @@
E3D3515F241B89D7008DC16E /* MarkedJSValueRefArray.h in Headers */ = {isa = PBXBuildFile; fileRef = E3D3515D241B89CE008DC16E /* MarkedJSValueRefArray.h */; };
E3D877741E65C0A000BE945A /* BytecodeDumper.h in Headers */ = {isa = PBXBuildFile; fileRef = E3D877721E65C08900BE945A /* BytecodeDumper.h */; };
E3EE137621FBD43500D83C4B /* ErrorType.h in Headers */ = {isa = PBXBuildFile; fileRef = E3EE137421FBD43400D83C4B /* ErrorType.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ E3F0FC9526A11B4A0099FFA0 /* YarrMatchingContextHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = E3F0FC9426A11B4A0099FFA0 /* YarrMatchingContextHolder.h */; settings = {ATTRIBUTES = (Private, ); }; };
E3F2193724C7882F003AE453 /* IntlSegmentIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = E3F2193124C78829003AE453 /* IntlSegmentIterator.h */; };
E3F2193824C78831003AE453 /* IntlSegmentIteratorPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = E3F2192F24C78829003AE453 /* IntlSegmentIteratorPrototype.h */; };
E3F2193924C78834003AE453 /* IntlSegments.h in Headers */ = {isa = PBXBuildFile; fileRef = E3F2193324C7882A003AE453 /* IntlSegments.h */; };
@@ -5230,6 +5231,7 @@
E3E9F8D525D7582F00F9F84B /* process-entitlements.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "process-entitlements.sh"; sourceTree = "<group>"; };
E3EE137421FBD43400D83C4B /* ErrorType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ErrorType.h; sourceTree = "<group>"; };
E3EE137521FBD43400D83C4B /* ErrorType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorType.cpp; sourceTree = "<group>"; };
+ E3F0FC9426A11B4A0099FFA0 /* YarrMatchingContextHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YarrMatchingContextHolder.h; path = yarr/YarrMatchingContextHolder.h; sourceTree = "<group>"; };
E3F2192F24C78829003AE453 /* IntlSegmentIteratorPrototype.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IntlSegmentIteratorPrototype.h; sourceTree = "<group>"; };
E3F2193024C78829003AE453 /* IntlSegmentIterator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = IntlSegmentIterator.cpp; sourceTree = "<group>"; };
E3F2193124C78829003AE453 /* IntlSegmentIterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IntlSegmentIterator.h; sourceTree = "<group>"; };
@@ -8081,6 +8083,7 @@
86704B7E12DBA33700A9FE7B /* YarrInterpreter.h */,
86704B7F12DBA33700A9FE7B /* YarrJIT.cpp */,
86704B8012DBA33700A9FE7B /* YarrJIT.h */,
+ E3F0FC9426A11B4A0099FFA0 /* YarrMatchingContextHolder.h */,
86704B8112DBA33700A9FE7B /* YarrParser.h */,
86704B8212DBA33700A9FE7B /* YarrPattern.cpp */,
86704B8312DBA33700A9FE7B /* YarrPattern.h */,
@@ -10843,6 +10846,7 @@
A3FF9BC72234749100B1A9AB /* YarrFlags.h in Headers */,
86704B8512DBA33700A9FE7B /* YarrInterpreter.h in Headers */,
86704B8712DBA33700A9FE7B /* YarrJIT.h in Headers */,
+ E3F0FC9526A11B4A0099FFA0 /* YarrMatchingContextHolder.h in Headers */,
86704B8812DBA33700A9FE7B /* YarrParser.h in Headers */,
86704B8A12DBA33700A9FE7B /* YarrPattern.h in Headers */,
86704B4312DB8A8100A9FE7B /* YarrSyntaxChecker.h in Headers */,
diff --git a/Source/JavaScriptCore/runtime/RegExp.cpp b/Source/JavaScriptCore/runtime/RegExp.cpp
index 8c382d4..c15e4f3 100644
--- a/Source/JavaScriptCore/runtime/RegExp.cpp
+++ b/Source/JavaScriptCore/runtime/RegExp.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 1999-2001, 2004 Harri Porten (porten@kde.org)
- * Copyright (c) 2007-2020 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2021 Apple Inc. All rights reserved.
* Copyright (C) 2009 Torch Mobile, Inc.
* Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged
*
@@ -23,6 +23,7 @@
#include "config.h"
#include "RegExp.h"
+#include "Lexer.h"
#include "RegExpCache.h"
#include "RegExpInlines.h"
#include "YarrJIT.h"
@@ -392,7 +393,7 @@
differences++;
if (differences) {
- dataLogF("RegExp Discrepency for /%s/\n string input ", pattern().utf8().data());
+ dataLog("RegExp Discrepency for ", toSourceString(), "\n string input ");
unsigned segmentLen = s.length() - static_cast<unsigned>(startOffset);
dataLogF((segmentLen < 150) ? "\"%s\"\n" : "\"%148s...\"\n", s.utf8().data() + startOffset);
@@ -468,14 +469,127 @@
}
#endif
-static CString regexpToSourceString(const RegExp* regExp)
-{
- return toCString("/", regExp->pattern().impl(), "/", Yarr::flagsString(regExp->flags()).data());
-}
-
void RegExp::dumpToStream(const JSCell* cell, PrintStream& out)
{
- out.print(regexpToSourceString(jsCast<const RegExp*>(cell)));
+ out.print(jsCast<const RegExp*>(cell)->toSourceString());
+}
+
+template <typename CharacterType>
+static inline void appendLineTerminatorEscape(StringBuilder&, CharacterType);
+
+template <>
+inline void appendLineTerminatorEscape<LChar>(StringBuilder& builder, LChar lineTerminator)
+{
+ if (lineTerminator == '\n')
+ builder.append('n');
+ else
+ builder.append('r');
+}
+
+template <>
+inline void appendLineTerminatorEscape<UChar>(StringBuilder& builder, UChar lineTerminator)
+{
+ if (lineTerminator == '\n')
+ builder.append('n');
+ else if (lineTerminator == '\r')
+ builder.append('r');
+ else if (lineTerminator == 0x2028)
+ builder.append("u2028");
+ else
+ builder.append("u2029");
+}
+
+template <typename CharacterType>
+static inline String escapePattern(const String& pattern, const CharacterType* characters, unsigned length)
+{
+ bool previousCharacterWasBackslash = false;
+ bool inBrackets = false;
+ bool shouldEscape = false;
+
+ // 15.10.6.4 specifies that RegExp.prototype.toString must return '/' + source + '/',
+ // and also states that the result must be a valid RegularExpressionLiteral. '//' is
+ // not a valid RegularExpressionLiteral (since it is a single line comment), and hence
+ // source cannot ever validly be "". If the source is empty, return a different Pattern
+ // that would match the same thing.
+ if (!length)
+ return "(?:)"_s;
+
+ // early return for strings that don't contain a forwards slash and LineTerminator
+ for (unsigned i = 0; i < length; ++i) {
+ CharacterType ch = characters[i];
+ if (!previousCharacterWasBackslash) {
+ if (inBrackets) {
+ if (ch == ']')
+ inBrackets = false;
+ } else {
+ if (ch == '/') {
+ shouldEscape = true;
+ break;
+ }
+ if (ch == '[')
+ inBrackets = true;
+ }
+ }
+
+ if (Lexer<CharacterType>::isLineTerminator(ch)) {
+ shouldEscape = true;
+ break;
+ }
+
+ if (previousCharacterWasBackslash)
+ previousCharacterWasBackslash = false;
+ else
+ previousCharacterWasBackslash = ch == '\\';
+ }
+
+ if (!shouldEscape)
+ return pattern;
+
+ previousCharacterWasBackslash = false;
+ inBrackets = false;
+ StringBuilder result;
+ for (unsigned i = 0; i < length; ++i) {
+ CharacterType ch = characters[i];
+ if (!previousCharacterWasBackslash) {
+ if (inBrackets) {
+ if (ch == ']')
+ inBrackets = false;
+ } else {
+ if (ch == '/')
+ result.append('\\');
+ else if (ch == '[')
+ inBrackets = true;
+ }
+ }
+
+ // escape LineTerminator
+ if (Lexer<CharacterType>::isLineTerminator(ch)) {
+ if (!previousCharacterWasBackslash)
+ result.append('\\');
+
+ appendLineTerminatorEscape<CharacterType>(result, ch);
+ } else
+ result.append(ch);
+
+ if (previousCharacterWasBackslash)
+ previousCharacterWasBackslash = false;
+ else
+ previousCharacterWasBackslash = ch == '\\';
+ }
+
+ return result.toString();
+}
+
+String RegExp::escapedPattern() const
+{
+ if (m_patternString.is8Bit())
+ return escapePattern(m_patternString, m_patternString.characters8(), m_patternString.length());
+ return escapePattern(m_patternString, m_patternString.characters16(), m_patternString.length());
+}
+
+String RegExp::toSourceString() const
+{
+ return makeString('/', escapedPattern(), '/', Yarr::flagsString(flags()).data());
}
} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/RegExp.h b/Source/JavaScriptCore/runtime/RegExp.h
index 4e37d9f..ff86fc6 100644
--- a/Source/JavaScriptCore/runtime/RegExp.h
+++ b/Source/JavaScriptCore/runtime/RegExp.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- * Copyright (C) 2007-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2021 Apple Inc. All rights reserved.
* Copyright (C) 2009 Torch Mobile, Inc.
*
* This library is free software; you can redistribute it and/or
@@ -137,6 +137,10 @@
RegExpKey key() { return RegExpKey(m_flags, m_patternString); }
+ String escapedPattern() const;
+
+ String toSourceString() const;
+
private:
friend class RegExpCache;
RegExp(VM&, const String&, OptionSet<Yarr::Flags>);
diff --git a/Source/JavaScriptCore/runtime/RegExpInlines.h b/Source/JavaScriptCore/runtime/RegExpInlines.h
index 326e7ae..6d44ba9 100644
--- a/Source/JavaScriptCore/runtime/RegExpInlines.h
+++ b/Source/JavaScriptCore/runtime/RegExpInlines.h
@@ -27,6 +27,7 @@
#include "Yarr.h"
#include "YarrInterpreter.h"
#include "YarrJIT.h"
+#include "YarrMatchingContextHolder.h"
#define REGEXP_FUNC_TEST_DATA_GEN 0
@@ -131,7 +132,7 @@
if (m_state == JITCode) {
{
ASSERT(m_regExpJITCode);
- Yarr::MatchingContextHolder regExpContext(vm, m_regExpJITCode.get(), matchFrom);
+ Yarr::MatchingContextHolder regExpContext(vm, m_regExpJITCode.get(), this, matchFrom);
if (s.is8Bit())
result = m_regExpJITCode->execute(s.characters8(), startOffset, s.length(), offsetVector, regExpContext).start;
@@ -144,7 +145,10 @@
byteCodeCompileIfNecessary(&vm);
if (m_state == ParseError)
return throwError();
- result = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
+ {
+ Yarr::MatchingContextHolder regExpContext(vm, nullptr, this, matchFrom);
+ result = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
+ }
}
#if ENABLE(YARR_JIT_DEBUG)
@@ -157,7 +161,10 @@
#endif
} else
#endif
+ {
+ Yarr::MatchingContextHolder regExpContext(vm, nullptr, this, matchFrom);
result = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
+ }
// FIXME: The YARR engine should handle unsigned or size_t length matches.
// The YARR Interpreter is "unsigned" clean, while the YARR JIT hasn't been addressed.
@@ -255,12 +262,11 @@
return throwError();
#if ENABLE(YARR_JIT)
- MatchResult result;
-
if (m_state == JITCode) {
+ MatchResult result;
{
ASSERT(m_regExpJITCode);
- Yarr::MatchingContextHolder regExpContext(vm, m_regExpJITCode.get(), matchFrom);
+ Yarr::MatchingContextHolder regExpContext(vm, m_regExpJITCode.get(), this, matchFrom);
if (s.is8Bit())
result = m_regExpJITCode->execute(s.characters8(), startOffset, s.length(), regExpContext);
@@ -284,19 +290,23 @@
int offsetVectorSize = (m_numSubpatterns + 1) * 2;
int* offsetVector;
+ int result;
Vector<int, 32> nonReturnedOvector;
nonReturnedOvector.grow(offsetVectorSize);
offsetVector = nonReturnedOvector.data();
- int r = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
+ {
+ Yarr::MatchingContextHolder regExpContext(vm, nullptr, this, matchFrom);
+ result = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
+ }
#if REGEXP_FUNC_TEST_DATA_GEN
RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result);
#endif
- if (r >= 0) {
+ if (result >= 0) {
#if ENABLE(REGEXP_TRACING)
m_rtMatchOnlyFoundCount++;
#endif
- return MatchResult(r, reinterpret_cast<unsigned*>(offsetVector)[1]);
+ return MatchResult(result, reinterpret_cast<unsigned*>(offsetVector)[1]);
}
return MatchResult::failed();
diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp
index ebf5f06..6c99d23 100644
--- a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp
+++ b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- * Copyright (C) 2003-2020 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2003-2021 Apple Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -27,7 +27,6 @@
#include "JSCJSValue.h"
#include "JSGlobalObject.h"
#include "JSStringInlines.h"
-#include "Lexer.h"
#include "RegExpObject.h"
#include "RegExpObjectInlines.h"
#include "StringRecursionChecker.h"
@@ -339,113 +338,6 @@
return JSValue::encode(jsString(vm, flags.data()));
}
-template <typename CharacterType>
-static inline void appendLineTerminatorEscape(StringBuilder&, CharacterType);
-
-template <>
-inline void appendLineTerminatorEscape<LChar>(StringBuilder& builder, LChar lineTerminator)
-{
- if (lineTerminator == '\n')
- builder.append('n');
- else
- builder.append('r');
-}
-
-template <>
-inline void appendLineTerminatorEscape<UChar>(StringBuilder& builder, UChar lineTerminator)
-{
- if (lineTerminator == '\n')
- builder.append('n');
- else if (lineTerminator == '\r')
- builder.append('r');
- else if (lineTerminator == 0x2028)
- builder.append("u2028");
- else
- builder.append("u2029");
-}
-
-template <typename CharacterType>
-static inline JSValue regExpProtoGetterSourceInternal(JSGlobalObject* globalObject, const String& pattern, const CharacterType* characters, unsigned length)
-{
- VM& vm = globalObject->vm();
- bool previousCharacterWasBackslash = false;
- bool inBrackets = false;
- bool shouldEscape = false;
-
- // 15.10.6.4 specifies that RegExp.prototype.toString must return '/' + source + '/',
- // and also states that the result must be a valid RegularExpressionLiteral. '//' is
- // not a valid RegularExpressionLiteral (since it is a single line comment), and hence
- // source cannot ever validly be "". If the source is empty, return a different Pattern
- // that would match the same thing.
- if (!length)
- return jsNontrivialString(vm, "(?:)"_s);
-
- // early return for strings that don't contain a forwards slash and LineTerminator
- for (unsigned i = 0; i < length; ++i) {
- CharacterType ch = characters[i];
- if (!previousCharacterWasBackslash) {
- if (inBrackets) {
- if (ch == ']')
- inBrackets = false;
- } else {
- if (ch == '/') {
- shouldEscape = true;
- break;
- }
- if (ch == '[')
- inBrackets = true;
- }
- }
-
- if (Lexer<CharacterType>::isLineTerminator(ch)) {
- shouldEscape = true;
- break;
- }
-
- if (previousCharacterWasBackslash)
- previousCharacterWasBackslash = false;
- else
- previousCharacterWasBackslash = ch == '\\';
- }
-
- if (!shouldEscape)
- return jsString(vm, pattern);
-
- previousCharacterWasBackslash = false;
- inBrackets = false;
- StringBuilder result;
- for (unsigned i = 0; i < length; ++i) {
- CharacterType ch = characters[i];
- if (!previousCharacterWasBackslash) {
- if (inBrackets) {
- if (ch == ']')
- inBrackets = false;
- } else {
- if (ch == '/')
- result.append('\\');
- else if (ch == '[')
- inBrackets = true;
- }
- }
-
- // escape LineTerminator
- if (Lexer<CharacterType>::isLineTerminator(ch)) {
- if (!previousCharacterWasBackslash)
- result.append('\\');
-
- appendLineTerminatorEscape<CharacterType>(result, ch);
- } else
- result.append(ch);
-
- if (previousCharacterWasBackslash)
- previousCharacterWasBackslash = false;
- else
- previousCharacterWasBackslash = ch == '\\';
- }
-
- return jsString(vm, result.toString());
-}
-
JSC_DEFINE_HOST_FUNCTION(regExpProtoGetterSource, (JSGlobalObject* globalObject, CallFrame* callFrame))
{
VM& vm = globalObject->vm();
@@ -459,10 +351,7 @@
return throwVMTypeError(globalObject, scope, "The RegExp.prototype.source getter can only be called on a RegExp object"_s);
}
- String pattern = regexp->regExp()->pattern();
- if (pattern.is8Bit())
- return JSValue::encode(regExpProtoGetterSourceInternal(globalObject, pattern, pattern.characters8(), pattern.length()));
- return JSValue::encode(regExpProtoGetterSourceInternal(globalObject, pattern, pattern.characters16(), pattern.length()));
+ return JSValue::encode(jsString(vm, regexp->regExp()->escapedPattern()));
}
JSC_DEFINE_HOST_FUNCTION(regExpProtoFuncSearchFast, (JSGlobalObject* globalObject, CallFrame* callFrame))
diff --git a/Source/JavaScriptCore/runtime/SamplingProfiler.cpp b/Source/JavaScriptCore/runtime/SamplingProfiler.cpp
index 73db61c..b27e8e3 100644
--- a/Source/JavaScriptCore/runtime/SamplingProfiler.cpp
+++ b/Source/JavaScriptCore/runtime/SamplingProfiler.cpp
@@ -366,6 +366,7 @@
CallFrame* callFrame;
void* machinePC;
bool topFrameIsLLInt = false;
+ RegExp* regExp = nullptr;
void* llintPC;
{
PlatformRegisters registers;
@@ -385,16 +386,16 @@
// FIXME: Lets have a way of detecting when we're parsing code.
// https://bugs.webkit.org/show_bug.cgi?id=152761
if (ExecutableAllocator::singleton().isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
- if (m_vm.isExecutingInRegExpJIT) {
- // FIXME: We're executing a regexp. Lets gather more intersting data.
- // https://bugs.webkit.org/show_bug.cgi?id=152729
+ regExp = m_vm.m_executingRegExp;
+ if (regExp)
callFrame = m_vm.topCallFrame; // We need to do this or else we'd fail our backtrace validation b/c this isn't a JS frame.
- }
} else if (LLInt::isLLIntPC(machinePC)) {
topFrameIsLLInt = true;
// We're okay to take a normal stack trace when the PC
// is in LLInt code.
} else {
+ // RegExp evaluation is leaf. So if RegExp evaluation exists, we can say it is RegExp evaluation is the top user-visible frame.
+ regExp = m_vm.m_executingRegExp;
// We resort to topCallFrame to see if we can get anything
// useful. We usually get here when we're executing C code.
callFrame = m_vm.topCallFrame;
@@ -434,7 +435,7 @@
stackTrace.uncheckedAppend(frame);
}
- m_unprocessedStackTraces.append(UnprocessedStackTrace { nowTime, machinePC, topFrameIsLLInt, llintPC, WTFMove(stackTrace) });
+ m_unprocessedStackTraces.append(UnprocessedStackTrace { nowTime, machinePC, topFrameIsLLInt, llintPC, regExp, WTFMove(stackTrace) });
if (didRunOutOfVectorSpace)
m_currentFrames.grow(m_currentFrames.size() * 1.25);
@@ -592,7 +593,14 @@
// Prepend the top-most inlined frame if needed and gather
// location information about where the top frame is executing.
size_t startIndex = 0;
- if (unprocessedStackTrace.frames.size() && !!unprocessedStackTrace.frames[0].verifiedCodeBlock) {
+ if (unprocessedStackTrace.regExp) {
+ // If the stack-trace is annotated with RegExp, the top-frame must be RegExp since RegExp evaluation is leaf function.
+ appendEmptyFrame();
+ stackTrace.frames.last().regExp = unprocessedStackTrace.regExp;
+ stackTrace.frames.last().frameType = FrameType::RegExp;
+ stackTrace.frames.last().semanticLocation.isRegExp = true;
+ m_liveCellPointers.add(unprocessedStackTrace.regExp);
+ } else if (!unprocessedStackTrace.frames.isEmpty() && !!unprocessedStackTrace.frames[0].verifiedCodeBlock) {
CodeBlock* topCodeBlock = unprocessedStackTrace.frames[0].verifiedCodeBlock;
if (unprocessedStackTrace.topFrameIsLLInt) {
// We reuse LLInt CodeBlocks for the baseline JIT, so we need to check for both jit types.
@@ -735,8 +743,11 @@
String SamplingProfiler::StackFrame::nameFromCallee(VM& vm)
{
- if (!callee)
+ if (!callee) {
+ if (regExp)
+ return regExp->toSourceString();
return String();
+ }
DeferTermination deferScope(vm);
auto scope = DECLARE_CATCH_SCOPE(vm);
@@ -782,12 +793,16 @@
}
#endif
return "(unknown C PC)"_s;
+
case FrameType::Unknown:
return "(unknown)"_s;
case FrameType::Host:
return "(host)"_s;
+ case FrameType::RegExp:
+ return "(regexp)"_s;
+
case FrameType::Wasm:
#if ENABLE(WEBASSEMBLY)
if (wasmIndexOrName)
@@ -826,6 +841,9 @@
case FrameType::C:
return "(unknown)"_s;
+ case FrameType::RegExp:
+ return "(regexp)"_s;
+
case FrameType::Host:
return "(host)"_s;
@@ -866,6 +884,7 @@
switch (frameType) {
case FrameType::Unknown:
case FrameType::Host:
+ case FrameType::RegExp:
case FrameType::C:
case FrameType::Wasm:
return -1;
@@ -884,6 +903,7 @@
switch (frameType) {
case FrameType::Unknown:
case FrameType::Host:
+ case FrameType::RegExp:
case FrameType::C:
case FrameType::Wasm:
return std::numeric_limits<unsigned>::max();
@@ -903,6 +923,7 @@
switch (frameType) {
case FrameType::Unknown:
case FrameType::Host:
+ case FrameType::RegExp:
case FrameType::C:
case FrameType::Wasm:
return internalSourceID;
@@ -922,6 +943,7 @@
switch (frameType) {
case FrameType::Unknown:
case FrameType::Host:
+ case FrameType::RegExp:
case FrameType::C:
case FrameType::Wasm:
return emptyString();
@@ -1115,6 +1137,7 @@
String builtin = "js builtin"_s;
String wasm = "Wasm"_s;
String host = "Host"_s;
+ String regexp = "RegExp"_s;
String cpp = "C/C++"_s;
String unknownFrame = "Unknown Frame"_s;
String unknownExecutable = "Unknown Executable"_s;
@@ -1127,6 +1150,7 @@
func(builtin);
func(wasm);
func(host);
+ func(regexp);
func(cpp);
func(unknownFrame);
func(unknownExecutable);
@@ -1143,21 +1167,23 @@
if (location.hasBytecodeIndex())
bytecodeIndex = toString(location.bytecodeIndex);
else
- bytecodeIndex = "<nil>";
+ bytecodeIndex = "<nil>"_s;
if (location.hasCodeBlockHash()) {
StringPrintStream stream;
location.codeBlockHash.dump(stream);
codeBlockHash = stream.toString();
} else
- codeBlockHash = "<nil>";
+ codeBlockHash = "<nil>"_s;
if (wasmCompilationMode)
jitType = Wasm::makeString(wasmCompilationMode.value());
+ else if (location.isRegExp)
+ jitType = "RegExp"_s;
else
jitType = JITCode::typeName(location.jitType);
- return makeString("#", codeBlockHash, ":", jitType, ":", bytecodeIndex);
+ return makeString('#', codeBlockHash, ':', jitType, ':', bytecodeIndex);
};
StackFrame& frame = stackTrace.frames.first();
@@ -1207,6 +1233,9 @@
case SamplingProfiler::FrameType::Host:
tierName = host;
break;
+ case SamplingProfiler::FrameType::RegExp:
+ tierName = regexp;
+ break;
case SamplingProfiler::FrameType::C:
tierName = cpp;
break;
@@ -1291,6 +1320,9 @@
case SamplingProfiler::FrameType::Host:
out.print("Host");
break;
+ case SamplingProfiler::FrameType::RegExp:
+ out.print("RegExp");
+ break;
case SamplingProfiler::FrameType::C:
case SamplingProfiler::FrameType::Unknown:
out.print("Unknown");
diff --git a/Source/JavaScriptCore/runtime/SamplingProfiler.h b/Source/JavaScriptCore/runtime/SamplingProfiler.h
index be6bec2..9af2756 100644
--- a/Source/JavaScriptCore/runtime/SamplingProfiler.h
+++ b/Source/JavaScriptCore/runtime/SamplingProfiler.h
@@ -75,6 +75,7 @@
Executable,
Wasm,
Host,
+ RegExp,
C,
Unknown,
};
@@ -94,6 +95,7 @@
const void* cCodePC { nullptr };
ExecutableBase* executable { nullptr };
JSObject* callee { nullptr };
+ RegExp* regExp { nullptr };
#if ENABLE(WEBASSEMBLY)
std::optional<Wasm::IndexOrName> wasmIndexOrName;
#endif
@@ -122,6 +124,7 @@
BytecodeIndex bytecodeIndex;
CodeBlockHash codeBlockHash;
JITType jitType { JITType::None };
+ bool isRegExp { false };
};
CodeLocation semanticLocation;
@@ -154,6 +157,7 @@
void* topPC;
bool topFrameIsLLInt;
void* llintPC;
+ RegExp* regExp;
Vector<UnprocessedStackFrame> frames;
};
diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h
index c9c53e2..bced984 100644
--- a/Source/JavaScriptCore/runtime/VM.h
+++ b/Source/JavaScriptCore/runtime/VM.h
@@ -217,6 +217,9 @@
namespace DOMJIT {
class Signature;
}
+namespace Yarr {
+class MatchingContextHolder;
+}
struct EntryFrame;
struct HashTable;
@@ -983,7 +986,7 @@
const Instruction* targetInterpreterPCForThrow;
uint32_t osrExitIndex;
void* osrExitJumpDestination;
- bool isExecutingInRegExpJIT { false };
+ RegExp* m_executingRegExp { nullptr };
// The threading protocol here is as follows:
// - You can call scratchBufferForSize from any thread.
diff --git a/Source/JavaScriptCore/yarr/YarrJIT.cpp b/Source/JavaScriptCore/yarr/YarrJIT.cpp
index f4bcc1a..c2a2d88 100644
--- a/Source/JavaScriptCore/yarr/YarrJIT.cpp
+++ b/Source/JavaScriptCore/yarr/YarrJIT.cpp
@@ -32,6 +32,7 @@
#include "Yarr.h"
#include "YarrCanonicalize.h"
#include "YarrDisassembler.h"
+#include "YarrMatchingContextHolder.h"
#include <wtf/ASCIICType.h>
#include <wtf/Threading.h>
@@ -44,34 +45,6 @@
JSC_ANNOTATE_JIT_OPERATION(_JITTarget_vmEntryToYarrJITAfter, vmEntryToYarrJITAfter);
#endif
-MatchingContextHolder::MatchingContextHolder(VM& vm, YarrCodeBlock* yarrCodeBlock, MatchFrom matchFrom)
- : m_vm(vm)
-{
- if (matchFrom == MatchFrom::VMThread)
- m_stackLimit = vm.softStackLimit();
- else {
- StackBounds stack = Thread::current().stack();
- m_stackLimit = stack.recursionLimit(Options::reservedZoneSize());
- }
-
-#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
- if (yarrCodeBlock->usesPatternContextBuffer()) {
- m_patternContextBuffer = m_vm.acquireRegExpPatternContexBuffer();
- m_patternContextBufferSize = VM::patternContextBufferSize;
- }
-#else
- UNUSED_PARAM(yarrCodeBlock);
-#endif
-}
-
-MatchingContextHolder::~MatchingContextHolder()
-{
-#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
- if (m_patternContextBuffer)
- m_vm.releaseRegExpPatternContexBuffer();
-#endif
-}
-
template<YarrJITCompileMode compileMode>
class YarrGenerator final : public YarrJITInfo, private MacroAssembler {
@@ -3771,14 +3744,10 @@
#elif CPU(MIPS)
// Do nothing.
#endif
-
- store8(TrustedImm32(1), &m_vm->isExecutingInRegExpJIT);
}
void generateReturn()
{
- store8(TrustedImm32(0), &m_vm->isExecutingInRegExpJIT);
-
#if CPU(X86_64)
#if OS(WINDOWS)
// Store the return value in the allocated space pointed by rcx.
@@ -3821,7 +3790,6 @@
#elif CPU(MIPS)
// Do nothing
#endif
-
#if CPU(ARM64E)
if (Options::useJITCage())
farJump(TrustedImmPtr(retagCodePtr<void*, CFunctionPtrTag, OperationPtrTag>(&vmEntryToYarrJITAfter)), OperationPtrTag);
diff --git a/Source/JavaScriptCore/yarr/YarrJIT.h b/Source/JavaScriptCore/yarr/YarrJIT.h
index 974d581..3d2fc4a 100644
--- a/Source/JavaScriptCore/yarr/YarrJIT.h
+++ b/Source/JavaScriptCore/yarr/YarrJIT.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -42,6 +42,7 @@
namespace Yarr {
+class MatchingContextHolder;
class YarrCodeBlock;
enum class JITFailureReason : uint8_t {
@@ -55,27 +56,6 @@
ExecutableMemoryAllocationFailure,
};
-class MatchingContextHolder {
- WTF_FORBID_HEAP_ALLOCATION;
-public:
- MatchingContextHolder(VM&, YarrCodeBlock*, MatchFrom matchFrom = MatchFrom::VMThread);
- ~MatchingContextHolder();
-
- static ptrdiff_t offsetOfStackLimit() { return OBJECT_OFFSETOF(MatchingContextHolder, m_stackLimit); }
-#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
- static ptrdiff_t offsetOfPatternContextBuffer() { return OBJECT_OFFSETOF(MatchingContextHolder, m_patternContextBuffer); }
- static ptrdiff_t offsetOfPatternContextBufferSize() { return OBJECT_OFFSETOF(MatchingContextHolder, m_patternContextBufferSize); }
-#endif
-
-private:
- VM& m_vm;
- void* m_stackLimit;
-#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
- void* m_patternContextBuffer { nullptr };
- unsigned m_patternContextBufferSize { 0 };
-#endif
-};
-
#if CPU(ARM64E)
extern "C" EncodedMatchResult vmEntryToYarrJIT(const void* input, unsigned start, unsigned length, int* output, MatchingContextHolder* matchingContext, const void* codePtr);
extern "C" void vmEntryToYarrJITAfter(void);
diff --git a/Source/JavaScriptCore/yarr/YarrMatchingContextHolder.h b/Source/JavaScriptCore/yarr/YarrMatchingContextHolder.h
new file mode 100644
index 0000000..139d1fe
--- /dev/null
+++ b/Source/JavaScriptCore/yarr/YarrMatchingContextHolder.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009-2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "VM.h"
+#include "Yarr.h"
+#include "YarrJIT.h"
+
+namespace JSC {
+
+class VM;
+class ExecutablePool;
+class RegExp;
+
+namespace Yarr {
+
+class YarrCodeBlock;
+
+class MatchingContextHolder {
+ WTF_FORBID_HEAP_ALLOCATION;
+public:
+ MatchingContextHolder(VM&, YarrCodeBlock*, RegExp*, MatchFrom);
+ ~MatchingContextHolder();
+
+ static ptrdiff_t offsetOfStackLimit() { return OBJECT_OFFSETOF(MatchingContextHolder, m_stackLimit); }
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ static ptrdiff_t offsetOfPatternContextBuffer() { return OBJECT_OFFSETOF(MatchingContextHolder, m_patternContextBuffer); }
+ static ptrdiff_t offsetOfPatternContextBufferSize() { return OBJECT_OFFSETOF(MatchingContextHolder, m_patternContextBufferSize); }
+#endif
+
+private:
+ VM& m_vm;
+ void* m_stackLimit;
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ void* m_patternContextBuffer { nullptr };
+ unsigned m_patternContextBufferSize { 0 };
+#endif
+ MatchFrom m_matchFrom;
+};
+
+inline MatchingContextHolder::MatchingContextHolder(VM& vm, YarrCodeBlock* yarrCodeBlock, RegExp* regExp, MatchFrom matchFrom)
+ : m_vm(vm)
+ , m_matchFrom(matchFrom)
+{
+ if (matchFrom == MatchFrom::VMThread) {
+ m_stackLimit = vm.softStackLimit();
+ vm.m_executingRegExp = regExp;
+ } else {
+ StackBounds stack = Thread::current().stack();
+ m_stackLimit = stack.recursionLimit(Options::reservedZoneSize());
+ }
+
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ if (yarrCodeBlock && yarrCodeBlock->usesPatternContextBuffer()) {
+ m_patternContextBuffer = m_vm.acquireRegExpPatternContexBuffer();
+ m_patternContextBufferSize = VM::patternContextBufferSize;
+ }
+#else
+ UNUSED_PARAM(yarrCodeBlock);
+#endif
+}
+
+inline MatchingContextHolder::~MatchingContextHolder()
+{
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ if (m_patternContextBuffer)
+ m_vm.releaseRegExpPatternContexBuffer();
+#endif
+ if (m_matchFrom == MatchFrom::VMThread)
+ m_vm.m_executingRegExp = nullptr;
+}
+
+} } // namespace JSC::Yarr