From 5058924d85b264c77be1a803f854edee79cf1d24 Mon Sep 17 00:00:00 2001 From: "bak@chromium.org" Date: Tue, 7 Oct 2008 10:54:50 +0000 Subject: [PATCH] - Added %IsArrayClass, %IsDateClass, and %IsStringClass. - Added the FLOOR macro that only works on Number objects. - Added LocalTimeNoCheck in the date code to eliminate some isNaN checks. - Change computation of four_year_cycle_table to load time. - Added fast case check to EQUALS and STRICT_EQUALS. Review URL: http://codereview.chromium.org/6531 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@458 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/date-delay.js | 77 ++++++++++++++++++++++++----------------------- src/heap.h | 2 ++ src/macros.py | 5 +-- src/runtime.cc | 30 +++++++++++++++++- src/runtime.h | 3 ++ src/runtime.js | 25 +++++++-------- src/string.js | 4 +-- 7 files changed, 91 insertions(+), 55 deletions(-) diff --git a/src/date-delay.js b/src/date-delay.js index 9a52ec4891..da7aebd5e9 100644 --- a/src/date-delay.js +++ b/src/date-delay.js @@ -43,7 +43,7 @@ const $Date = global.Date; // ECMA 262 - 15.9.1.2 function Day(time) { - return $floor(time/msPerDay); + return FLOOR(time/msPerDay); } @@ -69,9 +69,9 @@ function DaysInYear(year) { function DayFromYear(year) { return 365 * (year-1970) - + $floor((year-1969)/4) - - $floor((year-1901)/100) - + $floor((year-1601)/400); + + FLOOR((year-1969)/4) + - FLOOR((year-1901)/100) + + FLOOR((year-1601)/400); } @@ -170,6 +170,10 @@ function LocalTime(time) { return time + local_time_offset + DaylightSavingsOffset(time); } +function LocalTimeNoCheck(time) { + return time + local_time_offset + DaylightSavingsOffset(time); +} + function UTC(time) { if ($isNaN(time)) return time; @@ -180,17 +184,17 @@ function UTC(time) { // ECMA 262 - 15.9.1.10 function HourFromTime(time) { - return Modulo($floor(time / msPerHour), HoursPerDay); + return Modulo(FLOOR(time / msPerHour), HoursPerDay); } function MinFromTime(time) { - return Modulo($floor(time / msPerMinute), MinutesPerHour); + return Modulo(FLOOR(time / msPerMinute), MinutesPerHour); } function SecFromTime(time) { - return Modulo($floor(time / msPerSecond), SecondsPerMinute); + return Modulo(FLOOR(time / msPerSecond), SecondsPerMinute); } @@ -223,12 +227,11 @@ function TimeInYear(year) { function ToJulianDay(year, month, date) { var jy = (month > 1) ? year : year - 1; var jm = (month > 1) ? month + 2 : month + 14; - var ja = $floor(0.01*jy); - return $floor($floor(365.25*jy) + $floor(30.6001*jm) + date + 1720995) + 2 - ja + $floor(0.25*ja); + var ja = FLOOR(0.01*jy); + return FLOOR(FLOOR(365.25*jy) + FLOOR(30.6001*jm) + date + 1720995) + 2 - ja + FLOOR(0.25*ja); } - -var four_year_cycle_table; +var four_year_cycle_table = CalculateDateTable(); function CalculateDateTable() { @@ -261,7 +264,6 @@ function CalculateDateTable() { } - // Constructor for creating objects holding year, month, and date. // Introduced to ensure the two return points in FromJulianDay match same map. function DayTriplet(year, month, date) { @@ -279,8 +281,6 @@ function FromJulianDay(julian) { // when doing the multiply-to-divide trick. if (julian > kDayZeroInJulianDay && (julian - kDayZeroInJulianDay) < 40177) { // 1970 - 2080 - if (!four_year_cycle_table) - four_year_cycle_table = CalculateDateTable(); var jsimple = (julian - kDayZeroInJulianDay) + 731; // Day 0 is 1st January 1968 var y = 1968; // Divide by 1461 by multiplying with 22967 and shifting down by 25! @@ -292,19 +292,20 @@ function FromJulianDay(julian) { (four_year_cycle & kMonthMask) >> kMonthShift, four_year_cycle & kDayMask); } - var jalpha = $floor((julian - 1867216.25) / 36524.25); - var jb = julian + 1 + jalpha - $floor(0.25 * jalpha) + 1524; - var jc = $floor(6680.0 + ((jb-2439870) - 122.1)/365.25); - var jd = $floor(365 * jc + (0.25 * jc)); - var je = $floor((jb - jd)/30.6001); + var jalpha = FLOOR((julian - 1867216.25) / 36524.25); + var jb = julian + 1 + jalpha - FLOOR(0.25 * jalpha) + 1524; + var jc = FLOOR(6680.0 + ((jb-2439870) - 122.1)/365.25); + var jd = FLOOR(365 * jc + (0.25 * jc)); + var je = FLOOR((jb - jd)/30.6001); var m = je - 1; if (m > 12) m -= 13; var y = jc - 4715; if (m > 2) { --y; --m; } - var d = jb - jd - $floor(30.6001 * je); + var d = jb - jd - FLOOR(30.6001 * je); return new DayTriplet(y, m, d); } + // Compute number of days given a year, month, date. // Note that month and date can lie outside the normal range. // For example: @@ -320,7 +321,7 @@ function MakeDay(year, month, date) { date = TO_INTEGER(date); // Overflow months into year. - year = year + $floor(month/12); + year = year + FLOOR(month/12); month = month % 12; if (month < 0) { month += 12; @@ -400,7 +401,7 @@ function GetTimeFrom(aDate) { function GetMillisecondsFrom(aDate) { var t = GetTimeFrom(aDate); if ($isNaN(t)) return t; - return msFromTime(LocalTime(t)); + return msFromTime(LocalTimeNoCheck(t)); } @@ -414,7 +415,7 @@ function GetUTCMillisecondsFrom(aDate) { function GetSecondsFrom(aDate) { var t = GetTimeFrom(aDate); if ($isNaN(t)) return t; - return SecFromTime(LocalTime(t)); + return SecFromTime(LocalTimeNoCheck(t)); } @@ -428,7 +429,7 @@ function GetUTCSecondsFrom(aDate) { function GetMinutesFrom(aDate) { var t = GetTimeFrom(aDate); if ($isNaN(t)) return t; - return MinFromTime(LocalTime(t)); + return MinFromTime(LocalTimeNoCheck(t)); } @@ -442,7 +443,7 @@ function GetUTCMinutesFrom(aDate) { function GetHoursFrom(aDate) { var t = GetTimeFrom(aDate); if ($isNaN(t)) return t; - return HourFromTime(LocalTime(t)); + return HourFromTime(LocalTimeNoCheck(t)); } @@ -456,7 +457,7 @@ function GetUTCHoursFrom(aDate) { function GetFullYearFrom(aDate) { var t = GetTimeFrom(aDate); if ($isNaN(t)) return t; - return YearFromTime(LocalTime(t)); + return YearFromTime(LocalTimeNoCheck(t)); } @@ -470,7 +471,7 @@ function GetUTCFullYearFrom(aDate) { function GetMonthFrom(aDate) { var t = GetTimeFrom(aDate); if ($isNaN(t)) return t; - return MonthFromTime(LocalTime(t)); + return MonthFromTime(LocalTimeNoCheck(t)); } @@ -484,7 +485,7 @@ function GetUTCMonthFrom(aDate) { function GetDateFrom(aDate) { var t = GetTimeFrom(aDate); if ($isNaN(t)) return t; - return DateFromTime(LocalTime(t)); + return DateFromTime(LocalTimeNoCheck(t)); } @@ -526,8 +527,8 @@ function TimeString(time) { function LocalTimezoneString(time) { var timezoneOffset = (local_time_offset + DaylightSavingsOffset(time)) / msPerMinute; var sign = (timezoneOffset >= 0) ? 1 : -1; - var hours = $floor((sign * timezoneOffset)/60); - var min = $floor((sign * timezoneOffset)%60); + var hours = FLOOR((sign * timezoneOffset)/60); + var min = FLOOR((sign * timezoneOffset)%60); var gmt = ' GMT' + ((sign == 1) ? '+' : '-') + TwoDigitString(hours) + TwoDigitString(min); return gmt + ' (' + LocalTimezone(time) + ')'; } @@ -586,7 +587,7 @@ function DateNow() { function DateToString() { var t = GetTimeFrom(this); if ($isNaN(t)) return kInvalidDate; - return DatePrintString(LocalTime(t)) + LocalTimezoneString(t); + return DatePrintString(LocalTimeNoCheck(t)) + LocalTimezoneString(t); } @@ -594,7 +595,7 @@ function DateToString() { function DateToDateString() { var t = GetTimeFrom(this); if ($isNaN(t)) return kInvalidDate; - return DateString(LocalTime(t)); + return DateString(LocalTimeNoCheck(t)); } @@ -602,7 +603,7 @@ function DateToDateString() { function DateToTimeString() { var t = GetTimeFrom(this); if ($isNaN(t)) return kInvalidDate; - var lt = LocalTime(t); + var lt = LocalTimeNoCheck(t); return TimeString(lt) + LocalTimezoneString(lt); } @@ -623,7 +624,7 @@ function DateToLocaleDateString() { function DateToLocaleTimeString() { var t = GetTimeFrom(this); if ($isNaN(t)) return kInvalidDate; - var lt = LocalTime(t); + var lt = LocalTimeNoCheck(t); return TimeString(lt); } @@ -680,7 +681,7 @@ function DateGetUTCDate() { function DateGetDay() { var t = GetTimeFrom(this); if ($isNaN(t)) return t; - return WeekDay(LocalTime(t)); + return WeekDay(LocalTimeNoCheck(t)); } @@ -744,7 +745,7 @@ function DateGetUTCMilliseconds() { function DateGetTimezoneOffset() { var t = GetTimeFrom(this); if ($isNaN(t)) return t; - return (t - LocalTime(t)) / msPerMinute; + return (t - LocalTimeNoCheck(t)) / msPerMinute; } @@ -884,7 +885,7 @@ function DateSetUTCMonth(month, date) { // ECMA 262 - 15.9.5.40 function DateSetFullYear(year, month, date) { var t = GetTimeFrom(this); - t = $isNaN(t) ? 0 : LocalTime(t); + t = $isNaN(t) ? 0 : LocalTimeNoCheck(t); year = ToNumber(year); var argc = %_ArgumentsLength(); month = argc < 2 ? MonthFromTime(t) : ToNumber(month); @@ -924,7 +925,7 @@ function DateToUTCString() { function DateGetYear() { var t = GetTimeFrom(this); if ($isNaN(t)) return $NaN; - return YearFromTime(LocalTime(t)) - 1900; + return YearFromTime(LocalTimeNoCheck(t)) - 1900; } diff --git a/src/heap.h b/src/heap.h index fb7c3be8e9..4da9a98ed1 100644 --- a/src/heap.h +++ b/src/heap.h @@ -154,6 +154,8 @@ namespace v8 { namespace internal { V(object_symbol, "object") \ V(prototype_symbol, "prototype") \ V(string_symbol, "string") \ + V(String_symbol, "String") \ + V(Date_symbol, "Date") \ V(this_symbol, "this") \ V(to_string_symbol, "toString") \ V(char_at_symbol, "CharAt") \ diff --git a/src/macros.py b/src/macros.py index dbdaa340b2..48ca367666 100644 --- a/src/macros.py +++ b/src/macros.py @@ -79,10 +79,11 @@ macro IS_STRING(arg) = (typeof(arg) === 'string'); macro IS_OBJECT(arg) = (typeof(arg) === 'object'); macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean'); macro IS_REGEXP(arg) = (%ClassOf(arg) === 'RegExp'); -macro IS_ARRAY(arg) = (%ClassOf(arg) === 'Array'); -macro IS_DATE(arg) = (%ClassOf(arg) === 'Date'); +macro IS_ARRAY(arg) = %IsArrayClass(arg); +macro IS_DATE(arg) = %IsDateClass(arg); macro IS_ERROR(arg) = (%ClassOf(arg) === 'Error'); macro IS_SCRIPT(arg) = (%ClassOf(arg) === 'Script'); +macro FLOOR(arg) = %Math_floor(arg); # Inline macros. Use %IS_VAR to make sure arg is evaluated only once. macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg)); diff --git a/src/runtime.cc b/src/runtime.cc index 1b9233f091..2b0b2cd0c4 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -221,6 +221,30 @@ static Object* Runtime_ClassOf(Arguments args) { return JSObject::cast(obj)->class_name(); } +inline static Object* IsSpecificClassOf(Arguments args, String* name) { + NoHandleAllocation ha; + ASSERT(args.length() == 1); + Object* obj = args[0]; + if (obj->IsJSObject() && (JSObject::cast(obj)->class_name() == name)) { + return Heap::true_value(); + } + return Heap::false_value(); +} + +static Object* Runtime_IsStringClass(Arguments args) { + return IsSpecificClassOf(args, Heap::String_symbol()); +} + + +static Object* Runtime_IsDateClass(Arguments args) { + return IsSpecificClassOf(args, Heap::Date_symbol()); +} + + +static Object* Runtime_IsArrayClass(Arguments args) { + return IsSpecificClassOf(args, Heap::Array_symbol()); +} + static Object* Runtime_IsInPrototypeChain(Arguments args) { NoHandleAllocation ha; @@ -2508,8 +2532,12 @@ static Object* Runtime_StringEquals(Arguments args) { int len = x->length(); if (len != y->length()) return Smi::FromInt(NOT_EQUAL); if (len == 0) return Smi::FromInt(EQUAL); - // Fast case: First, middle and last characters. + + // Handle one elment strings. if (x->Get(0) != y->Get(0)) return Smi::FromInt(NOT_EQUAL); + if (len == 1) return Smi::FromInt(EQUAL); + + // Fast case: First, middle and last characters. if (x->Get(len>>1) != y->Get(len>>1)) return Smi::FromInt(NOT_EQUAL); if (x->Get(len - 1) != y->Get(len - 1)) return Smi::FromInt(NOT_EQUAL); diff --git a/src/runtime.h b/src/runtime.h index 23b4b1709f..47536aecd2 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -164,6 +164,9 @@ namespace v8 { namespace internal { F(GetScript, 1) \ \ F(ClassOf, 1) \ + F(IsDateClass, 1) \ + F(IsStringClass, 1) \ + F(IsArrayClass, 1) \ F(SetCode, 2) \ \ F(CreateApiFunction, 1) \ diff --git a/src/runtime.js b/src/runtime.js index 471019ae18..77a9562e4c 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -52,36 +52,31 @@ const $NaN = 0/0; // ECMA-262, section 11.9.1, page 55. function EQUALS(y) { + if (IS_STRING(this) && IS_STRING(y)) return %StringEquals(this, y); var x = this; // NOTE: We use iteration instead of recursion, because it is // difficult to call EQUALS with the correct setting of 'this' in // an efficient way. - while (true) { - if (IS_NUMBER(x)) { if (y == null) return 1; // not equal return %NumberEquals(x, %ToNumber(y)); - } else if (IS_STRING(x)) { if (IS_STRING(y)) return %StringEquals(x, y); if (IS_NUMBER(y)) return %NumberEquals(%ToNumber(x), y); if (IS_BOOLEAN(y)) return %NumberEquals(%ToNumber(x), %ToNumber(y)); if (y == null) return 1; // not equal y = %ToPrimitive(y, NO_HINT); - } else if (IS_BOOLEAN(x)) { if (IS_BOOLEAN(y)) { return %_ObjectEquals(x, y) ? 0 : 1; } if (y == null) return 1; // not equal return %NumberEquals(%ToNumber(x), %ToNumber(y)); - } else if (x == null) { // NOTE: This checks for both null and undefined. return (y == null) ? 0 : 1; - } else { if (IS_OBJECT(y)) { return %_ObjectEquals(x, y) ? 0 : 1; @@ -90,12 +85,10 @@ function EQUALS(y) { return %_ObjectEquals(x, y) ? 0 : 1; } x = %ToPrimitive(x, NO_HINT); - } } } - // ECMA-262, section 11.9.4, page 56. function STRICT_EQUALS(x) { if (IS_NUMBER(this)) { @@ -125,11 +118,15 @@ function STRICT_EQUALS(x) { // ECMA-262, section 11.8.5, page 53. The 'ncr' parameter is used as // the result when either (or both) the operands are NaN. function COMPARE(x, ncr) { - // Improve performance for floating point compares + // Fast case for numbers and strings. if (IS_NUMBER(this) && IS_NUMBER(x)) { return %NumberCompare(this, x, ncr); } + if (IS_STRING(this) && IS_STRING(x)) { + return %StringCompare(this, x); + } + // Default implementation. var a = %ToPrimitive(this, NUMBER_HINT); var b = %ToPrimitive(x, NUMBER_HINT); if (IS_STRING(a) && IS_STRING(b)) { @@ -149,10 +146,10 @@ function COMPARE(x, ncr) { // ECMA-262, section 11.6.1, page 50. function ADD(x) { // Fast case: Check for number operands and do the addition. - if (IS_NUMBER(this) && IS_NUMBER(x)) { - return %NumberAdd(this, x); - } + if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x); + if (IS_STRING(this) && IS_STRING(x)) return %StringAdd(this, x); + // Default implementation. var a = %ToPrimitive(this, NO_HINT); var b = %ToPrimitive(x, NO_HINT); @@ -397,6 +394,10 @@ function TO_STRING() { // ECMA-262, section 9.1, page 30. Use null/undefined for no hint, // (1) for number hint, and (2) for string hint. function ToPrimitive(x, hint) { + // Fast case check. + if (IS_STRING(x)) return x; + if ((hint != NUMBER_HINT) && %IsStringClass(x)) return %_ValueOf(x); + // Normal behaior. if (!IS_OBJECT(x) && !IS_FUNCTION(x)) return x; if (x == null) return x; // check for null, undefined if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT; diff --git a/src/string.js b/src/string.js index bb12c01c9c..26318a3bdc 100644 --- a/src/string.js +++ b/src/string.js @@ -46,7 +46,7 @@ // ECMA-262 section 15.5.4.2 function StringToString() { - if (!IS_STRING(this) && %ClassOf(this) !== 'String') + if (!IS_STRING(this) && !%IsStringClass(this)) throw new $TypeError('String.prototype.toString is not generic'); return %_ValueOf(this); } @@ -54,7 +54,7 @@ function StringToString() { // ECMA-262 section 15.5.4.3 function StringValueOf() { - if (!IS_STRING(this) && %ClassOf(this) !== 'String') + if (!IS_STRING(this) && !%IsStringClass(this)) throw new $TypeError('String.prototype.valueOf is not generic'); return %_ValueOf(this); }