Implement String.prototype.codePointAt and String.fromCodePoint.
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 21 Jul 2014 08:45:32 +0000 (08:45 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 21 Jul 2014 08:45:32 +0000 (08:45 +0000)
Contributed by Mathias Bynens <mathiasb@opera.com>.

TBR=mathiasb@opera.com, rossberg@chromium.org
BUG=v8:2840
LOG=Y

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22493 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/harmony-string.js
src/messages.js
test/mjsunit/harmony/string-codepointat.js [new file with mode: 0644]
test/mjsunit/harmony/string-fromcodepoint.js [new file with mode: 0644]
tools/generate-runtime-tests.py

index 4cd8e66..a8ef1dd 100644 (file)
@@ -120,17 +120,73 @@ function StringContains(searchString /* position */) {  // length == 1
 }
 
 
+// ES6 Draft 05-22-2014, section 21.1.3.3
+function StringCodePointAt(pos) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.codePointAt");
+
+  var string = TO_STRING_INLINE(this);
+  var size = string.length;
+  pos = TO_INTEGER(pos);
+  if (pos < 0 || pos >= size) {
+    return UNDEFINED;
+  }
+  var first = %_StringCharCodeAt(string, pos);
+  if (first < 0xD800 || first > 0xDBFF || pos + 1 == size) {
+    return first;
+  }
+  var second = %_StringCharCodeAt(string, pos + 1);
+  if (second < 0xDC00 || second > 0xDFFF) {
+    return first;
+  }
+  return (first - 0xD800) * 0x400 + second + 0x2400;
+}
+
+
+// ES6 Draft 05-22-2014, section 21.1.2.2
+function StringFromCodePoint(_) {  // length = 1
+  var code;
+  var length = %_ArgumentsLength();
+  var index;
+  var result = "";
+  for (index = 0; index < length; index++) {
+    code = %_Arguments(index);
+    if (!%_IsSmi(code)) {
+      code = ToNumber(code);
+    }
+    if (code < 0 || code > 0x10FFFF || code !== TO_INTEGER(code)) {
+      throw MakeRangeError("invalid_code_point", [code]);
+    }
+    if (code <= 0xFFFF) {
+      result += %_StringCharFromCode(code);
+    } else {
+      code -= 0x10000;
+      result += StringFromCharCode(
+        code >>> 10 & 0x3FF | 0xD800,
+        0xDC00 | code & 0x3FF
+      );
+    }
+  }
+  return result;
+}
+
+
 // -------------------------------------------------------------------
 
 function ExtendStringPrototype() {
   %CheckIsBootstrapping();
 
+  // Set up the non-enumerable functions on the String object.
+  InstallFunctions($String, DONT_ENUM, $Array(
+    "fromCodePoint", StringFromCodePoint
+  ));
+
   // Set up the non-enumerable functions on the String prototype object.
   InstallFunctions($String.prototype, DONT_ENUM, $Array(
-    "repeat", StringRepeat,
-    "startsWith", StringStartsWith,
+    "codePointAt", StringCodePointAt,
+    "contains", StringContains,
     "endsWith", StringEndsWith,
-    "contains", StringContains
+    "repeat", StringRepeat,
+    "startsWith", StringStartsWith
   ));
 }
 
index ee5b195..90a756c 100644 (file)
@@ -113,6 +113,7 @@ var kMessages = {
   stack_overflow:                ["Maximum call stack size exceeded"],
   invalid_time_value:            ["Invalid time value"],
   invalid_count_value:           ["Invalid count value"],
+  invalid_code_point:            ["Invalid code point ", "%0"],
   // ReferenceError
   invalid_lhs_in_assignment:     ["Invalid left-hand side in assignment"],
   invalid_lhs_in_for:            ["Invalid left-hand side in for-loop"],
diff --git a/test/mjsunit/harmony/string-codepointat.js b/test/mjsunit/harmony/string-codepointat.js
new file mode 100644 (file)
index 0000000..411b0f2
--- /dev/null
@@ -0,0 +1,91 @@
+// 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.
+
+// Flags: --harmony-strings
+
+// Tests taken from:
+// https://github.com/mathiasbynens/String.prototype.codePointAt
+
+assertEquals(String.prototype.codePointAt.length, 1);
+assertEquals(String.prototype.propertyIsEnumerable("codePointAt"), false);
+
+// String that starts with a BMP symbol
+assertEquals("abc\uD834\uDF06def".codePointAt(""), 0x61);
+assertEquals("abc\uD834\uDF06def".codePointAt("_"), 0x61);
+assertEquals("abc\uD834\uDF06def".codePointAt(), 0x61);
+assertEquals("abc\uD834\uDF06def".codePointAt(-Infinity), undefined);
+assertEquals("abc\uD834\uDF06def".codePointAt(-1), undefined);
+assertEquals("abc\uD834\uDF06def".codePointAt(-0), 0x61);
+assertEquals("abc\uD834\uDF06def".codePointAt(0), 0x61);
+assertEquals("abc\uD834\uDF06def".codePointAt(3), 0x1D306);
+assertEquals("abc\uD834\uDF06def".codePointAt(4), 0xDF06);
+assertEquals("abc\uD834\uDF06def".codePointAt(5), 0x64);
+assertEquals("abc\uD834\uDF06def".codePointAt(42), undefined);
+assertEquals("abc\uD834\uDF06def".codePointAt(Infinity), undefined);
+assertEquals("abc\uD834\uDF06def".codePointAt(Infinity), undefined);
+assertEquals("abc\uD834\uDF06def".codePointAt(NaN), 0x61);
+assertEquals("abc\uD834\uDF06def".codePointAt(false), 0x61);
+assertEquals("abc\uD834\uDF06def".codePointAt(null), 0x61);
+assertEquals("abc\uD834\uDF06def".codePointAt(undefined), 0x61);
+
+// String that starts with an astral symbol
+assertEquals("\uD834\uDF06def".codePointAt(""), 0x1D306);
+assertEquals("\uD834\uDF06def".codePointAt("1"), 0xDF06);
+assertEquals("\uD834\uDF06def".codePointAt("_"), 0x1D306);
+assertEquals("\uD834\uDF06def".codePointAt(), 0x1D306);
+assertEquals("\uD834\uDF06def".codePointAt(-1), undefined);
+assertEquals("\uD834\uDF06def".codePointAt(-0), 0x1D306);
+assertEquals("\uD834\uDF06def".codePointAt(0), 0x1D306);
+assertEquals("\uD834\uDF06def".codePointAt(1), 0xDF06);
+assertEquals("\uD834\uDF06def".codePointAt(42), undefined);
+assertEquals("\uD834\uDF06def".codePointAt(false), 0x1D306);
+assertEquals("\uD834\uDF06def".codePointAt(null), 0x1D306);
+assertEquals("\uD834\uDF06def".codePointAt(undefined), 0x1D306);
+
+// Lone high surrogates
+assertEquals("\uD834abc".codePointAt(""), 0xD834);
+assertEquals("\uD834abc".codePointAt("_"), 0xD834);
+assertEquals("\uD834abc".codePointAt(), 0xD834);
+assertEquals("\uD834abc".codePointAt(-1), undefined);
+assertEquals("\uD834abc".codePointAt(-0), 0xD834);
+assertEquals("\uD834abc".codePointAt(0), 0xD834);
+assertEquals("\uD834abc".codePointAt(false), 0xD834);
+assertEquals("\uD834abc".codePointAt(NaN), 0xD834);
+assertEquals("\uD834abc".codePointAt(null), 0xD834);
+assertEquals("\uD834abc".codePointAt(undefined), 0xD834);
+
+// Lone low surrogates
+assertEquals("\uDF06abc".codePointAt(""), 0xDF06);
+assertEquals("\uDF06abc".codePointAt("_"), 0xDF06);
+assertEquals("\uDF06abc".codePointAt(), 0xDF06);
+assertEquals("\uDF06abc".codePointAt(-1), undefined);
+assertEquals("\uDF06abc".codePointAt(-0), 0xDF06);
+assertEquals("\uDF06abc".codePointAt(0), 0xDF06);
+assertEquals("\uDF06abc".codePointAt(false), 0xDF06);
+assertEquals("\uDF06abc".codePointAt(NaN), 0xDF06);
+assertEquals("\uDF06abc".codePointAt(null), 0xDF06);
+assertEquals("\uDF06abc".codePointAt(undefined), 0xDF06);
+
+assertThrows(function() {
+  String.prototype.codePointAt.call(undefined);
+}, TypeError);
+assertThrows(function() {
+  String.prototype.codePointAt.call(undefined, 4);
+}, TypeError);
+assertThrows(function() {
+  String.prototype.codePointAt.call(null);
+}, TypeError);
+assertThrows(function() {
+  String.prototype.codePointAt.call(null, 4);
+}, TypeError);
+assertEquals(String.prototype.codePointAt.call(42, 0), 0x34);
+assertEquals(String.prototype.codePointAt.call(42, 1), 0x32);
+assertEquals(String.prototype.codePointAt.call({
+  toString: function() { return "abc"; }
+}, 2), 0x63);
+var tmp = 0;
+assertEquals(String.prototype.codePointAt.call({
+  toString: function() { ++tmp; return String(tmp); }
+}, 0), 0x31);
+assertEquals(tmp, 1);
diff --git a/test/mjsunit/harmony/string-fromcodepoint.js b/test/mjsunit/harmony/string-fromcodepoint.js
new file mode 100644 (file)
index 0000000..97ecf0e
--- /dev/null
@@ -0,0 +1,62 @@
+// 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.
+
+// Flags: --harmony-strings
+
+// Tests taken from:
+// https://github.com/mathiasbynens/String.fromCodePoint
+
+assertEquals(String.fromCodePoint.length, 1);
+assertEquals(String.propertyIsEnumerable("fromCodePoint"), false);
+
+assertEquals(String.fromCodePoint(""), "\0");
+assertEquals(String.fromCodePoint(), "");
+assertEquals(String.fromCodePoint(-0), "\0");
+assertEquals(String.fromCodePoint(0), "\0");
+assertEquals(String.fromCodePoint(0x1D306), "\uD834\uDF06");
+assertEquals(
+    String.fromCodePoint(0x1D306, 0x61, 0x1D307),
+    "\uD834\uDF06a\uD834\uDF07");
+assertEquals(String.fromCodePoint(0x61, 0x62, 0x1D307), "ab\uD834\uDF07");
+assertEquals(String.fromCodePoint(false), "\0");
+assertEquals(String.fromCodePoint(null), "\0");
+
+assertThrows(function() { String.fromCodePoint("_"); }, RangeError);
+assertThrows(function() { String.fromCodePoint("+Infinity"); }, RangeError);
+assertThrows(function() { String.fromCodePoint("-Infinity"); }, RangeError);
+assertThrows(function() { String.fromCodePoint(-1); }, RangeError);
+assertThrows(function() { String.fromCodePoint(0x10FFFF + 1); }, RangeError);
+assertThrows(function() { String.fromCodePoint(3.14); }, RangeError);
+assertThrows(function() { String.fromCodePoint(3e-2); }, RangeError);
+assertThrows(function() { String.fromCodePoint(-Infinity); }, RangeError);
+assertThrows(function() { String.fromCodePoint(+Infinity); }, RangeError);
+assertThrows(function() { String.fromCodePoint(NaN); }, RangeError);
+assertThrows(function() { String.fromCodePoint(undefined); }, RangeError);
+assertThrows(function() { String.fromCodePoint({}); }, RangeError);
+assertThrows(function() { String.fromCodePoint(/./); }, RangeError);
+assertThrows(function() { String.fromCodePoint({
+  valueOf: function() { throw Error(); } });
+}, Error);
+assertThrows(function() { String.fromCodePoint({
+  valueOf: function() { throw Error(); } });
+}, Error);
+var tmp = 0x60;
+assertEquals(String.fromCodePoint({
+  valueOf: function() { ++tmp; return tmp; }
+}), "a");
+assertEquals(tmp, 0x61);
+
+var counter = Math.pow(2, 15) * 3 / 2;
+var result = [];
+while (--counter >= 0) {
+  result.push(0); // one code unit per symbol
+}
+String.fromCodePoint.apply(null, result); // must not throw
+
+var counter = Math.pow(2, 15) * 3 / 2;
+var result = [];
+while (--counter >= 0) {
+  result.push(0xFFFF + 1); // two code units per symbol
+}
+String.fromCodePoint.apply(null, result); // must not throw
index ad9e79c..a760bee 100755 (executable)
@@ -51,7 +51,7 @@ EXPECTED_FUNCTION_COUNT = 418
 EXPECTED_FUZZABLE_COUNT = 333
 EXPECTED_CCTEST_COUNT = 8
 EXPECTED_UNKNOWN_COUNT = 4
-EXPECTED_BUILTINS_COUNT = 810
+EXPECTED_BUILTINS_COUNT = 812
 
 
 # Don't call these at all.