Remove regexp caching.
Review URL: http://codereview.chromium.org/4308001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5755 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
b9860d3af8
commit
e1458503d1
@ -5496,73 +5496,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
|
||||
}
|
||||
|
||||
|
||||
void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
|
||||
ASSERT_EQ(1, args->length());
|
||||
|
||||
Load(args->at(0));
|
||||
frame_->PopToR0();
|
||||
{
|
||||
VirtualFrame::SpilledScope spilled_scope(frame_);
|
||||
|
||||
Label done;
|
||||
Label call_runtime;
|
||||
__ BranchOnSmi(r0, &done);
|
||||
|
||||
// Load JSRegExp map into r1. Check that argument object has this map.
|
||||
// Arguments to this function should be results of calling RegExp exec,
|
||||
// which is either an unmodified JSRegExpResult or null. Anything not having
|
||||
// the unmodified JSRegExpResult map is returned unmodified.
|
||||
// This also ensures that elements are fast.
|
||||
|
||||
__ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX));
|
||||
__ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset));
|
||||
__ ldr(r1, ContextOperand(r1, Context::REGEXP_RESULT_MAP_INDEX));
|
||||
__ ldr(ip, FieldMemOperand(r0, HeapObject::kMapOffset));
|
||||
__ cmp(r1, Operand(ip));
|
||||
__ b(ne, &done);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
__ LoadRoot(r2, Heap::kEmptyFixedArrayRootIndex);
|
||||
__ ldr(ip, FieldMemOperand(r0, JSObject::kPropertiesOffset));
|
||||
__ cmp(ip, r2);
|
||||
__ Check(eq, "JSRegExpResult: default map but non-empty properties.");
|
||||
}
|
||||
|
||||
// All set, copy the contents to a new object.
|
||||
__ AllocateInNewSpace(JSRegExpResult::kSize,
|
||||
r2,
|
||||
r3,
|
||||
r4,
|
||||
&call_runtime,
|
||||
NO_ALLOCATION_FLAGS);
|
||||
// Store RegExpResult map as map of allocated object.
|
||||
ASSERT(JSRegExpResult::kSize == 6 * kPointerSize);
|
||||
// Copy all fields (map is already in r1) from (untagged) r0 to r2.
|
||||
// Change map of elements array (ends up in r4) to be a FixedCOWArray.
|
||||
__ bic(r0, r0, Operand(kHeapObjectTagMask));
|
||||
__ ldm(ib, r0, r3.bit() | r4.bit() | r5.bit() | r6.bit() | r7.bit());
|
||||
__ stm(ia, r2,
|
||||
r1.bit() | r3.bit() | r4.bit() | r5.bit() | r6.bit() | r7.bit());
|
||||
ASSERT(JSRegExp::kElementsOffset == 2 * kPointerSize);
|
||||
// Check whether elements array is empty fixed array, and otherwise make
|
||||
// it copy-on-write (it never should be empty unless someone is messing
|
||||
// with the arguments to the runtime function).
|
||||
__ LoadRoot(ip, Heap::kEmptyFixedArrayRootIndex);
|
||||
__ add(r0, r2, Operand(kHeapObjectTag)); // Tag result and move it to r0.
|
||||
__ cmp(r4, ip);
|
||||
__ b(eq, &done);
|
||||
__ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
|
||||
__ str(ip, FieldMemOperand(r4, HeapObject::kMapOffset));
|
||||
__ b(&done);
|
||||
__ bind(&call_runtime);
|
||||
__ push(r0);
|
||||
__ CallRuntime(Runtime::kRegExpCloneResult, 1);
|
||||
__ bind(&done);
|
||||
}
|
||||
frame_->EmitPush(r0);
|
||||
}
|
||||
|
||||
|
||||
class DeferredSearchCache: public DeferredCode {
|
||||
public:
|
||||
DeferredSearchCache(Register dst, Register cache, Register key)
|
||||
|
@ -518,8 +518,6 @@ class CodeGenerator: public AstVisitor {
|
||||
|
||||
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
|
||||
|
||||
void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
|
||||
|
||||
// Support for fast native caches.
|
||||
void GenerateGetFromCache(ZoneList<Expression*>* args);
|
||||
|
||||
|
@ -1225,13 +1225,6 @@ int FullCodeGenerator::TryCatch::Exit(int stack_depth) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitRegExpCloneResult(ZoneList<Expression*>* args) {
|
||||
ASSERT(args->length() == 1);
|
||||
VisitForStackValue(args->at(0));
|
||||
__ CallRuntime(Runtime::kRegExpCloneResult, 1);
|
||||
context()->Plug(result_register());
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
||||
|
||||
|
@ -7292,88 +7292,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
|
||||
}
|
||||
|
||||
|
||||
void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
|
||||
ASSERT_EQ(1, args->length());
|
||||
|
||||
Load(args->at(0));
|
||||
Result object_result = frame_->Pop();
|
||||
object_result.ToRegister(eax);
|
||||
object_result.Unuse();
|
||||
{
|
||||
VirtualFrame::SpilledScope spilled_scope;
|
||||
|
||||
Label done;
|
||||
|
||||
__ test(eax, Immediate(kSmiTagMask));
|
||||
__ j(zero, &done);
|
||||
|
||||
// Load JSRegExpResult map into edx.
|
||||
// Arguments to this function should be results of calling RegExp exec,
|
||||
// which is either an unmodified JSRegExpResult or null. Anything not having
|
||||
// the unmodified JSRegExpResult map is returned unmodified.
|
||||
// This also ensures that elements are fast.
|
||||
__ mov(edx, ContextOperand(esi, Context::GLOBAL_INDEX));
|
||||
__ mov(edx, FieldOperand(edx, GlobalObject::kGlobalContextOffset));
|
||||
__ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
|
||||
__ cmp(edx, FieldOperand(eax, HeapObject::kMapOffset));
|
||||
__ j(not_equal, &done);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
// Check that object really has empty properties array, as the map
|
||||
// should guarantee.
|
||||
__ cmp(FieldOperand(eax, JSObject::kPropertiesOffset),
|
||||
Immediate(Factory::empty_fixed_array()));
|
||||
__ Check(equal, "JSRegExpResult: default map but non-empty properties.");
|
||||
}
|
||||
|
||||
DeferredAllocateInNewSpace* allocate_fallback =
|
||||
new DeferredAllocateInNewSpace(JSRegExpResult::kSize,
|
||||
ebx,
|
||||
edx.bit() | eax.bit());
|
||||
|
||||
// All set, copy the contents to a new object.
|
||||
__ AllocateInNewSpace(JSRegExpResult::kSize,
|
||||
ebx,
|
||||
ecx,
|
||||
no_reg,
|
||||
allocate_fallback->entry_label(),
|
||||
TAG_OBJECT);
|
||||
__ bind(allocate_fallback->exit_label());
|
||||
|
||||
// Copy all fields from eax to ebx.
|
||||
STATIC_ASSERT(JSRegExpResult::kSize % (2 * kPointerSize) == 0);
|
||||
// There is an even number of fields, so unroll the loop once
|
||||
// for efficiency.
|
||||
for (int i = 0; i < JSRegExpResult::kSize; i += 2 * kPointerSize) {
|
||||
STATIC_ASSERT(JSObject::kMapOffset % (2 * kPointerSize) == 0);
|
||||
if (i != JSObject::kMapOffset) {
|
||||
// The map was already loaded into edx.
|
||||
__ mov(edx, FieldOperand(eax, i));
|
||||
}
|
||||
__ mov(ecx, FieldOperand(eax, i + kPointerSize));
|
||||
|
||||
STATIC_ASSERT(JSObject::kElementsOffset % (2 * kPointerSize) == 0);
|
||||
if (i == JSObject::kElementsOffset) {
|
||||
// If the elements array isn't empty, make it copy-on-write
|
||||
// before copying it.
|
||||
Label empty;
|
||||
__ cmp(Operand(edx), Immediate(Factory::empty_fixed_array()));
|
||||
__ j(equal, &empty);
|
||||
__ mov(FieldOperand(edx, HeapObject::kMapOffset),
|
||||
Immediate(Factory::fixed_cow_array_map()));
|
||||
__ bind(&empty);
|
||||
}
|
||||
__ mov(FieldOperand(ebx, i), edx);
|
||||
__ mov(FieldOperand(ebx, i + kPointerSize), ecx);
|
||||
}
|
||||
__ mov(eax, ebx);
|
||||
|
||||
__ bind(&done);
|
||||
}
|
||||
frame_->Push(eax);
|
||||
}
|
||||
|
||||
|
||||
class DeferredSearchCache: public DeferredCode {
|
||||
public:
|
||||
DeferredSearchCache(Register dst, Register cache, Register key)
|
||||
|
@ -697,11 +697,6 @@ class CodeGenerator: public AstVisitor {
|
||||
// Construct a RegExp exec result with two in-object properties.
|
||||
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
|
||||
|
||||
// Clone the result of a regexp function.
|
||||
// Must be an object created by GenerateRegExpConstructResult with
|
||||
// no extra properties.
|
||||
void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
|
||||
|
||||
// Support for fast native caches.
|
||||
void GenerateGetFromCache(ZoneList<Expression*>* args);
|
||||
|
||||
|
119
src/regexp.js
119
src/regexp.js
@ -71,9 +71,6 @@ function DoConstructRegExp(object, pattern, flags, isConstructorCall) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isConstructorCall) {
|
||||
regExpCache.type = 'none';
|
||||
}
|
||||
%RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
|
||||
|
||||
// Call internal function to compile the pattern.
|
||||
@ -121,22 +118,6 @@ function DoRegExpExec(regexp, string, index) {
|
||||
}
|
||||
|
||||
|
||||
function RegExpCache() {
|
||||
this.type = 'none';
|
||||
this.regExp = 0;
|
||||
this.subject = 0;
|
||||
this.replaceString = 0;
|
||||
this.answer = 0;
|
||||
// answerSaved marks whether the contents of answer is valid for a cache
|
||||
// hit in RegExpExec, StringMatch and StringSplit.
|
||||
this.answerSaved = false;
|
||||
this.splitLimit = 0; // Used only when type is "split".
|
||||
}
|
||||
|
||||
|
||||
var regExpCache = new RegExpCache();
|
||||
|
||||
|
||||
function BuildResultFromMatchInfo(lastMatchInfo, s) {
|
||||
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
|
||||
var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s);
|
||||
@ -178,32 +159,6 @@ function RegExpExec(string) {
|
||||
['RegExp.prototype.exec', this]);
|
||||
}
|
||||
|
||||
var cache = regExpCache;
|
||||
var saveAnswer = false;
|
||||
|
||||
var lastIndex = this.lastIndex;
|
||||
|
||||
// Since cache.subject is always a string, a matching input can not
|
||||
// cause visible side-effects when converted to a string, so we can omit
|
||||
// the conversion required by the specification.
|
||||
// Likewise, the regexp.lastIndex and regexp.global properties are value
|
||||
// properties that are not configurable, so reading them can also not cause
|
||||
// any side effects (converting lastIndex to a number can, though).
|
||||
if (%_ObjectEquals(cache.type, 'exec') &&
|
||||
%_ObjectEquals(0, lastIndex) &&
|
||||
%_IsRegExpEquivalent(cache.regExp, this) &&
|
||||
%_ObjectEquals(cache.subject, string)) {
|
||||
if (cache.answerSaved) {
|
||||
// The regexp.lastIndex value must be 0 for non-global RegExps, and for
|
||||
// global RegExps we only cache negative results, which gives a lastIndex
|
||||
// of zero as well.
|
||||
this.lastIndex = 0;
|
||||
return %_RegExpCloneResult(cache.answer);
|
||||
} else {
|
||||
saveAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (%_ArgumentsLength() === 0) {
|
||||
var regExpInput = LAST_INPUT(lastMatchInfo);
|
||||
if (IS_UNDEFINED(regExpInput)) {
|
||||
@ -217,11 +172,13 @@ function RegExpExec(string) {
|
||||
} else {
|
||||
s = ToString(string);
|
||||
}
|
||||
var global = this.global;
|
||||
var lastIndex = this.lastIndex;
|
||||
|
||||
// Conversion is required by the ES5 specification (RegExp.prototype.exec
|
||||
// algorithm, step 5) even if the value is discarded for non-global RegExps.
|
||||
var i = TO_INTEGER(lastIndex);
|
||||
|
||||
var global = this.global;
|
||||
if (global) {
|
||||
if (i < 0 || i > s.length) {
|
||||
this.lastIndex = 0;
|
||||
@ -237,16 +194,9 @@ function RegExpExec(string) {
|
||||
|
||||
if (matchIndices === null) {
|
||||
if (global) {
|
||||
// Cache negative result only if initial lastIndex was zero.
|
||||
this.lastIndex = 0;
|
||||
if (lastIndex !== 0) return matchIndices;
|
||||
}
|
||||
cache.regExp = this;
|
||||
cache.subject = s; // Always a string.
|
||||
cache.answer = null;
|
||||
cache.answerSaved = true; // Safe since no cloning is needed.
|
||||
cache.type = 'exec';
|
||||
return matchIndices; // No match.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Successful match.
|
||||
@ -254,17 +204,9 @@ function RegExpExec(string) {
|
||||
var result = BuildResultFromMatchInfo(matchIndices, s);
|
||||
|
||||
if (global) {
|
||||
// Don't cache positive results for global regexps.
|
||||
this.lastIndex = lastMatchInfo[CAPTURE1];
|
||||
} else {
|
||||
cache.regExp = this;
|
||||
cache.subject = s;
|
||||
if (saveAnswer) cache.answer = %_RegExpCloneResult(result);
|
||||
cache.answerSaved = saveAnswer;
|
||||
cache.type = 'exec';
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -289,20 +231,6 @@ function RegExpTest(string) {
|
||||
string = regExpInput;
|
||||
}
|
||||
|
||||
var lastIndex = this.lastIndex;
|
||||
|
||||
var cache = regExpCache;
|
||||
if (%_ObjectEquals(cache.type, 'test') &&
|
||||
%_IsRegExpEquivalent(cache.regExp, this) &&
|
||||
%_ObjectEquals(cache.subject, string) &&
|
||||
%_ObjectEquals(0, lastIndex)) {
|
||||
// The regexp.lastIndex value must be 0 for non-global RegExps, and for
|
||||
// global RegExps we only cache negative results, which gives a resulting
|
||||
// lastIndex of zero as well.
|
||||
if (global) this.lastIndex = 0;
|
||||
return cache.answer;
|
||||
}
|
||||
|
||||
var s;
|
||||
if (IS_STRING(string)) {
|
||||
s = string;
|
||||
@ -311,9 +239,13 @@ function RegExpTest(string) {
|
||||
}
|
||||
var length = s.length;
|
||||
|
||||
var lastIndex = this.lastIndex;
|
||||
|
||||
// Conversion is required by the ES5 specification (RegExp.prototype.exec
|
||||
// algorithm, step 5) even if the value is discarded for non-global RegExps.
|
||||
var i = TO_INTEGER(lastIndex);
|
||||
|
||||
var global = this.global;
|
||||
if (global) {
|
||||
if (i < 0 || i > length) {
|
||||
this.lastIndex = 0;
|
||||
@ -323,8 +255,6 @@ function RegExpTest(string) {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
var global = this.global;
|
||||
|
||||
// Remove irrelevant preceeding '.*' in a test regexp. The expression
|
||||
// checks whether this.source starts with '.*' and that the third
|
||||
// char is not a '?'
|
||||
@ -334,35 +264,31 @@ function RegExpTest(string) {
|
||||
if (!%_ObjectEquals(regexp_key, this)) {
|
||||
regexp_key = this;
|
||||
regexp_val = new $RegExp(this.source.substring(2, this.source.length),
|
||||
(this.global ? 'g' : '')
|
||||
(global ? 'g' : '')
|
||||
+ (this.ignoreCase ? 'i' : '')
|
||||
+ (this.multiline ? 'm' : ''));
|
||||
}
|
||||
if (!regexp_val.test(s)) return false;
|
||||
}
|
||||
|
||||
var length = s.length;
|
||||
|
||||
if (i < 0 || i > length) {
|
||||
this.lastIndex = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
%_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
|
||||
// matchIndices is either null or the lastMatchInfo array.
|
||||
var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
|
||||
|
||||
var result = (matchIndices !== null);
|
||||
if (result) {
|
||||
lastMatchInfoOverride = null;
|
||||
if (matchIndices === null) {
|
||||
if (global) this.lastIndex = 0;
|
||||
return false;
|
||||
}
|
||||
if (global) {
|
||||
if (result) {
|
||||
this.lastIndex = lastMatchInfo[CAPTURE1];
|
||||
return true;
|
||||
} else {
|
||||
this.lastIndex = 0;
|
||||
if (lastIndex !== 0) return false;
|
||||
}
|
||||
}
|
||||
cache.type = 'test';
|
||||
cache.regExp = this;
|
||||
cache.subject = s;
|
||||
cache.answer = result;
|
||||
return result;
|
||||
lastMatchInfoOverride = null;
|
||||
if (global) this.lastIndex = lastMatchInfo[CAPTURE1];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -510,7 +436,6 @@ function SetupRegExp() {
|
||||
return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
|
||||
}
|
||||
function RegExpSetInput(string) {
|
||||
regExpCache.type = 'none';
|
||||
LAST_INPUT(lastMatchInfo) = ToString(string);
|
||||
};
|
||||
|
||||
|
@ -1424,66 +1424,6 @@ static MaybeObject* Runtime_RegExpConstructResult(Arguments args) {
|
||||
}
|
||||
|
||||
|
||||
static MaybeObject* Runtime_RegExpCloneResult(Arguments args) {
|
||||
ASSERT(args.length() == 1);
|
||||
Map* regexp_result_map;
|
||||
{
|
||||
AssertNoAllocation no_gc;
|
||||
HandleScope handles;
|
||||
regexp_result_map = Top::global_context()->regexp_result_map();
|
||||
}
|
||||
if (!args[0]->IsJSArray()) return args[0];
|
||||
|
||||
JSArray* result = JSArray::cast(args[0]);
|
||||
// Arguments to RegExpCloneResult should always be fresh RegExp exec call
|
||||
// results (either a fresh JSRegExpResult or null).
|
||||
// If the argument is not a JSRegExpResult, or isn't unmodified, just return
|
||||
// the argument uncloned.
|
||||
if (result->map() != regexp_result_map) return result;
|
||||
|
||||
// Having the original JSRegExpResult map guarantees that we have
|
||||
// fast elements and no properties except the two in-object properties.
|
||||
ASSERT(result->HasFastElements());
|
||||
ASSERT(result->properties() == Heap::empty_fixed_array());
|
||||
ASSERT_EQ(2, regexp_result_map->inobject_properties());
|
||||
|
||||
Object* new_array_alloc;
|
||||
{ MaybeObject* maybe_new_array_alloc =
|
||||
Heap::AllocateRaw(JSRegExpResult::kSize, NEW_SPACE, OLD_POINTER_SPACE);
|
||||
if (!maybe_new_array_alloc->ToObject(&new_array_alloc)) {
|
||||
return maybe_new_array_alloc;
|
||||
}
|
||||
}
|
||||
|
||||
// Set HeapObject map to JSRegExpResult map.
|
||||
reinterpret_cast<HeapObject*>(new_array_alloc)->set_map(regexp_result_map);
|
||||
|
||||
JSArray* new_array = JSArray::cast(new_array_alloc);
|
||||
|
||||
// Copy JSObject properties.
|
||||
new_array->set_properties(result->properties()); // Empty FixedArray.
|
||||
|
||||
// Copy JSObject elements as copy-on-write.
|
||||
FixedArray* elements = FixedArray::cast(result->elements());
|
||||
if (elements != Heap::empty_fixed_array()) {
|
||||
elements->set_map(Heap::fixed_cow_array_map());
|
||||
}
|
||||
new_array->set_elements(elements);
|
||||
|
||||
// Copy JSArray length.
|
||||
new_array->set_length(result->length());
|
||||
|
||||
// Copy JSRegExpResult in-object property fields input and index.
|
||||
new_array->FastPropertyAtPut(JSRegExpResult::kIndexIndex,
|
||||
result->FastPropertyAt(
|
||||
JSRegExpResult::kIndexIndex));
|
||||
new_array->FastPropertyAtPut(JSRegExpResult::kInputIndex,
|
||||
result->FastPropertyAt(
|
||||
JSRegExpResult::kInputIndex));
|
||||
return new_array;
|
||||
}
|
||||
|
||||
|
||||
static MaybeObject* Runtime_RegExpInitializeObject(Arguments args) {
|
||||
AssertNoAllocation no_alloc;
|
||||
ASSERT(args.length() == 5);
|
||||
|
@ -162,7 +162,6 @@ namespace internal {
|
||||
F(RegExpExecMultiple, 4, 1) \
|
||||
F(RegExpInitializeObject, 5, 1) \
|
||||
F(RegExpConstructResult, 3, 1) \
|
||||
F(RegExpCloneResult, 1, 1) \
|
||||
\
|
||||
/* JSON */ \
|
||||
F(ParseJson, 1, 1) \
|
||||
@ -425,7 +424,7 @@ namespace internal {
|
||||
// ----------------------------------------------------------------------------
|
||||
// INLINE_AND_RUNTIME_FUNCTION_LIST defines all inlined functions accessed
|
||||
// with a native call of the form %_name from within JS code that also have
|
||||
// a corresponding runtime function, that is called for slow cases.
|
||||
// a corresponding runtime function, that is called for slow cases.
|
||||
// Entries have the form F(name, number of arguments, number of return values).
|
||||
#define INLINE_RUNTIME_FUNCTION_LIST(F) \
|
||||
F(IsConstructCall, 0, 1) \
|
||||
@ -437,7 +436,6 @@ namespace internal {
|
||||
F(StringCompare, 2, 1) \
|
||||
F(RegExpExec, 4, 1) \
|
||||
F(RegExpConstructResult, 3, 1) \
|
||||
F(RegExpCloneResult, 1, 1) \
|
||||
F(GetFromCache, 2, 1) \
|
||||
F(NumberToString, 1, 1) \
|
||||
F(SwapElements, 3, 1)
|
||||
|
@ -144,16 +144,6 @@ 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
|
||||
@ -173,32 +163,14 @@ function StringMatch(regexp) {
|
||||
if (IS_REGEXP(regexp)) {
|
||||
if (!regexp.global) return regexp.exec(subject);
|
||||
|
||||
var cache = regExpCache;
|
||||
var saveAnswer = false;
|
||||
|
||||
if (%_ObjectEquals(cache.type, 'match') &&
|
||||
%_IsRegExpEquivalent(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;
|
||||
return %StringMatch(subject, regexp, lastMatchInfo);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -231,7 +203,6 @@ function StringReplace(search, replace) {
|
||||
if (IS_REGEXP(search)) {
|
||||
%_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
|
||||
if (IS_FUNCTION(replace)) {
|
||||
regExpCache.type = 'none';
|
||||
if (search.global) {
|
||||
return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
|
||||
} else {
|
||||
@ -273,24 +244,10 @@ function StringReplace(search, replace) {
|
||||
|
||||
// Helper function for regular expressions in String.prototype.replace.
|
||||
function StringReplaceRegExp(subject, regexp, replace) {
|
||||
var cache = regExpCache;
|
||||
if (%_ObjectEquals(cache.type, 'replace') &&
|
||||
%_IsRegExpEquivalent(cache.regExp, regexp) &&
|
||||
%_ObjectEquals(cache.replaceString, replace) &&
|
||||
%_ObjectEquals(cache.subject, subject)) {
|
||||
return cache.answer;
|
||||
}
|
||||
replace = TO_STRING_INLINE(replace);
|
||||
var answer = %StringReplaceRegExpWithString(subject,
|
||||
regexp,
|
||||
replace,
|
||||
lastMatchInfo);
|
||||
cache.subject = subject;
|
||||
cache.regExp = regexp;
|
||||
cache.replaceString = replace;
|
||||
cache.answer = answer;
|
||||
cache.type = 'replace';
|
||||
return answer;
|
||||
return %StringReplaceRegExpWithString(subject,
|
||||
regexp,
|
||||
TO_STRING_INLINE(replace),
|
||||
lastMatchInfo);
|
||||
}
|
||||
|
||||
|
||||
@ -605,34 +562,14 @@ function StringSplit(separator, limit) {
|
||||
return result;
|
||||
}
|
||||
|
||||
var cache = regExpCache;
|
||||
var saveAnswer = false;
|
||||
|
||||
if (%_ObjectEquals(cache.type, 'split') &&
|
||||
%_IsRegExpEquivalent(cache.regExp, separator) &&
|
||||
%_ObjectEquals(cache.subject, subject) &&
|
||||
%_ObjectEquals(cache.splitLimit, limit)) {
|
||||
if (cache.answerSaved) {
|
||||
return CloneDenseArray(cache.answer);
|
||||
} else {
|
||||
saveAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
cache.type = 'split';
|
||||
cache.regExp = separator;
|
||||
cache.subject = subject;
|
||||
cache.splitLimit = limit;
|
||||
|
||||
%_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
|
||||
|
||||
if (length === 0) {
|
||||
cache.answerSaved = true;
|
||||
if (splitMatch(separator, subject, 0, 0) != null) {
|
||||
cache.answer = [];
|
||||
return [];
|
||||
}
|
||||
cache.answer = [subject];
|
||||
return [subject];
|
||||
}
|
||||
|
||||
@ -680,8 +617,6 @@ function StringSplit(separator, limit) {
|
||||
|
||||
startIndex = currentIndex = endIndex;
|
||||
}
|
||||
if (saveAnswer) cache.answer = CloneDenseArray(result);
|
||||
cache.answerSaved = saveAnswer;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -6564,86 +6564,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
|
||||
}
|
||||
|
||||
|
||||
void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
|
||||
ASSERT_EQ(1, args->length());
|
||||
|
||||
Load(args->at(0));
|
||||
Result object_result = frame_->Pop();
|
||||
object_result.ToRegister(rax);
|
||||
object_result.Unuse();
|
||||
{
|
||||
VirtualFrame::SpilledScope spilled_scope;
|
||||
|
||||
Label done;
|
||||
__ JumpIfSmi(rax, &done);
|
||||
|
||||
// Load JSRegExpResult map into rdx.
|
||||
// Arguments to this function should be results of calling RegExp exec,
|
||||
// which is either an unmodified JSRegExpResult or null. Anything not having
|
||||
// the unmodified JSRegExpResult map is returned unmodified.
|
||||
// This also ensures that elements are fast.
|
||||
|
||||
__ movq(rdx, ContextOperand(rsi, Context::GLOBAL_INDEX));
|
||||
__ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset));
|
||||
__ movq(rdx, ContextOperand(rdx, Context::REGEXP_RESULT_MAP_INDEX));
|
||||
__ cmpq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
|
||||
__ j(not_equal, &done);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
// Check that object really has empty properties array, as the map
|
||||
// should guarantee.
|
||||
__ CompareRoot(FieldOperand(rax, JSObject::kPropertiesOffset),
|
||||
Heap::kEmptyFixedArrayRootIndex);
|
||||
__ Check(equal, "JSRegExpResult: default map but non-empty properties.");
|
||||
}
|
||||
|
||||
DeferredAllocateInNewSpace* allocate_fallback =
|
||||
new DeferredAllocateInNewSpace(JSRegExpResult::kSize,
|
||||
rbx,
|
||||
rdx.bit() | rax.bit());
|
||||
|
||||
// All set, copy the contents to a new object.
|
||||
__ AllocateInNewSpace(JSRegExpResult::kSize,
|
||||
rbx,
|
||||
no_reg,
|
||||
no_reg,
|
||||
allocate_fallback->entry_label(),
|
||||
TAG_OBJECT);
|
||||
__ bind(allocate_fallback->exit_label());
|
||||
|
||||
STATIC_ASSERT(JSRegExpResult::kSize % (2 * kPointerSize) == 0);
|
||||
// There is an even number of fields, so unroll the loop once
|
||||
// for efficiency.
|
||||
for (int i = 0; i < JSRegExpResult::kSize; i += 2 * kPointerSize) {
|
||||
STATIC_ASSERT(JSObject::kMapOffset % (2 * kPointerSize) == 0);
|
||||
if (i != JSObject::kMapOffset) {
|
||||
// The map was already loaded into edx.
|
||||
__ movq(rdx, FieldOperand(rax, i));
|
||||
}
|
||||
__ movq(rcx, FieldOperand(rax, i + kPointerSize));
|
||||
|
||||
STATIC_ASSERT(JSObject::kElementsOffset % (2 * kPointerSize) == 0);
|
||||
if (i == JSObject::kElementsOffset) {
|
||||
// If the elements array isn't empty, make it copy-on-write
|
||||
// before copying it.
|
||||
Label empty;
|
||||
__ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
|
||||
__ j(equal, &empty);
|
||||
__ LoadRoot(kScratchRegister, Heap::kFixedCOWArrayMapRootIndex);
|
||||
__ movq(FieldOperand(rdx, HeapObject::kMapOffset), kScratchRegister);
|
||||
__ bind(&empty);
|
||||
}
|
||||
__ movq(FieldOperand(rbx, i), rdx);
|
||||
__ movq(FieldOperand(rbx, i + kPointerSize), rcx);
|
||||
}
|
||||
__ movq(rax, rbx);
|
||||
|
||||
__ bind(&done);
|
||||
}
|
||||
frame_->Push(rax);
|
||||
}
|
||||
|
||||
|
||||
class DeferredSearchCache: public DeferredCode {
|
||||
public:
|
||||
DeferredSearchCache(Register dst,
|
||||
|
@ -656,8 +656,6 @@ class CodeGenerator: public AstVisitor {
|
||||
|
||||
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
|
||||
|
||||
void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
|
||||
|
||||
// Support for fast native caches.
|
||||
void GenerateGetFromCache(ZoneList<Expression*>* args);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user