Skip to content

Commit 6879e09

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
[skip ci] Do not consume component value on visitor failure (#48985)
Summary: This reverts some of the behavior I added in D68357624, since peeking a component value is non-obviously more expensive than manually copying the parser, and needing to peek will be a pain for flat lists of values (like for box-shadow). Changelog: [internal] Reviewed By: lenaic Differential Revision: D68733518
1 parent a702238 commit 6879e09

File tree

2 files changed

+73
-167
lines changed

2 files changed

+73
-167
lines changed

packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h

+38-85
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,21 @@ struct CSSSimpleBlock {
3535
CSSTokenType openBracketType{};
3636
};
3737

38+
/**
39+
* Describes a valid return type for a CSSSyntaxParser visitor
40+
*/
41+
template <typename T>
42+
concept CSSSyntaxVisitorReturn =
43+
std::is_default_constructible_v<T> && std::equality_comparable<T>;
44+
3845
/**
3946
* A CSSFunctionVisitor is called to start parsing a function component value.
4047
* At this point, the Parser is positioned at the start of the function
4148
* component value list. It is expected that the visitor finishes before the end
4249
* of the function block.
4350
*/
4451
template <typename T, typename ReturnT>
45-
concept CSSFunctionVisitor =
52+
concept CSSFunctionVisitor = CSSSyntaxVisitorReturn<ReturnT> &&
4653
requires(T visitor, CSSFunctionBlock func, CSSSyntaxParser& blockParser) {
4754
{ visitor(func, blockParser) } -> std::convertible_to<ReturnT>;
4855
};
@@ -52,7 +59,7 @@ concept CSSFunctionVisitor =
5259
* component value.
5360
*/
5461
template <typename T, typename ReturnT>
55-
concept CSSPreservedTokenVisitor =
62+
concept CSSPreservedTokenVisitor = CSSSyntaxVisitorReturn<ReturnT> &&
5663
requires(T visitor, CSSPreservedToken token) {
5764
{ visitor(token) } -> std::convertible_to<ReturnT>;
5865
};
@@ -63,7 +70,7 @@ concept CSSPreservedTokenVisitor =
6370
* of the block.
6471
*/
6572
template <typename T, typename ReturnT>
66-
concept CSSSimpleBlockVisitor =
73+
concept CSSSimpleBlockVisitor = CSSSyntaxVisitorReturn<ReturnT> &&
6774
requires(T visitor, CSSSimpleBlock block, CSSSyntaxParser& blockParser) {
6875
{ visitor(block, blockParser) } -> std::convertible_to<ReturnT>;
6976
};
@@ -72,16 +79,17 @@ concept CSSSimpleBlockVisitor =
7279
* Any visitor for a component value.
7380
*/
7481
template <typename T, typename ReturnT>
75-
concept CSSComponentValueVisitor = CSSFunctionVisitor<T, ReturnT> ||
76-
CSSPreservedTokenVisitor<T, ReturnT> || CSSSimpleBlockVisitor<T, ReturnT>;
82+
concept CSSComponentValueVisitor = CSSSyntaxVisitorReturn<ReturnT> &&
83+
(CSSFunctionVisitor<T, ReturnT> || CSSPreservedTokenVisitor<T, ReturnT> ||
84+
CSSSimpleBlockVisitor<T, ReturnT>);
7785

7886
/**
7987
* Represents a variadic set of CSSComponentValueVisitor with no more than one
8088
* of a specific type of visitor.
8189
*/
8290
template <typename ReturnT, typename... VisitorsT>
83-
concept CSSUniqueComponentValueVisitors =
84-
(CSSComponentValueVisitor<VisitorsT, ReturnT> && ... && true) &&
91+
concept CSSUniqueComponentValueVisitors = CSSSyntaxVisitorReturn<ReturnT> &&
92+
(CSSComponentValueVisitor<VisitorsT, ReturnT> && ...) &&
8593
((CSSFunctionVisitor<VisitorsT, ReturnT> ? 1 : 0) + ... + 0) <= 1 &&
8694
((CSSPreservedTokenVisitor<VisitorsT, ReturnT> ? 1 : 0) + ... + 0) <= 1 &&
8795
((CSSSimpleBlockVisitor<VisitorsT, ReturnT> ? 1 : 0) + ... + 0) <= 1;
@@ -106,7 +114,9 @@ enum class CSSDelimiter {
106114
* https://www.w3.org/TR/css-syntax-3/#component-value
107115
*/
108116
class CSSSyntaxParser {
109-
template <typename ReturnT, CSSComponentValueVisitor<ReturnT>... VisitorsT>
117+
template <
118+
CSSSyntaxVisitorReturn ReturnT,
119+
CSSComponentValueVisitor<ReturnT>... VisitorsT>
110120
friend struct CSSComponentValueVisitorDispatcher;
111121

112122
public:
@@ -130,6 +140,11 @@ class CSSSyntaxParser {
130140
* higher-level data structure, and continue parsing within its scope using
131141
* the same underlying CSSSyntaxParser.
132142
*
143+
* The state of the parser is reset if a visitor returns a default-constructed
144+
* value for the given return type, even if it previously advanced the parser.
145+
* If no visitor returns a non-default-constructed value, the component value
146+
* will not be consumed.
147+
*
133148
* https://www.w3.org/TR/css-syntax-3/#consume-component-value
134149
*
135150
* @param <ReturnT> caller-specified return type of visitors. This type will
@@ -142,47 +157,16 @@ class CSSSyntaxParser {
142157
* @returns the visitor returned value, or a default constructed value if no
143158
* visitor was matched, or a syntax error occurred.
144159
*/
145-
template <typename ReturnT = std::nullptr_t>
160+
template <CSSSyntaxVisitorReturn ReturnT>
146161
constexpr ReturnT consumeComponentValue(
147162
CSSDelimiter delimiter,
148163
const CSSComponentValueVisitor<ReturnT> auto&... visitors)
149164
requires(CSSUniqueComponentValueVisitors<ReturnT, decltype(visitors)...>);
150165

151-
template <typename ReturnT = std::nullptr_t>
166+
template <CSSSyntaxVisitorReturn ReturnT>
152167
constexpr ReturnT consumeComponentValue(
153168
const CSSComponentValueVisitor<ReturnT> auto&... visitors)
154169
requires(CSSUniqueComponentValueVisitors<ReturnT, decltype(visitors)...>);
155-
156-
/**
157-
* Peek at the next component value without consuming it. The component value
158-
* is provided to a passed in "visitor", typically a lambda which accepts the
159-
* component value in a new scope. The visitor may read this component
160-
* parameter into a higher-level data structure, and continue parsing within
161-
* its scope using the same underlying CSSSyntaxParser.
162-
*
163-
* https://www.w3.org/TR/css-syntax-3/#consume-component-value
164-
*
165-
* @param <ReturnT> caller-specified return type of visitors. This type will
166-
* be set to its default constructed state if consuming a component value with
167-
* no matching visitors, or syntax error
168-
* @param visitors A unique list of CSSComponentValueVisitor to be called on a
169-
* match
170-
* @param delimiter The expected delimeter to occur before the next component
171-
* value
172-
* @returns the visitor returned value, or a default constructed value if no
173-
* visitor was matched, or a syntax error occurred.
174-
*/
175-
template <typename ReturnT = std::nullptr_t>
176-
constexpr ReturnT peekComponentValue(
177-
CSSDelimiter delimiter,
178-
const CSSComponentValueVisitor<ReturnT> auto&... visitors)
179-
requires(CSSUniqueComponentValueVisitors<ReturnT, decltype(visitors)...>);
180-
181-
template <typename ReturnT = std::nullptr_t>
182-
constexpr ReturnT peekComponentValue(
183-
const CSSComponentValueVisitor<ReturnT> auto&... visitors)
184-
requires(CSSUniqueComponentValueVisitors<ReturnT, decltype(visitors)...>);
185-
186170
/**
187171
* The parser is considered finished when there are no more remaining tokens
188172
* to be processed
@@ -226,7 +210,9 @@ class CSSSyntaxParser {
226210
CSSTokenType terminator_{CSSTokenType::EndOfFile};
227211
};
228212

229-
template <typename ReturnT, CSSComponentValueVisitor<ReturnT>... VisitorsT>
213+
template <
214+
CSSSyntaxVisitorReturn ReturnT,
215+
CSSComponentValueVisitor<ReturnT>... VisitorsT>
230216
struct CSSComponentValueVisitorDispatcher {
231217
CSSSyntaxParser& parser;
232218

@@ -275,6 +261,7 @@ struct CSSComponentValueVisitorDispatcher {
275261
break;
276262
}
277263

264+
parser = originalParser;
278265
return ReturnT{};
279266
}
280267

@@ -331,15 +318,6 @@ struct CSSComponentValueVisitorDispatcher {
331318
return false;
332319
}
333320

334-
constexpr ReturnT peekComponentValue(
335-
CSSDelimiter delimiter,
336-
const VisitorsT&... visitors) {
337-
auto originalParser = parser;
338-
auto ret = consumeComponentValue(delimiter, visitors...);
339-
parser = originalParser;
340-
return ret;
341-
}
342-
343321
constexpr std::optional<ReturnT> visitFunction(
344322
const CSSComponentValueVisitor<ReturnT> auto& visitor,
345323
const CSSComponentValueVisitor<ReturnT> auto&... rest) {
@@ -357,7 +335,8 @@ struct CSSComponentValueVisitorDispatcher {
357335
auto functionValue = visitor({name}, blockParser);
358336
parser.advanceToBlockParser(blockParser);
359337
parser.consumeWhitespace();
360-
if (parser.peek().type() == CSSTokenType::CloseParen) {
338+
if (parser.peek().type() == CSSTokenType::CloseParen &&
339+
functionValue != ReturnT{}) {
361340
parser.consumeToken();
362341
return functionValue;
363342
}
@@ -369,11 +348,6 @@ struct CSSComponentValueVisitorDispatcher {
369348
}
370349

371350
constexpr std::optional<ReturnT> visitFunction() {
372-
while (parser.peek().type() != CSSTokenType::CloseParen) {
373-
parser.consumeToken();
374-
}
375-
parser.consumeToken();
376-
377351
return {};
378352
}
379353

@@ -388,7 +362,7 @@ struct CSSComponentValueVisitorDispatcher {
388362
auto blockValue = visitor({openBracketType}, blockParser);
389363
parser.advanceToBlockParser(blockParser);
390364
parser.consumeWhitespace();
391-
if (parser.peek().type() == endToken) {
365+
if (parser.peek().type() == endToken && blockValue != ReturnT{}) {
392366
parser.consumeToken();
393367
return blockValue;
394368
}
@@ -399,29 +373,27 @@ struct CSSComponentValueVisitorDispatcher {
399373
}
400374

401375
constexpr std::optional<ReturnT> visitSimpleBlock(CSSTokenType endToken) {
402-
while (parser.peek().type() != endToken) {
403-
parser.consumeToken();
404-
}
405-
parser.consumeToken();
406376
return {};
407377
}
408378

409379
constexpr std::optional<ReturnT> visitPreservedToken(
410380
const CSSComponentValueVisitor<ReturnT> auto& visitor,
411381
const CSSComponentValueVisitor<ReturnT> auto&... rest) {
412382
if constexpr (CSSPreservedTokenVisitor<decltype(visitor), ReturnT>) {
413-
return visitor(parser.consumeToken());
383+
auto ret = visitor(parser.consumeToken());
384+
if (ret != ReturnT{}) {
385+
return ret;
386+
}
414387
}
415388
return visitPreservedToken(rest...);
416389
}
417390

418391
constexpr std::optional<ReturnT> visitPreservedToken() {
419-
parser.consumeToken();
420392
return {};
421393
}
422394
};
423395

424-
template <typename ReturnT>
396+
template <CSSSyntaxVisitorReturn ReturnT>
425397
constexpr ReturnT CSSSyntaxParser::consumeComponentValue(
426398
CSSDelimiter delimiter,
427399
const CSSComponentValueVisitor<ReturnT> auto&... visitors)
@@ -432,31 +404,12 @@ constexpr ReturnT CSSSyntaxParser::consumeComponentValue(
432404
.consumeComponentValue(delimiter, visitors...);
433405
}
434406

435-
template <typename ReturnT>
407+
template <CSSSyntaxVisitorReturn ReturnT>
436408
constexpr ReturnT CSSSyntaxParser::consumeComponentValue(
437409
const CSSComponentValueVisitor<ReturnT> auto&... visitors)
438410
requires(CSSUniqueComponentValueVisitors<ReturnT, decltype(visitors)...>)
439411
{
440412
return consumeComponentValue<ReturnT>(CSSDelimiter::None, visitors...);
441413
}
442414

443-
template <typename ReturnT>
444-
constexpr ReturnT CSSSyntaxParser::peekComponentValue(
445-
CSSDelimiter delimiter,
446-
const CSSComponentValueVisitor<ReturnT> auto&... visitors)
447-
requires(CSSUniqueComponentValueVisitors<ReturnT, decltype(visitors)...>)
448-
{
449-
return CSSComponentValueVisitorDispatcher<ReturnT, decltype(visitors)...>{
450-
*this}
451-
.peekComponentValue(delimiter, visitors...);
452-
}
453-
454-
template <typename ReturnT>
455-
constexpr ReturnT CSSSyntaxParser::peekComponentValue(
456-
const CSSComponentValueVisitor<ReturnT> auto&... visitors)
457-
requires(CSSUniqueComponentValueVisitors<ReturnT, decltype(visitors)...>)
458-
{
459-
return peekComponentValue<ReturnT>(CSSDelimiter::None, visitors...);
460-
}
461-
462415
} // namespace facebook::react

packages/react-native/ReactCommon/react/renderer/css/tests/CSSSyntaxParserTest.cpp

+35-82
Original file line numberDiff line numberDiff line change
@@ -382,88 +382,6 @@ TEST(CSSSyntaxParser, unconsumed_simple_block_args) {
382382
EXPECT_EQ(funcValue, std::nullopt);
383383
}
384384

385-
TEST(CSSSyntaxParser, peek_does_not_consume) {
386-
CSSSyntaxParser parser{"foo()"};
387-
auto funcValue = parser.peekComponentValue<std::optional<std::string_view>>(
388-
[&](const CSSFunctionBlock& function, CSSSyntaxParser& /*blockParser*/) {
389-
EXPECT_EQ(function.name, "foo");
390-
return function.name;
391-
});
392-
393-
EXPECT_EQ(funcValue, "foo");
394-
395-
auto funcValue2 = parser.peekComponentValue<std::optional<std::string_view>>(
396-
[&](const CSSFunctionBlock& function, CSSSyntaxParser& /*blockParser*/) {
397-
EXPECT_EQ(function.name, "foo");
398-
return function.name;
399-
});
400-
401-
EXPECT_EQ(funcValue2, "foo");
402-
403-
auto funcValue3 =
404-
parser.consumeComponentValue<std::optional<std::string_view>>(
405-
[&](const CSSFunctionBlock& function,
406-
CSSSyntaxParser& /*blockParser*/) {
407-
EXPECT_EQ(function.name, "foo");
408-
return function.name;
409-
});
410-
411-
EXPECT_EQ(funcValue3, "foo");
412-
413-
auto funcValue4 = parser.peekComponentValue<std::optional<std::string_view>>(
414-
[&](const CSSFunctionBlock& function, CSSSyntaxParser& /*blockParser*/) {
415-
EXPECT_EQ(function.name, "foo");
416-
return function.name;
417-
});
418-
419-
EXPECT_EQ(funcValue4, std::nullopt);
420-
}
421-
422-
TEST(CSSSyntaxParser, preserved_token_without_visitor_consumed) {
423-
CSSSyntaxParser parser{"foo bar"};
424-
425-
parser.consumeComponentValue();
426-
427-
auto identValue = parser.consumeComponentValue<std::string_view>(
428-
CSSDelimiter::Whitespace, [](const CSSPreservedToken& token) {
429-
EXPECT_EQ(token.type(), CSSTokenType::Ident);
430-
EXPECT_EQ(token.stringValue(), "bar");
431-
return token.stringValue();
432-
});
433-
434-
EXPECT_EQ(identValue, "bar");
435-
}
436-
437-
TEST(CSSSyntaxParser, function_without_visitor_consumed) {
438-
CSSSyntaxParser parser{"foo(a, b, c) bar"};
439-
440-
parser.consumeComponentValue();
441-
442-
auto identValue = parser.consumeComponentValue<std::string_view>(
443-
CSSDelimiter::Whitespace, [](const CSSPreservedToken& token) {
444-
EXPECT_EQ(token.type(), CSSTokenType::Ident);
445-
EXPECT_EQ(token.stringValue(), "bar");
446-
return token.stringValue();
447-
});
448-
449-
EXPECT_EQ(identValue, "bar");
450-
}
451-
452-
TEST(CSSSyntaxParser, simple_block_without_visitor_consumed) {
453-
CSSSyntaxParser parser{"{a foo(abc)} bar"};
454-
455-
parser.consumeComponentValue();
456-
457-
auto identValue = parser.consumeComponentValue<std::string_view>(
458-
CSSDelimiter::Whitespace, [](const CSSPreservedToken& token) {
459-
EXPECT_EQ(token.type(), CSSTokenType::Ident);
460-
EXPECT_EQ(token.stringValue(), "bar");
461-
return token.stringValue();
462-
});
463-
464-
EXPECT_EQ(identValue, "bar");
465-
}
466-
467385
TEST(CSSSyntaxParser, solidus_delimiter) {
468386
CSSSyntaxParser parser{"foo / bar"};
469387

@@ -606,4 +524,39 @@ TEST(CSSSyntaxParser, delimeter_not_consumed_when_no_component_value) {
606524
EXPECT_TRUE(hasComma);
607525
}
608526

527+
TEST(CSSSyntaxParser, component_value_not_consumed_on_visitor_failure) {
528+
CSSSyntaxParser parser{"foo"};
529+
530+
bool visitor1Attempted = false;
531+
bool visitor1Ret =
532+
parser.consumeComponentValue<bool>([&](const CSSPreservedToken& token) {
533+
EXPECT_EQ(token.stringValue(), "foo");
534+
visitor1Attempted = true;
535+
return false;
536+
});
537+
538+
EXPECT_TRUE(visitor1Attempted);
539+
EXPECT_FALSE(visitor1Ret);
540+
541+
bool visitor2Attempted = false;
542+
parser.consumeComponentValue<bool>([&](const CSSPreservedToken& token) {
543+
EXPECT_EQ(token.stringValue(), "foo");
544+
visitor2Attempted = true;
545+
return true;
546+
});
547+
548+
EXPECT_TRUE(visitor2Attempted);
549+
EXPECT_TRUE(visitor2Attempted);
550+
551+
bool visitor3Attempted = false;
552+
bool visitor3Ret =
553+
parser.consumeComponentValue<bool>([&](const CSSPreservedToken& token) {
554+
visitor3Attempted = true;
555+
return true;
556+
});
557+
558+
EXPECT_FALSE(visitor3Attempted);
559+
EXPECT_FALSE(visitor3Ret);
560+
}
561+
609562
} // namespace facebook::react

0 commit comments

Comments
 (0)