Some string optimizations:
authorvitalyr@chromium.org <vitalyr@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 19 Feb 2010 13:07:37 +0000 (13:07 +0000)
committervitalyr@chromium.org <vitalyr@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 19 Feb 2010 13:07:37 +0000 (13:07 +0000)
 * Inlined checks for strings and regexps.
 * Rewrote split for the non-regexp case.
 * Implemented one-char case for lastIndexOf.

Review URL: http://codereview.chromium.org/596122

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

13 files changed:
src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/codegen.cc
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/macros.py
src/mips/codegen-mips.cc
src/mips/codegen-mips.h
src/runtime.cc
src/runtime.js
src/string.js
src/x64/codegen-x64.cc
src/x64/codegen-x64.h

index 046d7b95b17d09f6f4248b81803f30a60d6ad29e..c8c56ff6ab4bf5b09fa764555c2cdba827adf4ff 100644 (file)
@@ -3420,6 +3420,25 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
+  VirtualFrame::SpilledScope spilled_scope;
+  ASSERT(args->length() == 1);
+  LoadAndSpill(args->at(0));
+  JumpTarget answer;
+  // We need the CC bits to come out as not_equal in the case where the
+  // object is a smi.  This can't be done with the usual test opcode so
+  // we use XOR to get the right CC bits.
+  frame_->EmitPop(r0);
+  __ and_(r1, r0, Operand(kSmiTagMask));
+  __ eor(r1, r1, Operand(kSmiTagMask), SetCC);
+  answer.Branch(ne);
+  // It is a heap object - get the map. Check if the object is a regexp.
+  __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
+  answer.Bind();
+  cc_reg_ = eq;
+}
+
+
 void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
   // This generates a fast version of:
   // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
index 10e28f4110fba57752db7ea65db2c746c15e83dd..de098d0234aeebd7ea5b843f1c7fc3cd2fa309c4 100644 (file)
@@ -359,6 +359,7 @@ class CodeGenerator: public AstVisitor {
   void GenerateIsSmi(ZoneList<Expression*>* args);
   void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
   void GenerateIsArray(ZoneList<Expression*>* args);
+  void GenerateIsRegExp(ZoneList<Expression*>* args);
   void GenerateIsObject(ZoneList<Expression*>* args);
   void GenerateIsFunction(ZoneList<Expression*>* args);
   void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
index 01ee6d07204528e730192fffbfff149bd16f5cc2..7f121688c1086f65e061b757543cf36bbad9f70f 100644 (file)
@@ -360,6 +360,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
   {&CodeGenerator::GenerateIsSmi, "_IsSmi"},
   {&CodeGenerator::GenerateIsNonNegativeSmi, "_IsNonNegativeSmi"},
   {&CodeGenerator::GenerateIsArray, "_IsArray"},
+  {&CodeGenerator::GenerateIsRegExp, "_IsRegExp"},
   {&CodeGenerator::GenerateIsConstructCall, "_IsConstructCall"},
   {&CodeGenerator::GenerateArgumentsLength, "_ArgumentsLength"},
   {&CodeGenerator::GenerateArgumentsAccess, "_Arguments"},
index 8f06f5d6f52d13bd1c0e781b79fd1a59e9e8d853..f0ad26defed06940678cfde33cfd09a2692390bb 100644 (file)
@@ -5422,6 +5422,25 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
+  ASSERT(args->length() == 1);
+  Load(args->at(0));
+  Result value = frame_->Pop();
+  value.ToRegister();
+  ASSERT(value.is_valid());
+  __ test(value.reg(), Immediate(kSmiTagMask));
+  destination()->false_target()->Branch(equal);
+  // It is a heap object - get map.
+  Result temp = allocator()->Allocate();
+  ASSERT(temp.is_valid());
+  // Check if the object is a regexp.
+  __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, temp.reg());
+  value.Unuse();
+  temp.Unuse();
+  destination()->Split(equal);
+}
+
+
 void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
   // This generates a fast version of:
   // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
@@ -6370,13 +6389,10 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
       __ movzx_b(temp.reg(), FieldOperand(temp.reg(), Map::kBitFieldOffset));
       __ test(temp.reg(), Immediate(1 << Map::kIsUndetectable));
       destination()->false_target()->Branch(not_zero);
-      __ mov(temp.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
-      __ movzx_b(temp.reg(),
-                 FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
-      __ cmp(temp.reg(), FIRST_NONSTRING_TYPE);
+      __ CmpObjectType(answer.reg(), FIRST_NONSTRING_TYPE, temp.reg());
       temp.Unuse();
       answer.Unuse();
-      destination()->Split(less);
+      destination()->Split(below);
 
     } else if (check->Equals(Heap::boolean_symbol())) {
       __ cmp(answer.reg(), Factory::true_value());
index a6cb3164b2a3b5e2b65d9bde0f80773c6a5ef626..561e1523efb530d407b15605eeea1de521405289 100644 (file)
@@ -551,6 +551,7 @@ class CodeGenerator: public AstVisitor {
   void GenerateIsSmi(ZoneList<Expression*>* args);
   void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
   void GenerateIsArray(ZoneList<Expression*>* args);
+  void GenerateIsRegExp(ZoneList<Expression*>* args);
   void GenerateIsObject(ZoneList<Expression*>* args);
   void GenerateIsFunction(ZoneList<Expression*>* args);
   void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
index c160b4916842a456631c0b4a1adb07ff2c53ff59..ccc2037f233b4caf3ec3e246916547b4d1fd3d86 100644 (file)
@@ -74,6 +74,10 @@ const kYearShift          = 9;
 const kMonthShift         = 5;
 
 # Type query macros.
+#
+# Note: We have special support for typeof(foo) === 'bar' in the compiler.
+#       It will *not* generate a runtime typeof call for the most important
+#       values of 'bar'.
 macro IS_NULL(arg)              = (arg === null);
 macro IS_NULL_OR_UNDEFINED(arg) = (arg == null);
 macro IS_UNDEFINED(arg)         = (typeof(arg) === 'undefined');
@@ -83,7 +87,7 @@ macro IS_BOOLEAN(arg)           = (typeof(arg) === 'boolean');
 macro IS_OBJECT(arg)            = (%_IsObject(arg));
 macro IS_ARRAY(arg)             = (%_IsArray(arg));
 macro IS_FUNCTION(arg)          = (%_IsFunction(arg));
-macro IS_REGEXP(arg)            = (%_ClassOf(arg) === 'RegExp');
+macro IS_REGEXP(arg)            = (%_IsRegExp(arg));
 macro IS_DATE(arg)              = (%_ClassOf(arg) === 'Date');
 macro IS_NUMBER_WRAPPER(arg)    = (%_ClassOf(arg) === 'Number');
 macro IS_STRING_WRAPPER(arg)    = (%_ClassOf(arg) === 'String');
@@ -97,9 +101,11 @@ macro FLOOR(arg)                = $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));
-macro TO_INTEGER(arg)    = (%_IsSmi(%IS_VAR(arg)) ? arg : ToInteger(arg));
-macro TO_INT32(arg)      = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0));
-macro TO_UINT32(arg)     = (arg >>> 0);
+macro TO_INTEGER(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : ToInteger(arg));
+macro TO_INT32(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0));
+macro TO_UINT32(arg) = (arg >>> 0);
+macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : NonStringToString(arg));
+
 
 # Macros implemented in Python.
 python macro CHAR_CODE(str) = ord(str[1]);
index 5a27c2864856a806dde44a67334c54ef83fe43a5..2de45f67f0ac4e7d623e1ad4975a6ded30325d8e 100644 (file)
@@ -305,6 +305,11 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
+  UNIMPLEMENTED_MIPS();
+}
+
+
 void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
   UNIMPLEMENTED_MIPS();
 }
@@ -365,6 +370,11 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
+  UNIMPLEMENTED_MIPS();
+}
+
+
 void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
   UNIMPLEMENTED_MIPS();
 }
@@ -498,4 +508,3 @@ int CompareStub::MinorKey() {
 #undef __
 
 } }  // namespace v8::internal
-
index 05138bc6401becce3eafdb0b8291a2ea6a6e09bc..147b8724eddec9af4767655267b7f0f3b644f0fb 100644 (file)
@@ -210,6 +210,7 @@ class CodeGenerator: public AstVisitor {
   void GenerateIsSmi(ZoneList<Expression*>* args);
   void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
   void GenerateIsArray(ZoneList<Expression*>* args);
+  void GenerateIsRegExp(ZoneList<Expression*>* args);
 
   // Support for construct call checks.
   void GenerateIsConstructCall(ZoneList<Expression*>* args);
@@ -241,6 +242,8 @@ class CodeGenerator: public AstVisitor {
   void GenerateSubString(ZoneList<Expression*>* args);
   void GenerateStringCompare(ZoneList<Expression*>* args);
   void GenerateRegExpExec(ZoneList<Expression*>* args);
+  void GenerateNumberToString(ZoneList<Expression*>* args);
+
 
   // Fast support for Math.sin and Math.cos.
   inline void GenerateMathSin(ZoneList<Expression*>* args);
@@ -308,4 +311,3 @@ class CodeGenerator: public AstVisitor {
 } }  // namespace v8::internal
 
 #endif  // V8_MIPS_CODEGEN_MIPS_H_
-
index 38e332b2474c777b826a0dcf8826cb6c588f0995..12c6b4e320857b0927d207c3f6b721b1cbbdb77f 100644 (file)
@@ -2276,6 +2276,20 @@ static int SingleCharIndexOf(Vector<const schar> string,
   return -1;
 }
 
+
+template <typename schar>
+static int SingleCharLastIndexOf(Vector<const schar> string,
+                                 schar pattern_char,
+                                 int start_index) {
+  for (int i = start_index; i >= 0; i--) {
+    if (pattern_char == string[i]) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+
 // Trivial string search for shorter strings.
 // On return, if "complete" is set to true, the return value is the
 // final result of searching for the patter in the subject.
@@ -2459,16 +2473,17 @@ static Object* Runtime_StringLastIndexOf(Arguments args) {
   NoHandleAllocation ha;
   ASSERT(args.length() == 3);
 
-  CONVERT_CHECKED(String, sub, args[0]);
-  CONVERT_CHECKED(String, pat, args[1]);
+  CONVERT_ARG_CHECKED(String, sub, 0);
+  CONVERT_ARG_CHECKED(String, pat, 1);
   Object* index = args[2];
 
-  sub->TryFlattenIfNotFlat();
-  pat->TryFlattenIfNotFlat();
-
   uint32_t start_index;
   if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
 
+  if (!sub->IsFlat()) {
+    FlattenString(sub);
+  }
+
   uint32_t pattern_length = pat->length();
   uint32_t sub_length = sub->length();
 
@@ -2476,6 +2491,25 @@ static Object* Runtime_StringLastIndexOf(Arguments args) {
     start_index = sub_length - pattern_length;
   }
 
+  if (pattern_length == 1) {
+    AssertNoAllocation no_heap_allocation;  // ensure vectors stay valid
+    if (sub->IsAsciiRepresentation()) {
+      uc16 pchar = pat->Get(0);
+      if (pchar > String::kMaxAsciiCharCode) {
+        return Smi::FromInt(-1);
+      }
+      return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
+                                                static_cast<char>(pat->Get(0)),
+                                                start_index));
+    } else {
+      return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
+                                                pat->Get(0),
+                                                start_index));
+    }
+  }
+
+  pat->TryFlattenIfNotFlat();
+
   for (int i = start_index; i >= 0; i--) {
     bool found = true;
     for (uint32_t j = 0; j < pattern_length; j++) {
index 231763cbcabf6c2d3e392b509d25376fcd2c62f3..e9d98487721a07a1abe004fbde6377b0710b5c64 100644 (file)
@@ -529,6 +529,13 @@ function ToString(x) {
   return (IS_NULL(x)) ? 'null' : %ToString(%DefaultString(x));
 }
 
+function NonStringToString(x) {
+  if (IS_NUMBER(x)) return %NumberToString(x);
+  if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
+  if (IS_UNDEFINED(x)) return 'undefined';
+  return (IS_NULL(x)) ? 'null' : %ToString(%DefaultString(x));
+}
+
 
 // ECMA-262, section 9.9, page 36.
 function ToObject(x) {
index ba01ed67e855172a45951160f28ba4669af47153..49f403de7da8140bb8aeebd9d87c00c329850a0d 100644 (file)
@@ -34,7 +34,7 @@
 
 // Set the String function and constructor.
 %SetCode($String, function(x) {
-  var value = %_ArgumentsLength() == 0 ? '' : ToString(x);
+  var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
   if (%_IsConstructCall()) {
     %_SetValueOf(this, value);
   } else {
@@ -64,7 +64,7 @@ function StringValueOf() {
 function StringCharAt(pos) {
   var char_code = %_FastCharCodeAt(this, pos);
   if (!%_IsSmi(char_code)) {
-    var subject = ToString(this);
+    var subject = TO_STRING_INLINE(this);
     var index = TO_INTEGER(pos);
     if (index >= subject.length || index < 0) return "";
     char_code = %StringCharCodeAt(subject, index);
@@ -79,7 +79,7 @@ function StringCharCodeAt(pos) {
   if (%_IsSmi(fast_answer)) {
     return fast_answer;
   }
-  var subject = ToString(this);
+  var subject = TO_STRING_INLINE(this);
   var index = TO_INTEGER(pos);
   return %StringCharCodeAt(subject, index);
 }
@@ -88,7 +88,7 @@ function StringCharCodeAt(pos) {
 // ECMA-262, section 15.5.4.6
 function StringConcat() {
   var len = %_ArgumentsLength();
-  var this_as_string = IS_STRING(this) ? this : ToString(this);
+  var this_as_string = TO_STRING_INLINE(this);
   if (len === 1) {
     return this_as_string + %_Arguments(0);
   }
@@ -96,7 +96,7 @@ function StringConcat() {
   parts[0] = this_as_string;
   for (var i = 0; i < len; i++) {
     var part = %_Arguments(i);
-    parts[i + 1] = IS_STRING(part) ? part : ToString(part);
+    parts[i + 1] = TO_STRING_INLINE(part);
   }
   return %StringBuilderConcat(parts, len + 1, "");
 }
@@ -107,8 +107,8 @@ function StringConcat() {
 
 // ECMA-262 section 15.5.4.7
 function StringIndexOf(searchString /* position */) {  // length == 1
-  var subject_str = ToString(this);
-  var pattern_str = ToString(searchString);
+  var subject_str = TO_STRING_INLINE(this);
+  var pattern_str = TO_STRING_INLINE(searchString);
   var subject_str_len = subject_str.length;
   var pattern_str_len = pattern_str.length;
   var index = 0;
@@ -125,9 +125,9 @@ function StringIndexOf(searchString /* position */) {  // length == 1
 
 // ECMA-262 section 15.5.4.8
 function StringLastIndexOf(searchString /* position */) {  // length == 1
-  var sub = ToString(this);
+  var sub = TO_STRING_INLINE(this);
   var subLength = sub.length;
-  var pat = ToString(searchString);
+  var pat = TO_STRING_INLINE(searchString);
   var patLength = pat.length;
   var index = subLength - patLength;
   if (%_ArgumentsLength() > 1) {
@@ -156,8 +156,8 @@ function StringLastIndexOf(searchString /* position */) {  // length == 1
 function StringLocaleCompare(other) {
   if (%_ArgumentsLength() === 0) return 0;
 
-  var this_str = ToString(this);
-  var other_str = ToString(other);
+  var this_str = TO_STRING_INLINE(this);
+  var other_str = TO_STRING_INLINE(other);
   return %StringLocaleCompare(this_str, other_str);
 }
 
@@ -165,7 +165,7 @@ function StringLocaleCompare(other) {
 // ECMA-262 section 15.5.4.10
 function StringMatch(regexp) {
   if (!IS_REGEXP(regexp)) regexp = new ORIGINAL_REGEXP(regexp);
-  var subject = ToString(this);
+  var subject = TO_STRING_INLINE(this);
 
   if (!regexp.global) return regexp.exec(subject);
   %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
@@ -200,7 +200,7 @@ var reusableMatchInfo = [2, "", "", -1, -1];
 
 // ECMA-262, section 15.5.4.11
 function StringReplace(search, replace) {
-  var subject = IS_STRING(this) ? this : ToString(this);
+  var subject = TO_STRING_INLINE(this);
 
   // Delegate to one of the regular expression variants if necessary.
   if (IS_REGEXP(search)) {
@@ -213,7 +213,7 @@ function StringReplace(search, replace) {
   }
 
   // Convert the search argument to a string and search for it.
-  search = IS_STRING(search) ? search : ToString(search);
+  search = TO_STRING_INLINE(search);
   var start = %StringIndexOf(subject, search, 0);
   if (start < 0) return subject;
   var end = start + search.length;
@@ -228,7 +228,7 @@ function StringReplace(search, replace) {
   } else {
     reusableMatchInfo[CAPTURE0] = start;
     reusableMatchInfo[CAPTURE1] = end;
-    if (!IS_STRING(replace)) replace = ToString(replace);
+    replace = TO_STRING_INLINE(replace);
     ExpandReplacement(replace, subject, reusableMatchInfo, builder);
   }
 
@@ -241,7 +241,7 @@ function StringReplace(search, replace) {
 
 // Helper function for regular expressions in String.prototype.replace.
 function StringReplaceRegExp(subject, regexp, replace) {
-  replace = ToString(replace);
+  replace = TO_STRING_INLINE(replace);
   return %StringReplaceRegExpWithString(subject,
                                         regexp,
                                         replace,
@@ -462,7 +462,7 @@ function ApplyReplacementFunction(replace, matchInfo, subject) {
 // ECMA-262 section 15.5.4.12
 function StringSearch(re) {
   var regexp = new ORIGINAL_REGEXP(re);
-  var s = ToString(this);
+  var s = TO_STRING_INLINE(this);
   var last_idx = regexp.lastIndex; // keep old lastIndex
   regexp.lastIndex = 0;            // ignore re.global property
   var result = regexp.exec(s);
@@ -476,7 +476,7 @@ function StringSearch(re) {
 
 // ECMA-262 section 15.5.4.13
 function StringSlice(start, end) {
-  var s = ToString(this);
+  var s = TO_STRING_INLINE(this);
   var s_len = s.length;
   var start_i = TO_INTEGER(start);
   var end_i = s_len;
@@ -511,7 +511,7 @@ function StringSlice(start, end) {
 
 // ECMA-262 section 15.5.4.14
 function StringSplit(separator, limit) {
-  var subject = ToString(this);
+  var subject = TO_STRING_INLINE(this);
   limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
   if (limit === 0) return [];
 
@@ -525,18 +525,35 @@ function StringSplit(separator, limit) {
   }
 
   var length = subject.length;
-  if (IS_REGEXP(separator)) {
-    %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
-  } else {
-    separator = ToString(separator);
+  if (!IS_REGEXP(separator)) {
+    separator = TO_STRING_INLINE(separator);
+    var separator_length = separator.length;
+
     // If the separator string is empty then return the elements in the subject.
-    if (separator.length == 0) {
+    if (separator_length === 0) {
       var result = $Array(length);
       for (var i = 0; i < length; i++) result[i] = subject[i];
       return result;
     }
+
+    var result = [];
+    var start_index = 0;
+    var index;
+    while (true) {
+      if (start_index + separator_length > length ||
+          (index = %StringIndexOf(subject, separator, start_index)) === -1) {
+        result.push(SubString(subject, start_index, length));
+        break;
+      }
+      if (result.push(SubString(subject, start_index, index)) === limit) break;
+      start_index = index + separator_length;
+    }
+
+    return result;
   }
 
+  %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
+
   if (length === 0) {
     if (splitMatch(separator, subject, 0, 0) != null) return [];
     return [subject];
@@ -571,7 +588,8 @@ function StringSplit(separator, limit) {
     result[result.length] = SubString(subject, currentIndex, matchInfo[CAPTURE0]);
     if (result.length === limit) return result;
 
-    for (var i = 2; i < NUMBER_OF_CAPTURES(matchInfo); i += 2) {
+    var num_captures = NUMBER_OF_CAPTURES(matchInfo);
+    for (var i = 2; i < num_captures; i += 2) {
       var start = matchInfo[CAPTURE(i)];
       var end = matchInfo[CAPTURE(i + 1)];
       if (start != -1 && end != -1) {
@@ -591,28 +609,18 @@ function StringSplit(separator, limit) {
 // Helper function used by split.  This version returns the matchInfo
 // instead of allocating a new array with basically the same information.
 function splitMatch(separator, subject, current_index, start_index) {
-  if (IS_REGEXP(separator)) {
-    var matchInfo = DoRegExpExec(separator, subject, start_index);
-    if (matchInfo == null) return null;
-    // Section 15.5.4.14 paragraph two says that we do not allow zero length
-    // matches at the end of the string.
-    if (matchInfo[CAPTURE0] === subject.length) return null;
-    return matchInfo;
-  }
-
-  var separatorIndex = subject.indexOf(separator, start_index);
-  if (separatorIndex === -1) return null;
-
-  reusableMatchInfo[CAPTURE0] = separatorIndex;
-  reusableMatchInfo[CAPTURE1] = separatorIndex + separator.length;
-  return reusableMatchInfo;
-};
+  var matchInfo = DoRegExpExec(separator, subject, start_index);
+  if (matchInfo == null) return null;
+  // Section 15.5.4.14 paragraph two says that we do not allow zero length
+  // matches at the end of the string.
+  if (matchInfo[CAPTURE0] === subject.length) return null;
+  return matchInfo;
+}
 
 
 // ECMA-262 section 15.5.4.15
 function StringSubstring(start, end) {
-  var s = this;
-  if (!IS_STRING(s)) s = ToString(s);
+  var s = TO_STRING_INLINE(this);
   var s_len = s.length;
 
   var start_i = TO_INTEGER(start);
@@ -643,7 +651,7 @@ function StringSubstring(start, end) {
 
 // This is not a part of ECMA-262.
 function StringSubstr(start, n) {
-  var s = ToString(this);
+  var s = TO_STRING_INLINE(this);
   var len;
 
   // Correct n: If not given, set to string length; if explicitly
@@ -681,38 +689,38 @@ function StringSubstr(start, n) {
 
 // ECMA-262, 15.5.4.16
 function StringToLowerCase() {
-  return %StringToLowerCase(ToString(this));
+  return %StringToLowerCase(TO_STRING_INLINE(this));
 }
 
 
 // ECMA-262, 15.5.4.17
 function StringToLocaleLowerCase() {
-  return %StringToLowerCase(ToString(this));
+  return %StringToLowerCase(TO_STRING_INLINE(this));
 }
 
 
 // ECMA-262, 15.5.4.18
 function StringToUpperCase() {
-  return %StringToUpperCase(ToString(this));
+  return %StringToUpperCase(TO_STRING_INLINE(this));
 }
 
 
 // ECMA-262, 15.5.4.19
 function StringToLocaleUpperCase() {
-  return %StringToUpperCase(ToString(this));
+  return %StringToUpperCase(TO_STRING_INLINE(this));
 }
 
 // ES5, 15.5.4.20
 function StringTrim() {
-  return %StringTrim(ToString(this), true, true);
+  return %StringTrim(TO_STRING_INLINE(this), true, true);
 }
 
 function StringTrimLeft() {
-  return %StringTrim(ToString(this), true, false);
+  return %StringTrim(TO_STRING_INLINE(this), true, false);
 }
 
 function StringTrimRight() {
-  return %StringTrim(ToString(this), false, true);
+  return %StringTrim(TO_STRING_INLINE(this), false, true);
 }
 
 // ECMA-262, section 15.5.3.2
@@ -731,10 +739,10 @@ function StringFromCharCode(code) {
 
 // Helper function for very basic XSS protection.
 function HtmlEscape(str) {
-  return ToString(str).replace(/</g, "&lt;")
-                      .replace(/>/g, "&gt;")
-                      .replace(/"/g, "&quot;")
-                      .replace(/'/g, "&#039;");
+  return TO_STRING_INLINE(str).replace(/</g, "&lt;")
+                              .replace(/>/g, "&gt;")
+                              .replace(/"/g, "&quot;")
+                              .replace(/'/g, "&#039;");
 };
 
 
@@ -813,7 +821,7 @@ function ReplaceResultBuilder(str) {
 
 
 ReplaceResultBuilder.prototype.add = function(str) {
-  if (!IS_STRING(str)) str = ToString(str);
+  str = TO_STRING_INLINE(str);
   if (str.length > 0) {
     var elements = this.elements;
     elements[elements.length] = str;
index 6c063f3e77bc530965c7c0ad06f7500414fcda54..fa2cb8dfa00a202146bd895490429e43f9a8f2fa 100644 (file)
@@ -3627,6 +3627,22 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
+  ASSERT(args->length() == 1);
+  Load(args->at(0));
+  Result value = frame_->Pop();
+  value.ToRegister();
+  ASSERT(value.is_valid());
+  Condition is_smi = masm_->CheckSmi(value.reg());
+  destination()->false_target()->Branch(is_smi);
+  // It is a heap object - get map.
+  // Check if the object is a regexp.
+  __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, kScratchRegister);
+  value.Unuse();
+  destination()->Split(equal);
+}
+
+
 void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
   // This generates a fast version of:
   // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
index 345431241c273bdb53df3d72ef42db70444b40a1..b9a2c9994bea4b08fca4953dd49fca8d012bd764 100644 (file)
@@ -536,6 +536,7 @@ class CodeGenerator: public AstVisitor {
   void GenerateIsSmi(ZoneList<Expression*>* args);
   void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
   void GenerateIsArray(ZoneList<Expression*>* args);
+  void GenerateIsRegExp(ZoneList<Expression*>* args);
   void GenerateIsObject(ZoneList<Expression*>* args);
   void GenerateIsFunction(ZoneList<Expression*>* args);
   void GenerateIsUndetectableObject(ZoneList<Expression*>* args);