Commit 353b6964 authored by caitpotter88's avatar caitpotter88 Committed by Commit bot

Implement ES6 Template Literals

BUG=v8:3230

Review URL: https://codereview.chromium.org/663683006

Cr-Commit-Position: refs/heads/master@{#25362}
parent c97a7acf
......@@ -245,7 +245,8 @@ action("js2c_experimental") {
"src/harmony-array.js",
"src/harmony-typedarray.js",
"src/harmony-classes.js",
"src/harmony-tostring.js"
"src/harmony-tostring.js",
"src/harmony-templates.js"
]
outputs = [
......
......@@ -248,6 +248,7 @@ class AstValue : public ZoneObject {
F(dot_result, ".result") \
F(empty, "") \
F(eval, "eval") \
F(get_template_callsite, "GetTemplateCallSite") \
F(initialize_const_global, "initializeConstGlobal") \
F(initialize_var_global, "initializeVarGlobal") \
F(make_reference_error, "MakeReferenceErrorEmbedded") \
......
......@@ -1597,6 +1597,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_regexps)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_arrow_functions)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_numeric_literals)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_tostring)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_templates)
void Genesis::InstallNativeFunctions_harmony_proxies() {
......@@ -1623,6 +1624,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_arrow_functions)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_numeric_literals)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tostring)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_proxies)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_templates)
void Genesis::InitializeGlobal_harmony_regexps() {
Handle<JSObject> builtins(native_context()->builtins());
......@@ -2176,6 +2178,8 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_numeric_literals_natives[] = {NULL};
static const char* harmony_tostring_natives[] = {"native harmony-tostring.js",
NULL};
static const char* harmony_templates_natives[] = {
"native harmony-templates.js", NULL};
for (int i = ExperimentalNatives::GetDebuggerCount();
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
......
......@@ -172,7 +172,8 @@ DEFINE_IMPLICATION(harmony, es_staging)
V(harmony_regexps, "harmony regular expression extensions") \
V(harmony_arrow_functions, "harmony arrow functions") \
V(harmony_tostring, "harmony toString") \
V(harmony_proxies, "harmony proxies")
V(harmony_proxies, "harmony proxies") \
V(harmony_templates, "harmony template literals")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) V(harmony_strings, "harmony string methods")
......
// Copyright 2014 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.
'use strict';
function GetTemplateCallSite(siteObj, rawStrings) {
// TODO(caitp): ensure same template callsite is used for subsequent tag calls
%AddNamedProperty(siteObj, "raw", %ObjectFreeze(rawStrings),
READ_ONLY | DONT_ENUM | DONT_DELETE);
return %ObjectFreeze(siteObj);
}
......@@ -18,9 +18,12 @@ var kMessages = {
unexpected_reserved: ["Unexpected reserved word"],
unexpected_strict_reserved: ["Unexpected strict mode reserved word"],
unexpected_eos: ["Unexpected end of input"],
unexpected_template_string: ["Unexpected template string"],
malformed_regexp: ["Invalid regular expression: /", "%0", "/: ", "%1"],
malformed_regexp_flags: ["Invalid regular expression flags"],
unterminated_regexp: ["Invalid regular expression: missing /"],
unterminated_template: ["Unterminated template literal"],
unterminated_template_expr: ["Missing } in template expression"],
regexp_flags: ["Cannot supply flags when constructing one RegExp from another"],
incompatible_method_receiver: ["Method ", "%0", " called on incompatible receiver ", "%1"],
multiple_defaults_in_switch: ["More than one default clause in switch statement"],
......
......@@ -802,6 +802,7 @@ Parser::Parser(CompilationInfo* info, ParseInfo* parse_info)
set_allow_harmony_numeric_literals(FLAG_harmony_numeric_literals);
set_allow_classes(FLAG_harmony_classes);
set_allow_harmony_object_literals(FLAG_harmony_object_literals);
set_allow_harmony_templates(FLAG_harmony_templates);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
use_counts_[feature] = 0;
......@@ -3882,6 +3883,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
reusable_preparser_->set_allow_classes(allow_classes());
reusable_preparser_->set_allow_harmony_object_literals(
allow_harmony_object_literals());
reusable_preparser_->set_allow_harmony_templates(allow_harmony_templates());
}
PreParser::PreParseResult result =
reusable_preparser_->PreParseLazyFunction(strict_mode(),
......@@ -5084,4 +5086,141 @@ void Parser::ParseOnBackground() {
log_ = NULL;
}
}
ParserTraits::TemplateLiteralState Parser::OpenTemplateLiteral(int pos) {
return new (zone()) ParserTraits::TemplateLiteral(zone(), pos);
}
void Parser::AddTemplateSpan(TemplateLiteralState* state, bool tail) {
int pos = scanner()->location().beg_pos;
int end = scanner()->location().end_pos - (tail ? 1 : 2);
const AstRawString* tv = scanner()->CurrentSymbol(ast_value_factory());
Literal* cooked = factory()->NewStringLiteral(tv, pos);
(*state)->AddTemplateSpan(cooked, end, zone());
}
void Parser::AddTemplateExpression(TemplateLiteralState* state,
Expression* expression) {
(*state)->AddExpression(expression, zone());
}
Expression* Parser::CloseTemplateLiteral(TemplateLiteralState* state, int start,
Expression* tag) {
TemplateLiteral* lit = *state;
int pos = lit->position();
const ZoneList<Expression*>* cooked_strings = lit->cooked();
const ZoneList<Expression*>* expressions = lit->expressions();
CHECK(cooked_strings->length() == (expressions->length() + 1));
if (!tag) {
// Build tree of BinaryOps to simplify code-generation
Expression* expr = NULL;
if (expressions->length() == 0) {
// Simple case: treat as string literal
expr = cooked_strings->at(0);
} else {
int i;
Expression* cooked_str = cooked_strings->at(0);
expr = factory()->NewBinaryOperation(
Token::ADD, cooked_str, expressions->at(0), cooked_str->position());
for (i = 1; i < expressions->length(); ++i) {
cooked_str = cooked_strings->at(i);
expr = factory()->NewBinaryOperation(
Token::ADD, expr, factory()->NewBinaryOperation(
Token::ADD, cooked_str, expressions->at(i),
cooked_str->position()),
cooked_str->position());
}
cooked_str = cooked_strings->at(i);
expr = factory()->NewBinaryOperation(Token::ADD, expr, cooked_str,
cooked_str->position());
}
return expr;
} else {
ZoneList<Expression*>* raw_strings = TemplateRawStrings(lit);
Handle<String> source(String::cast(script()->source()));
int cooked_idx = function_state_->NextMaterializedLiteralIndex();
int raw_idx = function_state_->NextMaterializedLiteralIndex();
// GetTemplateCallSite
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(4, zone());
args->Add(factory()->NewArrayLiteral(
const_cast<ZoneList<Expression*>*>(cooked_strings),
cooked_idx, pos),
zone());
args->Add(
factory()->NewArrayLiteral(
const_cast<ZoneList<Expression*>*>(raw_strings), raw_idx, pos),
zone());
this->CheckPossibleEvalCall(tag, scope_);
Expression* call_site = factory()->NewCallRuntime(
ast_value_factory()->get_template_callsite_string(), NULL, args, start);
// Call TagFn
ZoneList<Expression*>* call_args =
new (zone()) ZoneList<Expression*>(expressions->length() + 1, zone());
call_args->Add(call_site, zone());
call_args->AddAll(*expressions, zone());
return factory()->NewCall(tag, call_args, pos);
}
}
ZoneList<Expression*>* Parser::TemplateRawStrings(const TemplateLiteral* lit) {
const ZoneList<int>* lengths = lit->lengths();
const ZoneList<Expression*>* cooked_strings = lit->cooked();
int total = lengths->length();
ZoneList<Expression*>* raw_strings;
// Given a TemplateLiteral, produce a list of raw strings, used for generating
// a CallSite object for a tagged template invocations.
//
// A raw string will consist of the unescaped characters of a template span,
// with end-of-line sequences normalized to U+000A LINE FEEDs, and without
// leading or trailing template delimiters.
//
DCHECK(total);
Handle<String> source(String::cast(script()->source()));
raw_strings = new (zone()) ZoneList<Expression*>(total, zone());
for (int index = 0; index < total; ++index) {
int span_start = cooked_strings->at(index)->position() + 1;
int span_end = lengths->at(index) - 1;
int length;
int to_index = 0;
SmartArrayPointer<char> raw_chars =
source->ToCString(ALLOW_NULLS, FAST_STRING_TRAVERSAL, span_start,
span_end, &length);
// Normalize raw line-feeds. [U+000D U+000A] (CRLF) and [U+000D] (CR) must
// be translated into U+000A (LF).
for (int from_index = 0; from_index < length; ++from_index) {
char ch = raw_chars[from_index];
if (ch == '\r') {
ch = '\n';
if (from_index + 1 < length && raw_chars[from_index + 1] == '\n') {
++from_index;
}
}
raw_chars[to_index++] = ch;
}
const AstRawString* raw_str = ast_value_factory()->GetOneByteString(
OneByteVector(raw_chars.get(), to_index));
Literal* raw_lit = factory()->NewStringLiteral(raw_str, span_start - 1);
raw_strings->Add(raw_lit, zone());
}
return raw_strings;
}
} } // namespace v8::internal
......@@ -586,6 +586,47 @@ class ParserTraits {
V8_INLINE void CheckConflictingVarDeclarations(v8::internal::Scope* scope,
bool* ok);
class TemplateLiteral : public ZoneObject {
public:
TemplateLiteral(Zone* zone, int pos)
: cooked_(8, zone),
lengths_(8, zone),
expressions_(8, zone),
pos_(pos) {}
const ZoneList<Expression*>* cooked() const { return &cooked_; }
const ZoneList<int>* lengths() const { return &lengths_; }
const ZoneList<Expression*>* expressions() const { return &expressions_; }
int position() const { return pos_; }
void AddTemplateSpan(Literal* cooked, int end, Zone* zone) {
DCHECK_NOT_NULL(cooked);
cooked_.Add(cooked, zone);
lengths_.Add(end - cooked->position(), zone);
}
void AddExpression(Expression* expression, Zone* zone) {
DCHECK_NOT_NULL(expression);
expressions_.Add(expression, zone);
}
private:
ZoneList<Expression*> cooked_;
ZoneList<int> lengths_;
ZoneList<Expression*> expressions_;
int pos_;
};
typedef TemplateLiteral* TemplateLiteralState;
V8_INLINE TemplateLiteralState OpenTemplateLiteral(int pos);
V8_INLINE void AddTemplateSpan(TemplateLiteralState* state, bool tail);
V8_INLINE void AddTemplateExpression(TemplateLiteralState* state,
Expression* expression);
V8_INLINE Expression* CloseTemplateLiteral(TemplateLiteralState* state,
int start, Expression* tag);
V8_INLINE Expression* NoTemplateTag() { return NULL; }
private:
Parser* parser_;
};
......@@ -826,6 +867,13 @@ class Parser : public ParserBase<ParserTraits> {
void ThrowPendingError();
TemplateLiteralState OpenTemplateLiteral(int pos);
void AddTemplateSpan(TemplateLiteralState* state, bool tail);
void AddTemplateExpression(TemplateLiteralState* state,
Expression* expression);
Expression* CloseTemplateLiteral(TemplateLiteralState* state, int start,
Expression* tag);
ZoneList<Expression*>* TemplateRawStrings(const TemplateLiteral* lit);
Scanner scanner_;
PreParser* reusable_preparser_;
Scope* original_scope_; // for ES5 function declarations in sloppy eval
......@@ -925,6 +973,27 @@ class CompileTimeValue: public AllStatic {
DISALLOW_IMPLICIT_CONSTRUCTORS(CompileTimeValue);
};
ParserTraits::TemplateLiteralState ParserTraits::OpenTemplateLiteral(int pos) {
return parser_->OpenTemplateLiteral(pos);
}
void ParserTraits::AddTemplateSpan(TemplateLiteralState* state, bool tail) {
parser_->AddTemplateSpan(state, tail);
}
void ParserTraits::AddTemplateExpression(TemplateLiteralState* state,
Expression* expression) {
parser_->AddTemplateExpression(state, expression);
}
Expression* ParserTraits::CloseTemplateLiteral(TemplateLiteralState* state,
int start, Expression* tag) {
return parser_->CloseTemplateLiteral(state, start, tag);
}
} } // namespace v8::internal
#endif // V8_PARSER_H_
......@@ -102,6 +102,7 @@ class ParserBase : public Traits {
bool allow_harmony_object_literals() const {
return allow_harmony_object_literals_;
}
bool allow_harmony_templates() const { return scanner()->HarmonyTemplates(); }
// Setters that determine whether certain syntactical constructs are
// allowed to be parsed by this instance of the parser.
......@@ -119,6 +120,9 @@ class ParserBase : public Traits {
void set_allow_harmony_object_literals(bool allow) {
allow_harmony_object_literals_ = allow;
}
void set_allow_harmony_templates(bool allow) {
scanner()->SetHarmonyTemplates(allow);
}
protected:
enum AllowEvalOrArgumentsAsIdentifier {
......@@ -490,6 +494,8 @@ class ParserBase : public Traits {
bool* ok);
ExpressionT ParseArrowFunctionLiteral(int start_pos, ExpressionT params_ast,
bool* ok);
ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool* ok);
void AddTemplateExpression(ExpressionT);
// Checks if the expression is a valid reference expression (e.g., on the
// left-hand side of assignments). Although ruled out by ECMA as early errors,
......@@ -1365,6 +1371,18 @@ class PreParserTraits {
return 0;
}
struct TemplateLiteralState {};
TemplateLiteralState OpenTemplateLiteral(int pos) {
return TemplateLiteralState();
}
void AddTemplateSpan(TemplateLiteralState*, bool) {}
void AddTemplateExpression(TemplateLiteralState*, PreParserExpression) {}
PreParserExpression CloseTemplateLiteral(TemplateLiteralState*, int,
PreParserExpression) {
return EmptyExpression();
}
PreParserExpression NoTemplateTag() { return PreParserExpression::Default(); }
static AstValueFactory* ast_value_factory() { return NULL; }
void CheckConflictingVarDeclarations(PreParserScope scope, bool* ok) {}
......@@ -1599,6 +1617,10 @@ void ParserBase<Traits>::ReportUnexpectedToken(Token::Value token) {
case Token::FUTURE_STRICT_RESERVED_WORD:
return ReportMessageAt(source_location, strict_mode() == SLOPPY
? "unexpected_token_identifier" : "unexpected_strict_reserved");
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL:
return Traits::ReportMessageAt(source_location,
"unexpected_template_string");
default:
const char* name = Token::String(token);
DCHECK(name != NULL);
......@@ -1745,6 +1767,7 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
// RegExpLiteral
// ClassLiteral
// '(' Expression ')'
// TemplateLiteral
int pos = peek_position();
ExpressionT result = this->EmptyExpression();
......@@ -1833,6 +1856,12 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
break;
}
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL:
result =
this->ParseTemplateLiteral(Traits::NoTemplateTag(), pos, CHECK_OK);
break;
case Token::MOD:
if (allow_natives_syntax() || extension_ != NULL) {
result = this->ParseV8Intrinsic(CHECK_OK);
......@@ -2456,6 +2485,23 @@ ParserBase<Traits>::ParseLeftHandSideExpression(bool* ok) {
break;
}
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL: {
int pos;
if (scanner()->current_token() == Token::IDENTIFIER) {
pos = position();
} else {
pos = peek_position();
if (result->IsFunctionLiteral() && mode() == PARSE_EAGERLY) {
// If the tag function looks like an IIFE, set_parenthesized() to
// force eager compilation.
result->AsFunctionLiteral()->set_parenthesized();
}
}
result = ParseTemplateLiteral(result, pos, CHECK_OK);
break;
}
case Token::PERIOD: {
Consume(Token::PERIOD);
int pos = position();
......@@ -2724,9 +2770,86 @@ typename ParserBase<Traits>::ExpressionT ParserBase<
template <typename Traits>
typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::CheckAndRewriteReferenceExpression(
ExpressionT expression,
Scanner::Location location, const char* message, bool* ok) {
ParserBase<Traits>::ParseTemplateLiteral(ExpressionT tag, int start, bool* ok) {
// A TemplateLiteral is made up of 0 or more TEMPLATE_SPAN tokens (literal
// text followed by a substitution expression), finalized by a single
// TEMPLATE_TAIL.
//
// In terms of draft language, TEMPLATE_SPAN may be either the TemplateHead or
// TemplateMiddle productions, while TEMPLATE_TAIL is either TemplateTail, or
// NoSubstitutionTemplate.
//
// When parsing a TemplateLiteral, we must have scanned either an initial
// TEMPLATE_SPAN, or a TEMPLATE_TAIL.
CHECK(peek() == Token::TEMPLATE_SPAN || peek() == Token::TEMPLATE_TAIL);
// If we reach a TEMPLATE_TAIL first, we are parsing a NoSubstitutionTemplate.
// In this case we may simply consume the token and build a template with a
// single TEMPLATE_SPAN and no expressions.
if (peek() == Token::TEMPLATE_TAIL) {
Consume(Token::TEMPLATE_TAIL);
int pos = position();
typename Traits::TemplateLiteralState ts = Traits::OpenTemplateLiteral(pos);
Traits::AddTemplateSpan(&ts, true);
return Traits::CloseTemplateLiteral(&ts, start, tag);
}
Consume(Token::TEMPLATE_SPAN);
int pos = position();
typename Traits::TemplateLiteralState ts = Traits::OpenTemplateLiteral(pos);
Traits::AddTemplateSpan(&ts, false);
Token::Value next;
// If we open with a TEMPLATE_SPAN, we must scan the subsequent expression,
// and repeat if the following token is a TEMPLATE_SPAN as well (in this
// case, representing a TemplateMiddle).
do {
next = peek();
if (!next) {
ReportMessageAt(Scanner::Location(start, peek_position()),
"unterminated_template");
*ok = false;
return Traits::EmptyExpression();
}
int pos = peek_position();
ExpressionT expression = this->ParseExpression(true, CHECK_OK);
Traits::AddTemplateExpression(&ts, expression);
if (peek() != Token::RBRACE) {
ReportMessageAt(Scanner::Location(pos, peek_position()),
"unterminated_template_expr");
*ok = false;
return Traits::EmptyExpression();
}
// If we didn't die parsing that expression, our next token should be a
// TEMPLATE_SPAN or TEMPLATE_TAIL.
next = scanner()->ScanTemplateSpan();
Next();
if (!next) {
ReportMessageAt(Scanner::Location(start, position()),
"unterminated_template");
*ok = false;
return Traits::EmptyExpression();
}
Traits::AddTemplateSpan(&ts, next == Token::TEMPLATE_TAIL);
} while (next == Token::TEMPLATE_SPAN);
DCHECK_EQ(next, Token::TEMPLATE_TAIL);
// Once we've reached a TEMPLATE_TAIL, we can close the TemplateLiteral.
return Traits::CloseTemplateLiteral(&ts, start, tag);
}
template <typename Traits>
typename ParserBase<Traits>::ExpressionT ParserBase<
Traits>::CheckAndRewriteReferenceExpression(ExpressionT expression,
Scanner::Location location,
const char* message, bool* ok) {
if (strict_mode() == STRICT && this->IsIdentifier(expression) &&
this->IsEvalOrArguments(this->AsIdentifier(expression))) {
this->ReportMessageAt(location, "strict_eval_arguments", false);
......
......@@ -38,7 +38,8 @@ Scanner::Scanner(UnicodeCache* unicode_cache)
harmony_scoping_(false),
harmony_modules_(false),
harmony_numeric_literals_(false),
harmony_classes_(false) { }
harmony_classes_(false),
harmony_templates_(false) {}
void Scanner::Initialize(Utf16CharacterStream* source) {
......@@ -626,6 +627,12 @@ void Scanner::Scan() {
token = Select(Token::BIT_NOT);
break;
case '`':
if (HarmonyTemplates()) {
token = ScanTemplateSpan();
break;
}
default:
if (c0_ < 0) {
token = Token::EOS;
......@@ -770,6 +777,77 @@ Token::Value Scanner::ScanString() {
}
Token::Value Scanner::ScanTemplateSpan() {
// When scanning a TemplateSpan, we are looking for the following construct:
// TEMPLATE_SPAN ::
// ` LiteralChars* ${
// | } LiteralChars* ${
//
// TEMPLATE_TAIL ::
// ` LiteralChars* `
// | } LiteralChar* `
//
// A TEMPLATE_SPAN should always be followed by an Expression, while a
// TEMPLATE_TAIL terminates a TemplateLiteral and does not need to be
// followed by an Expression.
//
if (next_.token == Token::RBRACE) {
// After parsing an Expression, the source position is incorrect due to
// having scanned the brace. Push the RBRACE back into the stream.
PushBack('}');
}
next_.location.beg_pos = source_pos();
Token::Value result = Token::TEMPLATE_SPAN;
DCHECK(c0_ == '`' || c0_ == '}');
Advance(); // Consume ` or }
LiteralScope literal(this);
while (true) {
uc32 c = c0_;
Advance();
if (c == '`') {
result = Token::TEMPLATE_TAIL;
break;
} else if (c == '$' && c0_ == '{') {
Advance(); // Consume '{'
break;
} else if (c == '\\') {
if (unicode_cache_->IsLineTerminator(c0_)) {
// The TV of LineContinuation :: \ LineTerminatorSequence is the empty
// code unit sequence.
uc32 lastChar = c0_;
Advance();
if (lastChar == '\r' && c0_ == '\n') Advance();
} else if (c0_ == '0') {
Advance();
AddLiteralChar('0');
} else {
ScanEscape();
}
} else if (c < 0) {
// Unterminated template literal
PushBack(c);
break;
} else {
// The TRV of LineTerminatorSequence :: <CR> is the CV 0x000A.
// The TRV of LineTerminatorSequence :: <CR><LF> is the sequence
// consisting of the CV 0x000A.
if (c == '\r') {
if (c0_ == '\n') Advance();
c = '\n';
}
AddLiteralChar(c);
}
}
literal.Complete();
next_.location.end_pos = source_pos();
next_.token = result;
return result;
}
void Scanner::ScanDecimalDigits() {
while (IsDecimalDigit(c0_))
AddLiteralCharAdvance();
......
......@@ -458,6 +458,8 @@ class Scanner {
void SetHarmonyClasses(bool classes) {
harmony_classes_ = classes;
}
bool HarmonyTemplates() const { return harmony_templates_; }
void SetHarmonyTemplates(bool templates) { harmony_templates_ = templates; }
// Returns true if there was a line terminator before the peek'ed token,
// possibly inside a multi-line comment.
......@@ -473,6 +475,9 @@ class Scanner {
// be empty).
bool ScanRegExpFlags();
// Scans the input as a template literal
Token::Value ScanTemplateSpan();
const LiteralBuffer* source_url() const { return &source_url_; }
const LiteralBuffer* source_mapping_url() const {
return &source_mapping_url_;
......@@ -681,6 +686,8 @@ class Scanner {
bool harmony_numeric_literals_;
// Whether we scan 'class', 'extends', 'static' and 'super' as keywords.
bool harmony_classes_;
// Whether we scan TEMPLATE_SPAN and TEMPLATE_TAIL
bool harmony_templates_;
};
} } // namespace v8::internal
......
......@@ -163,7 +163,11 @@ namespace internal {
T(ILLEGAL, "ILLEGAL", 0) \
\
/* Scanner-internal use only. */ \
T(WHITESPACE, NULL, 0)
T(WHITESPACE, NULL, 0) \
\
/* ES6 Template Literals */ \
T(TEMPLATE_SPAN, NULL, 0) \
T(TEMPLATE_TAIL, NULL, 0)
class Token {
......
......@@ -1344,7 +1344,8 @@ enum ParserFlag {
kAllowHarmonyNumericLiterals,
kAllowArrowFunctions,
kAllowClasses,
kAllowHarmonyObjectLiterals
kAllowHarmonyObjectLiterals,
kAllowHarmonyTemplates
};
......@@ -1367,6 +1368,7 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
flags.Contains(kAllowHarmonyObjectLiterals));
parser->set_allow_arrow_functions(flags.Contains(kAllowArrowFunctions));
parser->set_allow_classes(flags.Contains(kAllowClasses));
parser->set_allow_harmony_templates(flags.Contains(kAllowHarmonyTemplates));
}
......@@ -1666,6 +1668,7 @@ void RunParserSyncTest(const char* context_data[][2],
kAllowLazy,
kAllowModules,
kAllowNativesSyntax,
kAllowHarmonyTemplates
};
ParserFlag* generated_flags = NULL;
if (flags == NULL) {
......@@ -4318,3 +4321,112 @@ TEST(InvalidUnicodeEscapes) {
NULL};
RunParserSyncTest(context_data, data, kError);
}
TEST(ScanTemplateLiterals) {
const char* context_data[][2] = {{"'use strict';", ""},
{"function foo(){ 'use strict';"
" var a, b, c; return ", "}"},
{NULL, NULL}};
const char* data[] = {
"``",
"`no-subst-template`",
"`template-head${a}`",
"`${a}`",
"`${a}template-tail`",
"`template-head${a}template-tail`",
"`${a}${b}${c}`",
"`a${a}b${b}c${c}`",
"`${a}a${b}b${c}c`",
"`foo\n\nbar\r\nbaz`",
"`foo\n\n${ bar }\r\nbaz`",
"`foo${a /* comment */}`",
"`foo${a // comment\n}`",
"`foo${a \n}`",
"`foo${a \r\n}`",
"`foo${a \r}`",
"`foo${/* comment */ a}`",
"`foo${// comment\na}`",
"`foo${\n a}`",
"`foo${\r\n a}`",
"`foo${\r a}`",
"`foo${'a' in a}`",
NULL};
static const ParserFlag always_flags[] = {kAllowHarmonyTemplates};
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
}
TEST(ScanTaggedTemplateLiterals) {
const char* context_data[][2] = {{"'use strict';", ""},
{"function foo(){ 'use strict';"
" function tag() {}"
" var a, b, c; return ", "}"},
{NULL, NULL}};
const char* data[] = {
"tag ``",
"tag `no-subst-template`",
"tag`template-head${a}`",
"tag `${a}`",
"tag `${a}template-tail`",
"tag `template-head${a}template-tail`",
"tag\n`${a}${b}${c}`",
"tag\r\n`a${a}b${b}c${c}`",
"tag `${a}a${b}b${c}c`",
"tag\t`foo\n\nbar\r\nbaz`",
"tag\r`foo\n\n${ bar }\r\nbaz`",
"tag`foo${a /* comment */}`",
"tag`foo${a // comment\n}`",
"tag`foo${a \n}`",
"tag`foo${a \r\n}`",
"tag`foo${a \r}`",
"tag`foo${/* comment */ a}`",
"tag`foo${// comment\na}`",
"tag`foo${\n a}`",
"tag`foo${\r\n a}`",
"tag`foo${\r a}`",
"tag`foo${'a' in a}`",
NULL};
static const ParserFlag always_flags[] = {kAllowHarmonyTemplates};
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
}
TEST(ScanUnterminatedTemplateLiterals) {
const char* context_data[][2] = {{"'use strict';", ""},
{"function foo(){ 'use strict';"
" var a, b, c; return ", "}"},
{NULL, NULL}};
const char* data[] = {
"`no-subst-template",
"`template-head${a}",
"`${a}template-tail",
"`template-head${a}template-tail",
"`${a}${b}${c}",
"`a${a}b${b}c${c}",
"`${a}a${b}b${c}c",
"`foo\n\nbar\r\nbaz",
"`foo\n\n${ bar }\r\nbaz",
"`foo${a /* comment } */`",
"`foo${a /* comment } `*/",
"`foo${a // comment}`",
"`foo${a \n`",
"`foo${a \r\n`",
"`foo${a \r`",
"`foo${/* comment */ a`",
"`foo${// commenta}`",
"`foo${\n a`",
"`foo${\r\n a`",
"`foo${\r a`",
"`foo${fn(}`",
"`foo${1 if}`",
NULL};
static const ParserFlag always_flags[] = {kAllowHarmonyTemplates};
RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
}
This diff is collapsed.
......@@ -1628,6 +1628,7 @@
'../../src/harmony-tostring.js',
'../../src/harmony-typedarray.js',
'../../src/harmony-classes.js',
'../../src/harmony-templates.js'
],
'libraries_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries.bin',
'libraries_experimental_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries-experimental.bin',
......
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