From: lrn@chromium.org Date: Tue, 13 Apr 2010 09:31:03 +0000 (+0000) Subject: Native construction of RegExp result objects, with in-object index and input. X-Git-Tag: upstream/4.7.83~22031 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=285d5bc48ac0ccf0fde6f89041c1a8ac7f5310d1;p=platform%2Fupstream%2Fv8.git Native construction of RegExp result objects, with in-object index and input. 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 --- diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 92dcdd143..0fc7b6d1d 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -4021,6 +4021,100 @@ void CodeGenerator::GenerateRegExpExec(ZoneList* args) { } +void CodeGenerator::GenerateRegExpConstructResult(ZoneList* 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(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* args) { ASSERT_EQ(args->length(), 1); diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 62e9fe401..74aed1d79 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -407,6 +407,8 @@ class CodeGenerator: public AstVisitor { // Support for direct calls from JavaScript to native RegExp code. void GenerateRegExpExec(ZoneList* args); + void GenerateRegExpConstructResult(ZoneList* args); + // Fast support for number to string. void GenerateNumberToString(ZoneList* args); diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 82a63f0ae..d88c8e7f0 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -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 array_constructor(global_context()->array_function()); + Handle array_prototype( + JSObject::cast(array_constructor->instance_prototype())); + + // Add initial map. + Handle 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 array_map(global_context()->js_array_map()); + Handle array_descriptors( + array_map->instance_descriptors()); + ASSERT_EQ(1, array_descriptors->number_of_descriptors()); + + Handle 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 diff --git a/src/codegen.h b/src/codegen.h index d4518e6c3..d56d4eee2 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -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) \ diff --git a/src/contexts.h b/src/contexts.h index 44c90b642..ce112f3e8 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -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, diff --git a/src/heap.h b/src/heap.h index ade5b7b3c..9a8e86962 100644 --- a/src/heap.h +++ b/src/heap.h @@ -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") \ diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 67507ffb5..5e96f7874 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -6523,7 +6523,7 @@ void CodeGenerator::GenerateStringCompare(ZoneList* args) { void CodeGenerator::GenerateRegExpExec(ZoneList* 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* args) { } +void CodeGenerator::GenerateRegExpConstructResult(ZoneList* 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* args) { ASSERT_EQ(args->length(), 1); diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index 03a0fbacc..a8568f0a6 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -628,6 +628,8 @@ class CodeGenerator: public AstVisitor { // Support for direct calls from JavaScript to native RegExp code. void GenerateRegExpExec(ZoneList* args); + void GenerateRegExpConstructResult(ZoneList* args); + // Fast support for number to string. void GenerateNumberToString(ZoneList* args); diff --git a/src/objects.h b/src/objects.h index 9197466e8..b07792cd5 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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. diff --git a/src/regexp.js b/src/regexp.js index c76b09d37..9929b112e 100644 --- a/src/regexp.js +++ b/src/regexp.js @@ -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'; } diff --git a/src/runtime.cc b/src/runtime.cc index dc0e9c860..7de610da1 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -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(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); diff --git a/src/runtime.h b/src/runtime.h index 870cfa65e..69acd045f 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -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) \ diff --git a/src/string.js b/src/string.js index 300baf968..7ddc467fc 100644 --- a/src/string.js +++ b/src/string.js @@ -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; - } diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 87ad4b02c..3b75ab3da 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -4130,6 +4130,97 @@ void CodeGenerator::GenerateRegExpExec(ZoneList* args) { } +void CodeGenerator::GenerateRegExpConstructResult(ZoneList* 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* args) { ASSERT_EQ(args->length(), 1); diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 081a8a837..95c4773ec 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -582,6 +582,8 @@ class CodeGenerator: public AstVisitor { // Support for direct calls from JavaScript to native RegExp code. void GenerateRegExpExec(ZoneList* args); + void GenerateRegExpConstructResult(ZoneList* args); + // Fast support for number to string. void GenerateNumberToString(ZoneList* args); diff --git a/test/mjsunit/fuzz-natives.js b/test/mjsunit/fuzz-natives.js index a1a5821b3..f1c246216 100644 --- a/test/mjsunit/fuzz-natives.js +++ b/test/mjsunit/fuzz-natives.js @@ -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,