From 6fcc22dce19d358dcf5fc43710ecc70575190bad Mon Sep 17 00:00:00 2001 From: caitpotter88 Date: Tue, 24 Mar 2015 05:43:50 -0700 Subject: [PATCH] [es6] call ToString() on template substitutions BUG=v8:3980 R=arv@chromium.org LOG=N Review URL: https://codereview.chromium.org/1027183002 Cr-Commit-Position: refs/heads/master@{#27402} --- src/ast-value-factory.h | 1 + src/parser.cc | 35 ++++++++-------- test/mjsunit/es6/string-raw.js | 36 ++++++++++++++++ test/mjsunit/es6/templates.js | 93 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 19 deletions(-) diff --git a/src/ast-value-factory.h b/src/ast-value-factory.h index 43fa2d0..b34a55b 100644 --- a/src/ast-value-factory.h +++ b/src/ast-value-factory.h @@ -261,6 +261,7 @@ class AstValue : public ZoneObject { F(prototype, "prototype") \ F(this, "this") \ F(throw_iterator_result_not_an_object, "ThrowIteratorResultNotAnObject") \ + F(to_string, "ToString") \ F(use_asm, "use asm") \ F(use_strong, "use strong") \ F(use_strict, "use strict") \ diff --git a/src/parser.cc b/src/parser.cc index 7c2f8fe..ccfefb8 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -5476,27 +5476,24 @@ Expression* Parser::CloseTemplateLiteral(TemplateLiteralState* state, int start, if (!tag) { // Build tree of BinaryOps to simplify code-generation - Expression* expr = NULL; + Expression* expr = cooked_strings->at(0); + int i = 0; + while (i < expressions->length()) { + Expression* sub = expressions->at(i++); + Expression* cooked_str = cooked_strings->at(i); + + // Let middle be ToString(sub). + ZoneList* args = + new (zone()) ZoneList(1, zone()); + args->Add(sub, zone()); + Expression* middle = factory()->NewCallRuntime( + ast_value_factory()->to_string_string(), NULL, args, + sub->position()); - 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()); + Token::ADD, factory()->NewBinaryOperation( + Token::ADD, expr, middle, expr->position()), + cooked_str, sub->position()); } return expr; } else { diff --git a/test/mjsunit/es6/string-raw.js b/test/mjsunit/es6/string-raw.js index 7a7f4c2..2c6bb2f 100644 --- a/test/mjsunit/es6/string-raw.js +++ b/test/mjsunit/es6/string-raw.js @@ -254,3 +254,39 @@ assertEquals("12345", String.raw(callSiteObj, arg(2), arg(4), arg(6))); assertEquals(["length", "raw1", "arg2", "raw3", "arg4", "raw5"], order); })(); + + +(function testStringRawToStringSubstitutionsOrder() { + var subs = []; + var log = []; + function stringify(toString) { + var valueOf = "_" + toString + "_"; + return { + toString: function() { return toString; }, + valueOf: function() { return valueOf; } + }; + } + function getter(name, value) { + return { + get: function() { + log.push("get" + name); + return value; + }, + set: function(v) { + log.push("set" + name); + } + }; + } + Object.defineProperties(subs, { + 0: getter(0, stringify("a")), + 1: getter(1, stringify("b")), + 2: getter(2, stringify("c")) + }); + + assertEquals("-a-b-c-", String.raw`-${subs[0]}-${subs[1]}-${subs[2]}-`); + assertArrayEquals(["get0", "get1", "get2"], log); + + log.length = 0; + assertEquals("-a-", String.raw`-${subs[0]}-`); + assertArrayEquals(["get0"], log); +})(); diff --git a/test/mjsunit/es6/templates.js b/test/mjsunit/es6/templates.js index a2b77b1..15296e8 100644 --- a/test/mjsunit/es6/templates.js +++ b/test/mjsunit/es6/templates.js @@ -586,3 +586,96 @@ var global = this; "raw;test3" ], raw); })(); + + +(function testToStringSubstitutions() { + var a = { + toString: function() { return "a"; }, + valueOf: function() { return "-a-"; } + }; + var b = { + toString: function() { return "b"; }, + valueOf: function() { return "-b-"; } + }; + assertEquals("a", `${a}`); + assertEquals("ab", `${a}${b}`); + assertEquals("-a--b-", `${a + b}`); + assertEquals("-a-", `${a + ""}`); + assertEquals("1a", `1${a}`); + assertEquals("1a2", `1${a}2`); + assertEquals("1a2b", `1${a}2${b}`); + assertEquals("1a2b3", `1${a}2${b}3`); +})(); + + +(function testToStringSubstitutionsOrder() { + var subs = []; + var log = []; + function getter(name, value) { + return { + get: function() { + log.push("get" + name); + return value; + }, + set: function(v) { + log.push("set" + name); + } + }; + } + Object.defineProperties(subs, { + 0: getter(0, "a"), + 1: getter(1, "b"), + 2: getter(2, "c") + }); + + assertEquals("-a-b-c-", `-${subs[0]}-${subs[1]}-${subs[2]}-`); + assertArrayEquals(["get0", "get1", "get2"], log); +})(); + + +(function testTaggedToStringSubstitutionsOrder() { + var subs = []; + var log = []; + var tagged = []; + function getter(name, value) { + return { + get: function() { + log.push("get" + name); + return value; + }, + set: function(v) { + log.push("set" + name); + } + }; + } + Object.defineProperties(subs, { + 0: getter(0, 1), + 1: getter(1, 2), + 2: getter(2, 3) + }); + + function tag(cs) { + var n_substitutions = arguments.length - 1; + var n_cooked = cs.length; + var e = cs[0]; + var i = 0; + assertEquals(n_cooked, n_substitutions + 1); + while (i < n_substitutions) { + var sub = arguments[i++ + 1]; + var tail = cs[i]; + tagged.push(sub); + e = e.concat(sub, tail); + } + return e; + } + + assertEquals("-1-2-3-", tag`-${subs[0]}-${subs[1]}-${subs[2]}-`); + assertArrayEquals(["get0", "get1", "get2"], log); + assertArrayEquals([1, 2, 3], tagged); + + tagged.length = 0; + log.length = 0; + assertEquals("-1-", tag`-${subs[0]}-`); + assertArrayEquals(["get0"], log); + assertArrayEquals([1], tagged); +})(); -- 2.7.4