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 {
|
class DeferredSearchCache: public DeferredCode {
|
||||||
public:
|
public:
|
||||||
DeferredSearchCache(Register dst, Register cache, Register key)
|
DeferredSearchCache(Register dst, Register cache, Register key)
|
||||||
|
@ -518,8 +518,6 @@ class CodeGenerator: public AstVisitor {
|
|||||||
|
|
||||||
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
|
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
|
|
||||||
|
|
||||||
// Support for fast native caches.
|
// Support for fast native caches.
|
||||||
void GenerateGetFromCache(ZoneList<Expression*>* args);
|
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 __
|
#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 {
|
class DeferredSearchCache: public DeferredCode {
|
||||||
public:
|
public:
|
||||||
DeferredSearchCache(Register dst, Register cache, Register key)
|
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.
|
// Construct a RegExp exec result with two in-object properties.
|
||||||
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
|
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.
|
// Support for fast native caches.
|
||||||
void GenerateGetFromCache(ZoneList<Expression*>* args);
|
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);
|
%RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
|
||||||
|
|
||||||
// Call internal function to compile the pattern.
|
// 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) {
|
function BuildResultFromMatchInfo(lastMatchInfo, s) {
|
||||||
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
|
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
|
||||||
var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s);
|
var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s);
|
||||||
@ -178,32 +159,6 @@ function RegExpExec(string) {
|
|||||||
['RegExp.prototype.exec', this]);
|
['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) {
|
if (%_ArgumentsLength() === 0) {
|
||||||
var regExpInput = LAST_INPUT(lastMatchInfo);
|
var regExpInput = LAST_INPUT(lastMatchInfo);
|
||||||
if (IS_UNDEFINED(regExpInput)) {
|
if (IS_UNDEFINED(regExpInput)) {
|
||||||
@ -217,11 +172,13 @@ function RegExpExec(string) {
|
|||||||
} else {
|
} else {
|
||||||
s = ToString(string);
|
s = ToString(string);
|
||||||
}
|
}
|
||||||
var global = this.global;
|
var lastIndex = this.lastIndex;
|
||||||
|
|
||||||
// Conversion is required by the ES5 specification (RegExp.prototype.exec
|
// Conversion is required by the ES5 specification (RegExp.prototype.exec
|
||||||
// algorithm, step 5) even if the value is discarded for non-global RegExps.
|
// algorithm, step 5) even if the value is discarded for non-global RegExps.
|
||||||
var i = TO_INTEGER(lastIndex);
|
var i = TO_INTEGER(lastIndex);
|
||||||
|
|
||||||
|
var global = this.global;
|
||||||
if (global) {
|
if (global) {
|
||||||
if (i < 0 || i > s.length) {
|
if (i < 0 || i > s.length) {
|
||||||
this.lastIndex = 0;
|
this.lastIndex = 0;
|
||||||
@ -237,16 +194,9 @@ function RegExpExec(string) {
|
|||||||
|
|
||||||
if (matchIndices === null) {
|
if (matchIndices === null) {
|
||||||
if (global) {
|
if (global) {
|
||||||
// Cache negative result only if initial lastIndex was zero.
|
|
||||||
this.lastIndex = 0;
|
this.lastIndex = 0;
|
||||||
if (lastIndex !== 0) return matchIndices;
|
|
||||||
}
|
}
|
||||||
cache.regExp = this;
|
return null;
|
||||||
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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Successful match.
|
// Successful match.
|
||||||
@ -254,17 +204,9 @@ function RegExpExec(string) {
|
|||||||
var result = BuildResultFromMatchInfo(matchIndices, s);
|
var result = BuildResultFromMatchInfo(matchIndices, s);
|
||||||
|
|
||||||
if (global) {
|
if (global) {
|
||||||
// Don't cache positive results for global regexps.
|
|
||||||
this.lastIndex = lastMatchInfo[CAPTURE1];
|
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;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -289,20 +231,6 @@ function RegExpTest(string) {
|
|||||||
string = regExpInput;
|
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;
|
var s;
|
||||||
if (IS_STRING(string)) {
|
if (IS_STRING(string)) {
|
||||||
s = string;
|
s = string;
|
||||||
@ -311,9 +239,13 @@ function RegExpTest(string) {
|
|||||||
}
|
}
|
||||||
var length = s.length;
|
var length = s.length;
|
||||||
|
|
||||||
|
var lastIndex = this.lastIndex;
|
||||||
|
|
||||||
// Conversion is required by the ES5 specification (RegExp.prototype.exec
|
// Conversion is required by the ES5 specification (RegExp.prototype.exec
|
||||||
// algorithm, step 5) even if the value is discarded for non-global RegExps.
|
// algorithm, step 5) even if the value is discarded for non-global RegExps.
|
||||||
var i = TO_INTEGER(lastIndex);
|
var i = TO_INTEGER(lastIndex);
|
||||||
|
|
||||||
|
var global = this.global;
|
||||||
if (global) {
|
if (global) {
|
||||||
if (i < 0 || i > length) {
|
if (i < 0 || i > length) {
|
||||||
this.lastIndex = 0;
|
this.lastIndex = 0;
|
||||||
@ -323,8 +255,6 @@ function RegExpTest(string) {
|
|||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var global = this.global;
|
|
||||||
|
|
||||||
// Remove irrelevant preceeding '.*' in a test regexp. The expression
|
// Remove irrelevant preceeding '.*' in a test regexp. The expression
|
||||||
// checks whether this.source starts with '.*' and that the third
|
// checks whether this.source starts with '.*' and that the third
|
||||||
// char is not a '?'
|
// char is not a '?'
|
||||||
@ -334,35 +264,31 @@ function RegExpTest(string) {
|
|||||||
if (!%_ObjectEquals(regexp_key, this)) {
|
if (!%_ObjectEquals(regexp_key, this)) {
|
||||||
regexp_key = this;
|
regexp_key = this;
|
||||||
regexp_val = new $RegExp(this.source.substring(2, this.source.length),
|
regexp_val = new $RegExp(this.source.substring(2, this.source.length),
|
||||||
(this.global ? 'g' : '')
|
(global ? 'g' : '')
|
||||||
+ (this.ignoreCase ? 'i' : '')
|
+ (this.ignoreCase ? 'i' : '')
|
||||||
+ (this.multiline ? 'm' : ''));
|
+ (this.multiline ? 'm' : ''));
|
||||||
}
|
}
|
||||||
if (!regexp_val.test(s)) return false;
|
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]);
|
%_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
|
||||||
// matchIndices is either null or the lastMatchInfo array.
|
// matchIndices is either null or the lastMatchInfo array.
|
||||||
var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
|
var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
|
||||||
|
|
||||||
var result = (matchIndices !== null);
|
if (matchIndices === null) {
|
||||||
if (result) {
|
if (global) this.lastIndex = 0;
|
||||||
lastMatchInfoOverride = null;
|
return false;
|
||||||
}
|
}
|
||||||
if (global) {
|
lastMatchInfoOverride = null;
|
||||||
if (result) {
|
if (global) this.lastIndex = lastMatchInfo[CAPTURE1];
|
||||||
this.lastIndex = lastMatchInfo[CAPTURE1];
|
return true;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -510,7 +436,6 @@ function SetupRegExp() {
|
|||||||
return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
|
return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
|
||||||
}
|
}
|
||||||
function RegExpSetInput(string) {
|
function RegExpSetInput(string) {
|
||||||
regExpCache.type = 'none';
|
|
||||||
LAST_INPUT(lastMatchInfo) = ToString(string);
|
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) {
|
static MaybeObject* Runtime_RegExpInitializeObject(Arguments args) {
|
||||||
AssertNoAllocation no_alloc;
|
AssertNoAllocation no_alloc;
|
||||||
ASSERT(args.length() == 5);
|
ASSERT(args.length() == 5);
|
||||||
|
@ -162,7 +162,6 @@ namespace internal {
|
|||||||
F(RegExpExecMultiple, 4, 1) \
|
F(RegExpExecMultiple, 4, 1) \
|
||||||
F(RegExpInitializeObject, 5, 1) \
|
F(RegExpInitializeObject, 5, 1) \
|
||||||
F(RegExpConstructResult, 3, 1) \
|
F(RegExpConstructResult, 3, 1) \
|
||||||
F(RegExpCloneResult, 1, 1) \
|
|
||||||
\
|
\
|
||||||
/* JSON */ \
|
/* JSON */ \
|
||||||
F(ParseJson, 1, 1) \
|
F(ParseJson, 1, 1) \
|
||||||
@ -425,7 +424,7 @@ namespace internal {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// INLINE_AND_RUNTIME_FUNCTION_LIST defines all inlined functions accessed
|
// 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
|
// 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).
|
// Entries have the form F(name, number of arguments, number of return values).
|
||||||
#define INLINE_RUNTIME_FUNCTION_LIST(F) \
|
#define INLINE_RUNTIME_FUNCTION_LIST(F) \
|
||||||
F(IsConstructCall, 0, 1) \
|
F(IsConstructCall, 0, 1) \
|
||||||
@ -437,7 +436,6 @@ namespace internal {
|
|||||||
F(StringCompare, 2, 1) \
|
F(StringCompare, 2, 1) \
|
||||||
F(RegExpExec, 4, 1) \
|
F(RegExpExec, 4, 1) \
|
||||||
F(RegExpConstructResult, 3, 1) \
|
F(RegExpConstructResult, 3, 1) \
|
||||||
F(RegExpCloneResult, 1, 1) \
|
|
||||||
F(GetFromCache, 2, 1) \
|
F(GetFromCache, 2, 1) \
|
||||||
F(NumberToString, 1, 1) \
|
F(NumberToString, 1, 1) \
|
||||||
F(SwapElements, 3, 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
|
// ECMA-262 section 15.5.4.9
|
||||||
//
|
//
|
||||||
// This function is implementation specific. For now, we do not
|
// This function is implementation specific. For now, we do not
|
||||||
@ -173,32 +163,14 @@ function StringMatch(regexp) {
|
|||||||
if (IS_REGEXP(regexp)) {
|
if (IS_REGEXP(regexp)) {
|
||||||
if (!regexp.global) return regexp.exec(subject);
|
if (!regexp.global) return regexp.exec(subject);
|
||||||
|
|
||||||
var cache = regExpCache;
|
|
||||||
var saveAnswer = false;
|
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]);
|
%_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
|
||||||
// lastMatchInfo is defined in regexp.js.
|
// lastMatchInfo is defined in regexp.js.
|
||||||
var result = %StringMatch(subject, regexp, lastMatchInfo);
|
return %StringMatch(subject, regexp, lastMatchInfo);
|
||||||
cache.type = 'match';
|
|
||||||
cache.regExp = regexp;
|
|
||||||
cache.subject = subject;
|
|
||||||
if (saveAnswer) cache.answer = CloneDenseArray(result);
|
|
||||||
cache.answerSaved = saveAnswer;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
// Non-regexp argument.
|
// Non-regexp argument.
|
||||||
regexp = new $RegExp(regexp);
|
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);
|
return RegExpExecNoTests(regexp, subject, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +203,6 @@ function StringReplace(search, replace) {
|
|||||||
if (IS_REGEXP(search)) {
|
if (IS_REGEXP(search)) {
|
||||||
%_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
|
%_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
|
||||||
if (IS_FUNCTION(replace)) {
|
if (IS_FUNCTION(replace)) {
|
||||||
regExpCache.type = 'none';
|
|
||||||
if (search.global) {
|
if (search.global) {
|
||||||
return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
|
return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
|
||||||
} else {
|
} else {
|
||||||
@ -273,24 +244,10 @@ function StringReplace(search, replace) {
|
|||||||
|
|
||||||
// Helper function for regular expressions in String.prototype.replace.
|
// Helper function for regular expressions in String.prototype.replace.
|
||||||
function StringReplaceRegExp(subject, regexp, replace) {
|
function StringReplaceRegExp(subject, regexp, replace) {
|
||||||
var cache = regExpCache;
|
return %StringReplaceRegExpWithString(subject,
|
||||||
if (%_ObjectEquals(cache.type, 'replace') &&
|
regexp,
|
||||||
%_IsRegExpEquivalent(cache.regExp, regexp) &&
|
TO_STRING_INLINE(replace),
|
||||||
%_ObjectEquals(cache.replaceString, replace) &&
|
lastMatchInfo);
|
||||||
%_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -605,34 +562,14 @@ function StringSplit(separator, limit) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cache = regExpCache;
|
|
||||||
var saveAnswer = false;
|
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]);
|
%_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
|
||||||
|
|
||||||
if (length === 0) {
|
if (length === 0) {
|
||||||
cache.answerSaved = true;
|
|
||||||
if (splitMatch(separator, subject, 0, 0) != null) {
|
if (splitMatch(separator, subject, 0, 0) != null) {
|
||||||
cache.answer = [];
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
cache.answer = [subject];
|
|
||||||
return [subject];
|
return [subject];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,8 +617,6 @@ function StringSplit(separator, limit) {
|
|||||||
|
|
||||||
startIndex = currentIndex = endIndex;
|
startIndex = currentIndex = endIndex;
|
||||||
}
|
}
|
||||||
if (saveAnswer) cache.answer = CloneDenseArray(result);
|
|
||||||
cache.answerSaved = saveAnswer;
|
|
||||||
return result;
|
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 {
|
class DeferredSearchCache: public DeferredCode {
|
||||||
public:
|
public:
|
||||||
DeferredSearchCache(Register dst,
|
DeferredSearchCache(Register dst,
|
||||||
|
@ -656,8 +656,6 @@ class CodeGenerator: public AstVisitor {
|
|||||||
|
|
||||||
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
|
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
|
|
||||||
|
|
||||||
// Support for fast native caches.
|
// Support for fast native caches.
|
||||||
void GenerateGetFromCache(ZoneList<Expression*>* args);
|
void GenerateGetFromCache(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user