From 699bc8f73d52e3d5a9bc6d3eacb445509c24c74e Mon Sep 17 00:00:00 2001 From: "wingo@igalia.com" Date: Wed, 25 Jun 2014 07:43:14 +0000 Subject: [PATCH] Add @@iterator support for strings R=rossberg@chromium.org BUG= Review URL: https://codereview.chromium.org/335423002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21994 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/bootstrapper.cc | 1 + src/string-iterator.js | 106 ++++++++++++++++++++ test/mjsunit/harmony/iteration-semantics.js | 2 +- test/mjsunit/harmony/string-iterator.js | 91 +++++++++++++++++ tools/generate-runtime-tests.py | 2 +- tools/gyp/v8.gyp | 1 + 6 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 src/string-iterator.js create mode 100644 test/mjsunit/harmony/string-iterator.js diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 771b5b2d1..6b4a4f1d2 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -2052,6 +2052,7 @@ bool Genesis::InstallExperimentalNatives() { INSTALL_EXPERIMENTAL_NATIVE(i, collections, "collection-iterator.js") INSTALL_EXPERIMENTAL_NATIVE(i, generators, "generator.js") INSTALL_EXPERIMENTAL_NATIVE(i, iteration, "array-iterator.js") + INSTALL_EXPERIMENTAL_NATIVE(i, iteration, "string-iterator.js") INSTALL_EXPERIMENTAL_NATIVE(i, strings, "harmony-string.js") INSTALL_EXPERIMENTAL_NATIVE(i, arrays, "harmony-array.js") INSTALL_EXPERIMENTAL_NATIVE(i, maths, "harmony-math.js") diff --git a/src/string-iterator.js b/src/string-iterator.js new file mode 100644 index 000000000..728f48490 --- /dev/null +++ b/src/string-iterator.js @@ -0,0 +1,106 @@ +// 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'; + + +// This file relies on the fact that the following declaration has been made +// in runtime.js: +// var $String = global.String; + + +var stringIteratorIteratedStringSymbol = + GLOBAL_PRIVATE("StringIterator#iteratedString"); +var stringIteratorNextIndexSymbol = GLOBAL_PRIVATE("StringIterator#next"); + + +function StringIterator() {} + + +// 21.1.5.1 CreateStringIterator Abstract Operation +function CreateStringIterator(string) { + var s = TO_STRING_INLINE(string); + var iterator = new StringIterator; + SET_PRIVATE(iterator, stringIteratorIteratedStringSymbol, s); + SET_PRIVATE(iterator, stringIteratorNextIndexSymbol, 0); + return iterator; +} + + +// 21.1.5.2.2 %StringIteratorPrototype%[@@iterator] +function StringIteratorIterator() { + return this; +} + + +// 21.1.5.2.1 %StringIteratorPrototype%.next( ) +function StringIteratorNext() { + var iterator = ToObject(this); + + if (!HAS_PRIVATE(iterator, stringIteratorIteratedStringSymbol)) { + throw MakeTypeError('incompatible_method_receiver', + ['String Iterator.prototype.next']); + } + + var s = GET_PRIVATE(iterator, stringIteratorIteratedStringSymbol); + if (IS_UNDEFINED(s)) { + return CreateIteratorResultObject(UNDEFINED, true); + } + + var position = GET_PRIVATE(iterator, stringIteratorNextIndexSymbol); + var length = TO_UINT32(s.length); + + if (position >= length) { + SET_PRIVATE(iterator, stringIteratorIteratedStringSymbol, UNDEFINED); + return CreateIteratorResultObject(UNDEFINED, true); + } + + var first = %_StringCharCodeAt(s, position); + var resultString = %_StringCharFromCode(first); + position++; + + if (first >= 0xD800 && first <= 0xDBFF && position < length) { + var second = %_StringCharCodeAt(s, position); + if (second >= 0xDC00 && second <= 0xDFFF) { + resultString += %_StringCharFromCode(second); + position++; + } + } + + SET_PRIVATE(iterator, stringIteratorNextIndexSymbol, position); + + return CreateIteratorResultObject(resultString, false); +} + + +function SetUpStringIterator() { + %CheckIsBootstrapping(); + + %FunctionSetPrototype(StringIterator, new $Object()); + %FunctionSetInstanceClassName(StringIterator, 'String Iterator'); + + InstallFunctions(StringIterator.prototype, DONT_ENUM, $Array( + 'next', StringIteratorNext + )); + %FunctionSetName(StringIteratorIterator, '[Symbol.iterator]'); + %SetProperty(StringIterator.prototype, symbolIterator, StringIteratorIterator, + DONT_ENUM); +} +SetUpStringIterator(); + + +// 21.1.3.27 String.prototype [ @@iterator ]( ) +function StringPrototypeIterator() { + return CreateStringIterator(this); +} + + +function ExtendStringPrototypeWithIterator() { + %CheckIsBootstrapping(); + + %FunctionSetName(StringPrototypeIterator, '[Symbol.iterator]'); + %SetProperty($String.prototype, symbolIterator, StringPrototypeIterator, + DONT_ENUM); +} +ExtendStringPrototypeWithIterator(); diff --git a/test/mjsunit/harmony/iteration-semantics.js b/test/mjsunit/harmony/iteration-semantics.js index 27033e18b..803f12faa 100644 --- a/test/mjsunit/harmony/iteration-semantics.js +++ b/test/mjsunit/harmony/iteration-semantics.js @@ -219,7 +219,7 @@ assertEquals(0, fold(sum, 0, unreachable(undefined))); // Other non-iterators do cause an error. assertThrows('fold(sum, 0, unreachable({}))', TypeError); -assertThrows('fold(sum, 0, unreachable("foo"))', TypeError); +assertThrows('fold(sum, 0, unreachable(false))', TypeError); assertThrows('fold(sum, 0, unreachable(37))', TypeError); // "next" is looked up each time. diff --git a/test/mjsunit/harmony/string-iterator.js b/test/mjsunit/harmony/string-iterator.js new file mode 100644 index 000000000..3e371781a --- /dev/null +++ b/test/mjsunit/harmony/string-iterator.js @@ -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-iteration + + +function TestStringPrototypeIterator() { + assertTrue(String.prototype.hasOwnProperty(Symbol.iterator)); + assertFalse("".hasOwnProperty(Symbol.iterator)); + assertFalse("".propertyIsEnumerable(Symbol.iterator)); +} +TestStringPrototypeIterator(); + + +function assertIteratorResult(value, done, result) { + assertEquals({value: value, done: done}, result); +} + + +function TestManualIteration() { + var string = "abc"; + var iterator = string[Symbol.iterator](); + assertIteratorResult('a', false, iterator.next()); + assertIteratorResult('b', false, iterator.next()); + assertIteratorResult('c', false, iterator.next()); + assertIteratorResult(void 0, true, iterator.next()); + assertIteratorResult(void 0, true, iterator.next()); +} +TestManualIteration(); + + +function TestSurrogatePairs() { + var lo = "\uD834"; + var hi = "\uDF06"; + var pair = lo + hi; + var string = "abc" + pair + "def" + lo + pair + hi + lo; + var iterator = string[Symbol.iterator](); + assertIteratorResult('a', false, iterator.next()); + assertIteratorResult('b', false, iterator.next()); + assertIteratorResult('c', false, iterator.next()); + assertIteratorResult(pair, false, iterator.next()); + assertIteratorResult('d', false, iterator.next()); + assertIteratorResult('e', false, iterator.next()); + assertIteratorResult('f', false, iterator.next()); + assertIteratorResult(lo, false, iterator.next()); + assertIteratorResult(pair, false, iterator.next()); + assertIteratorResult(hi, false, iterator.next()); + assertIteratorResult(lo, false, iterator.next()); + assertIteratorResult(void 0, true, iterator.next()); + assertIteratorResult(void 0, true, iterator.next()); +} +TestSurrogatePairs(); + + +function TestStringIteratorPrototype() { + var iterator = ""[Symbol.iterator](); + var StringIteratorPrototype = iterator.__proto__; + assertFalse(StringIteratorPrototype.hasOwnProperty('constructor')); + assertEquals(StringIteratorPrototype.__proto__, Object.prototype); + assertArrayEquals(['next'], + Object.getOwnPropertyNames(StringIteratorPrototype)); + assertEquals('[object String Iterator]', "" + iterator); +} +TestStringIteratorPrototype(); + + +function TestForOf() { + var lo = "\uD834"; + var hi = "\uDF06"; + var pair = lo + hi; + var string = "abc" + pair + "def" + lo + pair + hi + lo; + var expected = ['a', 'b', 'c', pair, 'd', 'e', 'f', lo, pair, hi, lo]; + + var i = 0; + for (var char of string) { + assertEquals(expected[i++], char); + } + + assertEquals(expected.length, i); +} +TestForOf(); + + +function TestNonOwnSlots() { + var iterator = ""[Symbol.iterator](); + var object = {__proto__: iterator}; + + assertThrows(function() { object.next(); }, TypeError); +} +TestNonOwnSlots(); diff --git a/tools/generate-runtime-tests.py b/tools/generate-runtime-tests.py index e8d4c64c0..d15d0d451 100755 --- a/tools/generate-runtime-tests.py +++ b/tools/generate-runtime-tests.py @@ -51,7 +51,7 @@ EXPECTED_FUNCTION_COUNT = 358 EXPECTED_FUZZABLE_COUNT = 326 EXPECTED_CCTEST_COUNT = 6 EXPECTED_UNKNOWN_COUNT = 4 -EXPECTED_BUILTINS_COUNT = 800 +EXPECTED_BUILTINS_COUNT = 807 # Don't call these at all. diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index c7d04c6b3..06990c711 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -1261,6 +1261,7 @@ '../../src/collection-iterator.js', '../../src/generator.js', '../../src/array-iterator.js', + '../../src/string-iterator.js', '../../src/harmony-string.js', '../../src/harmony-array.js', '../../src/harmony-math.js' -- 2.34.1