Native construction of RegExp result objects, with in-object index and input.
authorlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 13 Apr 2010 09:31:03 +0000 (09:31 +0000)
committerlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 13 Apr 2010 09:31:03 +0000 (09:31 +0000)
Avoid cloning using CloneRegExpResult for results that are just arrays.
Made a more direct path for string.match with string argument.

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

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

16 files changed:
src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/bootstrapper.cc
src/codegen.h
src/contexts.h
src/heap.h
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/objects.h
src/regexp.js
src/runtime.cc
src/runtime.h
src/string.js
src/x64/codegen-x64.cc
src/x64/codegen-x64.h
test/mjsunit/fuzz-natives.js

index 92dcdd1437b5208fa570090ccb9f03c0e7f77dcc..0fc7b6d1d67164c576cc5423d4be272f684f05c7 100644 (file)
@@ -4021,6 +4021,100 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
+  // No stub. This code only occurs a few times in regexp.js.
+  const int kMaxInlineLength = 100;
+  ASSERT_EQ(3, args->length());
+  Load(args->at(0));  // Size of array, smi.
+  Load(args->at(1));  // "index" property value.
+  Load(args->at(2));  // "input" property value.
+  {
+    VirtualFrame::SpilledScope spilled_scope(frame_);
+    Label slowcase;
+    Label done;
+    __ ldr(r1, MemOperand(sp, kPointerSize * 2));
+    STATIC_ASSERT(kSmiTag == 0);
+    STATIC_ASSERT(kSmiTagSize == 1);
+    __ tst(r1, Operand(kSmiTagMask));
+    __ b(ne, &slowcase);
+    __ cmp(r1, Operand(Smi::FromInt(kMaxInlineLength)));
+    __ b(hi, &slowcase);
+    // Smi-tagging is equivalent to multiplying by 2.
+    // Allocate RegExpResult followed by FixedArray with size in ebx.
+    // JSArray:   [Map][empty properties][Elements][Length-smi][index][input]
+    // Elements:  [Map][Length][..elements..]
+    // Size of JSArray with two in-object properties and the header of a
+    // FixedArray.
+    int objects_size =
+        (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
+    __ mov(r5, Operand(r1, LSR, kSmiTagSize + kSmiShiftSize));
+    __ add(r2, r5, Operand(objects_size));
+    __ AllocateInNewSpace(r2,  // In: Size, in words.
+                          r0,  // Out: Start of allocation (tagged).
+                          r3,  // Scratch register.
+                          r4,  // Scratch register.
+                          &slowcase,
+                          TAG_OBJECT);
+    // r0: Start of allocated area, object-tagged.
+    // r1: Number of elements in array, as smi.
+    // r5: Number of elements, untagged.
+
+    // Set JSArray map to global.regexp_result_map().
+    // Set empty properties FixedArray.
+    // Set elements to point to FixedArray allocated right after the JSArray.
+    // Interleave operations for better latency.
+    __ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX));
+    __ add(r3, r0, Operand(JSRegExpResult::kSize));
+    __ mov(r4, Operand(Factory::empty_fixed_array()));
+    __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
+    __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
+    __ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
+    __ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
+    __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
+
+    // Set input, index and length fields from arguments.
+    __ ldm(ia_w, sp, static_cast<RegList>(r2.bit() | r4.bit()));
+    __ str(r1, FieldMemOperand(r0, JSArray::kLengthOffset));
+    __ add(sp, sp, Operand(kPointerSize));
+    __ str(r4, FieldMemOperand(r0, JSRegExpResult::kIndexOffset));
+    __ str(r2, FieldMemOperand(r0, JSRegExpResult::kInputOffset));
+
+    // Fill out the elements FixedArray.
+    // r0: JSArray, tagged.
+    // r3: FixedArray, tagged.
+    // r5: Number of elements in array, untagged.
+
+    // Set map.
+    __ mov(r2, Operand(Factory::fixed_array_map()));
+    __ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
+    // Set FixedArray length.
+    __ str(r5, FieldMemOperand(r3, FixedArray::kLengthOffset));
+    // Fill contents of fixed-array with the-hole.
+    __ mov(r2, Operand(Factory::the_hole_value()));
+    __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+    // Fill fixed array elements with hole.
+    // r0: JSArray, tagged.
+    // r2: the hole.
+    // r3: Start of elements in FixedArray.
+    // r5: Number of elements to fill.
+    Label loop;
+    __ tst(r5, Operand(r5));
+    __ bind(&loop);
+    __ b(le, &done);  // Jump if r1 is negative or zero.
+    __ sub(r5, r5, Operand(1), SetCC);
+    __ str(r2, MemOperand(r3, r5, LSL, kPointerSizeLog2));
+    __ jmp(&loop);
+
+    __ bind(&slowcase);
+    __ CallRuntime(Runtime::kRegExpConstructResult, 3);
+
+    __ bind(&done);
+  }
+  frame_->Forget(3);
+  frame_->EmitPush(r0);
+}
+
+
 void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
   ASSERT_EQ(args->length(), 1);
 
index 62e9fe4018073ce529f9ee3b7e6e03b71636fac8..74aed1d79e3c183f79bd6972a521fa3e40ec1e79 100644 (file)
@@ -407,6 +407,8 @@ class CodeGenerator: public AstVisitor {
   // Support for direct calls from JavaScript to native RegExp code.
   void GenerateRegExpExec(ZoneList<Expression*>* args);
 
+  void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
+
   // Fast support for number to string.
   void GenerateNumberToString(ZoneList<Expression*>* args);
 
index 82a63f0ae60e1474d5c2a66ecc2c018cea6ea8ff..d88c8e7f0c3c64f0ee48eae4e9945402621c7791 100644 (file)
@@ -1237,6 +1237,62 @@ bool Genesis::InstallNatives() {
     apply->shared()->set_length(2);
   }
 
+  // Create a constructor for RegExp results (a variant of Array that
+  // predefines the two properties index and match).
+  {
+    // RegExpResult initial map.
+
+    // Find global.Array.prototype to inherit from.
+    Handle<JSFunction> array_constructor(global_context()->array_function());
+    Handle<JSObject> array_prototype(
+        JSObject::cast(array_constructor->instance_prototype()));
+
+    // Add initial map.
+    Handle<Map> initial_map =
+        Factory::NewMap(JS_ARRAY_TYPE, JSRegExpResult::kSize);
+    initial_map->set_constructor(*array_constructor);
+
+    // Set prototype on map.
+    initial_map->set_non_instance_prototype(false);
+    initial_map->set_prototype(*array_prototype);
+
+    // Update map with length accessor from Array and add "index" and "input".
+    Handle<Map> array_map(global_context()->js_array_map());
+    Handle<DescriptorArray> array_descriptors(
+        array_map->instance_descriptors());
+    ASSERT_EQ(1, array_descriptors->number_of_descriptors());
+
+    Handle<DescriptorArray> reresult_descriptors =
+        Factory::NewDescriptorArray(3);
+
+    reresult_descriptors->CopyFrom(0, *array_descriptors, 0);
+
+    int enum_index = 0;
+    {
+      FieldDescriptor index_field(Heap::index_symbol(),
+                                  JSRegExpResult::kIndexIndex,
+                                  NONE,
+                                  enum_index++);
+      reresult_descriptors->Set(1, &index_field);
+    }
+
+    {
+      FieldDescriptor input_field(Heap::input_symbol(),
+                                  JSRegExpResult::kInputIndex,
+                                  NONE,
+                                  enum_index++);
+      reresult_descriptors->Set(2, &input_field);
+    }
+    reresult_descriptors->Sort();
+
+    initial_map->set_inobject_properties(2);
+    initial_map->set_pre_allocated_property_fields(2);
+    initial_map->set_unused_property_fields(0);
+    initial_map->set_instance_descriptors(*reresult_descriptors);
+
+    global_context()->set_regexp_result_map(*initial_map);
+  }
+
 #ifdef DEBUG
   builtins->Verify();
 #endif
index d4518e6c3dcb0447ec90f1a68afd1b5291631e0a..d56d4eee20d26bced9fb90c905145b0c1531c967 100644 (file)
@@ -123,6 +123,7 @@ namespace internal {
   F(SubString, 3, 1)                                                         \
   F(StringCompare, 2, 1)                                                     \
   F(RegExpExec, 4, 1)                                                        \
+  F(RegExpConstructResult, 3, 1)                                             \
   F(NumberToString, 1, 1)                                                    \
   F(MathPow, 2, 1)                                                           \
   F(MathSin, 1, 1)                                                           \
index 44c90b6428f3f14a9344056d6dea012ecc860dc1..ce112f3e800a50bdfaf43921cace6b656614a5a8 100644 (file)
@@ -76,6 +76,7 @@ enum ContextLookupFlags {
   V(FUNCTION_MAP_INDEX, Map, function_map) \
   V(FUNCTION_INSTANCE_MAP_INDEX, Map, function_instance_map) \
   V(JS_ARRAY_MAP_INDEX, Map, js_array_map)\
+  V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map)\
   V(ARGUMENTS_BOILERPLATE_INDEX, JSObject, arguments_boilerplate) \
   V(MESSAGE_LISTENERS_INDEX, JSObject, message_listeners) \
   V(MAKE_MESSAGE_FUN_INDEX, JSFunction, make_message_fun) \
@@ -175,6 +176,7 @@ class Context: public FixedArray {
     SECURITY_TOKEN_INDEX,
     ARGUMENTS_BOILERPLATE_INDEX,
     JS_ARRAY_MAP_INDEX,
+    REGEXP_RESULT_MAP_INDEX,
     FUNCTION_MAP_INDEX,
     FUNCTION_INSTANCE_MAP_INDEX,
     INITIAL_OBJECT_PROTOTYPE_INDEX,
index ade5b7b3cb61edb42423fdfa1c41eaaa032ce5c8..9a8e869622d2c841160b6f3da782851613edef10 100644 (file)
@@ -153,6 +153,8 @@ class ZoneScopeInfo;
   V(global_symbol, "global")                                             \
   V(ignore_case_symbol, "ignoreCase")                                    \
   V(multiline_symbol, "multiline")                                       \
+  V(input_symbol, "input")                                               \
+  V(index_symbol, "index")                                               \
   V(last_index_symbol, "lastIndex")                                      \
   V(object_symbol, "object")                                             \
   V(prototype_symbol, "prototype")                                       \
index 67507ffb52d9f0bc9dd6f71b5e21fa08f6770679..5e96f787423a9c5ad5c90df88ea09d8f8f4bacb6 100644 (file)
@@ -6523,7 +6523,7 @@ void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
 
 
 void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
-  ASSERT_EQ(args->length(), 4);
+  ASSERT_EQ(4, args->length());
 
   // Load the arguments on the stack and call the stub.
   Load(args->at(0));
@@ -6536,6 +6536,95 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
+  // No stub. This code only occurs a few times in regexp.js.
+  const int kMaxInlineLength = 100;
+  ASSERT_EQ(3, args->length());
+  Load(args->at(0));  // Size of array, smi.
+  Load(args->at(1));  // "index" property value.
+  Load(args->at(2));  // "input" property value.
+  {
+    VirtualFrame::SpilledScope spilled_scope;
+
+    Label slowcase;
+    Label done;
+    __ mov(ebx, Operand(esp, kPointerSize * 2));
+    __ test(ebx, Immediate(kSmiTagMask));
+    __ j(not_zero, &slowcase);
+    __ cmp(Operand(ebx), Immediate(Smi::FromInt(kMaxInlineLength)));
+    __ j(above, &slowcase);
+    // Smi-tagging is equivalent to multiplying by 2.
+    STATIC_ASSERT(kSmiTag == 0);
+    STATIC_ASSERT(kSmiTagSize == 1);
+    // Allocate RegExpResult followed by FixedArray with size in ebx.
+    // JSArray:   [Map][empty properties][Elements][Length-smi][index][input]
+    // Elements:  [Map][Length][..elements..]
+    __ AllocateInNewSpace(JSRegExpResult::kSize + FixedArray::kHeaderSize,
+                          times_half_pointer_size,
+                          ebx,  // In: Number of elements (times 2, being a smi)
+                          eax,  // Out: Start of allocation (tagged).
+                          ecx,  // Out: End of allocation.
+                          edx,  // Scratch register
+                          &slowcase,
+                          TAG_OBJECT);
+    // eax: Start of allocated area, object-tagged.
+
+    // Set JSArray map to global.regexp_result_map().
+    // Set empty properties FixedArray.
+    // Set elements to point to FixedArray allocated right after the JSArray.
+    // Interleave operations for better latency.
+    __ mov(edx, ContextOperand(esi, Context::GLOBAL_INDEX));
+    __ mov(ecx, Immediate(Factory::empty_fixed_array()));
+    __ lea(ebx, Operand(eax, JSRegExpResult::kSize));
+    __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalContextOffset));
+    __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
+    __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
+    __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
+    __ mov(FieldOperand(eax, HeapObject::kMapOffset), edx);
+
+    // Set input, index and length fields from arguments.
+    __ pop(FieldOperand(eax, JSRegExpResult::kInputOffset));
+    __ pop(FieldOperand(eax, JSRegExpResult::kIndexOffset));
+    __ pop(ecx);
+    __ mov(FieldOperand(eax, JSArray::kLengthOffset), ecx);
+
+    // Fill out the elements FixedArray.
+    // eax: JSArray.
+    // ebx: FixedArray.
+    // ecx: Number of elements in array, as smi.
+
+    // Set map.
+    __ mov(FieldOperand(ebx, HeapObject::kMapOffset),
+           Immediate(Factory::fixed_array_map()));
+    // Set length.
+    __ SmiUntag(ecx);
+    __ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
+    // Fill contents of fixed-array with the-hole.
+    __ mov(edx, Immediate(Factory::the_hole_value()));
+    __ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
+    // Fill fixed array elements with hole.
+    // eax: JSArray.
+    // ecx: Number of elements to fill.
+    // ebx: Start of elements in FixedArray.
+    // edx: the hole.
+    Label loop;
+    __ test(ecx, Operand(ecx));
+    __ bind(&loop);
+    __ j(less_equal, &done);  // Jump if ecx is negative or zero.
+    __ sub(Operand(ecx), Immediate(1));
+    __ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
+    __ jmp(&loop);
+
+    __ bind(&slowcase);
+    __ CallRuntime(Runtime::kRegExpConstructResult, 3);
+
+    __ bind(&done);
+  }
+  frame_->Forget(3);
+  frame_->Push(eax);
+}
+
+
 void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
   ASSERT_EQ(args->length(), 1);
 
index 03a0fbacc25d5e9870c9339d92cafc4643591f06..a8568f0a68da89d432aa7929556ec407de4c5958 100644 (file)
@@ -628,6 +628,8 @@ class CodeGenerator: public AstVisitor {
   // Support for direct calls from JavaScript to native RegExp code.
   void GenerateRegExpExec(ZoneList<Expression*>* args);
 
+  void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
+
   // Fast support for number to string.
   void GenerateNumberToString(ZoneList<Expression*>* args);
 
index 9197466e85e4564cfe29e38e814e4865e5c06a64..b07792cd5070cbceb87dc5cfbfb68f8a35c6beb9 100644 (file)
@@ -4632,6 +4632,26 @@ class JSArray: public JSObject {
 };
 
 
+// JSRegExpResult is just a JSArray with a specific initial map.
+// This initial map adds in-object properties for "index" and "input"
+// properties, as assigned by RegExp.prototype.exec, which allows
+// faster creation of RegExp exec results.
+// This class just holds constants used when creating the result.
+// After creation the result must be treated as a JSArray in all regards.
+class JSRegExpResult: public JSArray {
+ public:
+  // Offsets of object fields.
+  static const int kIndexOffset = JSArray::kSize;
+  static const int kInputOffset = kIndexOffset + kPointerSize;
+  static const int kSize = kInputOffset + kPointerSize;
+  // Indices of in-object properties.
+  static const int kIndexIndex = 0;
+  static const int kInputIndex = 1;
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(JSRegExpResult);
+};
+
+
 // An accessor must have a getter, but can have no setter.
 //
 // When setting a property, V8 searches accessors in prototypes.
index c76b09d37706d9025614623f27d703a17aff0fbb..9929b112e8f1fff849c2fe1f83303fd6fc3fe111 100644 (file)
@@ -135,19 +135,52 @@ function RegExpCache() {
 var regExpCache = new RegExpCache();
 
 
-function CloneRegexpAnswer(array) {
+function CloneRegExpResult(array) {
   if (array == null) return null; 
-  var len = array.length;
-  var answer = new $Array(len);
-  for (var i = 0; i < len; i++) {
+  var length = array.length;
+  var answer = %_RegExpConstructResult(length, array.index, array.input);
+  for (var i = 0; i < length; i++) {
     answer[i] = array[i];
   }
-  answer.index = array.index;
-  answer.input = array.input;
   return answer;
 }
 
 
+function BuildResultFromMatchInfo(lastMatchInfo, s) {
+  var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
+  var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s);
+  if (numResults === 1) {
+    var matchStart = lastMatchInfo[CAPTURE(0)];
+    var matchEnd = lastMatchInfo[CAPTURE(1)];
+    result[0] = SubString(s, matchStart, matchEnd);
+  } else {
+    for (var i = 0; i < numResults; i++) {
+      var matchStart = lastMatchInfo[CAPTURE(i << 1)];
+      var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)];
+      if (matchStart != -1 && matchEnd != -1) {
+        result[i] = SubString(s, matchStart, matchEnd);
+      } else {
+        // Make sure the element is present. Avoid reading the undefined
+        // property from the global object since this may change.
+        result[i] = void 0;
+      }
+    }
+  }
+  return result;
+}
+
+
+function RegExpExecNoTests(regexp, string, start) {
+  // Must be called with RegExp, string and positive integer as arguments.
+  var matchInfo = DoRegExpExec(regexp, string, start);
+  var result = null;
+  if (matchInfo !== null) {
+    result = BuildResultFromMatchInfo(matchInfo, string);
+  }
+  return result;
+}
+
+
 function RegExpExec(string) {
   if (!IS_REGEXP(this)) {
     throw MakeTypeError('incompatible_method_receiver',
@@ -162,7 +195,7 @@ function RegExpExec(string) {
       %_ObjectEquals(cache.regExp, this) &&
       %_ObjectEquals(cache.subject, string)) {
     if (cache.answerSaved) {
-      return CloneRegexpAnswer(cache.answer);
+      return CloneRegExpResult(cache.answer);
     } else {
       saveAnswer = true;
     }
@@ -205,36 +238,15 @@ function RegExpExec(string) {
     return matchIndices;        // No match.
   }
 
-  var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
-  var result;
-  if (numResults === 1) {
-    var matchStart = lastMatchInfo[CAPTURE(0)];
-    var matchEnd = lastMatchInfo[CAPTURE(1)];
-    result = [SubString(s, matchStart, matchEnd)];
-  } else {
-    result = new $Array(numResults);
-    for (var i = 0; i < numResults; i++) {
-      var matchStart = lastMatchInfo[CAPTURE(i << 1)];
-      var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)];
-      if (matchStart != -1 && matchEnd != -1) {
-        result[i] = SubString(s, matchStart, matchEnd);
-      } else {
-        // Make sure the element is present. Avoid reading the undefined
-        // property from the global object since this may change.
-        result[i] = void 0;
-      }
-    }
-  }
+  var result = BuildResultFromMatchInfo(matchIndices, s);
 
-  result.index = lastMatchInfo[CAPTURE0];
-  result.input = s;
   if (this.global) {
     this.lastIndex = lastMatchInfo[CAPTURE1];
   } else {
     cache.regExp = this;
     cache.subject = s;
     cache.lastIndex = lastIndex;
-    if (saveAnswer) cache.answer = CloneRegexpAnswer(result);
+    if (saveAnswer) cache.answer = CloneRegExpResult(result);
     cache.answerSaved = saveAnswer;
     cache.type = 'exec';
   }
index dc0e9c86065d3631f864371a08927d17ca377ac3..7de610da185b49f681e790642644f7e728ce80b3 100644 (file)
@@ -1228,6 +1228,36 @@ static Object* Runtime_RegExpExec(Arguments args) {
 }
 
 
+static Object* Runtime_RegExpConstructResult(Arguments args) {
+  ASSERT(args.length() == 3);
+  CONVERT_SMI_CHECKED(elements_count, args[0]);
+  if (elements_count > JSArray::kMaxFastElementsLength) {
+    return Top::ThrowIllegalOperation();
+  }
+  Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
+  if (new_object->IsFailure()) return new_object;
+  FixedArray* elements = FixedArray::cast(new_object);
+  new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
+                                 NEW_SPACE,
+                                 OLD_POINTER_SPACE);
+  if (new_object->IsFailure()) return new_object;
+  {
+    AssertNoAllocation no_gc;
+    HandleScope scope;
+    reinterpret_cast<HeapObject*>(new_object)->
+        set_map(Top::global_context()->regexp_result_map());
+  }
+  JSArray* array = JSArray::cast(new_object);
+  array->set_properties(Heap::empty_fixed_array());
+  array->set_elements(elements);
+  array->set_length(Smi::FromInt(elements_count));
+  // Write in-object properties after the length of the array.
+  array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
+  array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
+  return array;
+}
+
+
 static Object* Runtime_RegExpInitializeObject(Arguments args) {
   AssertNoAllocation no_alloc;
   ASSERT(args.length() == 5);
index 870cfa65ef66a900f03d1c03015711e30236b2ef..69acd045fd18f0f5c0b2d757cd016afe7f432d05 100644 (file)
@@ -157,6 +157,7 @@ namespace internal {
   F(RegExpExec, 4, 1) \
   F(RegExpExecMultiple, 4, 1) \
   F(RegExpInitializeObject, 5, 1) \
+  F(RegExpConstructResult, 3, 1) \
   \
   /* Strings */ \
   F(StringCharCodeAt, 2, 1) \
index 300baf968bf556351d56194ab301b994402245d2..7ddc467fc0de2421c21fd39e36456a438dcb30e3 100644 (file)
@@ -149,6 +149,16 @@ function StringLastIndexOf(searchString /* position */) {  // length == 1
 }
 
 
+function CloneDenseArray(array) {
+  if (array === null) return null;
+  var clone = new $Array(array.length);
+  for (var i = 0; i < array.length; i++) {
+    clone[i] = array[i];
+  }
+  return clone;
+}
+
+
 // ECMA-262 section 15.5.4.9
 //
 // This function is implementation specific.  For now, we do not
@@ -164,33 +174,37 @@ function StringLocaleCompare(other) {
 
 // ECMA-262 section 15.5.4.10
 function StringMatch(regexp) {
-  if (!IS_REGEXP(regexp)) regexp = new $RegExp(regexp);
   var subject = TO_STRING_INLINE(this);
-
-  if (!regexp.global) return regexp.exec(subject);
-
-  var cache = regExpCache;
-  var saveAnswer = false;
-
-  if (%_ObjectEquals(cache.type, 'match') &&
-      %_ObjectEquals(cache.regExp, regexp) &&
-      %_ObjectEquals(cache.subject, subject)) {
-    if (cache.answerSaved) {
-      return CloneRegexpAnswer(cache.answer);
-    } else {
-      saveAnswer = true;
+  if (IS_REGEXP(regexp)) { 
+    if (!regexp.global) return regexp.exec(subject); 
+    
+    var cache = regExpCache;
+    var saveAnswer = false;
+
+    if (%_ObjectEquals(cache.type, 'match') &&
+        %_ObjectEquals(cache.regExp, regexp) &&
+        %_ObjectEquals(cache.subject, subject)) {
+      if (cache.answerSaved) {
+        return CloneDenseArray(cache.answer);
+      } else {
+        saveAnswer = true;
+      }
     }
+    %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
+    // lastMatchInfo is defined in regexp.js.
+    var result = %StringMatch(subject, regexp, lastMatchInfo);
+    cache.type = 'match';
+    cache.regExp = regexp;
+    cache.subject = subject;
+    if (saveAnswer) cache.answer = CloneDenseArray(result);
+    cache.answerSaved = saveAnswer;
+    return result;
   }
-
-  %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
-  // lastMatchInfo is defined in regexp.js.
-  var result = %StringMatch(subject, regexp, lastMatchInfo);
-  cache.type = 'match';
-  cache.regExp = regexp;
-  cache.subject = subject;
-  if (saveAnswer) cache.answer = CloneRegexpAnswer(result);
-  cache.answerSaved = saveAnswer;
-  return result;
+  // Non-regexp argument.
+  regexp = new $RegExp(regexp);
+  // Don't check regexp exec cache, since the regexp is new.
+  // TODO(lrn): Change this if we start caching regexps here.
+  return RegExpExecNoTests(regexp, subject, 0);
 }
 
 
@@ -599,7 +613,7 @@ function StringSplit(separator, limit) {
       %_ObjectEquals(cache.regExp, separator) &&
       %_ObjectEquals(cache.subject, subject)) {
     if (cache.answerSaved) {
-      return CloneRegexpAnswer(cache.answer);
+      return CloneDenseArray(cache.answer);
     } else {
       saveAnswer = true;
     }
@@ -665,10 +679,9 @@ function StringSplit(separator, limit) {
 
     startIndex = currentIndex = endIndex;
   }
-  if (saveAnswer) cache.answer = CloneRegexpAnswer(result);
+  if (saveAnswer) cache.answer = CloneDenseArray(result);
   cache.answerSaved = saveAnswer;
   return result;
-  
 }
 
 
index 87ad4b02c6c13550675427ee68744c75a214294a..3b75ab3dadb07863c47ddfce57e5a10013db659b 100644 (file)
@@ -4130,6 +4130,97 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
+  // No stub. This code only occurs a few times in regexp.js.
+  const int kMaxInlineLength = 100;
+  ASSERT_EQ(3, args->length());
+  Load(args->at(0));  // Size of array, smi.
+  Load(args->at(1));  // "index" property value.
+  Load(args->at(2));  // "input" property value.
+  {
+    VirtualFrame::SpilledScope spilled_scope;
+
+    Label slowcase;
+    Label done;
+    __ movq(r8, Operand(rsp, kPointerSize * 2));
+    __ JumpIfNotSmi(r8, &slowcase);
+    __ SmiToInteger32(rbx, r8);
+    __ cmpl(rbx, Immediate(kMaxInlineLength));
+    __ j(above, &slowcase);
+    // Smi-tagging is equivalent to multiplying by 2.
+    STATIC_ASSERT(kSmiTag == 0);
+    STATIC_ASSERT(kSmiTagSize == 1);
+    // Allocate RegExpResult followed by FixedArray with size in ebx.
+    // JSArray:   [Map][empty properties][Elements][Length-smi][index][input]
+    // Elements:  [Map][Length][..elements..]
+    __ AllocateInNewSpace(JSRegExpResult::kSize + FixedArray::kHeaderSize,
+                          times_pointer_size,
+                          rbx,  // In: Number of elements.
+                          rax,  // Out: Start of allocation (tagged).
+                          rcx,  // Out: End of allocation.
+                          rdx,  // Scratch register
+                          &slowcase,
+                          TAG_OBJECT);
+    // rax: Start of allocated area, object-tagged.
+    // rbx: Number of array elements as int32.
+    // r8: Number of array elements as smi.
+
+    // Set JSArray map to global.regexp_result_map().
+    __ movq(rdx, ContextOperand(rsi, Context::GLOBAL_INDEX));
+    __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset));
+    __ movq(rdx, ContextOperand(rdx, Context::REGEXP_RESULT_MAP_INDEX));
+    __ movq(FieldOperand(rax, HeapObject::kMapOffset), rdx);
+
+    // Set empty properties FixedArray.
+    __ Move(FieldOperand(rax, JSObject::kPropertiesOffset),
+            Factory::empty_fixed_array());
+
+    // Set elements to point to FixedArray allocated right after the JSArray.
+    __ lea(rcx, Operand(rax, JSRegExpResult::kSize));
+    __ movq(FieldOperand(rax, JSObject::kElementsOffset), rcx);
+
+    // Set input, index and length fields from arguments.
+    __ pop(FieldOperand(rax, JSRegExpResult::kInputOffset));
+    __ pop(FieldOperand(rax, JSRegExpResult::kIndexOffset));
+    __ lea(rsp, Operand(rsp, kPointerSize));
+    __ movq(FieldOperand(rax, JSArray::kLengthOffset), r8);
+
+    // Fill out the elements FixedArray.
+    // rax: JSArray.
+    // rcx: FixedArray.
+    // rbx: Number of elements in array as int32.
+
+    // Set map.
+    __ Move(FieldOperand(rcx, HeapObject::kMapOffset),
+            Factory::fixed_array_map());
+    // Set length.
+    __ movq(FieldOperand(rcx, FixedArray::kLengthOffset), rbx);
+    // Fill contents of fixed-array with the-hole.
+    __ Move(rdx, Factory::the_hole_value());
+    __ lea(rcx, FieldOperand(rcx, FixedArray::kHeaderSize));
+    // Fill fixed array elements with hole.
+    // rax: JSArray.
+    // rbx: Number of elements in array that remains to be filled, as int32.
+    // rcx: Start of elements in FixedArray.
+    // rdx: the hole.
+    Label loop;
+    __ testl(rbx, rbx);
+    __ bind(&loop);
+    __ j(less_equal, &done);  // Jump if ecx is negative or zero.
+    __ subl(rbx, Immediate(1));
+    __ movq(Operand(rcx, rbx, times_pointer_size, 0), rdx);
+    __ jmp(&loop);
+
+    __ bind(&slowcase);
+    __ CallRuntime(Runtime::kRegExpConstructResult, 3);
+
+    __ bind(&done);
+  }
+  frame_->Forget(3);
+  frame_->Push(rax);
+}
+
+
 void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
   ASSERT_EQ(args->length(), 1);
 
index 081a8a83719b81dd7fbc9b7cf5288e8db897dfbb..95c4773ec43994474bde4f1328090faa4c32ed60 100644 (file)
@@ -582,6 +582,8 @@ class CodeGenerator: public AstVisitor {
   // Support for direct calls from JavaScript to native RegExp code.
   void GenerateRegExpExec(ZoneList<Expression*>* args);
 
+  void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
+
   // Fast support for number to string.
   void GenerateNumberToString(ZoneList<Expression*>* args);
 
index a1a5821b373394573461ab769c59e0ca8bb8a5d6..f1c246216b3f8f7dc3d7fd602023ca524c689d98 100644 (file)
@@ -163,6 +163,12 @@ var knownProblems = {
   // Performance critical function which cannot afford type checks.
   "_CallFunction": true,
 
+  // Tries to allocate based on argument, and (correctly) throws
+  // out-of-memory if the request is too large. In practice, the
+  // size will be the number of captures of a RegExp.
+  "RegExpConstructResult": true,
+  "_RegExpConstructResult": true,
+
   // LiveEdit feature is under development currently and has fragile input.
   "LiveEditFindSharedFunctionInfosForScript": true,
   "LiveEditGatherCompileInfo": true,