[es6] call ToString() on template substitutions
authorcaitpotter88 <caitpotter88@gmail.com>
Tue, 24 Mar 2015 12:43:50 +0000 (05:43 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 24 Mar 2015 12:44:01 +0000 (12:44 +0000)
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
src/parser.cc
test/mjsunit/es6/string-raw.js
test/mjsunit/es6/templates.js

index 43fa2d0..b34a55b 100644 (file)
@@ -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")                                              \
index 7c2f8fe..ccfefb8 100644 (file)
@@ -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<Expression*>* args =
+          new (zone()) ZoneList<Expression*>(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 {
index 7a7f4c2..2c6bb2f 100644 (file)
   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);
+})();
index a2b77b1..15296e8 100644 (file)
@@ -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);
+})();