[esnext] add support for hashbang syntax

Implements https://tc39.github.io/proposal-hashbang/, which simply
ignores the first line of a source file if it begins with '#!'
(U+0023 U+0021).

The test cases are influenced by
https://github.com/tc39/test262/pull/1983, which have not been pulled
into test262 local-tests due to issues with parseTestRecord.

BUG=v8:8523
R=gsathya@chromium.org, adamk@chromium.org, littledan@chromium.org

Change-Id: I4ae40222298de768a170c7a1d45fec118ed5713c
Reviewed-on: https://chromium-review.googlesource.com/c/1409527
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: Daniel Ehrenberg <littledan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58838}
This commit is contained in:
Caitlin Potter 2019-01-14 20:10:55 -05:00 committed by Commit Bot
parent 93283bf04a
commit 10a408a6a7
8 changed files with 99 additions and 1 deletions

View File

@ -4209,6 +4209,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_numeric_separator)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_json_stringify)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_sequence)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_await_optimization)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_hashbang)
#undef EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE

View File

@ -201,7 +201,8 @@ DEFINE_IMPLICATION(harmony_private_methods, harmony_private_fields)
V(harmony_class_fields, "harmony fields in class literals") \
V(harmony_private_methods, "harmony private methods in class literals") \
V(harmony_regexp_sequence, "RegExp Unicode sequence properties") \
V(harmony_weak_refs, "harmony weak references")
V(harmony_weak_refs, "harmony weak references") \
V(harmony_hashbang, "harmony hashbang syntax")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \

View File

@ -495,6 +495,9 @@ FunctionLiteral* Parser::ParseProgram(Isolate* isolate, ParseInfo* info) {
DeserializeScopeChain(isolate, info, info->maybe_outer_scope_info());
scanner_.Initialize();
if (FLAG_harmony_hashbang && !info->is_eval()) {
scanner_.SkipHashBang();
}
FunctionLiteral* result = DoParseProgram(isolate, info);
MaybeResetCharacterStream(info, result);
MaybeProcessSourceRanges(info, result, stack_limit_);

View File

@ -78,6 +78,12 @@ PreParser::PreParseResult PreParser::PreParseProgram() {
scope->set_is_being_lazily_parsed(true);
#endif
if (FLAG_harmony_hashbang) {
// Note: We should only skip the hashbang in non-Eval scripts
// (currently, Eval is not handled by the PreParser).
scanner()->SkipHashBang();
}
// ModuleDeclarationInstantiation for Source Text Module Records creates a
// new Module Environment Record whose outer lexical environment record is
// the global scope.

View File

@ -361,6 +361,13 @@ Token::Value Scanner::SkipMultiLineComment() {
return Token::ILLEGAL;
}
void Scanner::SkipHashBang() {
if (c0_ == '#' && Peek() == '!' && source_pos() == 0) {
SkipSingleLineComment();
Scan();
}
}
Token::Value Scanner::ScanHtmlComment() {
// Check for <!-- comments.
DCHECK_EQ(c0_, '!');

View File

@ -407,6 +407,9 @@ class Scanner {
const Utf16CharacterStream* stream() const { return source_; }
// If the next characters in the stream are "#!", the line is skipped.
void SkipHashBang();
private:
// Scoped helper for saving & restoring scanner error state.
// This is used for tagged template literals, in which normally forbidden

View File

@ -11416,6 +11416,72 @@ TEST(PrivateNamesSyntaxError) {
}
}
TEST(HashbangSyntax) {
const char* context_data[][2] = {
{"#!\n", ""}, {"#!---IGNORED---\n", ""}, {nullptr, nullptr}};
const char* data[] = {"function\nFN\n(\n)\n {\n}\nFN();", nullptr};
i::FLAG_harmony_hashbang = true;
RunParserSyncTest(context_data, data, kSuccess);
RunParserSyncTest(context_data, data, kSuccess, nullptr, 0, nullptr, 0,
nullptr, 0, true);
i::FLAG_harmony_hashbang = false;
RunParserSyncTest(context_data, data, kError);
RunParserSyncTest(context_data, data, kError, nullptr, 0, nullptr, 0, nullptr,
0, true);
}
TEST(HashbangSyntaxErrors) {
const char* file_context_data[][2] = {{"", ""}, {nullptr, nullptr}};
const char* other_context_data[][2] = {{"/**/", ""},
{"//---\n", ""},
{";", ""},
{"function fn() {", "}"},
{"function* fn() {", "}"},
{"async function fn() {", "}"},
{"async function* fn() {", "}"},
{"() => {", "}"},
{"() => ", ""},
{"function fn(a = ", ") {}"},
{"function* fn(a = ", ") {}"},
{"async function fn(a = ", ") {}"},
{"async function* fn(a = ", ") {}"},
{"(a = ", ") => {}"},
{"(a = ", ") => a"},
{"class k {", "}"},
{"[", "]"},
{"{", "}"},
{"({", "})"},
{nullptr, nullptr}};
const char* invalid_hashbang_data[] = {// Encoded characters are not allowed
"#\\u0021\n"
"\\u0023!\n",
"\\u0023\\u0021\n",
"\n#!---IGNORED---\n",
" #!---IGNORED---\n", nullptr};
const char* hashbang_data[] = {"#!\n", "#!---IGNORED---\n", nullptr};
auto SyntaxErrorTest = [](const char* context_data[][2], const char* data[]) {
i::FLAG_harmony_hashbang = true;
RunParserSyncTest(context_data, data, kError);
RunParserSyncTest(context_data, data, kError, nullptr, 0, nullptr, 0,
nullptr, 0, true);
i::FLAG_harmony_hashbang = false;
RunParserSyncTest(context_data, data, kError);
RunParserSyncTest(context_data, data, kError, nullptr, 0, nullptr, 0,
nullptr, 0, true);
};
SyntaxErrorTest(file_context_data, invalid_hashbang_data);
SyntaxErrorTest(other_context_data, invalid_hashbang_data);
SyntaxErrorTest(other_context_data, hashbang_data);
}
} // namespace test_parsing
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,11 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-hashbang
// Hashbang syntax is not allowed in eval.
assertThrows("#!", SyntaxError);
assertThrows("#!\n", SyntaxError);
assertThrows("#!---IGNORED---", SyntaxError);
assertThrows("#!---IGNORED---\n", SyntaxError);