Diff
Modified: trunk/JSTests/ChangeLog (280451 => 280452)
--- trunk/JSTests/ChangeLog 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/JSTests/ChangeLog 2021-07-29 22:26:13 UTC (rev 280452)
@@ -1,3 +1,22 @@
+2021-07-28 Yusuke Suzuki <ysuz...@apple.com>
+
+ [JSC] Yarr should perform BoyerMoore search
+ https://bugs.webkit.org/show_bug.cgi?id=228301
+
+ Reviewed by Saam Barati.
+
+ * microbenchmarks/jquery-todomvc-regexp.js:
+ * stress/regexp--bm-search-long-character.js: Added.
+ (shouldBe):
+ * stress/regexp--bm-search-long-map.js: Added.
+ (shouldBe):
+ * stress/regexp-bitvector-reuse.js: Added.
+ (shouldBe):
+ * stress/regexp-non-ascii-bm-search-character.js: Added.
+ (shouldBe):
+ * stress/regexp-non-ascii-bm-search-map.js: Added.
+ (shouldBe):
+
2021-07-25 Alexey Shvayka <shvaikal...@gmail.com>
Partly implement Function.prototype.{caller,arguments} reflection proposal
Modified: trunk/JSTests/microbenchmarks/jquery-todomvc-regexp.js (280451 => 280452)
--- trunk/JSTests/microbenchmarks/jquery-todomvc-regexp.js 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/JSTests/microbenchmarks/jquery-todomvc-regexp.js 2021-07-29 22:26:13 UTC (rev 280452)
@@ -84209,7 +84209,8 @@
regExp$16.lastIndex = 0;
results = regExp$16.exec(string$215);
}
-noInline(test);
+if (typeof noInline !== "undefined")
+ noInline(test);
regExps.push(/avoid constant folding/gi);
strings.push(/avoid constant folding/gi);
for (let i = 0; i < 20; ++i)
Added: trunk/JSTests/stress/regexp--bm-search-long-character.js (0 => 280452)
--- trunk/JSTests/stress/regexp--bm-search-long-character.js (rev 0)
+++ trunk/JSTests/stress/regexp--bm-search-long-character.js 2021-07-29 22:26:13 UTC (rev 280452)
@@ -0,0 +1,14 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+var regexp = /aaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbb/i;
+
+for (var i = 0; i < 1e2; ++i) {
+ shouldBe(regexp.test("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgaaaabbbbccccddddaaaabbbbccccdddaaaabbbbccccddddfffff"), false);
+ shouldBe(regexp.test("abcdefgabcdefgabcdefgabcdefgabcdefgaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaabbabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgaaaabbbbccccddddaaaabbbbccccdddaaaabbbbccccddddaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbfffff"), true);
+ shouldBe(RegExp.leftContext, "abcdefgabcdefgabcdefgabcdefgabcdefgaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaabbabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgaaaabbbbccccddddaaaabbbbccccdddaaaabbbbccccdddd");
+ shouldBe(RegExp.rightContext, "fffff");
+ shouldBe(regexp.test("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgaaaabbbbccccddddaaaabbbbccccddfffffffffffffffffffffff"), false);
+}
Added: trunk/JSTests/stress/regexp--bm-search-long-map.js (0 => 280452)
--- trunk/JSTests/stress/regexp--bm-search-long-map.js (rev 0)
+++ trunk/JSTests/stress/regexp--bm-search-long-map.js 2021-07-29 22:26:13 UTC (rev 280452)
@@ -0,0 +1,13 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+var regexp = /aaaabbbbccccddddaaaabbbbccccdddaaaabbbbccccdddd/i;
+
+for (var i = 0; i < 1e2; ++i) {
+ shouldBe(regexp.test("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgaaaabbbbccccddddaaaabbbbccccdddaaaabbbbccccddddfffff"), true);
+ shouldBe(RegExp.leftContext, "abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg");
+ shouldBe(RegExp.rightContext, "fffff");
+ shouldBe(regexp.test("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgaaaabbbbccccddddaaaabbbbccccddfffffffffffffffffffffff"), false);
+}
Added: trunk/JSTests/stress/regexp-bitvector-reuse.js (0 => 280452)
--- trunk/JSTests/stress/regexp-bitvector-reuse.js (rev 0)
+++ trunk/JSTests/stress/regexp-bitvector-reuse.js 2021-07-29 22:26:13 UTC (rev 280452)
@@ -0,0 +1,11 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+var regexp = /script/;
+for (var i = 0; i < 1e2; ++i) {
+ shouldBe(/script/.test("testtestscript"), true);
+ shouldBe(RegExp.leftContext, "testtest");
+ shouldBe(/script/.test($vm.make16BitStringIfPossible("testtestscript")), true);
+ shouldBe(RegExp.leftContext, "testtest");
+}
Added: trunk/JSTests/stress/regexp-non-ascii-bm-search-character.js (0 => 280452)
--- trunk/JSTests/stress/regexp-non-ascii-bm-search-character.js (rev 0)
+++ trunk/JSTests/stress/regexp-non-ascii-bm-search-character.js 2021-07-29 22:26:13 UTC (rev 280452)
@@ -0,0 +1,13 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+var regexp = /今日/i;
+
+for (var i = 0; i < 1e2; ++i) {
+ shouldBe(regexp.test("昨日遅くに寝たので今日はまだ眠い。"), true);
+ shouldBe(RegExp.leftContext, "昨日遅くに寝たので");
+ shouldBe(RegExp.rightContext, "はまだ眠い。");
+ shouldBe(regexp.test("Because I went to bed late last night, I'm still sleepy"), false);
+}
Added: trunk/JSTests/stress/regexp-non-ascii-bm-search-map.js (0 => 280452)
--- trunk/JSTests/stress/regexp-non-ascii-bm-search-map.js (rev 0)
+++ trunk/JSTests/stress/regexp-non-ascii-bm-search-map.js 2021-07-29 22:26:13 UTC (rev 280452)
@@ -0,0 +1,13 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+var regexp = /今日|明日/i;
+
+for (var i = 0; i < 1e2; ++i) {
+ shouldBe(regexp.test("昨日遅くに寝たので今日はまだ眠い。"), true);
+ shouldBe(RegExp.leftContext, "昨日遅くに寝たので");
+ shouldBe(RegExp.rightContext, "はまだ眠い。");
+ shouldBe(regexp.test("Because I went to bed late last night, I'm still sleepy"), false);
+}
Modified: trunk/Source/_javascript_Core/ChangeLog (280451 => 280452)
--- trunk/Source/_javascript_Core/ChangeLog 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-07-29 22:26:13 UTC (rev 280452)
@@ -1,5 +1,87 @@
2021-07-28 Yusuke Suzuki <ysuz...@apple.com>
+ [JSC] Yarr should perform BoyerMoore search
+ https://bugs.webkit.org/show_bug.cgi?id=228301
+
+ Reviewed by Saam Barati.
+
+ This patch emits skipping fast-path at the beginning of body alternatives with a large stride. So we can quickly discard unrelated characters
+ and attempt to find possibly related sequence in the long sequence. The method is derived from V8's implementation (with some extensions).
+
+ If we have a searching pattern /abcdef/, then we can check the 6th character against a set of {a, b, c, d, e, f}.
+ If it does not match, we can shift 6 characters. We use this strategy since this way can be extended easily to support
+ disjunction, character-class, and ignore-cases. For example, in the case of /(?:abc|def)/, we can check 3rd character
+ against {a, b, c, d, e, f} and shift 3 characters if it does not match.
+
+ Then, the best way to perform the above shifting is that finding the longest character sequence which does not have
+ many candidates. In the case of /[a-z]aaaaaaa[a-z]/, we can extract "aaaaaaa" sequence and check 8th character against {a}.
+ If it does not match, then we can shift 7 characters (length of "aaaaaaa"). This shifting is better than using "[a-z]aaaaaaa[a-z]"
+ sequence and {a-z} set since {a-z} set will almost always match.
+
+ We first collect possible characters for each character position. Then, apply heuristics to extract good character sequence from
+ that and construct fast searching with long stride.
+
+ Microbenchmark which performs RegExp ops in Speedometer2/jQuery-TodoMVC shows 25% improvement.
+
+ ToT Patched
+
+ jquery-todomvc-regexp 723.9739+-1.3997 ^ 579.1698+-1.2505 ^ definitely 1.2500x faster
+
+ This improves Speedometer2/jQuery-TodoMVC by 3%.
+
+ ----------------------------------------------------------------------------------------------------------------------------------
+ | subtest | ms | ms | b / a | pValue (significance using False Discovery Rate) |
+ ----------------------------------------------------------------------------------------------------------------------------------
+ | Elm-TodoMVC |123.365625 |123.456250 |1.000735 | 0.804077 |
+ | VueJS-TodoMVC |26.912500 |26.925000 |1.000464 | 0.969603 |
+ | EmberJS-TodoMVC |127.540625 |127.562500 |1.000172 | 0.960474 |
+ | BackboneJS-TodoMVC |50.606250 |50.518750 |0.998271 | 0.670313 |
+ | Preact-TodoMVC |21.018750 |20.850000 |0.991971 | 0.563818 |
+ | AngularJS-TodoMVC |136.943750 |137.271875 |1.002396 | 0.531513 |
+ | Vanilla-ES2015-TodoMVC |68.521875 |68.593750 |1.001049 | 0.701376 |
+ | Inferno-TodoMVC |65.559375 |65.803125 |1.003718 | 0.414418 |
+ | Flight-TodoMVC |77.284375 |76.715625 |0.992641 | 0.219870 |
+ | Angular2-TypeScript-TodoMVC |40.725000 |40.318750 |0.990025 | 0.281212 |
+ | VanillaJS-TodoMVC |55.209375 |54.715625 |0.991057 | 0.056921 |
+ | jQuery-TodoMVC |266.396875 |258.471875 |0.970251 | 0.000000 (significant) |
+ | EmberJS-Debug-TodoMVC |341.550000 |341.856250 |1.000897 | 0.618140 |
+ | React-TodoMVC |88.731250 |88.871875 |1.001585 | 0.512407 |
+ | React-Redux-TodoMVC |150.340625 |150.065625 |0.998171 | 0.412940 |
+ | Vanilla-ES2015-Babel-Webpack-TodoMVC |65.390625 |65.362500 |0.999570 | 0.834760 |
+ ----------------------------------------------------------------------------------------------------------------------------------
+ a mean = 245.96997
+ b mean = 246.86366
+ pValue = 0.0061448402
+ (Bigger means are better.)
+ 1.004 times better
+ Results ARE significant
+
+ * runtime/OptionsList.h:
+ * yarr/YarrJIT.cpp:
+ (JSC::Yarr::BoyerMooreInfo::BoyerMooreInfo):
+ (JSC::Yarr::BoyerMooreInfo::length const):
+ (JSC::Yarr::BoyerMooreInfo::set):
+ (JSC::Yarr::BoyerMooreInfo::index const):
+ (JSC::Yarr::BoyerMooreInfo::setIndex):
+ (JSC::Yarr::BoyerMooreInfo::create):
+ (JSC::Yarr::BoyerMooreInfo::findBestCharacterSequence const):
+ (JSC::Yarr::BoyerMooreInfo::findWorthwhileCharacterSequenceForLookahead const):
+ (JSC::Yarr::BoyerMooreInfo::createCandidateBitmap const):
+ * yarr/YarrJIT.h:
+ (JSC::Yarr::BoyerMooreBitmap::count const):
+ (JSC::Yarr::BoyerMooreBitmap::map const):
+ (JSC::Yarr::BoyerMooreBitmap::isMaskEffective const):
+ (JSC::Yarr::BoyerMooreBitmap::add):
+ (JSC::Yarr::BoyerMooreByteVector::BoyerMooreByteVector):
+ (JSC::Yarr::YarrCodeBlock::set8BitCode):
+ (JSC::Yarr::YarrCodeBlock::set16BitCode):
+ (JSC::Yarr::YarrCodeBlock::set8BitCodeMatchOnly):
+ (JSC::Yarr::YarrCodeBlock::set16BitCodeMatchOnly):
+ (JSC::Yarr::YarrCodeBlock::clear):
+ (JSC::Yarr::YarrCodeBlock::findSameVector const):
+
+2021-07-28 Yusuke Suzuki <ysuz...@apple.com>
+
[JSC] load/store with BaseIndex is inefficient in ARM64
https://bugs.webkit.org/show_bug.cgi?id=228543
Modified: trunk/Source/_javascript_Core/runtime/OptionsList.h (280451 => 280452)
--- trunk/Source/_javascript_Core/runtime/OptionsList.h 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/Source/_javascript_Core/runtime/OptionsList.h 2021-07-29 22:26:13 UTC (rev 280452)
@@ -452,6 +452,7 @@
v(Unsigned, prototypeHitCountForLLIntCaching, 2, Normal, "Number of prototype property hits before caching a prototype in the LLInt. A count of 0 means never cache.") \
\
v(Bool, dumpCompiledRegExpPatterns, false, Normal, nullptr) \
+ v(Bool, verboseRegExpCompilation, false, Normal, nullptr) \
\
v(Bool, dumpModuleRecord, false, Normal, nullptr) \
v(Bool, dumpModuleLoadingState, false, Normal, nullptr) \
Modified: trunk/Source/_javascript_Core/runtime/RegExp.cpp (280451 => 280452)
--- trunk/Source/_javascript_Core/runtime/RegExp.cpp 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/Source/_javascript_Core/runtime/RegExp.cpp 2021-07-29 22:26:13 UTC (rev 280452)
@@ -361,7 +361,7 @@
m_state = NotCompiled;
#if ENABLE(YARR_JIT)
if (m_regExpJITCode)
- m_regExpJITCode->clear();
+ m_regExpJITCode->clear(locker);
#endif
m_regExpBytecode = nullptr;
}
Modified: trunk/Source/_javascript_Core/yarr/YarrJIT.cpp (280451 => 280452)
--- trunk/Source/_javascript_Core/yarr/YarrJIT.cpp 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/Source/_javascript_Core/yarr/YarrJIT.cpp 2021-07-29 22:26:13 UTC (rev 280452)
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2009-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2021 Apple Inc. All rights reserved.
+ * Copyright (C) 2019 the V8 project authors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -34,6 +35,7 @@
#include "YarrDisassembler.h"
#include "YarrMatchingContextHolder.h"
#include <wtf/ASCIICType.h>
+#include <wtf/HexNumber.h>
#include <wtf/Threading.h>
@@ -40,11 +42,120 @@
#if ENABLE(YARR_JIT)
namespace JSC { namespace Yarr {
+namespace YarrJITInternal {
+static constexpr bool verbose = false;
+}
#if CPU(ARM64E)
JSC_ANNOTATE_JIT_OPERATION(_JITTarget_vmEntryToYarrJITAfter, vmEntryToYarrJITAfter);
#endif
+class BoyerMooreInfo {
+ WTF_MAKE_NONCOPYABLE(BoyerMooreInfo);
+ WTF_MAKE_FAST_ALLOCATED(BoyerMooreInfo);
+public:
+ static constexpr unsigned maxLength = 32;
+
+ explicit BoyerMooreInfo(unsigned length)
+ : m_characters(length)
+ {
+ ASSERT(this->length() <= maxLength);
+ }
+
+ unsigned length() const { return m_characters.size(); }
+
+ void set(unsigned index, UChar32 character)
+ {
+ m_characters[index].add(character);
+ }
+
+ unsigned index() const { return m_index; }
+ void setIndex(unsigned index)
+ {
+ m_index = index;
+ }
+
+ static UniqueRef<BoyerMooreInfo> create(unsigned length)
+ {
+ return makeUniqueRef<BoyerMooreInfo>(length);
+ }
+
+ std::optional<std::tuple<unsigned, unsigned>> findWorthwhileCharacterSequenceForLookahead() const;
+ std::tuple<BoyerMooreBitmap::Map, bool> createCandidateBitmap(unsigned begin, unsigned end) const;
+
+private:
+ std::tuple<int, unsigned, unsigned> findBestCharacterSequence(unsigned numberOfCandidatesLimit) const;
+
+ Vector<BoyerMooreBitmap> m_characters;
+ unsigned m_index { 0 };
+};
+
+std::tuple<int, unsigned, unsigned> BoyerMooreInfo::findBestCharacterSequence(unsigned numberOfCandidatesLimit) const
+{
+ int biggestPoint = 0;
+ unsigned beginResult = 0;
+ unsigned endResult = 0;
+ for (unsigned index = 0; index < length();) {
+ while (index < length() && m_characters[index].count() > numberOfCandidatesLimit)
+ ++index;
+ if (index == length())
+ break;
+ unsigned begin = index;
+ BoyerMooreBitmap::Map map { };
+ for (; index < length() && m_characters[index].count() <= numberOfCandidatesLimit; ++index)
+ map.merge(m_characters[index].map());
+
+ // If map has many candidates, then point of this sequence is low since it will match too many things.
+ // And if the sequence is longer, then the point of this sequence is higher since it can skip many characters.
+ // FIXME: Currently we are handling all characters equally. But we should have weight per character since e.g. 'e' should appear more frequently than '\v'.
+ // https://bugs.webkit.org/show_bug.cgi?id=228610
+ int frequency = map.count();
+ int matchingProbability = BoyerMooreBitmap::mapSize - frequency;
+ int point = (index - begin) * matchingProbability;
+ if (point > biggestPoint) {
+ biggestPoint = point;
+ beginResult = begin;
+ endResult = index;
+ }
+ }
+ return std::tuple { biggestPoint, beginResult, endResult };
+}
+
+std::optional<std::tuple<unsigned, unsigned>> BoyerMooreInfo::findWorthwhileCharacterSequenceForLookahead() const
+{
+ // If candiates-per-character becomes larger, then sequence is not profitable since this sequence will match against
+ // too many characters. But if we limit candiates-per-character smaller, it is possible that we only find very short
+ // character sequence. We start with low limit, then enlarging the limit to find more and more profitable
+ // character sequence.
+ int biggestPoint = 0;
+ unsigned begin = 0;
+ unsigned end = 0;
+ constexpr unsigned maxCandidatesPerCharacter = 32;
+ for (unsigned limit = 4; limit < maxCandidatesPerCharacter; limit *= 2) {
+ auto [newPoint, newBegin, newEnd] = findBestCharacterSequence(limit);
+ if (newPoint > biggestPoint) {
+ biggestPoint = newPoint;
+ begin = newBegin;
+ end = newEnd;
+ }
+ }
+ if (!biggestPoint)
+ return std::nullopt;
+ return std::tuple { begin, end };
+}
+
+std::tuple<BoyerMooreBitmap::Map, bool> BoyerMooreInfo::createCandidateBitmap(unsigned begin, unsigned end) const
+{
+ BoyerMooreBitmap::Map map { };
+ bool isMaskEffective = false;
+ for (unsigned index = begin; index < end; ++index) {
+ auto& bmBitmap = m_characters[index];
+ map.merge(bmBitmap.map());
+ isMaskEffective |= bmBitmap.isMaskEffective();
+ }
+ return std::tuple { map, isMaskEffective };
+}
+
class YarrGenerator final : public YarrJITInfo, private MacroAssembler {
#if CPU(ARM_THUMB2)
@@ -783,13 +894,11 @@
explicit YarrOp(PatternTerm* term)
: m_term(term)
, m_op(YarrOpCode::Term)
- , m_isDeadCode(false)
{
}
explicit YarrOp(YarrOpCode op)
: m_op(op)
- , m_isDeadCode(false)
{
}
@@ -819,7 +928,7 @@
// This flag is used to null out the second pattern character, when
// two are fused to match a pair together.
- bool m_isDeadCode;
+ bool m_isDeadCode { false };
// Currently used in the case of some of the more complex management of
// 'm_checkedOffset', to cache the offset used in this alternative, to avoid
@@ -830,6 +939,8 @@
// value that will be pushed into the pattern's frame to return to,
// upon backtracking back into the disjunction.
DataLabelPtr m_returnAddress;
+
+ BoyerMooreInfo* m_bmInfo { nullptr };
};
// BacktrackingState
@@ -1410,7 +1521,7 @@
allCharacters |= (static_cast<uint64_t>(currentCharacter) << shiftAmount);
- if ((m_pattern.ignoreCase()) && (isASCIIAlpha(currentCharacter)))
+ if (m_pattern.ignoreCase() && isASCIIAlpha(currentCharacter))
ignoreCaseMask |= 32ULL << shiftAmount;
}
@@ -2256,11 +2367,68 @@
// Upon entry at the head of the set of alternatives, check if input is available
// to run the first alternative. (This progresses the input position).
op.m_jumps.append(jumpIfNoAvailableInput(alternative->m_minimumSize));
+ m_checkedOffset += alternative->m_minimumSize;
+
// We will reenter after the check, and assume the input position to have been
// set as appropriate to this alternative.
op.m_reentry = label();
- m_checkedOffset += alternative->m_minimumSize;
+ // Emit fast skip path with stride if we have BoyerMooreInfo.
+ if (op.m_bmInfo) {
+ auto range = op.m_bmInfo->findWorthwhileCharacterSequenceForLookahead();
+ if (range) {
+ auto [beginIndex, endIndex] = *range;
+ ASSERT(endIndex <= alternative->m_minimumSize);
+
+ auto [map, isMaskEffective] = op.m_bmInfo->createCandidateBitmap(beginIndex, endIndex);
+ unsigned mapCount = map.count();
+ // If candiate characters are <= 2, checking each is better than using vector.
+ if (mapCount <= 2) {
+ UChar32 character1 = map.findBit(0, true);
+ ASSERT(character1 != BoyerMooreBitmap::Map::size());
+ UChar32 character2 = 0xff;
+ if (mapCount == 2) {
+ character2 = map.findBit(character1 + 1, true);
+ ASSERT(character2 != BoyerMooreBitmap::Map::size());
+ }
+ dataLogLnIf(Options::verboseRegExpCompilation(), "Found 1-or-2 characters lookahead character:(0x", hex(character1), "),character2:(", hex(character2), "),isMaskEffective:(", isMaskEffective,"),range:[", beginIndex, ", ", endIndex, ")");
+
+ JumpList matched;
+ auto loopHead = label();
+ readCharacter(m_checkedOffset - endIndex + 1, regT0);
+ if (isMaskEffective)
+ and32(TrustedImm32(BoyerMooreBitmap::mapMask), regT0, regT0);
+ matched.append(branch32(Equal, regT0, TrustedImm32(character1)));
+ if (mapCount == 2)
+ matched.append(branch32(Equal, regT0, TrustedImm32(character2)));
+ op.m_jumps.append(jumpIfNoAvailableInput(endIndex - beginIndex));
+ jump().linkTo(loopHead, this);
+ matched.link(this);
+ } else {
+ const uint8_t* pointer = getBoyerMooreByteVector(op.m_bmInfo->index(), map);
+ dataLogLnIf(Options::verboseRegExpCompilation(), "Found bitmap lookahead count:(", mapCount, "),range:[", beginIndex, ", ", endIndex, ")");
+
+ move(TrustedImmPtr(pointer), regT1);
+ auto loopHead = label();
+ readCharacter(m_checkedOffset - endIndex + 1, regT0);
+ and32(TrustedImm32(BoyerMooreBitmap::mapMask), regT0, regT0);
+ auto matched = branchTest32(NonZero, BaseIndex(regT1, regT0, TimesOne));
+ op.m_jumps.append(jumpIfNoAvailableInput(endIndex - beginIndex));
+ jump().linkTo(loopHead, this);
+ matched.link(this);
+ }
+
+ // If the pattern size is not fixed, then store the start index for use if we match.
+ if (!m_pattern.m_body->m_hasFixedSize) {
+ if (alternative->m_minimumSize) {
+ move(index, regT0);
+ sub32(Imm32(alternative->m_minimumSize), regT0);
+ setMatchStart(regT0);
+ } else
+ setMatchStart(index);
+ }
+ }
+ }
break;
}
case YarrOpCode::BodyAlternativeNext:
@@ -2780,6 +2948,7 @@
break;
}
YarrOp& endOp = m_ops[op.m_nextOp];
+ ASSERT(endOp.m_op == YarrOpCode::BodyAlternativeEnd);
YarrOp* beginOp = &op;
while (beginOp->m_op != YarrOpCode::BodyAlternativeBegin) {
@@ -2797,7 +2966,7 @@
if (onceThrough)
m_backtrackingState.linkTo(endOp.m_reentry, this);
else {
- if (m_pattern.sticky() && m_ops[op.m_nextOp].m_op == YarrOpCode::BodyAlternativeEnd) {
+ if (m_pattern.sticky()) {
// It is a sticky pattern and the last alternative failed, jump to the end.
m_backtrackingState.takeBacktracksToJumpList(lastStickyAlternativeFailures, this);
} else if (m_pattern.m_body->m_hasFixedSize
@@ -2813,41 +2982,41 @@
// around to the first alternative.
m_backtrackingState.link(this);
- // No need to advance and retry for a sticky pattern.
- if (!m_pattern.sticky()) {
- // If the pattern size is not fixed, then store the start index for use if we match.
- if (!m_pattern.m_body->m_hasFixedSize) {
- if (alternative->m_minimumSize == 1)
- setMatchStart(index);
- else {
- move(index, regT0);
- if (alternative->m_minimumSize)
- sub32(Imm32(alternative->m_minimumSize - 1), regT0);
- else
- add32(TrustedImm32(1), regT0);
- setMatchStart(regT0);
- }
+ // No need to advance and retry for a sticky pattern. And it is already handled before this branch.
+ ASSERT(!m_pattern.sticky());
+
+ // If the pattern size is not fixed, then store the start index for use if we match.
+ if (!m_pattern.m_body->m_hasFixedSize) {
+ if (alternative->m_minimumSize == 1)
+ setMatchStart(index);
+ else {
+ move(index, regT0);
+ if (alternative->m_minimumSize)
+ sub32(Imm32(alternative->m_minimumSize - 1), regT0);
+ else
+ add32(TrustedImm32(1), regT0);
+ setMatchStart(regT0);
}
+ }
- // Generate code to loop. Check whether the last alternative is longer than the
- // first (e.g. /a|xy/ or /a|xyz/).
- if (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) {
- // We want to loop, and increment input position. If the delta is 1, it is
- // already correctly incremented, if more than one then decrement as appropriate.
- unsigned delta = alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize;
- ASSERT(delta);
- if (delta != 1)
- sub32(Imm32(delta - 1), index);
- jump(beginOp->m_reentry);
- } else {
- // If the first alternative has minimum size 0xFFFFFFFFu, then there cannot
- // be sufficent input available to handle this, so just fall through.
- unsigned delta = beginOp->m_alternative->m_minimumSize - alternative->m_minimumSize;
- if (delta != 0xFFFFFFFFu) {
- // We need to check input because we are incrementing the input.
- add32(Imm32(delta + 1), index);
- checkInput().linkTo(beginOp->m_reentry, this);
- }
+ // Generate code to loop. Check whether the last alternative is longer than the
+ // first (e.g. /a|xy/ or /a|xyz/).
+ if (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) {
+ // We want to loop, and increment input position. If the delta is 1, it is
+ // already correctly incremented, if more than one then decrement as appropriate.
+ unsigned delta = alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize;
+ ASSERT(delta);
+ if (delta != 1)
+ sub32(Imm32(delta - 1), index);
+ jump(beginOp->m_reentry);
+ } else {
+ // If the first alternative has minimum size 0xFFFFFFFFu, then there cannot
+ // be sufficent input available to handle this, so just fall through.
+ unsigned delta = beginOp->m_alternative->m_minimumSize - alternative->m_minimumSize;
+ if (delta != 0xFFFFFFFFu) {
+ // We need to check input because we are incrementing the input.
+ add32(Imm32(delta + 1), index);
+ checkInput().linkTo(beginOp->m_reentry, this);
}
}
}
@@ -3643,6 +3812,21 @@
size_t repeatLoop = m_ops.size();
m_ops.append(YarrOp(YarrOpCode::BodyAlternativeBegin));
m_ops.last().m_previousOp = notFound;
+ // Collect BoyerMooreInfo if it is possible and profitable. BoyerMooreInfo will be used to emit fast skip path with large stride
+ // at the beginning of the body alternatives.
+ // We do not emit these fast path when RegExp has sticky or unicode flag. Sticky case does not need this since
+ // it fails when the body alternatives fail to match with the current offset.
+ // FIXME: Support unicode flag.
+ // https://bugs.webkit.org/show_bug.cgi?id=228611
+ if (disjunction->m_minimumSize && disjunction->m_hasFixedSize && !m_pattern.sticky() && !m_pattern.unicode()) {
+ auto bmInfo = BoyerMooreInfo::create(std::min<unsigned>(disjunction->m_minimumSize, BoyerMooreInfo::maxLength));
+ if (collectBoyerMooreInfo(disjunction, currentAlternativeIndex, bmInfo.get())) {
+ m_ops.last().m_bmInfo = bmInfo.ptr();
+ bmInfo->setIndex(m_bmInfos.size());
+ m_bmInfos.append(WTFMove(bmInfo));
+ }
+ }
+
do {
size_t lastOpIndex = m_ops.size() - 1;
PatternAlternative* alternative = alternatives[currentAlternativeIndex].get();
@@ -3668,6 +3852,82 @@
lastOp.m_nextOp = repeatLoop;
}
+ bool collectBoyerMooreInfo(PatternDisjunction* disjunction, size_t currentAlternativeIndex, BoyerMooreInfo& bmInfo)
+ {
+ // If we have a searching pattern /abcdef/, then we can check the 6th character against a set of {a, b, c, d, e, f}.
+ // If it does not match, we can shift 6 characters. We use this strategy since this way can be extended easily to support
+ // disjunction, character-class, and ignore-cases. For example, in the case of /(?:abc|def)/, we can check 3rd character
+ // against {a, b, c, d, e, f} and shift 3 characters if it does not match.
+ //
+ // Then, the best way to perform the above shifting is that finding the longest character sequence which does not have
+ // many candidates. In the case of /[a-z]aaaaaaa[a-z]/, we can extract "aaaaaaa" sequence and check 8th character against {a}.
+ // If it does not match, then we can shift 7 characters (length of "aaaaaaa"). This shifting is better than using "[a-z]aaaaaaa[a-z]"
+ // sequence and {a-z} set since {a-z} set will almost always match.
+ //
+ // We first collect possible characters for each character position. Then, apply heuristics to extract good character sequence from
+ // that and construct fast searching with long stride.
+
+ ASSERT(disjunction->m_hasFixedSize); // We only support fixed-sized lookahead for BoyerMoore search.
+ ASSERT(disjunction->m_minimumSize);
+
+ // FIXME: Support nested disjunctions (e.g. /(?:abc|def|g(?:hi|jk))/).
+ // https://bugs.webkit.org/show_bug.cgi?id=228614
+ // FIXME: Support character-class (e.g. /[\d]test/).
+ // https://bugs.webkit.org/show_bug.cgi?id=228613
+ // FIXME: Support non-fixed-sized lookahead (e.g. /.*abc/ and extract "abc" sequence).
+ // https://bugs.webkit.org/show_bug.cgi?id=228612
+ auto& alternatives = disjunction->m_alternatives;
+ for (; currentAlternativeIndex < alternatives.size(); ++currentAlternativeIndex) {
+ unsigned cursor = 0;
+ PatternAlternative* alternative = alternatives[currentAlternativeIndex].get();
+ for (unsigned index = 0; index < alternative->m_terms.size() && cursor < bmInfo.length(); ++index) {
+ PatternTerm& term = alternative->m_terms[index];
+ switch (term.type) {
+ case PatternTerm::Type::AssertionBOL:
+ case PatternTerm::Type::AssertionEOL:
+ case PatternTerm::Type::AssertionWordBoundary:
+ case PatternTerm::Type::CharacterClass:
+ case PatternTerm::Type::BackReference:
+ case PatternTerm::Type::ForwardReference:
+ case PatternTerm::Type::ParenthesesSubpattern:
+ case PatternTerm::Type::ParentheticalAssertion:
+ case PatternTerm::Type::DotStarEnclosure:
+ return false;
+ case PatternTerm::Type::PatternCharacter: {
+ if (term.quantityType != QuantifierType::FixedCount || term.quantityMaxCount != 1)
+ return false;
+ if (term.inputPosition != index)
+ return false;
+ if (U16_LENGTH(term.patternCharacter) != 1 && m_decodeSurrogatePairs)
+ return false;
+ // For case-insesitive compares, non-ascii characters that have different
+ // upper & lower case representations are already converted to a character class.
+ ASSERT(!m_pattern.ignoreCase() || isASCIIAlpha(term.patternCharacter) || isCanonicallyUnique(term.patternCharacter, m_canonicalMode));
+ if (m_pattern.ignoreCase() && isASCIIAlpha(term.patternCharacter)) {
+ bmInfo.set(cursor, toASCIIUpper(term.patternCharacter));
+ bmInfo.set(cursor, toASCIILower(term.patternCharacter));
+ } else
+ bmInfo.set(cursor, term.patternCharacter);
+ ++cursor;
+ break;
+ }
+ }
+ }
+ }
+ dataLogLnIf(YarrJITInternal::verbose, "Characters collected");
+ return true;
+ }
+
+ const uint8_t* getBoyerMooreByteVector(unsigned index, const BoyerMooreBitmap::Map& map)
+ {
+ auto vector = makeUniqueRef<BoyerMooreByteVector>(map);
+ if (const auto* existingPointer = m_codeBlock.tryReuseBoyerMooreByteVector(index, vector.get()))
+ return existingPointer;
+ const uint8_t* pointer = vector->data();
+ m_bmVector.append(WTFMove(vector));
+ return pointer;
+ }
+
void generateTryReadUnicodeCharacterHelper()
{
#ifdef JIT_UNICODE_EXPRESSIONS
@@ -3952,14 +4212,14 @@
if (m_compileMode == JITCompileMode::MatchOnly) {
if (m_charSize == CharSize::Char8)
- codeBlock.set8BitCodeMatchOnly(FINALIZE_REGEXP_CODE(linkBuffer, YarrMatchOnly8BitPtrTag, "Match-only 8-bit regular _expression_"));
+ codeBlock.set8BitCodeMatchOnly(FINALIZE_REGEXP_CODE(linkBuffer, YarrMatchOnly8BitPtrTag, "Match-only 8-bit regular _expression_"), WTFMove(m_bmVector));
else
- codeBlock.set16BitCodeMatchOnly(FINALIZE_REGEXP_CODE(linkBuffer, YarrMatchOnly16BitPtrTag, "Match-only 16-bit regular _expression_"));
+ codeBlock.set16BitCodeMatchOnly(FINALIZE_REGEXP_CODE(linkBuffer, YarrMatchOnly16BitPtrTag, "Match-only 16-bit regular _expression_"), WTFMove(m_bmVector));
} else {
if (m_charSize == CharSize::Char8)
- codeBlock.set8BitCode(FINALIZE_REGEXP_CODE(linkBuffer, Yarr8BitPtrTag, "8-bit regular _expression_"));
+ codeBlock.set8BitCode(FINALIZE_REGEXP_CODE(linkBuffer, Yarr8BitPtrTag, "8-bit regular _expression_"), WTFMove(m_bmVector));
else
- codeBlock.set16BitCode(FINALIZE_REGEXP_CODE(linkBuffer, Yarr16BitPtrTag, "16-bit regular _expression_"));
+ codeBlock.set16BitCode(FINALIZE_REGEXP_CODE(linkBuffer, Yarr16BitPtrTag, "16-bit regular _expression_"), WTFMove(m_bmVector));
}
if (m_failureReason)
codeBlock.setFallBackWithFailureReason(*m_failureReason);
@@ -4197,6 +4457,8 @@
// The regular _expression_ expressed as a linear sequence of operations.
Vector<YarrOp, 128> m_ops;
+ Vector<UniqueRef<BoyerMooreInfo>, 4> m_bmInfos;
+ Vector<UniqueRef<BoyerMooreByteVector>> m_bmVector;
// This records the current input offset being applied due to the current
// set of alternatives we are nested within. E.g. when matching the
Modified: trunk/Source/_javascript_Core/yarr/YarrJIT.h (280451 => 280452)
--- trunk/Source/_javascript_Core/yarr/YarrJIT.h 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/Source/_javascript_Core/yarr/YarrJIT.h 2021-07-29 22:26:13 UTC (rev 280452)
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2009-2021 Apple Inc. All rights reserved.
+ * Copyright (C) 2019 the V8 project authors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,6 +33,9 @@
#include "VM.h"
#include "Yarr.h"
#include "YarrPattern.h"
+#include <wtf/Bitmap.h>
+#include <wtf/FixedVector.h>
+#include <wtf/UniqueRef.h>
#define YARR_CALL
@@ -56,6 +60,48 @@
ExecutableMemoryAllocationFailure,
};
+class BoyerMooreBitmap {
+ WTF_MAKE_FAST_ALLOCATED(BoyerMooreBitmap);
+public:
+ static constexpr unsigned mapSize = 128;
+ static constexpr unsigned mapMask = 128 - 1;
+ using Map = Bitmap<mapSize>;
+
+ BoyerMooreBitmap() = default;
+
+ unsigned count() const { return m_count; }
+ const Map& map() const { return m_map; }
+ bool isMaskEffective() const { return m_isMaskEffective; }
+
+ void add(UChar32 character)
+ {
+ unsigned position = character & mapMask;
+ if (position != static_cast<unsigned>(character))
+ m_isMaskEffective = true;
+ if (!m_map.get(position)) {
+ m_map.set(position);
+ ++m_count;
+ }
+ }
+
+private:
+ Map m_map { };
+ unsigned m_count { 0 };
+ bool m_isMaskEffective { false };
+};
+
+class BoyerMooreByteVector : public std::array<uint8_t, BoyerMooreBitmap::mapSize> {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ BoyerMooreByteVector(const BoyerMooreBitmap::Map& map)
+ {
+ fill(0);
+ map.forEachSetBit([&](unsigned index) {
+ (*this)[index] = 1;
+ });
+ }
+};
+
#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);
@@ -78,13 +124,37 @@
bool has8BitCode() { return m_ref8.size(); }
bool has16BitCode() { return m_ref16.size(); }
- void set8BitCode(MacroAssemblerCodeRef<Yarr8BitPtrTag> ref) { m_ref8 = ref; }
- void set16BitCode(MacroAssemblerCodeRef<Yarr16BitPtrTag> ref) { m_ref16 = ref; }
+ void set8BitCode(MacroAssemblerCodeRef<Yarr8BitPtrTag> ref, Vector<UniqueRef<BoyerMooreByteVector>> maps)
+ {
+ m_ref8 = ref;
+ m_maps.reserveCapacity(m_maps.size() + maps.size());
+ for (unsigned index = 0; index < maps.size(); ++index)
+ m_maps.uncheckedAppend(WTFMove(maps[index]));
+ }
+ void set16BitCode(MacroAssemblerCodeRef<Yarr16BitPtrTag> ref, Vector<UniqueRef<BoyerMooreByteVector>> maps)
+ {
+ m_ref16 = ref;
+ m_maps.reserveCapacity(m_maps.size() + maps.size());
+ for (unsigned index = 0; index < maps.size(); ++index)
+ m_maps.uncheckedAppend(WTFMove(maps[index]));
+ }
bool has8BitCodeMatchOnly() { return m_matchOnly8.size(); }
bool has16BitCodeMatchOnly() { return m_matchOnly16.size(); }
- void set8BitCodeMatchOnly(MacroAssemblerCodeRef<YarrMatchOnly8BitPtrTag> matchOnly) { m_matchOnly8 = matchOnly; }
- void set16BitCodeMatchOnly(MacroAssemblerCodeRef<YarrMatchOnly16BitPtrTag> matchOnly) { m_matchOnly16 = matchOnly; }
+ void set8BitCodeMatchOnly(MacroAssemblerCodeRef<YarrMatchOnly8BitPtrTag> matchOnly, Vector<UniqueRef<BoyerMooreByteVector>> maps)
+ {
+ m_matchOnly8 = matchOnly;
+ m_maps.reserveCapacity(m_maps.size() + maps.size());
+ for (unsigned index = 0; index < maps.size(); ++index)
+ m_maps.uncheckedAppend(WTFMove(maps[index]));
+ }
+ void set16BitCodeMatchOnly(MacroAssemblerCodeRef<YarrMatchOnly16BitPtrTag> matchOnly, Vector<UniqueRef<BoyerMooreByteVector>> maps)
+ {
+ m_matchOnly16 = matchOnly;
+ m_maps.reserveCapacity(m_maps.size() + maps.size());
+ for (unsigned index = 0; index < maps.size(); ++index)
+ m_maps.uncheckedAppend(WTFMove(maps[index]));
+ }
bool usesPatternContextBuffer() { return m_usesPatternContextBuffer; }
#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
@@ -170,20 +240,31 @@
return m_ref8.size() + m_ref16.size() + m_matchOnly8.size() + m_matchOnly16.size();
}
- void clear()
+ void clear(const AbstractLocker&)
{
m_ref8 = MacroAssemblerCodeRef<Yarr8BitPtrTag>();
m_ref16 = MacroAssemblerCodeRef<Yarr16BitPtrTag>();
m_matchOnly8 = MacroAssemblerCodeRef<YarrMatchOnly8BitPtrTag>();
m_matchOnly16 = MacroAssemblerCodeRef<YarrMatchOnly16BitPtrTag>();
+ m_maps.clear();
m_failureReason = std::nullopt;
}
+ const uint8_t* tryReuseBoyerMooreByteVector(unsigned index, BoyerMooreByteVector& vector) const
+ {
+ if (index < m_maps.size()) {
+ if (m_maps[index].get() == vector)
+ return m_maps[index]->data();
+ }
+ return nullptr;
+ }
+
private:
MacroAssemblerCodeRef<Yarr8BitPtrTag> m_ref8;
MacroAssemblerCodeRef<Yarr16BitPtrTag> m_ref16;
MacroAssemblerCodeRef<YarrMatchOnly8BitPtrTag> m_matchOnly8;
MacroAssemblerCodeRef<YarrMatchOnly16BitPtrTag> m_matchOnly16;
+ Vector<UniqueRef<BoyerMooreByteVector>> m_maps;
bool m_usesPatternContextBuffer { false };
std::optional<JITFailureReason> m_failureReason;
};
Modified: trunk/Source/WTF/ChangeLog (280451 => 280452)
--- trunk/Source/WTF/ChangeLog 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/Source/WTF/ChangeLog 2021-07-29 22:26:13 UTC (rev 280452)
@@ -1,3 +1,18 @@
+2021-07-28 Yusuke Suzuki <ysuz...@apple.com>
+
+ [JSC] Yarr should perform BoyerMoore search
+ https://bugs.webkit.org/show_bug.cgi?id=228301
+
+ Reviewed by Saam Barati.
+
+ * wtf/BitVector.cpp:
+ (WTF::BitVector::dump const):
+ * wtf/Bitmap.h:
+ (WTF::WordType>::dump const):
+ * wtf/UniqueRef.h:
+ (WTF::makeUniqueRefFromNonNullUniquePtr):
+ (WTF::UniqueRef::UniqueRef):
+
2021-07-29 Kate Cheney <katherine_che...@apple.com>
REGRESSION (r278877) [Cocoa] WebAuthn stopped working for non-Safari browsers
Modified: trunk/Source/WTF/wtf/BitVector.cpp (280451 => 280452)
--- trunk/Source/WTF/wtf/BitVector.cpp 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/Source/WTF/wtf/BitVector.cpp 2021-07-29 22:26:13 UTC (rev 280452)
@@ -262,12 +262,8 @@
void BitVector::dump(PrintStream& out) const
{
- for (size_t i = 0; i < size(); ++i) {
- if (get(i))
- out.printf("1");
- else
- out.printf("-");
- }
+ for (size_t i = 0; i < size(); ++i)
+ out.print(get(i) ? "1" : "-");
}
} // namespace WTF
Modified: trunk/Source/WTF/wtf/Bitmap.h (280451 => 280452)
--- trunk/Source/WTF/wtf/Bitmap.h 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/Source/WTF/wtf/Bitmap.h 2021-07-29 22:26:13 UTC (rev 280452)
@@ -23,6 +23,7 @@
#include <wtf/Atomics.h>
#include <wtf/HashFunctions.h>
#include <wtf/MathExtras.h>
+#include <wtf/PrintStream.h>
#include <wtf/StdIntExtras.h>
#include <string.h>
#include <type_traits>
@@ -130,6 +131,8 @@
unsigned hash() const;
+ void dump(PrintStream& out) const;
+
private:
static constexpr unsigned wordSize = sizeof(WordType) * 8;
static constexpr unsigned words = (bitmapSize + wordSize - 1) / wordSize;
@@ -507,6 +510,13 @@
return result;
}
+template<size_t bitmapSize, typename WordType>
+inline void Bitmap<bitmapSize, WordType>::dump(PrintStream& out) const
+{
+ for (size_t i = 0; i < size(); ++i)
+ out.print(get(i) ? "1" : "-");
+}
+
} // namespace WTF
using WTF::Bitmap;
Modified: trunk/Source/WTF/wtf/UniqueRef.h (280451 => 280452)
--- trunk/Source/WTF/wtf/UniqueRef.h 2021-07-29 22:12:16 UTC (rev 280451)
+++ trunk/Source/WTF/wtf/UniqueRef.h 2021-07-29 22:26:13 UTC (rev 280452)
@@ -46,9 +46,9 @@
}
template<typename T>
-UniqueRef<T> makeUniqueRefFromNonNullUniquePtr(std::unique_ptr<T> ptr)
+UniqueRef<T> makeUniqueRefFromNonNullUniquePtr(std::unique_ptr<T>&& ptr)
{
- return UniqueRef<T>(*ptr.release());
+ return UniqueRef<T>(WTFMove(ptr));
}
template<typename T>
@@ -67,6 +67,9 @@
ASSERT(m_ref);
}
+ T* ptr() RETURNS_NONNULL { ASSERT(m_ref); return m_ref.get(); }
+ T* ptr() const RETURNS_NONNULL { ASSERT(m_ref); return m_ref.get(); }
+
T& get() { ASSERT(m_ref); return *m_ref; }
const T& get() const { ASSERT(m_ref); return *m_ref; }
@@ -83,9 +86,15 @@
private:
template<class U, class... Args> friend UniqueRef<U> makeUniqueRefWithoutFastMallocCheck(Args&&...);
- template<class U> friend UniqueRef<U> makeUniqueRefFromNonNullUniquePtr(std::unique_ptr<U>);
+ template<class U> friend UniqueRef<U> makeUniqueRefFromNonNullUniquePtr(std::unique_ptr<U>&&);
template<class U> friend class UniqueRef;
+ explicit UniqueRef(std::unique_ptr<T>&& ptr)
+ : m_ref(WTFMove(ptr))
+ {
+ ASSERT(m_ref);
+ }
+
std::unique_ptr<T> m_ref;
};