Commit 81d168d0 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by Commit Bot

Implement string literal module export names

Implements https://github.com/tc39/ecma262/pull/2154, which allows
module export names to be string literals.

Semantics highlights:
  - It is a SyntaxError for string literal export names to have unpaired
    UTF16 surrogates.
  - It is a SyntaxError for string literal export names to be used as
    the local name without being followed by a 'from' clause. For example,
    `export { "foo" }` and `export { "foo" as "bar" }` are errors, but
    `export { "foo" } from "./module.js"` is allowed.

The remaining failing test262 test is wrong:
https://github.com/tc39/test262/issues/2866

Bug: v8:10964
Change-Id: Ib3e06e1ee6b3f1b60ed7f24e21902e17ddfc0351
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2482335
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70692}
parent 757f0431
......@@ -395,6 +395,8 @@ namespace internal {
"Async functions can only be declared at the top level or inside a " \
"block.") \
T(IllegalBreak, "Illegal break statement") \
T(ModuleExportNameWithoutFromClause, \
"String literal module export names must be followed by a 'from' clause") \
T(NoIterationStatement, \
"Illegal continue statement: no surrounding iteration statement") \
T(IllegalContinue, \
......@@ -419,6 +421,8 @@ namespace internal {
"Invalid left-hand side expression in postfix operation") \
T(InvalidLhsInPrefixOp, \
"Invalid left-hand side expression in prefix operation") \
T(InvalidModuleExportName, \
"Invalid module export name: contains unpaired surrogate") \
T(InvalidRegExpFlags, "Invalid flags supplied to RegExp constructor '%'") \
T(InvalidOrUnexpectedToken, "Invalid or unexpected token") \
T(InvalidPrivateBrand, "Object must be an instance of class %") \
......
......@@ -27,6 +27,7 @@
#include "src/runtime/runtime.h"
#include "src/strings/char-predicates-inl.h"
#include "src/strings/string-stream.h"
#include "src/strings/unicode-inl.h"
#include "src/tracing/trace-event.h"
#include "src/zone/zone-list-inl.h"
......@@ -1071,7 +1072,8 @@ const AstRawString* Parser::ParseModuleSpecifier() {
}
ZoneChunkList<Parser::ExportClauseData>* Parser::ParseExportClause(
Scanner::Location* reserved_loc) {
Scanner::Location* reserved_loc,
Scanner::Location* string_literal_local_name_loc) {
// ExportClause :
// '{' '}'
// '{' ExportsList '}'
......@@ -1084,6 +1086,12 @@ ZoneChunkList<Parser::ExportClauseData>* Parser::ParseExportClause(
// ExportSpecifier :
// IdentifierName
// IdentifierName 'as' IdentifierName
// IdentifierName 'as' ModuleExportName
// ModuleExportName
// ModuleExportName 'as' ModuleExportName
//
// ModuleExportName :
// StringLiteral
ZoneChunkList<ExportClauseData>* export_data =
zone()->New<ZoneChunkList<ExportClauseData>>(zone());
......@@ -1091,23 +1099,27 @@ ZoneChunkList<Parser::ExportClauseData>* Parser::ParseExportClause(
Token::Value name_tok;
while ((name_tok = peek()) != Token::RBRACE) {
// Keep track of the first reserved word encountered in case our
// caller needs to report an error.
if (!reserved_loc->IsValid() &&
!Token::IsValidIdentifier(name_tok, LanguageMode::kStrict, false,
flags().is_module())) {
const AstRawString* local_name = ParseExportSpecifierName();
if (!string_literal_local_name_loc->IsValid() &&
name_tok == Token::STRING) {
// Keep track of the first string literal local name exported for error
// reporting. These must be followed by a 'from' clause.
*string_literal_local_name_loc = scanner()->location();
} else if (!reserved_loc->IsValid() &&
!Token::IsValidIdentifier(name_tok, LanguageMode::kStrict, false,
flags().is_module())) {
// Keep track of the first reserved word encountered in case our
// caller needs to report an error.
*reserved_loc = scanner()->location();
}
const AstRawString* local_name = ParsePropertyName();
const AstRawString* export_name = nullptr;
const AstRawString* export_name;
Scanner::Location location = scanner()->location();
if (CheckContextualKeyword(ast_value_factory()->as_string())) {
export_name = ParsePropertyName();
export_name = ParseExportSpecifierName();
// Set the location to the whole "a as b" string, so that it makes sense
// both for errors due to "a" and for errors due to "b".
location.end_pos = scanner()->location().end_pos;
}
if (export_name == nullptr) {
} else {
export_name = local_name;
}
export_data->push_back({export_name, local_name, location});
......@@ -1122,6 +1134,31 @@ ZoneChunkList<Parser::ExportClauseData>* Parser::ParseExportClause(
return export_data;
}
const AstRawString* Parser::ParseExportSpecifierName() {
Token::Value next = Next();
// IdentifierName
if (V8_LIKELY(Token::IsPropertyName(next))) {
return GetSymbol();
}
// ModuleExportName
if (next == Token::STRING) {
const AstRawString* export_name = GetSymbol();
if (V8_LIKELY(export_name->is_one_byte())) return export_name;
if (!unibrow::Utf16::HasUnpairedSurrogate(
reinterpret_cast<const uint16_t*>(export_name->raw_data()),
export_name->length())) {
return export_name;
}
ReportMessage(MessageTemplate::kInvalidModuleExportName);
return EmptyIdentifierString();
}
ReportUnexpectedToken(next);
return EmptyIdentifierString();
}
ZonePtrList<const Parser::NamedImport>* Parser::ParseNamedImports(int pos) {
// NamedImports :
// '{' '}'
......@@ -1135,12 +1172,13 @@ ZonePtrList<const Parser::NamedImport>* Parser::ParseNamedImports(int pos) {
// ImportSpecifier :
// BindingIdentifier
// IdentifierName 'as' BindingIdentifier
// ModuleExportName 'as' BindingIdentifier
Expect(Token::LBRACE);
auto result = zone()->New<ZonePtrList<const NamedImport>>(1, zone());
while (peek() != Token::RBRACE) {
const AstRawString* import_name = ParsePropertyName();
const AstRawString* import_name = ParseExportSpecifierName();
const AstRawString* local_name = import_name;
Scanner::Location location = scanner()->location();
// In the presence of 'as', the left-side of the 'as' can
......@@ -1450,9 +1488,14 @@ void Parser::ParseExportStar() {
// export * as x from "...";
// ~>
// import * as .x from "..."; export {.x as x};
//
// Note that the desugared internal namespace export name (.x above) will
// never conflict with a string literal export name, as literal string export
// names in local name positions (i.e. left of 'as' or in a clause without
// 'as') are disallowed without a following 'from' clause.
ExpectContextualKeyword(ast_value_factory()->as_string());
const AstRawString* export_name = ParsePropertyName();
const AstRawString* export_name = ParseExportSpecifierName();
Scanner::Location export_name_loc = scanner()->location();
const AstRawString* local_name = NextInternalNamespaceExportName();
Scanner::Location local_name_loc = Scanner::Location::invalid();
......@@ -1478,12 +1521,18 @@ Statement* Parser::ParseExportDeclaration() {
// 'export' '*' 'as' IdentifierName 'from' ModuleSpecifier ';'
// 'export' '*' 'as' IdentifierName 'from' ModuleSpecifier
// [no LineTerminator here] AssertClause ';'
// 'export' '*' 'as' ModuleExportName 'from' ModuleSpecifier ';'
// 'export' '*' 'as' ModuleExportName 'from' ModuleSpecifier ';'
// [no LineTerminator here] AssertClause ';'
// 'export' ExportClause ('from' ModuleSpecifier)? ';'
// 'export' ExportClause ('from' ModuleSpecifier [no LineTerminator here]
// AssertClause)? ';'
// 'export' VariableStatement
// 'export' Declaration
// 'export' 'default' ... (handled in ParseExportDefault)
//
// ModuleExportName :
// StringLiteral
Expect(Token::EXPORT);
Statement* result = nullptr;
......@@ -1510,8 +1559,10 @@ Statement* Parser::ParseExportDeclaration() {
// encountered, and then throw a SyntaxError if we are in the
// non-FromClause case.
Scanner::Location reserved_loc = Scanner::Location::invalid();
Scanner::Location string_literal_local_name_loc =
Scanner::Location::invalid();
ZoneChunkList<ExportClauseData>* export_data =
ParseExportClause(&reserved_loc);
ParseExportClause(&reserved_loc, &string_literal_local_name_loc);
if (CheckContextualKeyword(ast_value_factory()->from_string())) {
Scanner::Location specifier_loc = scanner()->peek_location();
const AstRawString* module_specifier = ParseModuleSpecifier();
......@@ -1533,6 +1584,10 @@ Statement* Parser::ParseExportDeclaration() {
// No FromClause, so reserved words are invalid in ExportClause.
ReportMessageAt(reserved_loc, MessageTemplate::kUnexpectedReserved);
return nullptr;
} else if (string_literal_local_name_loc.IsValid()) {
ReportMessageAt(string_literal_local_name_loc,
MessageTemplate::kModuleExportNameWithoutFromClause);
return nullptr;
}
ExpectSemicolon();
......
......@@ -269,7 +269,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Scanner::Location location;
};
ZoneChunkList<ExportClauseData>* ParseExportClause(
Scanner::Location* reserved_loc);
Scanner::Location* reserved_loc,
Scanner::Location* string_literal_local_name_loc);
struct NamedImport : public ZoneObject {
const AstRawString* import_name;
const AstRawString* local_name;
......@@ -280,6 +281,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
local_name(local_name),
location(location) {}
};
const AstRawString* ParseExportSpecifierName();
ZonePtrList<const NamedImport>* ParseNamedImports(int pos);
using ImportAssertions =
ZoneMap<const AstRawString*,
......
......@@ -59,6 +59,25 @@ int Mapping<T, s>::CalculateValue(uchar c, uchar n, uchar* result) {
}
#endif // !V8_INTL_SUPPORT
bool Utf16::HasUnpairedSurrogate(const uint16_t* code_units, size_t length) {
for (size_t i = 0; i < length; ++i) {
const int code_unit = code_units[i];
if (IsLeadSurrogate(code_unit)) {
// The current code unit is a leading surrogate. Check if it is followed
// by a trailing surrogate.
if (i == length - 1) return true;
if (!IsTrailSurrogate(code_units[i + 1])) return true;
// Skip the paired trailing surrogate.
++i;
} else if (IsTrailSurrogate(code_unit)) {
// All paired trailing surrogates are skipped above, so this branch is
// only for those that are unpaired.
return true;
}
}
return false;
}
// Decodes UTF-8 bytes incrementally, allowing the decoding of bytes as they
// stream in. This **must** be followed by a call to ValueOfIncrementalFinish
// when the stream is complete, to ensure incomplete sequences are handled.
......
......@@ -128,6 +128,8 @@ class Utf16 {
static inline uint16_t TrailSurrogate(uint32_t char_code) {
return 0xdc00 + (char_code & 0x3ff);
}
static inline bool HasUnpairedSurrogate(const uint16_t* code_units,
size_t length);
};
class Latin1 {
......
......@@ -84,7 +84,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 48 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 53 S> */ B(Wide), B(LdaSmi), I16(270),
/* 53 S> */ B(Wide), B(LdaSmi), I16(272),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
......@@ -115,7 +115,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 41 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 46 S> */ B(Wide), B(LdaSmi), I16(269),
/* 46 S> */ B(Wide), B(LdaSmi), I16(271),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
......@@ -146,7 +146,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 48 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 53 S> */ B(Wide), B(LdaSmi), I16(270),
/* 53 S> */ B(Wide), B(LdaSmi), I16(272),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
......@@ -177,7 +177,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 41 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 46 S> */ B(Wide), B(LdaSmi), I16(269),
/* 46 S> */ B(Wide), B(LdaSmi), I16(271),
B(Star), R(4),
B(LdaConstant), U8(0),
B(Star), R(5),
......
......@@ -57,7 +57,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 44 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 49 S> */ B(Wide), B(LdaSmi), I16(268),
/* 49 S> */ B(Wide), B(LdaSmi), I16(270),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
......@@ -89,7 +89,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 44 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 49 S> */ B(Wide), B(LdaSmi), I16(268),
/* 49 S> */ B(Wide), B(LdaSmi), I16(270),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
......
......@@ -25,7 +25,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(1),
B(JumpIfTrue), U8(18),
B(Wide), B(LdaSmi), I16(266),
B(Wide), B(LdaSmi), I16(268),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
......@@ -56,7 +56,7 @@ frame size: 2
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 56 S> */ B(Wide), B(LdaSmi), I16(268),
/* 56 S> */ B(Wide), B(LdaSmi), I16(270),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
......@@ -83,7 +83,7 @@ frame size: 2
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 56 S> */ B(Wide), B(LdaSmi), I16(268),
/* 56 S> */ B(Wide), B(LdaSmi), I16(270),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
......@@ -122,7 +122,7 @@ bytecodes: [
/* 94 E> */ B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(18),
B(Wide), B(LdaSmi), I16(266),
B(Wide), B(LdaSmi), I16(268),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
......@@ -144,7 +144,7 @@ bytecodes: [
/* 109 E> */ B(TestReferenceEqual), R(this),
B(Mov), R(this), R(1),
B(JumpIfTrue), U8(18),
B(Wide), B(LdaSmi), I16(267),
B(Wide), B(LdaSmi), I16(269),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
......@@ -159,7 +159,7 @@ bytecodes: [
/* 133 E> */ B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(18),
B(Wide), B(LdaSmi), I16(266),
B(Wide), B(LdaSmi), I16(268),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
......@@ -189,7 +189,7 @@ frame size: 2
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 60 S> */ B(Wide), B(LdaSmi), I16(270),
/* 60 S> */ B(Wide), B(LdaSmi), I16(272),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
......@@ -215,7 +215,7 @@ frame size: 2
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 53 S> */ B(Wide), B(LdaSmi), I16(269),
/* 53 S> */ B(Wide), B(LdaSmi), I16(271),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
......@@ -241,7 +241,7 @@ frame size: 2
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 60 S> */ B(Wide), B(LdaSmi), I16(270),
/* 60 S> */ B(Wide), B(LdaSmi), I16(272),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
......@@ -267,7 +267,7 @@ frame size: 3
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 46 S> */ B(Wide), B(LdaSmi), I16(269),
/* 46 S> */ B(Wide), B(LdaSmi), I16(271),
B(Star), R(1),
B(LdaConstant), U8(0),
B(Star), R(2),
......
......@@ -649,16 +649,6 @@
'built-ins/TypedArray/prototype/item/returns-undefined-for-holes-in-sparse-arrays': [FAIL],
'built-ins/TypedArray/prototype/item/returns-undefined-for-out-of-range-index': [FAIL],
# http://crbug/v8/10964
'language/module-code/export-expname-binding-string': [FAIL],
'language/module-code/export-expname-from-binding-string': [FAIL],
'language/module-code/export-expname-from-star': [FAIL],
'language/module-code/export-expname-from-star-string': [FAIL],
'language/module-code/export-expname-from-string': [FAIL],
'language/module-code/export-expname-from-string-binding': [FAIL],
'language/module-code/export-expname-from-string-string': [FAIL],
'language/module-code/export-expname-import-string-binding': [FAIL],
# http://crbug/v8/11039
'intl402/Locale/reject-duplicate-variants-in-tlang': [FAIL],
......
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