Bug 918879 - Implement String#codePointAt and String.fromCodePoint r=till

This commit is contained in:
Tom Schuster 2013-12-23 21:48:47 +01:00
parent b31872fe7b
commit 1ef4d3b422
8 changed files with 205 additions and 1 deletions

View File

@ -4,6 +4,35 @@
/*global intl_Collator: false, */
/* ES6 Draft September 5, 2013 21.1.3.3 */
function String_codePointAt(pos) {
// Steps 1-3.
CheckObjectCoercible(this);
var S = ToString(this);
// Steps 4-5.
var position = ToInteger(pos);
// Step 6.
var size = S.length;
// Step 7.
if (position < 0 || position >= size)
return undefined;
// Steps 8-9.
var first = callFunction(std_String_charCodeAt, S, position);
if (first < 0xD800 || first > 0xDBFF || position + 1 === size)
return first;
// Steps 10-11.
var second = callFunction(std_String_charCodeAt, S, position + 1);
if (second < 0xDC00 || second > 0xDFFF)
return first;
// Step 12.
return (first - 0xD800) * 0x400 + (second - 0xDC00) + 0x10000;
}
var collatorCache = new Record();
@ -117,6 +146,45 @@ function String_localeCompare(that) {
return intl_CompareStrings(collator, S, That);
}
/* ES6 Draft September 5, 2013 21.1.2.2 */
function String_static_fromCodePoint() {
// Step 1. is not relevant
// Step 2.
var length = arguments.length;
// Step 3.
var elements = new List();
// Step 4-5., 5g.
for (var nextIndex = 0; nextIndex < length; nextIndex++) {
// Step 5a.
var next = arguments[nextIndex];
// Step 5b-c.
var nextCP = ToNumber(next);
// Step 5d.
if (nextCP !== ToInteger(nextCP) || std_isNaN(nextCP))
ThrowError(JSMSG_NOT_A_CODEPOINT, ToString(nextCP));
// Step 5e.
if (nextCP < 0 || nextCP > 0x10FFFF)
ThrowError(JSMSG_NOT_A_CODEPOINT, ToString(nextCP));
// Step 5f.
// Inlined UTF-16 Encoding
if (nextCP <= 0xFFFF) {
elements.push(nextCP);
continue;
}
elements.push((((nextCP - 0x10000) / 0x400) | 0) + 0xD800);
elements.push((nextCP - 0x10000) % 0x400 + 0xDC00);
}
// Step 6.
return callFunction(std_Function_apply, std_String_fromCharCode, null, elements);
}
/**
* Compare String str1 against String str2, using the locale and collation
* options provided.
@ -131,3 +199,4 @@ function String_static_localeCompare(str1, str2) {
var options = arguments.length > 3 ? arguments[3] : undefined;
return callFunction(String_localeCompare, str1, str2, locales, options);
}

View File

@ -64,6 +64,7 @@ var std_Object_getOwnPropertyNames = Object.getOwnPropertyNames;
var std_Object_hasOwnProperty = Object.prototype.hasOwnProperty;
var std_RegExp_test = RegExp.prototype.test;
var Std_String = String;
var std_String_fromCharCode = String.fromCharCode;
var std_String_charCodeAt = String.prototype.charCodeAt;
var std_String_indexOf = String.prototype.indexOf;
var std_String_lastIndexOf = String.prototype.lastIndexOf;

View File

@ -223,7 +223,7 @@ MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 169, 0, JSEXN_SYNTAXERR, "too many catch v
MSG_DEF(JSMSG_NEGATIVE_REPETITION_COUNT, 170, 0, JSEXN_RANGEERR, "repeat count must be non-negative")
MSG_DEF(JSMSG_INVALID_FOR_OF_INIT, 171, 0, JSEXN_SYNTAXERR, "for-of loop variable declaration may not have an initializer")
MSG_DEF(JSMSG_INVALID_MAP_ITERABLE, 172, 0, JSEXN_TYPEERR, "iterable for map should have array-like objects")
MSG_DEF(JSMSG_UNUSED173, 173, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_NOT_A_CODEPOINT, 173, 1, JSEXN_RANGEERR, "{0} is not a valid code point")
MSG_DEF(JSMSG_UNUSED174, 174, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_NESTING_GENERATOR, 175, 0, JSEXN_TYPEERR, "already executing generator")
MSG_DEF(JSMSG_UNUSED176, 176, 0, JSEXN_NONE, "")

View File

@ -3678,6 +3678,7 @@ static const JSFunctionSpec string_methods[] = {
JS_FN("toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE),
JS_FN("charAt", js_str_charAt, 1,JSFUN_GENERIC_NATIVE),
JS_FN("charCodeAt", js_str_charCodeAt, 1,JSFUN_GENERIC_NATIVE),
JS_SELF_HOSTED_FN("codePointAt", "String_codePointAt", 1,0),
JS_FN("contains", str_contains, 1,JSFUN_GENERIC_NATIVE),
JS_FN("indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE),
JS_FN("lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE),
@ -3792,6 +3793,7 @@ js::str_fromCharCode(JSContext *cx, unsigned argc, Value *vp)
static const JSFunctionSpec string_static_methods[] = {
JS_FN("fromCharCode", js::str_fromCharCode, 1, 0),
JS_SELF_HOSTED_FN("fromCodePoint", "String_static_fromCodePoint", 0,0),
// This must be at the end because of bug 853075: functions listed after
// self-hosted methods aren't available in self-hosted code.

View File

View File

@ -0,0 +1,84 @@
var BUGNUMBER = 918879;
var summary = 'String.prototype.codePointAt';
print(BUGNUMBER + ": " + summary);
// Tests taken from:
// https://github.com/mathiasbynens/String.prototype.codePointAt/blob/master/tests/tests.js
assertEq(String.prototype.codePointAt.length, 1);
assertEq(String.prototype.propertyIsEnumerable('codePointAt'), false);
// String that starts with a BMP symbol
assertEq('abc\uD834\uDF06def'.codePointAt(''), 0x61);
assertEq('abc\uD834\uDF06def'.codePointAt('_'), 0x61);
assertEq('abc\uD834\uDF06def'.codePointAt(), 0x61);
assertEq('abc\uD834\uDF06def'.codePointAt(-Infinity), undefined);
assertEq('abc\uD834\uDF06def'.codePointAt(-1), undefined);
assertEq('abc\uD834\uDF06def'.codePointAt(-0), 0x61);
assertEq('abc\uD834\uDF06def'.codePointAt(0), 0x61);
assertEq('abc\uD834\uDF06def'.codePointAt(3), 0x1D306);
assertEq('abc\uD834\uDF06def'.codePointAt(4), 0xDF06);
assertEq('abc\uD834\uDF06def'.codePointAt(5), 0x64);
assertEq('abc\uD834\uDF06def'.codePointAt(42), undefined);
assertEq('abc\uD834\uDF06def'.codePointAt(Infinity), undefined);
assertEq('abc\uD834\uDF06def'.codePointAt(Infinity), undefined);
assertEq('abc\uD834\uDF06def'.codePointAt(NaN), 0x61);
assertEq('abc\uD834\uDF06def'.codePointAt(false), 0x61);
assertEq('abc\uD834\uDF06def'.codePointAt(null), 0x61);
assertEq('abc\uD834\uDF06def'.codePointAt(undefined), 0x61);
// String that starts with an astral symbol
assertEq('\uD834\uDF06def'.codePointAt(''), 0x1D306);
assertEq('\uD834\uDF06def'.codePointAt('1'), 0xDF06);
assertEq('\uD834\uDF06def'.codePointAt('_'), 0x1D306);
assertEq('\uD834\uDF06def'.codePointAt(), 0x1D306);
assertEq('\uD834\uDF06def'.codePointAt(-1), undefined);
assertEq('\uD834\uDF06def'.codePointAt(-0), 0x1D306);
assertEq('\uD834\uDF06def'.codePointAt(0), 0x1D306);
assertEq('\uD834\uDF06def'.codePointAt(1), 0xDF06);
assertEq('\uD834\uDF06def'.codePointAt(42), undefined);
assertEq('\uD834\uDF06def'.codePointAt(false), 0x1D306);
assertEq('\uD834\uDF06def'.codePointAt(null), 0x1D306);
assertEq('\uD834\uDF06def'.codePointAt(undefined), 0x1D306);
// Lone high surrogates
assertEq('\uD834abc'.codePointAt(''), 0xD834);
assertEq('\uD834abc'.codePointAt('_'), 0xD834);
assertEq('\uD834abc'.codePointAt(), 0xD834);
assertEq('\uD834abc'.codePointAt(-1), undefined);
assertEq('\uD834abc'.codePointAt(-0), 0xD834);
assertEq('\uD834abc'.codePointAt(0), 0xD834);
assertEq('\uD834abc'.codePointAt(false), 0xD834);
assertEq('\uD834abc'.codePointAt(NaN), 0xD834);
assertEq('\uD834abc'.codePointAt(null), 0xD834);
assertEq('\uD834abc'.codePointAt(undefined), 0xD834);
// Lone low surrogates
assertEq('\uDF06abc'.codePointAt(''), 0xDF06);
assertEq('\uDF06abc'.codePointAt('_'), 0xDF06);
assertEq('\uDF06abc'.codePointAt(), 0xDF06);
assertEq('\uDF06abc'.codePointAt(-1), undefined);
assertEq('\uDF06abc'.codePointAt(-0), 0xDF06);
assertEq('\uDF06abc'.codePointAt(0), 0xDF06);
assertEq('\uDF06abc'.codePointAt(false), 0xDF06);
assertEq('\uDF06abc'.codePointAt(NaN), 0xDF06);
assertEq('\uDF06abc'.codePointAt(null), 0xDF06);
assertEq('\uDF06abc'.codePointAt(undefined), 0xDF06);
(function() { String.prototype.codePointAt.call(undefined); }, TypeError);
assertThrowsInstanceOf(function() { String.prototype.codePointAt.call(undefined, 4); }, TypeError);
assertThrowsInstanceOf(function() { String.prototype.codePointAt.call(null); }, TypeError);
assertThrowsInstanceOf(function() { String.prototype.codePointAt.call(null, 4); }, TypeError);
assertEq(String.prototype.codePointAt.call(42, 0), 0x34);
assertEq(String.prototype.codePointAt.call(42, 1), 0x32);
assertEq(String.prototype.codePointAt.call({ 'toString': function() { return 'abc'; } }, 2), 0x63);
assertThrowsInstanceOf(function() { String.prototype.codePointAt.apply(undefined); }, TypeError);
assertThrowsInstanceOf(function() { String.prototype.codePointAt.apply(undefined, [4]); }, TypeError);
assertThrowsInstanceOf(function() { String.prototype.codePointAt.apply(null); }, TypeError);
assertThrowsInstanceOf(function() { String.prototype.codePointAt.apply(null, [4]); }, TypeError);
assertEq(String.prototype.codePointAt.apply(42, [0]), 0x34);
assertEq(String.prototype.codePointAt.apply(42, [1]), 0x32);
assertEq(String.prototype.codePointAt.apply({ 'toString': function() { return 'abc'; } }, [2]), 0x63);
reportCompare(0, 0, "ok");

View File

@ -0,0 +1,48 @@
var BUGNUMBER = 918879;
var summary = 'String.fromCodePoint';
print(BUGNUMBER + ": " + summary);
// Tests taken from:
// https://github.com/mathiasbynens/String.fromCodePoint/blob/master/tests/tests.js
assertEq(String.fromCodePoint.length, 0);
assertEq(String.propertyIsEnumerable('fromCodePoint'), false);
assertEq(String.fromCodePoint(''), '\0');
assertEq(String.fromCodePoint(), '');
assertEq(String.fromCodePoint(-0), '\0');
assertEq(String.fromCodePoint(0), '\0');
assertEq(String.fromCodePoint(0x1D306), '\uD834\uDF06');
assertEq(String.fromCodePoint(0x1D306, 0x61, 0x1D307), '\uD834\uDF06a\uD834\uDF07');
assertEq(String.fromCodePoint(0x61, 0x62, 0x1D307), 'ab\uD834\uDF07');
assertEq(String.fromCodePoint(false), '\0');
assertEq(String.fromCodePoint(null), '\0');
assertThrowsInstanceOf(function() { String.fromCodePoint('_'); }, RangeError);
assertThrowsInstanceOf(function() { String.fromCodePoint('+Infinity'); }, RangeError);
assertThrowsInstanceOf(function() { String.fromCodePoint('-Infinity'); }, RangeError);
assertThrowsInstanceOf(function() { String.fromCodePoint(-1); }, RangeError);
assertThrowsInstanceOf(function() { String.fromCodePoint(0x10FFFF + 1); }, RangeError);
assertThrowsInstanceOf(function() { String.fromCodePoint(3.14); }, RangeError);
assertThrowsInstanceOf(function() { String.fromCodePoint(3e-2); }, RangeError);
assertThrowsInstanceOf(function() { String.fromCodePoint(Infinity); }, RangeError);
assertThrowsInstanceOf(function() { String.fromCodePoint(NaN); }, RangeError);
assertThrowsInstanceOf(function() { String.fromCodePoint(undefined); }, RangeError);
assertThrowsInstanceOf(function() { String.fromCodePoint({}); }, RangeError);
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
reportCompare(0, 0, "ok");

View File