// Simple test from http://test262.ecmascript.org/# // Just checking whether Espruino is capable of running the tests or not // NOTE: RegEx checks have been removed here and 'result=1' was put at the end var strict_mode = false; function testRun(id, path, description, codeString, result, error) { if (result!=="pass") { throw new Error("Test '" + path + "'failed: " + error); } } function testFinished() { //no-op } function compareArray(aExpected, aActual) { if (aActual.length != aExpected.length) { return false; } aExpected.sort(); aActual.sort(); var s; for (var i = 0; i < aExpected.length; i++) { if (aActual[i] !== aExpected[i]) { return false; } } return true; } //----------------------------------------------------------------------------- function arrayContains(arr, expected) { var found; for (var i = 0; i < expected.length; i++) { found = false; for (var j = 0; j < arr.length; j++) { if (expected[i] === arr[j]) { found = true; break; } } if (!found) { return false; } } return true; } //----------------------------------------------------------------------------- var supportsArrayIndexGettersOnArrays = undefined; function fnSupportsArrayIndexGettersOnArrays() { if (typeof supportsArrayIndexGettersOnArrays !== "undefined") { return supportsArrayIndexGettersOnArrays; } supportsArrayIndexGettersOnArrays = false; if (fnExists(Object.defineProperty)) { var arr = []; Object.defineProperty(arr, "0", { get: function() { supportsArrayIndexGettersOnArrays = true; return 0; } }); var res = arr[0]; } return supportsArrayIndexGettersOnArrays; } //----------------------------------------------------------------------------- var supportsArrayIndexGettersOnObjects = undefined; function fnSupportsArrayIndexGettersOnObjects() { if (typeof supportsArrayIndexGettersOnObjects !== "undefined") return supportsArrayIndexGettersOnObjects; supportsArrayIndexGettersOnObjects = false; if (fnExists(Object.defineProperty)) { var obj = {}; Object.defineProperty(obj, "0", { get: function() { supportsArrayIndexGettersOnObjects = true; return 0; } }); var res = obj[0]; } return supportsArrayIndexGettersOnObjects; } //----------------------------------------------------------------------------- function ConvertToFileUrl(pathStr) { return "file:" + pathStr.replace(/\\/g, "/"); } //----------------------------------------------------------------------------- function fnExists(/*arguments*/) { for (var i = 0; i < arguments.length; i++) { if (typeof (arguments[i]) !== "function") return false; } return true; } //----------------------------------------------------------------------------- var __globalObject = Function("return this;")(); function fnGlobalObject() { return __globalObject; } //----------------------------------------------------------------------------- function fnSupportsStrict() { "use strict"; try { eval('with ({}) {}'); return false; } catch (e) { return true; } } //----------------------------------------------------------------------------- //Verify all attributes specified data property of given object: //value, writable, enumerable, configurable //If all attribute values are expected, return true, otherwise, return false function dataPropertyAttributesAreCorrect(obj, name, value, writable, enumerable, configurable) { var attributesCorrect = true; if (obj[name] !== value) { if (typeof obj[name] === "number" && isNaN(obj[name]) && typeof value === "number" && isNaN(value)) { // keep empty } else { attributesCorrect = false; } } try { if (obj[name] === "oldValue") { obj[name] = "newValue"; } else { obj[name] = "OldValue"; } } catch (we) { } var overwrited = false; if (obj[name] !== value) { if (typeof obj[name] === "number" && isNaN(obj[name]) && typeof value === "number" && isNaN(value)) { // keep empty } else { overwrited = true; } } if (overwrited !== writable) { attributesCorrect = false; } var enumerated = false; for (var prop in obj) { if (obj.hasOwnProperty(prop) && prop === name) { enumerated = true; } } if (enumerated !== enumerable) { attributesCorrect = false; } var deleted = false; try { delete obj[name]; } catch (de) { } if (!obj.hasOwnProperty(name)) { deleted = true; } if (deleted !== configurable) { attributesCorrect = false; } return attributesCorrect; } //----------------------------------------------------------------------------- //Verify all attributes specified accessor property of given object: //get, set, enumerable, configurable //If all attribute values are expected, return true, otherwise, return false function accessorPropertyAttributesAreCorrect(obj, name, get, set, setVerifyHelpProp, enumerable, configurable) { var attributesCorrect = true; if (get !== undefined) { if (obj[name] !== get()) { if (typeof obj[name] === "number" && isNaN(obj[name]) && typeof get() === "number" && isNaN(get())) { // keep empty } else { attributesCorrect = false; } } } else { if (obj[name] !== undefined) { attributesCorrect = false; } } try { var desc = Object.getOwnPropertyDescriptor(obj, name); if (typeof desc.set === "undefined") { if (typeof set !== "undefined") { attributesCorrect = false; } } else { obj[name] = "toBeSetValue"; if (obj[setVerifyHelpProp] !== "toBeSetValue") { attributesCorrect = false; } } } catch (se) { throw se; } var enumerated = false; for (var prop in obj) { if (obj.hasOwnProperty(prop) && prop === name) { enumerated = true; } } if (enumerated !== enumerable) { attributesCorrect = false; } var deleted = false; try { delete obj[name]; } catch (de) { throw de; } if (!obj.hasOwnProperty(name)) { deleted = true; } if (deleted !== configurable) { attributesCorrect = false; } return attributesCorrect; } //----------------------------------------------------------------------------- var NotEarlyErrorString = "NotEarlyError"; var EarlyErrorRePat = "^((?!" + NotEarlyErrorString + ").)*$"; var NotEarlyError = new Error(NotEarlyErrorString); //----------------------------------------------------------------------------- // Copyright 2009 the Sputnik authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. function Test262Error(message) { if (message) this.message = message; } Test262Error.prototype.toString = function () { return "Test262 Error: " + this.message; }; function testFailed(message) { throw new Test262Error(message); } function testPrint(message) { } //adaptors for Test262 framework function $PRINT(message) { } function $INCLUDE(message) { } function $ERROR(message) { testFailed(message); } function $FAIL(message) { testFailed(message); } //Sputnik library definitions //Ultimately these should be namespaced some how and only made //available to tests that explicitly include them. //For now, we just define the globally //math_precision.js // Copyright 2009 the Sputnik authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. function getPrecision(num) { //TODO: Create a table of prec's, // because using Math for testing Math isn't that correct. var log2num = Math.log(Math.abs(num)) / Math.LN2; var pernum = Math.ceil(log2num); return (2 * Math.pow(2, -52 + pernum)); //return(0); } //math_isequal.js // Copyright 2009 the Sputnik authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. var prec; function isEqual(num1, num2) { if ((num1 === Infinity) && (num2 === Infinity)) { return (true); } if ((num1 === -Infinity) && (num2 === -Infinity)) { return (true); } prec = getPrecision(Math.min(Math.abs(num1), Math.abs(num2))); return (Math.abs(num1 - num2) <= prec); //return(num1 === num2); } //numeric_conversion.js // Copyright 2009 the Sputnik authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. function ToInteger(p) { var x = Number(p); if (isNaN(x)) { return +0; } if ((x === +0) || (x === -0) || (x === Number.POSITIVE_INFINITY) || (x === Number.NEGATIVE_INFINITY)) { return x; } var sign = (x < 0) ? -1 : 1; return (sign * Math.floor(Math.abs(x))); } //Date_constants.js // Copyright 2009 the Sputnik authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. var HoursPerDay = 24; var MinutesPerHour = 60; var SecondsPerMinute = 60; var msPerDay = 86400000; var msPerSecond = 1000; var msPerMinute = 60000; var msPerHour = 3600000; var date_1899_end = -2208988800001; var date_1900_start = -2208988800000; var date_1969_end = -1; var date_1970_start = 0; var date_1999_end = 946684799999; var date_2000_start = 946684800000; var date_2099_end = 4102444799999; var date_2100_start = 4102444800000; // Copyright 2009 the Sputnik authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. //the following values are normally generated by the sputnik.py driver var $LocalTZ, $DST_start_month, $DST_start_sunday, $DST_start_hour, $DST_start_minutes, $DST_end_month, $DST_end_sunday, $DST_end_hour, $DST_end_minutes; (function () { /** * Finds the first date, starting from |start|, where |predicate| * holds. */ var findNearestDateBefore = function(start, predicate) { var current = start; var month = 1000 * 60 * 60 * 24 * 30; for (var step = month; step > 0; step = Math.floor(step / 3)) { if (!predicate(current)) { while (!predicate(current)) current = new Date(current.getTime() + step); current = new Date(current.getTime() - step); } } while (!predicate(current)) { current = new Date(current.getTime() + 1); } return current; }; var juneDate = new Date(2000, 5, 20, 0, 0, 0, 0); var decemberDate = new Date(2000, 11, 20, 0, 0, 0, 0); var juneOffset = juneDate.getTimezoneOffset(); var decemberOffset = decemberDate.getTimezoneOffset(); var isSouthernHemisphere = (juneOffset > decemberOffset); var winterTime = isSouthernHemisphere ? juneDate : decemberDate; var summerTime = isSouthernHemisphere ? decemberDate : juneDate; var dstStart = findNearestDateBefore(winterTime, function (date) { return date.getTimezoneOffset() == summerTime.getTimezoneOffset(); }); $DST_start_month = dstStart.getMonth(); $DST_start_sunday = dstStart.getDate() > 15 ? '"last"' : '"first"'; $DST_start_hour = dstStart.getHours(); $DST_start_minutes = dstStart.getMinutes(); var dstEnd = findNearestDateBefore(summerTime, function (date) { return date.getTimezoneOffset() == winterTime.getTimezoneOffset(); }); $DST_end_month = dstEnd.getMonth(); $DST_end_sunday = dstEnd.getDate() > 15 ? '"last"' : '"first"'; $DST_end_hour = dstEnd.getHours(); $DST_end_minutes = dstEnd.getMinutes(); return; })(); //Date.library.js // Copyright 2009 the Sputnik authors. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. //15.9.1.2 Day Number and Time within Day function Day(t) { return Math.floor(t/msPerDay); } function TimeWithinDay(t) { return t%msPerDay; } //15.9.1.3 Year Number function DaysInYear(y){ if(y%4 != 0) return 365; if(y%4 == 0 && y%100 != 0) return 366; if(y%100 == 0 && y%400 != 0) return 365; if(y%400 == 0) return 366; } function DayFromYear(y) { return (365*(y-1970) + Math.floor((y-1969)/4) - Math.floor((y-1901)/100) + Math.floor((y-1601)/400)); } function TimeFromYear(y){ return msPerDay*DayFromYear(y); } function YearFromTime(t) { t = Number(t); var sign = ( t < 0 ) ? -1 : 1; var year = ( sign < 0 ) ? 1969 : 1970; for(var time = 0;;year += sign){ time = TimeFromYear(year); if(sign > 0 && time > t){ year -= sign; break; } else if(sign < 0 && time <= t){ break; } }; return year; } function InLeapYear(t){ if(DaysInYear(YearFromTime(t)) == 365) return 0; if(DaysInYear(YearFromTime(t)) == 366) return 1; } function DayWithinYear(t) { return Day(t)-DayFromYear(YearFromTime(t)); } //15.9.1.4 Month Number function MonthFromTime(t){ var day = DayWithinYear(t); var leap = InLeapYear(t); if((0 <= day) && (day < 31)) return 0; if((31 <= day) && (day < (59+leap))) return 1; if(((59+leap) <= day) && (day < (90+leap))) return 2; if(((90+leap) <= day) && (day < (120+leap))) return 3; if(((120+leap) <= day) && (day < (151+leap))) return 4; if(((151+leap) <= day) && (day < (181+leap))) return 5; if(((181+leap) <= day) && (day < (212+leap))) return 6; if(((212+leap) <= day) && (day < (243+leap))) return 7; if(((243+leap) <= day) && (day < (273+leap))) return 8; if(((273+leap) <= day) && (day < (304+leap))) return 9; if(((304+leap) <= day) && (day < (334+leap))) return 10; if(((334+leap) <= day) && (day < (365+leap))) return 11; } //15.9.1.5 Date Number function DateFromTime(t) { var day = DayWithinYear(t); var month = MonthFromTime(t); var leap = InLeapYear(t); if(month == 0) return day+1; if(month == 1) return day-30; if(month == 2) return day-58-leap; if(month == 3) return day-89-leap; if(month == 4) return day-119-leap; if(month == 5) return day-150-leap; if(month == 6) return day-180-leap; if(month == 7) return day-211-leap; if(month == 8) return day-242-leap; if(month == 9) return day-272-leap; if(month == 10) return day-303-leap; if(month == 11) return day-333-leap; } //15.9.1.6 Week Day function WeekDay(t) { var weekday = (Day(t)+4)%7; return (weekday < 0 ? 7+weekday : weekday); } //15.9.1.9 Daylight Saving Time Adjustment $LocalTZ = (new Date()).getTimezoneOffset() / -60; if (DaylightSavingTA((new Date()).valueOf()) !== 0) { $LocalTZ -= 1; } var LocalTZA = $LocalTZ*msPerHour; function DaysInMonth(m, leap) { m = m%12; //April, June, Sept, Nov if(m == 3 || m == 5 || m == 8 || m == 10 ) { return 30; } //Jan, March, May, July, Aug, Oct, Dec if(m == 0 || m == 2 || m == 4 || m == 6 || m == 7 || m == 9 || m == 11){ return 31; } //Feb return 28+leap; } function GetSundayInMonth(t, m, count){ var year = YearFromTime(t); var tempDate; if (count==='"first"') { for (var d=1; d <= DaysInMonth(m, InLeapYear(t)); d++) { tempDate = new Date(year, m, d); if (tempDate.getDay()===0) { return tempDate.valueOf(); } } } else if(count==='"last"') { for (var d=DaysInMonth(m, InLeapYear(t)); d>0; d--) { tempDate = new Date(year, m, d); if (tempDate.getDay()===0) { return tempDate.valueOf(); } } } throw new Error("Unsupported 'count' arg:" + count); } /* function GetSundayInMonth(t, m, count){ var year = YearFromTime(t); var leap = InLeapYear(t); var day = 0; if(m >= 1) day += DaysInMonth(0, leap); if(m >= 2) day += DaysInMonth(1, leap); if(m >= 3) day += DaysInMonth(2, leap); if(m >= 4) day += DaysInMonth(3, leap); if(m >= 5) day += DaysInMonth(4, leap); if(m >= 6) day += DaysInMonth(5, leap); if(m >= 7) day += DaysInMonth(6, leap); if(m >= 8) day += DaysInMonth(7, leap); if(m >= 9) day += DaysInMonth(8, leap); if(m >= 10) day += DaysInMonth(9, leap); if(m >= 11) day += DaysInMonth(10, leap); var month_start = TimeFromYear(year)+day*msPerDay; var sunday = 0; if(count === "last"){ for(var last_sunday = month_start+DaysInMonth(m, leap)*msPerDay; WeekDay(last_sunday)>0; last_sunday -= msPerDay ){}; sunday = last_sunday; } else { for(var first_sunday = month_start; WeekDay(first_sunday)>0; first_sunday += msPerDay ){}; sunday = first_sunday+7*msPerDay*(count-1); } return sunday; }*/ function DaylightSavingTA(t) { // t = t-LocalTZA; var DST_start = GetSundayInMonth(t, $DST_start_month, $DST_start_sunday) + $DST_start_hour*msPerHour + $DST_start_minutes*msPerMinute; var k = new Date(DST_start); var DST_end = GetSundayInMonth(t, $DST_end_month, $DST_end_sunday) + $DST_end_hour*msPerHour + $DST_end_minutes*msPerMinute; if ( t >= DST_start && t < DST_end ) { return msPerHour; } else { return 0; } } //15.9.1.9 Local Time function LocalTime(t){ return t+LocalTZA+DaylightSavingTA(t); } function UTC(t) { return t-LocalTZA-DaylightSavingTA(t-LocalTZA); } //15.9.1.10 Hours, Minutes, Second, and Milliseconds function HourFromTime(t){ return Math.floor(t/msPerHour)%HoursPerDay; } function MinFromTime(t){ return Math.floor(t/msPerMinute)%MinutesPerHour; } function SecFromTime(t){ return Math.floor(t/msPerSecond)%SecondsPerMinute; } function msFromTime(t){ return t%msPerSecond; } //15.9.1.11 MakeTime (hour, min, sec, ms) function MakeTime(hour, min, sec, ms){ if ( !isFinite(hour) || !isFinite(min) || !isFinite(sec) || !isFinite(ms)) { return Number.NaN; } hour = ToInteger(hour); min = ToInteger(min); sec = ToInteger(sec); ms = ToInteger(ms); return ((hour*msPerHour) + (min*msPerMinute) + (sec*msPerSecond) + ms); } //15.9.1.12 MakeDay (year, month, date) function MakeDay(year, month, date) { if ( !isFinite(year) || !isFinite(month) || !isFinite(date)) { return Number.NaN; } year = ToInteger(year); month = ToInteger(month); date = ToInteger(date ); var result5 = year + Math.floor(month/12); var result6 = month%12; var sign = ( year < 1970 ) ? -1 : 1; var t = ( year < 1970 ) ? 1 : 0; var y = ( year < 1970 ) ? 1969 : 1970; if( sign == -1 ){ for ( y = 1969; y >= year; y += sign ) { t += sign * DaysInYear(y)*msPerDay; } } else { for ( y = 1970 ; y < year; y += sign ) { t += sign * DaysInYear(y)*msPerDay; } } var leap = 0; for ( var m = 0; m < month; m++ ) { //if year is changed, than we need to recalculate leep leap = InLeapYear(t); t += DaysInMonth(m, leap)*msPerDay; } if ( YearFromTime(t) != result5 ) { return Number.NaN; } if ( MonthFromTime(t) != result6 ) { return Number.NaN; } if ( DateFromTime(t) != 1 ) { return Number.NaN; } return Day(t)+date-1; } //15.9.1.13 MakeDate (day, time) function MakeDate( day, time ) { if(!isFinite(day) || !isFinite(time)) { return Number.NaN; } return day*msPerDay+time; } //15.9.1.14 TimeClip (time) function TimeClip(time) { if(!isFinite(time) || Math.abs(time) > 8.64e15){ return Number.NaN; } return ToInteger(time); } //Test Functions //ConstructDate is considered deprecated, and should not be used directly from //test262 tests as it's incredibly sensitive to DST start/end dates that //vary with geographic location. function ConstructDate(year, month, date, hours, minutes, seconds, ms){ /* * 1. Call ToNumber(year) * 2. Call ToNumber(month) * 3. If date is supplied use ToNumber(date); else use 1 * 4. If hours is supplied use ToNumber(hours); else use 0 * 5. If minutes is supplied use ToNumber(minutes); else use 0 * 6. If seconds is supplied use ToNumber(seconds); else use 0 * 7. If ms is supplied use ToNumber(ms); else use 0 * 8. If Result(1) is not NaN and 0 <= ToInteger(Result(1)) <= 99, Result(8) is * 1900+ToInteger(Result(1)); otherwise, Result(8) is Result(1) * 9. Compute MakeDay(Result(8), Result(2), Result(3)) * 10. Compute MakeTime(Result(4), Result(5), Result(6), Result(7)) * 11. Compute MakeDate(Result(9), Result(10)) * 12. Set the [[Value]] property of the newly constructed object to TimeClip(UTC(Result(11))) */ var r1 = Number(year); var r2 = Number(month); var r3 = ((date && arguments.length > 2) ? Number(date) : 1); var r4 = ((hours && arguments.length > 3) ? Number(hours) : 0); var r5 = ((minutes && arguments.length > 4) ? Number(minutes) : 0); var r6 = ((seconds && arguments.length > 5) ? Number(seconds) : 0); var r7 = ((ms && arguments.length > 6) ? Number(ms) : 0); var r8 = r1; if(!isNaN(r1) && (0 <= ToInteger(r1)) && (ToInteger(r1) <= 99)) r8 = 1900+r1; var r9 = MakeDay(r8, r2, r3); var r10 = MakeTime(r4, r5, r6, r7); var r11 = MakeDate(r9, r10); var retVal = TimeClip(UTC(r11)); return retVal; } /**** Python code for initialize the above constants // We may want to replicate the following in JavaScript. // However, using JS date operations to generate parameters that are then used to // test those some date operations seems unsound. However, it isn't clear if there //is a good interoperable alternative. # Copyright 2009 the Sputnik authors. All rights reserved. # This code is governed by the BSD license found in the LICENSE file. def GetDaylightSavingsTimes(): # Is the given floating-point time in DST? def IsDst(t): return time.localtime(t)[-1] # Binary search to find an interval between the two times no greater than # delta where DST switches, returning the midpoint. def FindBetween(start, end, delta): while end - start > delta: middle = (end + start) / 2 if IsDst(middle) == IsDst(start): start = middle else: end = middle return (start + end) / 2 now = time.time() one_month = (30 * 24 * 60 * 60) # First find a date with different daylight savings. To avoid corner cases # we try four months before and after today. after = now + 4 * one_month before = now - 4 * one_month if IsDst(now) == IsDst(before) and IsDst(now) == IsDst(after): logger.warning("Was unable to determine DST info.") return None # Determine when the change occurs between now and the date we just found # in a different DST. if IsDst(now) != IsDst(before): first = FindBetween(before, now, 1) else: first = FindBetween(now, after, 1) # Determine when the change occurs between three and nine months from the # first. second = FindBetween(first + 3 * one_month, first + 9 * one_month, 1) # Find out which switch is into and which if out of DST if IsDst(first - 1) and not IsDst(first + 1): start = second end = first else: start = first end = second return (start, end) def GetDaylightSavingsAttribs(): times = GetDaylightSavingsTimes() if not times: return None (start, end) = times def DstMonth(t): return time.localtime(t)[1] - 1 def DstHour(t): return time.localtime(t - 1)[3] + 1 def DstSunday(t): if time.localtime(t)[2] > 15: return "'last'" else: return "'first'" def DstMinutes(t): return (time.localtime(t - 1)[4] + 1) % 60 attribs = { } attribs['start_month'] = DstMonth(start) attribs['end_month'] = DstMonth(end) attribs['start_sunday'] = DstSunday(start) attribs['end_sunday'] = DstSunday(end) attribs['start_hour'] = DstHour(start) attribs['end_hour'] = DstHour(end) attribs['start_minutes'] = DstMinutes(start) attribs['end_minutes'] = DstMinutes(end) return attribs *********/ //--Test case registration----------------------------------------------------- function runTestCase(testcase) { if (testcase() !== true) { $ERROR("Test case returned non-true value!"); } } if (this.window!==undefined) { //for console support this.window.onerror = function(errorMsg, url, lineNumber) { this.window.iframeError = errorMsg; }; } //This doesn't work with early errors in current versions of Opera /* if (/opera/i.test(navigator.userAgent)) { (function() { var origError = window.Error; window.Error = function() { if (arguments.length>0) { try { window.onerror(arguments[0]); } catch(e) { alert("Failed to invoke window.onerror (from ed.js)"); } } return origError.apply(this, arguments); } })(); }*/ /** * @description Tests that obj meets the requirements for built-in objects * defined by the introduction of chapter 15 of the ECMAScript Language Specification. * @param {Object} obj the object to be tested. * @param {boolean} isFunction whether the specification describes obj as a function. * @param {boolean} isConstructor whether the specification describes obj as a constructor. * @param {String[]} properties an array with the names of the built-in properties of obj, * excluding length, prototype, or properties with non-default attributes. * @param {number} length for functions only: the length specified for the function * or derived from the argument list. * @author Norbert Lindenberg */ function testBuiltInObject(obj, isFunction, isConstructor, properties, length) { if (obj === undefined) { $ERROR("Object being tested is undefined."); } var objString = Object.prototype.toString.call(obj); if (isFunction) { if (objString !== "[object Function]") { $ERROR("The [[Class]] internal property of a built-in function must be " + "\"Function\", but toString() returns " + objString); } } else { if (objString !== "[object Object]") { $ERROR("The [[Class]] internal property of a built-in non-function object must be " + "\"Object\", but toString() returns " + objString); } } if (!Object.isExtensible(obj)) { $ERROR("Built-in objects must be extensible."); } if (isFunction && Object.getPrototypeOf(obj) !== Function.prototype) { $ERROR("Built-in functions must have Function.prototype as their prototype."); } if (isConstructor && Object.getPrototypeOf(obj.prototype) !== Object.prototype) { $ERROR("Built-in prototype objects must have Object.prototype as their prototype."); } // verification of the absence of the [[Construct]] internal property has // been moved to the end of the test // verification of the absence of the prototype property has // been moved to the end of the test if (isFunction) { if (typeof obj.length !== "number" || obj.length !== Math.floor(obj.length)) { $ERROR("Built-in functions must have a length property with an integer value."); } if (obj.length !== length) { $ERROR("Function's length property doesn't have specified value; expected " + length + ", got " + obj.length + "."); } var desc = Object.getOwnPropertyDescriptor(obj, "length"); if (desc.writable) { $ERROR("The length property of a built-in function must not be writable."); } if (desc.enumerable) { $ERROR("The length property of a built-in function must not be enumerable."); } if (desc.configurable) { $ERROR("The length property of a built-in function must not be configurable."); } } properties.forEach(function(prop) { var desc = Object.getOwnPropertyDescriptor(obj, prop); if (desc === undefined) { $ERROR("Missing property " + prop + "."); } // accessor properties don't have writable attribute if (desc.hasOwnProperty("writable") && !desc.writable) { $ERROR("The " + prop + " property of this built-in function must be writable."); } if (desc.enumerable) { $ERROR("The " + prop + " property of this built-in function must not be enumerable."); } if (!desc.configurable) { $ERROR("The " + prop + " property of this built-in function must be configurable."); } }); // The remaining sections have been moved to the end of the test because // unbound non-constructor functions written in JavaScript cannot possibly // pass them, and we still want to test JavaScript implementations as much // as possible. var exception; if (isFunction && !isConstructor) { // this is not a complete test for the presence of [[Construct]]: // if it's absent, the exception must be thrown, but it may also // be thrown if it's present and just has preconditions related to // arguments or the this value that this statement doesn't meet. try { /*jshint newcap:false*/ var instance = new obj(); } catch (e) { exception = e; } if (exception === undefined || exception.name !== "TypeError") { $ERROR("Built-in functions that aren't constructors must throw TypeError when " + "used in a \"new\" statement."); } } if (isFunction && !isConstructor && obj.hasOwnProperty("prototype")) { $ERROR("Built-in functions that aren't constructors must not have a prototype property."); } // passed the complete test! return true; } /** * This file contains shared functions for the tests in the conformance test * suite for the ECMAScript Internationalization API. * @author Norbert Lindenberg */ /** * @description Calls the provided function for every service constructor in * the Intl object, until f returns a falsy value. It returns the result of the * last call to f, mapped to a boolean. * @param {Function} f the function to call for each service constructor in * the Intl object. * @param {Function} Constructor the constructor object to test with. * @result {Boolean} whether the test succeeded. */ function testWithIntlConstructors(f) { var constructors = ["Collator", "NumberFormat", "DateTimeFormat"]; return constructors.every(function (constructor) { var Constructor = Intl[constructor]; var result; try { result = f(Constructor); } catch (e) { e.message += " (Testing with " + constructor + ".)"; throw e; } return result; }); } /** * Returns the name of the given constructor object, which must be one of * Intl.Collator, Intl.NumberFormat, or Intl.DateTimeFormat. * @param {object} Constructor a constructor * @return {string} the name of the constructor */ function getConstructorName(Constructor) { switch (Constructor) { case Intl.Collator: return "Collator"; case Intl.NumberFormat: return "NumberFormat"; case Intl.DateTimeFormat: return "DateTimeFormat"; default: $ERROR("test internal error: unknown Constructor"); } } /** * Taints a named data property of the given object by installing * a setter that throws an exception. * @param {object} obj the object whose data property to taint * @param {string} property the property to taint */ function taintDataProperty(obj, property) { Object.defineProperty(obj, property, { set: function(value) { $ERROR("Client code can adversely affect behavior: setter for " + property + "."); }, enumerable: false, configurable: true }); } /** * Taints a named method of the given object by replacing it with a function * that throws an exception. * @param {object} obj the object whose method to taint * @param {string} property the name of the method to taint */ function taintMethod(obj, property) { Object.defineProperty(obj, property, { value: function() { $ERROR("Client code can adversely affect behavior: method " + property + "."); }, writable: true, enumerable: false, configurable: true }); } /** * Taints the given properties (and similarly named properties) by installing * setters on Object.prototype that throw exceptions. * @param {Array} properties an array of property names to taint */ function taintProperties(properties) { properties.forEach(function (property) { var adaptedProperties = [property, "__" + property, "_" + property, property + "_", property + "__"]; adaptedProperties.forEach(function (property) { taintDataProperty(Object.prototype, property); }); }); } /** * Taints the Array object by creating a setter for the property "0" and * replacing some key methods with functions that throw exceptions. */ function taintArray() { taintDataProperty(Array.prototype, "0"); taintMethod(Array.prototype, "indexOf"); taintMethod(Array.prototype, "join"); taintMethod(Array.prototype, "push"); taintMethod(Array.prototype, "slice"); taintMethod(Array.prototype, "sort"); } // auxiliary data for getLocaleSupportInfo var languages = ["zh", "es", "en", "hi", "ur", "ar", "ja", "pa"]; var scripts = ["Latn", "Hans", "Deva", "Arab", "Jpan", "Hant"]; var countries = ["CN", "IN", "US", "PK", "JP", "TW", "HK", "SG"]; var localeSupportInfo = {}; /** * Gets locale support info for the given constructor object, which must be one * of Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat. * @param {object} Constructor the constructor for which to get locale support info * @return {object} locale support info with the following properties: * supported: array of fully supported language tags * byFallback: array of language tags that are supported through fallbacks * unsupported: array of unsupported language tags */ function getLocaleSupportInfo(Constructor) { var constructorName = getConstructorName(Constructor); if (localeSupportInfo[constructorName] !== undefined) { return localeSupportInfo[constructorName]; } var allTags = []; var i, j, k; var language, script, country; for (i = 0; i < languages.length; i++) { language = languages[i]; allTags.push(language); for (j = 0; j < scripts.length; j++) { script = scripts[j]; allTags.push(language + "-" + script); for (k = 0; k < countries.length; k++) { country = countries[k]; allTags.push(language + "-" + script + "-" + country); } } for (k = 0; k < countries.length; k++) { country = countries[k]; allTags.push(language + "-" + country); } } var supported = []; var byFallback = []; var unsupported = []; for (i = 0; i < allTags.length; i++) { var request = allTags[i]; var result = new Constructor([request], {localeMatcher: "lookup"}).resolvedOptions().locale; if (request === result) { supported.push(request); } else if (request.indexOf(result) === 0) { byFallback.push(request); } else { unsupported.push(request); } } localeSupportInfo[constructorName] = { supported: supported, byFallback: byFallback, unsupported: unsupported }; return localeSupportInfo[constructorName]; } /** * @description Tests whether locale is a String value representing a * structurally valid and canonicalized BCP 47 language tag, as defined in * sections 6.2.2 and 6.2.3 of the ECMAScript Internationalization API * Specification. * @param {String} locale the string to be tested. * @result {Boolean} whether the test succeeded. */ function isCanonicalizedStructurallyValidLanguageTag(locale) { /** * Regular expression defining BCP 47 language tags. * * Spec: RFC 5646 section 2.1. */ var alpha = "[a-zA-Z]", digit = "[0-9]", alphanum = "(" + alpha + "|" + digit + ")", regular = "(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)", irregular = "(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)", grandfathered = "(" + irregular + "|" + regular + ")", privateuse = "(x(-[a-z0-9]{1,8})+)", singleton = "(" + digit + "|[A-WY-Za-wy-z])", extension = "(" + singleton + "(-" + alphanum + "{2,8})+)", variant = "(" + alphanum + "{5,8}|(" + digit + alphanum + "{3}))", region = "(" + alpha + "{2}|" + digit + "{3})", script = "(" + alpha + "{4})", extlang = "(" + alpha + "{3}(-" + alpha + "{3}){0,2})", language = "(" + alpha + "{2,3}(-" + extlang + ")?|" + alpha + "{4}|" + alpha + "{5,8})", langtag = language + "(-" + script + ")?(-" + region + ")?(-" + variant + ")*(-" + extension + ")*(-" + privateuse + ")?", languageTag = "^(" + langtag + "|" + privateuse + "|" + grandfathered + ")$", languageTagRE = new RegExp(languageTag, "i"); var duplicateSingleton = "-" + singleton + "-(.*-)?\\1(?!" + alphanum + ")", duplicateSingletonRE = new RegExp(duplicateSingleton, "i"), duplicateVariant = "(" + alphanum + "{2,8}-)+" + variant + "-(" + alphanum + "{2,8}-)*\\3(?!" + alphanum + ")", duplicateVariantRE = new RegExp(duplicateVariant, "i"); /** * Verifies that the given string is a well-formed BCP 47 language tag * with no duplicate variant or singleton subtags. * * Spec: ECMAScript Internationalization API Specification, draft, 6.2.2. */ function isStructurallyValidLanguageTag(locale) { if (!languageTagRE.test(locale)) { return false; } locale = locale.split(/-x-/)[0]; return !duplicateSingletonRE.test(locale) && !duplicateVariantRE.test(locale); } /** * Mappings from complete tags to preferred values. * * Spec: IANA Language Subtag Registry. */ var __tagMappings = { // property names must be in lower case; values in canonical form // grandfathered tags from IANA language subtag registry, file date 2011-08-25 "art-lojban": "jbo", "cel-gaulish": "cel-gaulish", "en-gb-oed": "en-GB-oed", "i-ami": "ami", "i-bnn": "bnn", "i-default": "i-default", "i-enochian": "i-enochian", "i-hak": "hak", "i-klingon": "tlh", "i-lux": "lb", "i-mingo": "i-mingo", "i-navajo": "nv", "i-pwn": "pwn", "i-tao": "tao", "i-tay": "tay", "i-tsu": "tsu", "no-bok": "nb", "no-nyn": "nn", "sgn-be-fr": "sfb", "sgn-be-nl": "vgt", "sgn-ch-de": "sgg", "zh-guoyu": "cmn", "zh-hakka": "hak", "zh-min": "zh-min", "zh-min-nan": "nan", "zh-xiang": "hsn", // deprecated redundant tags from IANA language subtag registry, file date 2011-08-25 "sgn-br": "bzs", "sgn-co": "csn", "sgn-de": "gsg", "sgn-dk": "dsl", "sgn-es": "ssp", "sgn-fr": "fsl", "sgn-gb": "bfi", "sgn-gr": "gss", "sgn-ie": "isg", "sgn-it": "ise", "sgn-jp": "jsl", "sgn-mx": "mfs", "sgn-ni": "ncs", "sgn-nl": "dse", "sgn-no": "nsl", "sgn-pt": "psr", "sgn-se": "swl", "sgn-us": "ase", "sgn-za": "sfs", "zh-cmn": "cmn", "zh-cmn-hans": "cmn-Hans", "zh-cmn-hant": "cmn-Hant", "zh-gan": "gan", "zh-wuu": "wuu", "zh-yue": "yue", // deprecated variant with prefix from IANA language subtag registry, file date 2011-08-25 "ja-latn-hepburn-heploc": "ja-Latn-alalc97" }; /** * Mappings from non-extlang subtags to preferred values. * * Spec: IANA Language Subtag Registry. */ var __subtagMappings = { // property names and values must be in canonical case // language subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 "in": "id", "iw": "he", "ji": "yi", "jw": "jv", "mo": "ro", "ayx": "nun", "cjr": "mom", "cmk": "xch", "drh": "khk", "drw": "prs", "gav": "dev", "mst": "mry", "myt": "mry", "tie": "ras", "tkk": "twm", "tnf": "prs", // region subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 "BU": "MM", "DD": "DE", "FX": "FR", "TP": "TL", "YD": "YE", "ZR": "CD" }; /** * Mappings from extlang subtags to preferred values. * * Spec: IANA Language Subtag Registry. */ var __extlangMappings = { // extlang subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 // values are arrays with [0] the replacement value, [1] (if present) the prefix to be removed "aao": ["aao", "ar"], "abh": ["abh", "ar"], "abv": ["abv", "ar"], "acm": ["acm", "ar"], "acq": ["acq", "ar"], "acw": ["acw", "ar"], "acx": ["acx", "ar"], "acy": ["acy", "ar"], "adf": ["adf", "ar"], "ads": ["ads", "sgn"], "aeb": ["aeb", "ar"], "aec": ["aec", "ar"], "aed": ["aed", "sgn"], "aen": ["aen", "sgn"], "afb": ["afb", "ar"], "afg": ["afg", "sgn"], "ajp": ["ajp", "ar"], "apc": ["apc", "ar"], "apd": ["apd", "ar"], "arb": ["arb", "ar"], "arq": ["arq", "ar"], "ars": ["ars", "ar"], "ary": ["ary", "ar"], "arz": ["arz", "ar"], "ase": ["ase", "sgn"], "asf": ["asf", "sgn"], "asp": ["asp", "sgn"], "asq": ["asq", "sgn"], "asw": ["asw", "sgn"], "auz": ["auz", "ar"], "avl": ["avl", "ar"], "ayh": ["ayh", "ar"], "ayl": ["ayl", "ar"], "ayn": ["ayn", "ar"], "ayp": ["ayp", "ar"], "bbz": ["bbz", "ar"], "bfi": ["bfi", "sgn"], "bfk": ["bfk", "sgn"], "bjn": ["bjn", "ms"], "bog": ["bog", "sgn"], "bqn": ["bqn", "sgn"], "bqy": ["bqy", "sgn"], "btj": ["btj", "ms"], "bve": ["bve", "ms"], "bvl": ["bvl", "sgn"], "bvu": ["bvu", "ms"], "bzs": ["bzs", "sgn"], "cdo": ["cdo", "zh"], "cds": ["cds", "sgn"], "cjy": ["cjy", "zh"], "cmn": ["cmn", "zh"], "coa": ["coa", "ms"], "cpx": ["cpx", "zh"], "csc": ["csc", "sgn"], "csd": ["csd", "sgn"], "cse": ["cse", "sgn"], "csf": ["csf", "sgn"], "csg": ["csg", "sgn"], "csl": ["csl", "sgn"], "csn": ["csn", "sgn"], "csq": ["csq", "sgn"], "csr": ["csr", "sgn"], "czh": ["czh", "zh"], "czo": ["czo", "zh"], "doq": ["doq", "sgn"], "dse": ["dse", "sgn"], "dsl": ["dsl", "sgn"], "dup": ["dup", "ms"], "ecs": ["ecs", "sgn"], "esl": ["esl", "sgn"], "esn": ["esn", "sgn"], "eso": ["eso", "sgn"], "eth": ["eth", "sgn"], "fcs": ["fcs", "sgn"], "fse": ["fse", "sgn"], "fsl": ["fsl", "sgn"], "fss": ["fss", "sgn"], "gan": ["gan", "zh"], "gom": ["gom", "kok"], "gse": ["gse", "sgn"], "gsg": ["gsg", "sgn"], "gsm": ["gsm", "sgn"], "gss": ["gss", "sgn"], "gus": ["gus", "sgn"], "hab": ["hab", "sgn"], "haf": ["haf", "sgn"], "hak": ["hak", "zh"], "hds": ["hds", "sgn"], "hji": ["hji", "ms"], "hks": ["hks", "sgn"], "hos": ["hos", "sgn"], "hps": ["hps", "sgn"], "hsh": ["hsh", "sgn"], "hsl": ["hsl", "sgn"], "hsn": ["hsn", "zh"], "icl": ["icl", "sgn"], "ils": ["ils", "sgn"], "inl": ["inl", "sgn"], "ins": ["ins", "sgn"], "ise": ["ise", "sgn"], "isg": ["isg", "sgn"], "isr": ["isr", "sgn"], "jak": ["jak", "ms"], "jax": ["jax", "ms"], "jcs": ["jcs", "sgn"], "jhs": ["jhs", "sgn"], "jls": ["jls", "sgn"], "jos": ["jos", "sgn"], "jsl": ["jsl", "sgn"], "jus": ["jus", "sgn"], "kgi": ["kgi", "sgn"], "knn": ["knn", "kok"], "kvb": ["kvb", "ms"], "kvk": ["kvk", "sgn"], "kvr": ["kvr", "ms"], "kxd": ["kxd", "ms"], "lbs": ["lbs", "sgn"], "lce": ["lce", "ms"], "lcf": ["lcf", "ms"], "liw": ["liw", "ms"], "lls": ["lls", "sgn"], "lsg": ["lsg", "sgn"], "lsl": ["lsl", "sgn"], "lso": ["lso", "sgn"], "lsp": ["lsp", "sgn"], "lst": ["lst", "sgn"], "lsy": ["lsy", "sgn"], "ltg": ["ltg", "lv"], "lvs": ["lvs", "lv"], "lzh": ["lzh", "zh"], "max": ["max", "ms"], "mdl": ["mdl", "sgn"], "meo": ["meo", "ms"], "mfa": ["mfa", "ms"], "mfb": ["mfb", "ms"], "mfs": ["mfs", "sgn"], "min": ["min", "ms"], "mnp": ["mnp", "zh"], "mqg": ["mqg", "ms"], "mre": ["mre", "sgn"], "msd": ["msd", "sgn"], "msi": ["msi", "ms"], "msr": ["msr", "sgn"], "mui": ["mui", "ms"], "mzc": ["mzc", "sgn"], "mzg": ["mzg", "sgn"], "mzy": ["mzy", "sgn"], "nan": ["nan", "zh"], "nbs": ["nbs", "sgn"], "ncs": ["ncs", "sgn"], "nsi": ["nsi", "sgn"], "nsl": ["nsl", "sgn"], "nsp": ["nsp", "sgn"], "nsr": ["nsr", "sgn"], "nzs": ["nzs", "sgn"], "okl": ["okl", "sgn"], "orn": ["orn", "ms"], "ors": ["ors", "ms"], "pel": ["pel", "ms"], "pga": ["pga", "ar"], "pks": ["pks", "sgn"], "prl": ["prl", "sgn"], "prz": ["prz", "sgn"], "psc": ["psc", "sgn"], "psd": ["psd", "sgn"], "pse": ["pse", "ms"], "psg": ["psg", "sgn"], "psl": ["psl", "sgn"], "pso": ["pso", "sgn"], "psp": ["psp", "sgn"], "psr": ["psr", "sgn"], "pys": ["pys", "sgn"], "rms": ["rms", "sgn"], "rsi": ["rsi", "sgn"], "rsl": ["rsl", "sgn"], "sdl": ["sdl", "sgn"], "sfb": ["sfb", "sgn"], "sfs": ["sfs", "sgn"], "sgg": ["sgg", "sgn"], "sgx": ["sgx", "sgn"], "shu": ["shu", "ar"], "slf": ["slf", "sgn"], "sls": ["sls", "sgn"], "sqs": ["sqs", "sgn"], "ssh": ["ssh", "ar"], "ssp": ["ssp", "sgn"], "ssr": ["ssr", "sgn"], "svk": ["svk", "sgn"], "swc": ["swc", "sw"], "swh": ["swh", "sw"], "swl": ["swl", "sgn"], "syy": ["syy", "sgn"], "tmw": ["tmw", "ms"], "tse": ["tse", "sgn"], "tsm": ["tsm", "sgn"], "tsq": ["tsq", "sgn"], "tss": ["tss", "sgn"], "tsy": ["tsy", "sgn"], "tza": ["tza", "sgn"], "ugn": ["ugn", "sgn"], "ugy": ["ugy", "sgn"], "ukl": ["ukl", "sgn"], "uks": ["uks", "sgn"], "urk": ["urk", "ms"], "uzn": ["uzn", "uz"], "uzs": ["uzs", "uz"], "vgt": ["vgt", "sgn"], "vkk": ["vkk", "ms"], "vkt": ["vkt", "ms"], "vsi": ["vsi", "sgn"], "vsl": ["vsl", "sgn"], "vsv": ["vsv", "sgn"], "wuu": ["wuu", "zh"], "xki": ["xki", "sgn"], "xml": ["xml", "sgn"], "xmm": ["xmm", "ms"], "xms": ["xms", "sgn"], "yds": ["yds", "sgn"], "ysl": ["ysl", "sgn"], "yue": ["yue", "zh"], "zib": ["zib", "sgn"], "zlm": ["zlm", "ms"], "zmi": ["zmi", "ms"], "zsl": ["zsl", "sgn"], "zsm": ["zsm", "ms"] }; /** * Canonicalizes the given well-formed BCP 47 language tag, including regularized case of subtags. * * Spec: ECMAScript Internationalization API Specification, draft, 6.2.3. * Spec: RFC 5646, section 4.5. */ function canonicalizeLanguageTag(locale) { // start with lower case for easier processing, and because most subtags will need to be lower case anyway locale = locale.toLowerCase(); // handle mappings for complete tags if (__tagMappings.hasOwnProperty(locale)) { return __tagMappings[locale]; } var subtags = locale.split("-"); var i = 0; // handle standard part: all subtags before first singleton or "x" while (i < subtags.length) { var subtag = subtags[i]; if (subtag.length === 1 && (i > 0 || subtag === "x")) { break; } else if (i !== 0 && subtag.length === 2) { subtag = subtag.toUpperCase(); } else if (subtag.length === 4) { subtag = subtag[0].toUpperCase() + subtag.substring(1).toLowerCase(); } if (__subtagMappings.hasOwnProperty(subtag)) { subtag = __subtagMappings[subtag]; } else if (__extlangMappings.hasOwnProperty(subtag)) { subtag = __extlangMappings[subtag][0]; if (i === 1 && __extlangMappings[subtag][1] === subtags[0]) { subtags.shift(); i--; } } subtags[i] = subtag; i++; } var normal = subtags.slice(0, i).join("-"); // handle extensions var extensions = []; while (i < subtags.length && subtags[i] !== "x") { var extensionStart = i; i++; while (i < subtags.length && subtags[i].length > 1) { i++; } var extension = subtags.slice(extensionStart, i).join("-"); extensions.push(extension); } extensions.sort(); // handle private use var privateUse; if (i < subtags.length) { privateUse = subtags.slice(i).join("-"); } // put everything back together var canonical = normal; if (extensions.length > 0) { canonical += "-" + extensions.join("-"); } if (privateUse !== undefined) { if (canonical.length > 0) { canonical += "-" + privateUse; } else { canonical = privateUse; } } return canonical; } return typeof locale === "string" && isStructurallyValidLanguageTag(locale) && canonicalizeLanguageTag(locale) === locale; } /** * Tests whether the named options property is correctly handled by the given constructor. * @param {object} Constructor the constructor to test. * @param {string} property the name of the options property to test. * @param {string} type the type that values of the property are expected to have * @param {Array} [values] an array of allowed values for the property. Not needed for boolean. * @param {any} fallback the fallback value that the property assumes if not provided. * @param {object} testOptions additional options: * @param {boolean} isOptional whether support for this property is optional for implementations. * @param {boolean} noReturn whether the resulting value of the property is not returned. * @param {boolean} isILD whether the resulting value of the property is implementation and locale dependent. * @param {object} extra additional option to pass along, properties are value -> {option: value}. * @return {boolean} whether the test succeeded. */ function testOption(Constructor, property, type, values, fallback, testOptions) { var isOptional = testOptions !== undefined && testOptions.isOptional === true; var noReturn = testOptions !== undefined && testOptions.noReturn === true; var isILD = testOptions !== undefined && testOptions.isILD === true; function addExtraOptions(options, value, testOptions) { if (testOptions !== undefined && testOptions.extra !== undefined) { var extra; if (value !== undefined && testOptions.extra[value] !== undefined) { extra = testOptions.extra[value]; } else if (testOptions.extra.any !== undefined) { extra = testOptions.extra.any; } if (extra !== undefined) { Object.getOwnPropertyNames(extra).forEach(function (prop) { options[prop] = extra[prop]; }); } } } var testValues, options, obj, expected, actual, error; // test that the specified values are accepted. Also add values that convert to specified values. if (type === "boolean") { if (values === undefined) { values = [true, false]; } testValues = values.slice(0); testValues.push(888); testValues.push(0); } else if (type === "string") { testValues = values.slice(0); testValues.push({toString: function () { return values[0]; }}); } testValues.forEach(function (value) { options = {}; options[property] = value; addExtraOptions(options, value, testOptions); obj = new Constructor(undefined, options); if (noReturn) { if (obj.resolvedOptions().hasOwnProperty(property)) { $ERROR("Option property " + property + " is returned, but shouldn't be."); } } else { actual = obj.resolvedOptions()[property]; if (isILD) { if (actual !== undefined && values.indexOf(actual) === -1) { $ERROR("Invalid value " + actual + " returned for property " + property + "."); } } else { if (type === "boolean") { expected = Boolean(value); } else if (type === "string") { expected = String(value); } if (actual !== expected && !(isOptional && actual === undefined)) { $ERROR("Option value " + value + " for property " + property + " was not accepted; got " + actual + " instead."); } } } }); // test that invalid values are rejected if (type === "string") { var invalidValues = ["invalidValue", -1, null]; // assume that we won't have values in caseless scripts if (values[0].toUpperCase() !== values[0]) { invalidValues.push(values[0].toUpperCase()); } else { invalidValues.push(values[0].toLowerCase()); } invalidValues.forEach(function (value) { options = {}; options[property] = value; addExtraOptions(options, value, testOptions); error = undefined; try { obj = new Constructor(undefined, options); } catch (e) { error = e; } if (error === undefined) { $ERROR("Invalid option value " + value + " for property " + property + " was not rejected."); } else if (error.name !== "RangeError") { $ERROR("Invalid option value " + value + " for property " + property + " was rejected with wrong error " + error.name + "."); } }); } // test that fallback value or another valid value is used if no options value is provided if (!noReturn) { options = {}; addExtraOptions(options, undefined, testOptions); obj = new Constructor(undefined, options); actual = obj.resolvedOptions()[property]; if (!(isOptional && actual === undefined)) { if (fallback !== undefined) { if (actual !== fallback) { $ERROR("Option fallback value " + fallback + " for property " + property + " was not used; got " + actual + " instead."); } } else { if (values.indexOf(actual) === -1 && !(isILD && actual === undefined)) { $ERROR("Invalid value " + actual + " returned for property " + property + "."); } } } } return true; } /** * Tests whether the named property of the given object has a valid value * and the default attributes of the properties of an object literal. * @param {Object} obj the object to be tested. * @param {string} property the name of the property * @param {Function|Array} valid either a function that tests value for validity and returns a boolean, * an array of valid values. * @exception if the property has an invalid value. */ function testProperty(obj, property, valid) { var desc = Object.getOwnPropertyDescriptor(obj, property); if (!desc.writable) { $ERROR("Property " + property + " must be writable."); } if (!desc.enumerable) { $ERROR("Property " + property + " must be enumerable."); } if (!desc.configurable) { $ERROR("Property " + property + " must be configurable."); } var value = desc.value; var isValid = (typeof valid === "function") ? valid(value) : (valid.indexOf(value) !== -1); if (!isValid) { $ERROR("Property value " + value + " is not allowed for property " + property + "."); } } /** * Tests whether the named property of the given object, if present at all, has a valid value * and the default attributes of the properties of an object literal. * @param {Object} obj the object to be tested. * @param {string} property the name of the property * @param {Function|Array} valid either a function that tests value for validity and returns a boolean, * an array of valid values. * @exception if the property is present and has an invalid value. */ function mayHaveProperty(obj, property, valid) { if (obj.hasOwnProperty(property)) { testProperty(obj, property, valid); } } /** * Tests whether the given object has the named property with a valid value * and the default attributes of the properties of an object literal. * @param {Object} obj the object to be tested. * @param {string} property the name of the property * @param {Function|Array} valid either a function that tests value for validity and returns a boolean, * an array of valid values. * @exception if the property is missing or has an invalid value. */ function mustHaveProperty(obj, property, valid) { if (!obj.hasOwnProperty(property)) { $ERROR("Object is missing property " + property + "."); } testProperty(obj, property, valid); } /** * Tests whether the given object does not have the named property. * @param {Object} obj the object to be tested. * @param {string} property the name of the property * @exception if the property is present. */ function mustNotHaveProperty(obj, property) { if (obj.hasOwnProperty(property)) { $ERROR("Object has property it mustn't have: " + property + "."); } } /** * Properties of the RegExp constructor that may be affected by use of regular * expressions, and the default values of these properties. Properties are from * https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Deprecated_and_obsolete_features#RegExp_Properties */ // CHANGE GW /* var regExpProperties = ["$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$_", "$*", "$&", "$+", "$`", "$'", "input", "lastMatch", "lastParen", "leftContext", "rightContext" ]; var regExpPropertiesDefaultValues = (function () { var values = Object.create(null); regExpProperties.forEach(function (property) { values[property] = RegExp[property]; }); return values; }()); */ /** * Tests that executing the provided function (which may use regular expressions * in its implementation) does not create or modify unwanted properties on the * RegExp constructor. */ /* function testForUnwantedRegExpChanges(testFunc) { regExpProperties.forEach(function (property) { RegExp[property] = regExpPropertiesDefaultValues[property]; }); testFunc(); regExpProperties.forEach(function (property) { if (RegExp[property] !== regExpPropertiesDefaultValues[property]) { $ERROR("RegExp has unexpected property " + property + " with value " + RegExp[property] + "."); } }); } */ /** * Tests whether name is a valid BCP 47 numbering system name * and not excluded from use in the ECMAScript Internationalization API. * @param {string} name the name to be tested. * @return {boolean} whether name is a valid BCP 47 numbering system name and * allowed for use in the ECMAScript Internationalization API. */ function isValidNumberingSystem(name) { // source: CLDR file common/bcp47/number.xml; version CLDR 21. var numberingSystems = [ "arab", "arabext", "armn", "armnlow", "bali", "beng", "brah", "cakm", "cham", "deva", "ethi", "finance", "fullwide", "geor", "grek", "greklow", "gujr", "guru", "hanidec", "hans", "hansfin", "hant", "hantfin", "hebr", "java", "jpan", "jpanfin", "kali", "khmr", "knda", "osma", "lana", "lanatham", "laoo", "latn", "lepc", "limb", "mlym", "mong", "mtei", "mymr", "mymrshan", "native", "nkoo", "olck", "orya", "roman", "romanlow", "saur", "shrd", "sora", "sund", "talu", "takr", "taml", "tamldec", "telu", "thai", "tibt", "traditio", "vaii" ]; var excluded = [ "finance", "native", "traditio" ]; return numberingSystems.indexOf(name) !== -1 && excluded.indexOf(name) === -1; } /** * Provides the digits of numbering systems with simple digit mappings, * as specified in 11.3.2. */ var numberingSystemDigits = { arab: "٠١٢٣٤٥٦٧٨٩", arabext: "۰۱۲۳۴۵۶۷۸۹", beng: "০১২৩৪৫৬৭৮৯", deva: "०१२३४५६७८९", fullwide: "0123456789", gujr: "૦૧૨૩૪૫૬૭૮૯", guru: "੦੧੨੩੪੫੬੭੮੯", hanidec: "〇一二三四五六七八九", khmr: "០១២៣៤៥៦៧៨៩", knda: "೦೧೨೩೪೫೬೭೮೯", laoo: "໐໑໒໓໔໕໖໗໘໙", latn: "0123456789", mlym: "൦൧൨൩൪൫൬൭൮൯", mong: "᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙", mymr: "၀၁၂၃၄၅၆၇၈၉", orya: "୦୧୨୩୪୫୬୭୮୯", tamldec: "௦௧௨௩௪௫௬௭௮௯", telu: "౦౧౨౩౪౫౬౭౮౯", thai: "๐๑๒๓๔๕๖๗๘๙", tibt: "༠༡༢༣༤༥༦༧༨༩" }; /** * Tests that number formatting is handled correctly. The function checks that the * digit sequences in formatted output are as specified, converted to the * selected numbering system, and embedded in consistent localized patterns. * @param {Array} locales the locales to be tested. * @param {Array} numberingSystems the numbering systems to be tested. * @param {Object} options the options to pass to Intl.NumberFormat. Options * must include {useGrouping: false}, and must cause 1.1 to be formatted * pre- and post-decimal digits. * @param {Object} testData maps input data (in ES5 9.3.1 format) to expected output strings * in unlocalized format with Western digits. */ function testNumberFormat(locales, numberingSystems, options, testData) { locales.forEach(function (locale) { numberingSystems.forEach(function (numbering) { var digits = numberingSystemDigits[numbering]; var format = new Intl.NumberFormat([locale + "-u-nu-" + numbering], options); function getPatternParts(positive) { var n = positive ? 1.1 : -1.1; var formatted = format.format(n); var oneoneRE = "([^" + digits + "]*)[" + digits + "]+([^" + digits + "]+)[" + digits + "]+([^" + digits + "]*)"; var match = formatted.match(new RegExp(oneoneRE)); if (match === null) { $ERROR("Unexpected formatted " + n + " for " + format.resolvedOptions().locale + " and options " + JSON.stringify(options) + ": " + formatted); } return match; } function toNumbering(raw) { return raw.replace(/[0-9]/g, function (digit) { return digits[digit.charCodeAt(0) - "0".charCodeAt(0)]; }); } function buildExpected(raw, patternParts) { var period = raw.indexOf("."); if (period === -1) { return patternParts[1] + toNumbering(raw) + patternParts[3]; } else { return patternParts[1] + toNumbering(raw.substring(0, period)) + patternParts[2] + toNumbering(raw.substring(period + 1)) + patternParts[3]; } } if (format.resolvedOptions().numberingSystem === numbering) { // figure out prefixes, infixes, suffixes for positive and negative values var posPatternParts = getPatternParts(true); var negPatternParts = getPatternParts(false); Object.getOwnPropertyNames(testData).forEach(function (input) { var rawExpected = testData[input]; var patternParts; if (rawExpected[0] === "-") { patternParts = negPatternParts; rawExpected = rawExpected.substring(1); } else { patternParts = posPatternParts; } var expected = buildExpected(rawExpected, patternParts); var actual = format.format(input); if (actual !== expected) { $ERROR("Formatted value for " + input + ", " + format.resolvedOptions().locale + " and options " + JSON.stringify(options) + " is " + actual + "; expected " + expected + "."); } }); } }); }); } /** * Return the components of date-time formats. * @return {Array} an array with all date-time components. */ function getDateTimeComponents() { return ["weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"]; } /** * Return the valid values for the given date-time component, as specified * by the table in section 12.1.1. * @param {string} component a date-time component. * @return {Array} an array with the valid values for the component. */ function getDateTimeComponentValues(component) { var components = { weekday: ["narrow", "short", "long"], era: ["narrow", "short", "long"], year: ["2-digit", "numeric"], month: ["2-digit", "numeric", "narrow", "short", "long"], day: ["2-digit", "numeric"], hour: ["2-digit", "numeric"], minute: ["2-digit", "numeric"], second: ["2-digit", "numeric"], timeZoneName: ["short", "long"] }; var result = components[component]; if (result === undefined) { $ERROR("Internal error: No values defined for date-time component " + component + "."); } return result; } /** * Tests that the given value is valid for the given date-time component. * @param {string} component a date-time component. * @param {string} value the value to be tested. * @return {boolean} true if the test succeeds. * @exception if the test fails. */ function testValidDateTimeComponentValue(component, value) { if (getDateTimeComponentValues(component).indexOf(value) === -1) { $ERROR("Invalid value " + value + " for date-time component " + component + "."); } return true; } /** * Verifies that the actual array matches the expected one in length, elements, * and element order. * @param {Array} expected the expected array. * @param {Array} actual the actual array. * @return {boolean} true if the test succeeds. * @exception if the test fails. */ function testArraysAreSame(expected, actual) { for (i = 0; i < Math.max(actual.length, expected.length); i++) { if (actual[i] !== expected[i]) { $ERROR("Result array element at index " + i + " should be \"" + expected[i] + "\" but is \"" + actual[i] + "\"."); } } return true; } function testcase() { /*MultiLine Comments \u2028 var = ; */ return true; } runTestCase(testcase); // CHANGE GW result = 1;