Commit d85b5b75 authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

[parser] Reimplement duplicate __proto__ checking

Now that identifiers in the preparser also carry their string, we can simply
check that rather than relying on a weird "keyword". Dropping __proto__ as a
keyword allows us to delist '_' as keyword character.

Change-Id: I775df25f77a84de92a60790ca665f16d52abf4bf
Reviewed-on: https://chromium-review.googlesource.com/c/1329692
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57427}
parent de50808c
......@@ -321,7 +321,6 @@ class ParserBase {
};
class ClassLiteralChecker;
class ObjectLiteralChecker;
// ---------------------------------------------------------------------------
// BlockState and FunctionState implement the parser's scope stack.
......@@ -1018,8 +1017,8 @@ class ParserBase {
bool* is_private);
ExpressionT ParseMemberInitializer(ClassInfo* class_info, int beg_pos,
bool is_static);
ObjectLiteralPropertyT ParseObjectPropertyDefinition(
ObjectLiteralChecker* checker, bool* is_computed_name,
ObjectLiteralPropertyT ParseObjectPropertyDefinition(bool* has_seen_proto,
bool* is_computed_name,
bool* is_rest_property);
void ParseArguments(ExpressionListT* args, bool* has_spread,
bool maybe_arrow);
......@@ -1313,27 +1312,6 @@ class ParserBase {
return factory()->NewReturnStatement(expr, pos, end_pos);
}
// Validation per ES6 object literals.
class ObjectLiteralChecker {
public:
explicit ObjectLiteralChecker(ParserBase* parser)
: parser_(parser), has_seen_proto_(false) {}
void CheckDuplicateProto(Token::Value property);
private:
bool IsProto() const {
return this->scanner()->CurrentMatchesContextualEscaped(
Token::PROTO_UNDERSCORED);
}
ParserBase* parser() const { return parser_; }
Scanner* scanner() const { return parser_->scanner(); }
ParserBase* parser_;
bool has_seen_proto_;
};
// Validation per ES6 class literals.
class ClassLiteralChecker {
public:
......@@ -2354,7 +2332,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberInitializer(
template <typename Impl>
typename ParserBase<Impl>::ObjectLiteralPropertyT
ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker,
ParserBase<Impl>::ParseObjectPropertyDefinition(bool* has_seen_proto,
bool* is_computed_name,
bool* is_rest_property) {
ParseFunctionFlags function_flags = ParseFunctionFlag::kIsNormal;
......@@ -2389,8 +2367,14 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker,
case ParsePropertyKind::kValue: {
DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal);
if (!*is_computed_name) {
checker->CheckDuplicateProto(name_token);
if (!*is_computed_name &&
(name_token == Token::IDENTIFIER || name_token == Token::STRING) &&
impl()->IdentifierEquals(name, ast_value_factory()->proto_string())) {
if (*has_seen_proto) {
classifier()->RecordExpressionError(scanner()->location(),
MessageTemplate::kDuplicateProto);
}
*has_seen_proto = true;
}
Consume(Token::COLON);
int beg_pos = peek_position();
......@@ -2544,7 +2528,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral() {
bool has_computed_names = false;
bool has_rest_property = false;
ObjectLiteralChecker checker(this);
bool has_seen_proto = false;
Consume(Token::LBRACE);
......@@ -2554,7 +2538,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral() {
bool is_computed_name = false;
bool is_rest_property = false;
ObjectLiteralPropertyT property = ParseObjectPropertyDefinition(
&checker, &is_computed_name, &is_rest_property);
&has_seen_proto, &is_computed_name, &is_rest_property);
if (impl()->IsNull(property)) return impl()->FailureExpression();
if (is_computed_name) {
......@@ -5860,21 +5844,6 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
return final_loop;
}
template <typename Impl>
void ParserBase<Impl>::ObjectLiteralChecker::CheckDuplicateProto(
Token::Value property) {
if (property == Token::SMI || property == Token::NUMBER) return;
if (IsProto()) {
if (has_seen_proto_) {
this->parser()->classifier()->RecordExpressionError(
this->scanner()->location(), MessageTemplate::kDuplicateProto);
return;
}
has_seen_proto_ = true;
}
}
template <typename Impl>
void ParserBase<Impl>::ClassLiteralChecker::CheckClassMethodName(
Token::Value property, ParsePropertyKind type, ParseFunctionFlags flags,
......
......@@ -329,6 +329,11 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
const char* name, DeclarationScope* scope,
ZonePtrList<ClassLiteral::Property>* fields);
bool IdentifierEquals(const AstRawString* identifier,
const AstRawString* other) {
return identifier == other;
}
Statement* DeclareClass(const AstRawString* variable_name, Expression* value,
ZonePtrList<const AstRawString>* names,
int class_token_pos, int end_pos);
......
......@@ -387,6 +387,12 @@ PreParserStatement PreParser::BuildParameterInitializationBlock(
return PreParserStatement::Default();
}
bool PreParser::IdentifierEquals(const PreParserIdentifier& identifier,
const AstRawString* other) {
DCHECK_EQ(identifier.string_ == nullptr, has_error());
return identifier.string_ == other;
}
PreParserExpression PreParser::ExpressionFromIdentifier(
const PreParserIdentifier& name, int start_position, InferName infer) {
VariableProxy* proxy = nullptr;
......
......@@ -1146,6 +1146,9 @@ class PreParser : public ParserBase<PreParser> {
function_scope);
}
bool IdentifierEquals(const PreParserIdentifier& identifier,
const AstRawString* other);
// TODO(nikolaos): The preparser currently does not keep track of labels
// and targets.
V8_INLINE PreParserStatement
......
......@@ -234,8 +234,6 @@ static const Token::Value one_char_tokens[] = {
KEYWORD("with", Token::WITH) \
KEYWORD_GROUP('y') \
KEYWORD("yield", Token::YIELD) \
KEYWORD_GROUP('_') \
KEYWORD("__proto__", Token::PROTO_UNDERSCORED) \
KEYWORD_GROUP('#') \
KEYWORD("#constructor", Token::PRIVATE_CONSTRUCTOR)
......@@ -299,12 +297,12 @@ enum class ScanFlags : uint8_t {
};
constexpr uint8_t GetScanFlags(char c) {
return
// Keywords are all lowercase and only contain letters and '_'.
// Keywords are all lowercase and only contain letters.
// Note that non-identifier characters do not set this flag, so
// that it plays well with kTerminatesLiteral
// TODO(leszeks): We could probably get an even tighter measure
// here if not all letters are present in keywords.
(IsAsciiIdentifier(c) && !IsInRange(c, 'a', 'z') && c != '_'
(IsAsciiIdentifier(c) && !IsInRange(c, 'a', 'z')
? static_cast<uint8_t>(ScanFlags::kCannotBeKeyword)
: 0) |
// Anything that isn't an identifier character will terminate the
......
......@@ -204,7 +204,6 @@ namespace internal {
C(AS, "as", 0) \
C(FROM, "from", 0) \
C(NAME, "name", 0) \
C(PROTO_UNDERSCORED, "__proto__", 0) \
C(CONSTRUCTOR, "constructor", 0) \
C(PRIVATE_CONSTRUCTOR, "#constructor", 0) \
C(PROTOTYPE, "prototype", 0) \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment