}
+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);
// 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);
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
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) \
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) \
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,
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") \
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));
}
+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);
// 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);
};
+// 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.
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',
%_ObjectEquals(cache.regExp, this) &&
%_ObjectEquals(cache.subject, string)) {
if (cache.answerSaved) {
- return CloneRegexpAnswer(cache.answer);
+ return CloneRegExpResult(cache.answer);
} else {
saveAnswer = true;
}
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';
}
}
+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);
F(RegExpExec, 4, 1) \
F(RegExpExecMultiple, 4, 1) \
F(RegExpInitializeObject, 5, 1) \
+ F(RegExpConstructResult, 3, 1) \
\
/* Strings */ \
F(StringCharCodeAt, 2, 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
// 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);
}
%_ObjectEquals(cache.regExp, separator) &&
%_ObjectEquals(cache.subject, subject)) {
if (cache.answerSaved) {
- return CloneRegexpAnswer(cache.answer);
+ return CloneDenseArray(cache.answer);
} else {
saveAnswer = true;
}
startIndex = currentIndex = endIndex;
}
- if (saveAnswer) cache.answer = CloneRegexpAnswer(result);
+ if (saveAnswer) cache.answer = CloneDenseArray(result);
cache.answerSaved = saveAnswer;
return result;
-
}
}
+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);
// 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);
// 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,