- Specialized jscre on the type of the string involved.
- Specialized jscre on the type of the string involved. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@476 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
4f7b6654d4
commit
a7230abb92
@ -223,7 +223,6 @@ void Heap::ReportStatisticsAfterGC() {
|
||||
|
||||
|
||||
void Heap::GarbageCollectionPrologue() {
|
||||
RegExpImpl::NewSpaceCollectionPrologue();
|
||||
gc_count_++;
|
||||
#ifdef DEBUG
|
||||
ASSERT(allocation_allowed_ && gc_state_ == NOT_IN_GC);
|
||||
@ -424,7 +423,6 @@ void Heap::MarkCompact(GCTracer* tracer) {
|
||||
|
||||
void Heap::MarkCompactPrologue() {
|
||||
CompilationCache::MarkCompactPrologue();
|
||||
RegExpImpl::OldSpaceCollectionPrologue();
|
||||
Top::MarkCompactPrologue();
|
||||
ThreadManager::MarkCompactPrologue();
|
||||
}
|
||||
|
187
src/jsregexp.cc
187
src/jsregexp.cc
@ -65,27 +65,6 @@ static void JSREFree(void* p) {
|
||||
}
|
||||
|
||||
|
||||
String* RegExpImpl::last_ascii_string_ = NULL;
|
||||
String* RegExpImpl::two_byte_cached_string_ = NULL;
|
||||
|
||||
|
||||
void RegExpImpl::NewSpaceCollectionPrologue() {
|
||||
// The two byte string is always in the old space. The Ascii string may be
|
||||
// in either place. If it is in the old space we don't need to do anything.
|
||||
if (Heap::InNewSpace(last_ascii_string_)) {
|
||||
// Invalidate the cache.
|
||||
last_ascii_string_ = NULL;
|
||||
two_byte_cached_string_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RegExpImpl::OldSpaceCollectionPrologue() {
|
||||
last_ascii_string_ = NULL;
|
||||
two_byte_cached_string_ = NULL;
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> RegExpImpl::CreateRegExpLiteral(Handle<JSFunction> constructor,
|
||||
Handle<String> pattern,
|
||||
Handle<String> flags,
|
||||
@ -102,47 +81,6 @@ Handle<Object> RegExpImpl::CreateRegExpLiteral(Handle<JSFunction> constructor,
|
||||
}
|
||||
|
||||
|
||||
// Converts a source string to a 16 bit flat string or a SlicedString containing
|
||||
// a 16 bit flat string).
|
||||
Handle<String> RegExpImpl::CachedStringToTwoByte(Handle<String> subject) {
|
||||
if (*subject == last_ascii_string_) {
|
||||
ASSERT(two_byte_cached_string_ != NULL);
|
||||
return Handle<String>(String::cast(two_byte_cached_string_));
|
||||
}
|
||||
Handle<String> two_byte_string = StringToTwoByte(subject);
|
||||
last_ascii_string_ = *subject;
|
||||
two_byte_cached_string_ = *two_byte_string;
|
||||
return two_byte_string;
|
||||
}
|
||||
|
||||
|
||||
// Converts a source string to a 16 bit flat string or a SlicedString containing
|
||||
// a 16 bit flat string).
|
||||
Handle<String> RegExpImpl::StringToTwoByte(Handle<String> pattern) {
|
||||
if (!pattern->IsFlat()) {
|
||||
FlattenString(pattern);
|
||||
}
|
||||
Handle<String> flat_string(pattern->IsConsString() ?
|
||||
String::cast(ConsString::cast(*pattern)->first()) :
|
||||
*pattern);
|
||||
ASSERT(!flat_string->IsConsString());
|
||||
ASSERT(flat_string->IsSeqString() || flat_string->IsSlicedString() ||
|
||||
flat_string->IsExternalString());
|
||||
if (!flat_string->IsAsciiRepresentation()) {
|
||||
return flat_string;
|
||||
}
|
||||
|
||||
Handle<String> two_byte_string =
|
||||
Factory::NewRawTwoByteString(flat_string->length(), TENURED);
|
||||
static StringInputBuffer convert_to_two_byte_buffer;
|
||||
convert_to_two_byte_buffer.Reset(*flat_string);
|
||||
for (int i = 0; convert_to_two_byte_buffer.has_more(); i++) {
|
||||
two_byte_string->Set(i, convert_to_two_byte_buffer.GetNext());
|
||||
}
|
||||
return two_byte_string;
|
||||
}
|
||||
|
||||
|
||||
unibrow::Predicate<unibrow::RegExpSpecialChar, 128> is_reg_exp_special_char;
|
||||
|
||||
|
||||
@ -189,7 +127,14 @@ Handle<Object> RegExpImpl::ExecGlobal(Handle<JSRegExp> regexp,
|
||||
Handle<String> subject) {
|
||||
switch (regexp->type_tag()) {
|
||||
case JSRegExp::JSCRE:
|
||||
return JsreExecGlobal(regexp, subject);
|
||||
FlattenString(subject);
|
||||
if (subject->IsAsciiRepresentation()) {
|
||||
Vector<const char> contents = subject->ToAsciiVector();
|
||||
return JsreExecGlobal(regexp, subject, contents);
|
||||
} else {
|
||||
Vector<const uc16> contents = subject->ToUC16Vector();
|
||||
return JsreExecGlobal(regexp, subject, contents);
|
||||
}
|
||||
case JSRegExp::ATOM:
|
||||
return AtomExecGlobal(regexp, subject);
|
||||
default:
|
||||
@ -268,16 +213,34 @@ Handle<Object> RegExpImpl::JsreCompile(Handle<JSRegExp> re,
|
||||
if (flags->Get(i) == 'm') multiline_option = JSRegExpMultiline;
|
||||
}
|
||||
|
||||
Handle<String> two_byte_pattern = StringToTwoByte(pattern);
|
||||
|
||||
unsigned number_of_captures;
|
||||
const char* error_message = NULL;
|
||||
|
||||
malloc_failure = Failure::Exception();
|
||||
JscreRegExp* code = jsRegExpCompile(two_byte_pattern->GetTwoByteData(),
|
||||
pattern->length(), case_option,
|
||||
multiline_option, &number_of_captures,
|
||||
&error_message, &JSREMalloc, &JSREFree);
|
||||
JscreRegExp* code;
|
||||
FlattenString(pattern);
|
||||
|
||||
if (pattern->IsAsciiRepresentation()) {
|
||||
Vector<const char> contents = pattern->ToAsciiVector();
|
||||
code = jsRegExpCompile(contents.start(),
|
||||
contents.length(),
|
||||
case_option,
|
||||
multiline_option,
|
||||
&number_of_captures,
|
||||
&error_message,
|
||||
&JSREMalloc,
|
||||
&JSREFree);
|
||||
} else {
|
||||
Vector<const uc16> contents = pattern->ToUC16Vector();
|
||||
code = jsRegExpCompile(contents.start(),
|
||||
contents.length(),
|
||||
case_option,
|
||||
multiline_option,
|
||||
&number_of_captures,
|
||||
&error_message,
|
||||
&JSREMalloc,
|
||||
&JSREFree);
|
||||
}
|
||||
|
||||
if (code == NULL && malloc_failure->IsRetryAfterGC()) {
|
||||
// Performs a GC, then retries.
|
||||
@ -287,10 +250,29 @@ Handle<Object> RegExpImpl::JsreCompile(Handle<JSRegExp> re,
|
||||
V8::FatalProcessOutOfMemory("RegExpImpl::JsreCompile");
|
||||
}
|
||||
malloc_failure = Failure::Exception();
|
||||
code = jsRegExpCompile(two_byte_pattern->GetTwoByteData(),
|
||||
pattern->length(), case_option,
|
||||
multiline_option, &number_of_captures,
|
||||
&error_message, &JSREMalloc, &JSREFree);
|
||||
|
||||
if (pattern->IsAsciiRepresentation()) {
|
||||
Vector<const char> contents = pattern->ToAsciiVector();
|
||||
code = jsRegExpCompile(contents.start(),
|
||||
contents.length(),
|
||||
case_option,
|
||||
multiline_option,
|
||||
&number_of_captures,
|
||||
&error_message,
|
||||
&JSREMalloc,
|
||||
&JSREFree);
|
||||
} else {
|
||||
Vector<const uc16> contents = pattern->ToUC16Vector();
|
||||
code = jsRegExpCompile(contents.start(),
|
||||
contents.length(),
|
||||
case_option,
|
||||
multiline_option,
|
||||
&number_of_captures,
|
||||
&error_message,
|
||||
&JSREMalloc,
|
||||
&JSREFree);
|
||||
}
|
||||
|
||||
if (code == NULL && malloc_failure->IsRetryAfterGC()) {
|
||||
// TODO(1181417): Fix this.
|
||||
V8::FatalProcessOutOfMemory("RegExpImpl::JsreCompile");
|
||||
@ -299,10 +281,8 @@ Handle<Object> RegExpImpl::JsreCompile(Handle<JSRegExp> re,
|
||||
|
||||
if (error_message != NULL) {
|
||||
// Throw an exception.
|
||||
SmartPointer<char> char_pattern =
|
||||
two_byte_pattern->ToCString(DISALLOW_NULLS);
|
||||
Handle<JSArray> array = Factory::NewJSArray(2);
|
||||
SetElement(array, 0, Factory::NewStringFromUtf8(CStrVector(*char_pattern)));
|
||||
SetElement(array, 0, pattern);
|
||||
SetElement(array, 1, Factory::NewStringFromUtf8(CStrVector(error_message)));
|
||||
Handle<Object> regexp_err =
|
||||
Factory::NewSyntaxError("malformed_regexp", array);
|
||||
@ -325,11 +305,12 @@ Handle<Object> RegExpImpl::JsreCompile(Handle<JSRegExp> re,
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
Handle<Object> RegExpImpl::JsreExecOnce(Handle<JSRegExp> regexp,
|
||||
int num_captures,
|
||||
Handle<String> subject,
|
||||
int previous_index,
|
||||
const uc16* two_byte_subject,
|
||||
Vector<const T> contents,
|
||||
int* offsets_vector,
|
||||
int offsets_vector_length) {
|
||||
int rc;
|
||||
@ -341,12 +322,12 @@ Handle<Object> RegExpImpl::JsreExecOnce(Handle<JSRegExp> regexp,
|
||||
|
||||
LOG(RegExpExecEvent(regexp, previous_index, subject));
|
||||
|
||||
rc = jsRegExpExecute(js_regexp,
|
||||
two_byte_subject,
|
||||
subject->length(),
|
||||
previous_index,
|
||||
offsets_vector,
|
||||
offsets_vector_length);
|
||||
rc = jsRegExpExecute<T>(js_regexp,
|
||||
contents.start(),
|
||||
contents.length(),
|
||||
previous_index,
|
||||
offsets_vector,
|
||||
offsets_vector_length);
|
||||
}
|
||||
|
||||
// The KJS JavaScript engine returns null (ie, a failed match) when
|
||||
@ -428,19 +409,29 @@ Handle<Object> RegExpImpl::JsreExec(Handle<JSRegExp> regexp,
|
||||
|
||||
int previous_index = static_cast<int>(DoubleToInteger(index->Number()));
|
||||
|
||||
Handle<String> subject16 = CachedStringToTwoByte(subject);
|
||||
|
||||
Handle<Object> result(JsreExecOnce(regexp, num_captures, subject,
|
||||
previous_index,
|
||||
subject16->GetTwoByteData(),
|
||||
offsets.vector(), offsets.length()));
|
||||
|
||||
return result;
|
||||
FlattenString(subject);
|
||||
if (subject->IsAsciiRepresentation()) {
|
||||
Vector<const char> contents = subject->ToAsciiVector();
|
||||
Handle<Object> result(JsreExecOnce(regexp, num_captures, subject,
|
||||
previous_index,
|
||||
contents,
|
||||
offsets.vector(), offsets.length()));
|
||||
return result;
|
||||
} else {
|
||||
Vector<const uc16> contents = subject->ToUC16Vector();
|
||||
Handle<Object> result(JsreExecOnce(regexp, num_captures, subject,
|
||||
previous_index,
|
||||
contents,
|
||||
offsets.vector(), offsets.length()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
Handle<Object> RegExpImpl::JsreExecGlobal(Handle<JSRegExp> regexp,
|
||||
Handle<String> subject) {
|
||||
Handle<String> subject,
|
||||
Vector<const T> contents) {
|
||||
// Prepare space for the return values.
|
||||
int num_captures = JsreCapture(regexp);
|
||||
|
||||
@ -452,17 +443,19 @@ Handle<Object> RegExpImpl::JsreExecGlobal(Handle<JSRegExp> regexp,
|
||||
int i = 0;
|
||||
Handle<Object> matches;
|
||||
|
||||
Handle<String> subject16 = CachedStringToTwoByte(subject);
|
||||
|
||||
do {
|
||||
if (previous_index > subject->length() || previous_index < 0) {
|
||||
// Per ECMA-262 15.10.6.2, if the previous index is greater than the
|
||||
// string length, there is no match.
|
||||
matches = Factory::null_value();
|
||||
} else {
|
||||
matches = JsreExecOnce(regexp, num_captures, subject, previous_index,
|
||||
subject16->GetTwoByteData(),
|
||||
offsets.vector(), offsets.length());
|
||||
matches = JsreExecOnce<T>(regexp,
|
||||
num_captures,
|
||||
subject,
|
||||
previous_index,
|
||||
contents,
|
||||
offsets.vector(),
|
||||
offsets.length());
|
||||
|
||||
if (matches->IsJSArray()) {
|
||||
SetElement(result, i, matches);
|
||||
|
@ -79,32 +79,24 @@ class RegExpImpl {
|
||||
Handle<String> subject,
|
||||
Handle<Object> index);
|
||||
|
||||
template <typename T>
|
||||
static Handle<Object> JsreExecGlobal(Handle<JSRegExp> regexp,
|
||||
Handle<String> subject);
|
||||
|
||||
static void NewSpaceCollectionPrologue();
|
||||
static void OldSpaceCollectionPrologue();
|
||||
Handle<String> subject,
|
||||
Vector<const T> contents);
|
||||
|
||||
private:
|
||||
// Converts a source string to a 16 bit flat string. The string
|
||||
// will be either sequential or it will be a SlicedString backed
|
||||
// by a flat string.
|
||||
static Handle<String> StringToTwoByte(Handle<String> pattern);
|
||||
static Handle<String> CachedStringToTwoByte(Handle<String> pattern);
|
||||
|
||||
static String* last_ascii_string_;
|
||||
static String* two_byte_cached_string_;
|
||||
|
||||
// Returns the caputure from the re.
|
||||
static int JsreCapture(Handle<JSRegExp> re);
|
||||
static ByteArray* JsreInternal(Handle<JSRegExp> re);
|
||||
|
||||
// Call jsRegExpExecute once
|
||||
template <typename T>
|
||||
static Handle<Object> JsreExecOnce(Handle<JSRegExp> regexp,
|
||||
int num_captures,
|
||||
Handle<String> subject,
|
||||
int previous_index,
|
||||
const uc16* utf8_subject,
|
||||
Vector<const T> contents,
|
||||
int* ovector,
|
||||
int ovector_length);
|
||||
|
||||
|
361
src/objects.cc
361
src/objects.cc
@ -964,7 +964,21 @@ Object* JSObject::AddFastProperty(String* name,
|
||||
return AddSlowProperty(name, value, attributes);
|
||||
}
|
||||
|
||||
// Replace a CONSTANT_TRANSITION flag with a transition.
|
||||
// Do this by removing it, and the standard code for adding a map transition
|
||||
// will then run.
|
||||
DescriptorArray* old_descriptors = map()->instance_descriptors();
|
||||
int old_name_index = old_descriptors->Search(name);
|
||||
bool constant_transition = false; // Only used in assertions.
|
||||
if (old_name_index != DescriptorArray::kNotFound && CONSTANT_TRANSITION ==
|
||||
PropertyDetails(old_descriptors->GetDetails(old_name_index)).type()) {
|
||||
constant_transition = true;
|
||||
Object* r = old_descriptors->CopyRemove(name);
|
||||
if (r->IsFailure()) return r;
|
||||
old_descriptors = DescriptorArray::cast(r);
|
||||
old_name_index = DescriptorArray::kNotFound;
|
||||
}
|
||||
|
||||
// Compute the new index for new field.
|
||||
int index = map()->NextFreePropertyIndex();
|
||||
|
||||
@ -979,43 +993,64 @@ Object* JSObject::AddFastProperty(String* name,
|
||||
bool allow_map_transition =
|
||||
!old_descriptors->Contains(name) &&
|
||||
(Top::context()->global_context()->object_function()->map() != map());
|
||||
ASSERT(allow_map_transition || !constant_transition);
|
||||
|
||||
ASSERT(index < properties()->length() ||
|
||||
map()->unused_property_fields() == 0);
|
||||
// Allocate a new map for the object.
|
||||
Object* r = map()->Copy();
|
||||
if (r->IsFailure()) return r;
|
||||
Map* new_map = Map::cast(r);
|
||||
if (allow_map_transition) {
|
||||
// Allocate new instance descriptors for the old map with map transition.
|
||||
MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
|
||||
Object* r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
|
||||
if (map()->unused_property_fields() > 0) {
|
||||
ASSERT(index < properties()->length());
|
||||
// Allocate a new map for the object.
|
||||
Object* r = map()->Copy();
|
||||
if (r->IsFailure()) return r;
|
||||
old_descriptors = DescriptorArray::cast(r);
|
||||
}
|
||||
Map* new_map = Map::cast(r);
|
||||
if (allow_map_transition) {
|
||||
// Allocate new instance descriptors for the old map with map transition.
|
||||
MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
|
||||
Object* r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
|
||||
if (r->IsFailure()) return r;
|
||||
old_descriptors = DescriptorArray::cast(r);
|
||||
}
|
||||
// We have now allocated all the necessary objects.
|
||||
// All the changes can be applied at once, so they are atomic.
|
||||
map()->set_instance_descriptors(old_descriptors);
|
||||
new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
|
||||
new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
|
||||
set_map(new_map);
|
||||
properties()->set(index, value);
|
||||
} else {
|
||||
ASSERT(map()->unused_property_fields() == 0);
|
||||
|
||||
|
||||
if (map()->unused_property_fields() == 0) {
|
||||
if (properties()->length() > kMaxFastProperties) {
|
||||
Object* obj = NormalizeProperties();
|
||||
if (obj->IsFailure()) return obj;
|
||||
return AddSlowProperty(name, value, attributes);
|
||||
}
|
||||
|
||||
static const int kExtraFields = 3;
|
||||
// Make room for the new value
|
||||
Object* values =
|
||||
properties()->CopySize(properties()->length() + kFieldsAdded);
|
||||
properties()->CopySize(properties()->length() + kExtraFields);
|
||||
if (values->IsFailure()) return values;
|
||||
FixedArray::cast(values)->set(index, value);
|
||||
|
||||
// Allocate a new map for the object.
|
||||
Object* r = map()->Copy();
|
||||
if (r->IsFailure()) return r;
|
||||
Map* new_map = Map::cast(r);
|
||||
|
||||
if (allow_map_transition) {
|
||||
MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
|
||||
// Allocate new instance descriptors for the old map with map transition.
|
||||
Object* r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
|
||||
if (r->IsFailure()) return r;
|
||||
old_descriptors = DescriptorArray::cast(r);
|
||||
}
|
||||
// We have now allocated all the necessary objects.
|
||||
// All changes can be done at once, atomically.
|
||||
map()->set_instance_descriptors(old_descriptors);
|
||||
new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
|
||||
new_map->set_unused_property_fields(kExtraFields - 1);
|
||||
set_map(new_map);
|
||||
set_properties(FixedArray::cast(values));
|
||||
new_map->set_unused_property_fields(kFieldsAdded - 1);
|
||||
} else {
|
||||
new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
|
||||
}
|
||||
// We have now allocated all the necessary objects.
|
||||
// All the changes can be applied at once, so they are atomic.
|
||||
map()->set_instance_descriptors(old_descriptors);
|
||||
new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
|
||||
set_map(new_map);
|
||||
properties()->set(index, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
@ -1069,6 +1104,74 @@ Object* JSObject::AddConstantFunctionProperty(String* name,
|
||||
}
|
||||
|
||||
|
||||
Object* JSObject::ReplaceConstantFunctionProperty(String* name,
|
||||
Object* value) {
|
||||
// There are two situations to handle here:
|
||||
// 1: Replace a constant function with another function.
|
||||
// 2: Replace a constant function with an object.
|
||||
if (value->IsJSFunction()) {
|
||||
JSFunction* function = JSFunction::cast(value);
|
||||
|
||||
Object* new_map = map()->CopyDropTransitions();
|
||||
if (new_map->IsFailure()) return new_map;
|
||||
set_map(Map::cast(new_map));
|
||||
|
||||
// Replace the function entry
|
||||
int index = map()->instance_descriptors()->Search(name);
|
||||
ASSERT(index != DescriptorArray::kNotFound);
|
||||
map()->instance_descriptors()->ReplaceConstantFunction(index, function);
|
||||
} else {
|
||||
// Allocate new instance descriptors with updated property index.
|
||||
int index = map()->NextFreePropertyIndex();
|
||||
Object* new_descriptors =
|
||||
map()->instance_descriptors()->CopyReplace(name, index, NONE);
|
||||
if (new_descriptors->IsFailure()) return new_descriptors;
|
||||
|
||||
if (map()->unused_property_fields() > 0) {
|
||||
ASSERT(index < properties()->length());
|
||||
|
||||
// Allocate a new map for the object.
|
||||
Object* new_map = map()->Copy();
|
||||
if (new_map->IsFailure()) return new_map;
|
||||
|
||||
Map::cast(new_map)->
|
||||
set_instance_descriptors(DescriptorArray::cast(new_descriptors));
|
||||
Map::cast(new_map)->
|
||||
set_unused_property_fields(map()->unused_property_fields()-1);
|
||||
set_map(Map::cast(new_map));
|
||||
properties()->set(index, value);
|
||||
} else {
|
||||
ASSERT(map()->unused_property_fields() == 0);
|
||||
static const int kFastNofProperties = 20;
|
||||
if (properties()->length() > kFastNofProperties) {
|
||||
Object* obj = NormalizeProperties();
|
||||
if (obj->IsFailure()) return obj;
|
||||
return SetProperty(name, value, NONE);
|
||||
}
|
||||
|
||||
static const int kExtraFields = 5;
|
||||
// Make room for the more properties.
|
||||
Object* values =
|
||||
properties()->CopySize(properties()->length() + kExtraFields);
|
||||
if (values->IsFailure()) return values;
|
||||
FixedArray::cast(values)->set(index, value);
|
||||
|
||||
// Allocate a new map for the object.
|
||||
Object* new_map = map()->Copy();
|
||||
if (new_map->IsFailure()) return new_map;
|
||||
|
||||
Map::cast(new_map)->
|
||||
set_instance_descriptors(DescriptorArray::cast(new_descriptors));
|
||||
Map::cast(new_map)->
|
||||
set_unused_property_fields(kExtraFields - 1);
|
||||
set_map(Map::cast(new_map));
|
||||
set_properties(FixedArray::cast(values));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
// Add property in slow mode
|
||||
Object* JSObject::AddSlowProperty(String* name,
|
||||
Object* value,
|
||||
@ -1120,103 +1223,6 @@ Object* JSObject::SetPropertyPostInterceptor(String* name,
|
||||
}
|
||||
|
||||
|
||||
Object* JSObject::ReplaceSlowProperty(String* name,
|
||||
Object* value,
|
||||
PropertyAttributes attributes) {
|
||||
Dictionary* dictionary = property_dictionary();
|
||||
PropertyDetails old_details =
|
||||
dictionary->DetailsAt(dictionary->FindStringEntry(name));
|
||||
int new_index = old_details.index();
|
||||
if (old_details.IsTransition()) new_index = 0;
|
||||
|
||||
PropertyDetails new_details(attributes, NORMAL, old_details.index());
|
||||
Object* result =
|
||||
property_dictionary()->SetOrAddStringEntry(name, value, new_details);
|
||||
if (result->IsFailure()) return result;
|
||||
if (property_dictionary() != result) {
|
||||
set_properties(Dictionary::cast(result));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Object* JSObject::ConvertDescriptorToFieldAndMapTransition(
|
||||
String* name,
|
||||
Object* new_value,
|
||||
PropertyAttributes attributes) {
|
||||
Map* old_map = map();
|
||||
Object* result = ConvertDescriptorToField(name, new_value, attributes);
|
||||
if (result->IsFailure()) return result;
|
||||
// If we get to this point we have succeeded - do not return failure
|
||||
// after this point. Later stuff is optional.
|
||||
if (!HasFastProperties()) {
|
||||
return result;
|
||||
}
|
||||
// Do not add transitions to the map of "new Object()".
|
||||
if (map() == Top::context()->global_context()->object_function()->map()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
MapTransitionDescriptor transition(name,
|
||||
map(),
|
||||
attributes);
|
||||
Object* new_descriptors =
|
||||
old_map->instance_descriptors()->
|
||||
CopyInsert(&transition, KEEP_TRANSITIONS);
|
||||
if (new_descriptors->IsFailure()) return result; // Yes, return _result_.
|
||||
old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Object* JSObject::ConvertDescriptorToField(String* name,
|
||||
Object* new_value,
|
||||
PropertyAttributes attributes) {
|
||||
if (map()->unused_property_fields() == 0 &&
|
||||
properties()->length() > kMaxFastProperties) {
|
||||
Object* obj = NormalizeProperties();
|
||||
if (obj->IsFailure()) return obj;
|
||||
return ReplaceSlowProperty(name, new_value, attributes);
|
||||
}
|
||||
|
||||
int index = map()->NextFreePropertyIndex();
|
||||
FieldDescriptor new_field(name, index, attributes);
|
||||
// Make a new DescriptorArray replacing an entry with FieldDescriptor.
|
||||
Object* descriptors_unchecked = map()->instance_descriptors()->
|
||||
CopyInsert(&new_field, REMOVE_TRANSITIONS);
|
||||
if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
|
||||
DescriptorArray* new_descriptors =
|
||||
DescriptorArray::cast(descriptors_unchecked);
|
||||
|
||||
// Make a new map for the object.
|
||||
Object* new_map_unchecked = map()->Copy();
|
||||
if (new_map_unchecked->IsFailure()) return new_map_unchecked;
|
||||
Map* new_map = Map::cast(new_map_unchecked);
|
||||
new_map->set_instance_descriptors(new_descriptors);
|
||||
|
||||
// Make new properties array if necessary.
|
||||
FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
|
||||
int new_unused_property_fields = map()->unused_property_fields() - 1;
|
||||
if (map()->unused_property_fields() == 0) {
|
||||
new_unused_property_fields = kFieldsAdded - 1;
|
||||
Object* new_properties_unchecked =
|
||||
properties()->CopySize(properties()->length() + kFieldsAdded);
|
||||
if (new_properties_unchecked->IsFailure()) return new_properties_unchecked;
|
||||
new_properties = FixedArray::cast(new_properties_unchecked);
|
||||
}
|
||||
|
||||
// Update pointers to commit changes.
|
||||
// Object points to the new map.
|
||||
new_map->set_unused_property_fields(new_unused_property_fields);
|
||||
set_map(new_map);
|
||||
if (new_properties) {
|
||||
set_properties(FixedArray::cast(new_properties));
|
||||
}
|
||||
properties()->set(index, new_value);
|
||||
return new_value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Object* JSObject::SetPropertyWithInterceptor(String* name,
|
||||
Object* value,
|
||||
PropertyAttributes attributes) {
|
||||
@ -1522,12 +1528,13 @@ Object* JSObject::SetProperty(LookupResult* result,
|
||||
return AddFastPropertyUsingMap(result->GetTransitionMap(),
|
||||
name,
|
||||
value);
|
||||
} else {
|
||||
return AddFastProperty(name, value, attributes);
|
||||
}
|
||||
return ConvertDescriptorToField(name, value, attributes);
|
||||
case CONSTANT_FUNCTION:
|
||||
if (value == result->GetConstantFunction()) return value;
|
||||
// Only replace the function if necessary.
|
||||
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
|
||||
return ReplaceConstantFunctionProperty(name, value);
|
||||
case CALLBACKS:
|
||||
return SetPropertyWithCallback(result->GetCallbackObject(),
|
||||
name,
|
||||
@ -1538,9 +1545,10 @@ Object* JSObject::SetProperty(LookupResult* result,
|
||||
case CONSTANT_TRANSITION:
|
||||
// Replace with a MAP_TRANSITION to a new map with a FIELD, even
|
||||
// if the value is a function.
|
||||
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
|
||||
// AddProperty has been extended to do this, in this case.
|
||||
return AddFastProperty(name, value, attributes);
|
||||
case NULL_DESCRIPTOR:
|
||||
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
|
||||
UNREACHABLE();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -1572,14 +1580,33 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
|
||||
&& !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
||||
return SetPropertyWithFailedAccessCheck(result, name, value);
|
||||
}
|
||||
// Check for accessor in prototype chain removed here in clone.
|
||||
/*
|
||||
REMOVED FROM CLONE
|
||||
if (result->IsNotFound() || !result->IsProperty()) {
|
||||
// We could not find a local property so let's check whether there is an
|
||||
// accessor that wants to handle the property.
|
||||
LookupResult accessor_result;
|
||||
LookupCallbackSetterInPrototypes(name, &accessor_result);
|
||||
if (accessor_result.IsValid()) {
|
||||
return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
|
||||
name,
|
||||
value,
|
||||
accessor_result.holder());
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (result->IsNotFound()) {
|
||||
return AddProperty(name, value, attributes);
|
||||
}
|
||||
if (!result->IsLoaded()) {
|
||||
return SetLazyProperty(result, name, value, attributes);
|
||||
}
|
||||
// Check of IsReadOnly removed from here in clone.
|
||||
/*
|
||||
REMOVED FROM CLONE
|
||||
if (result->IsReadOnly() && result->IsProperty()) return value;
|
||||
*/
|
||||
// This is a real property that is not read-only, or it is a
|
||||
// transition or null descriptor and there are no setters in the prototypes.
|
||||
switch (result->type()) {
|
||||
case NORMAL:
|
||||
property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
|
||||
@ -1594,12 +1621,12 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
|
||||
name,
|
||||
value);
|
||||
} else {
|
||||
return ConvertDescriptorToField(name, value, attributes);
|
||||
return AddFastProperty(name, value, attributes);
|
||||
}
|
||||
case CONSTANT_FUNCTION:
|
||||
if (value == result->GetConstantFunction()) return value;
|
||||
// Only replace the function if necessary.
|
||||
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
|
||||
return ReplaceConstantFunctionProperty(name, value);
|
||||
case CALLBACKS:
|
||||
return SetPropertyWithCallback(result->GetCallbackObject(),
|
||||
name,
|
||||
@ -1610,9 +1637,10 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
|
||||
case CONSTANT_TRANSITION:
|
||||
// Replace with a MAP_TRANSITION to a new map with a FIELD, even
|
||||
// if the value is a function.
|
||||
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
|
||||
// AddProperty has been extended to do this, in this case.
|
||||
return AddFastProperty(name, value, attributes);
|
||||
case NULL_DESCRIPTOR:
|
||||
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
|
||||
UNREACHABLE();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -2635,6 +2663,14 @@ void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
|
||||
}
|
||||
|
||||
|
||||
void DescriptorArray::ReplaceConstantFunction(int descriptor_number,
|
||||
JSFunction* value) {
|
||||
ASSERT(!Heap::InNewSpace(value));
|
||||
FixedArray* content_array = GetContentArray();
|
||||
fast_set(content_array, ToValueIndex(descriptor_number), value);
|
||||
}
|
||||
|
||||
|
||||
Object* DescriptorArray::CopyInsert(Descriptor* descriptor,
|
||||
TransitionFlag transition_flag) {
|
||||
// Transitions are only kept when inserting another transition.
|
||||
@ -2735,6 +2771,69 @@ Object* DescriptorArray::CopyInsert(Descriptor* descriptor,
|
||||
}
|
||||
|
||||
|
||||
Object* DescriptorArray::CopyReplace(String* name,
|
||||
int index,
|
||||
PropertyAttributes attributes) {
|
||||
// Allocate the new descriptor array.
|
||||
Object* result = DescriptorArray::Allocate(number_of_descriptors());
|
||||
if (result->IsFailure()) return result;
|
||||
|
||||
// Make sure only symbols are added to the instance descriptor.
|
||||
if (!name->IsSymbol()) {
|
||||
Object* result = Heap::LookupSymbol(name);
|
||||
if (result->IsFailure()) return result;
|
||||
name = String::cast(result);
|
||||
}
|
||||
|
||||
DescriptorWriter w(DescriptorArray::cast(result));
|
||||
for (DescriptorReader r(this); !r.eos(); r.advance()) {
|
||||
if (r.Equals(name)) {
|
||||
FieldDescriptor d(name, index, attributes);
|
||||
d.SetEnumerationIndex(r.GetDetails().index());
|
||||
w.Write(&d);
|
||||
} else {
|
||||
w.WriteFrom(&r);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the next enumeration index.
|
||||
DescriptorArray::cast(result)->
|
||||
SetNextEnumerationIndex(NextEnumerationIndex());
|
||||
|
||||
ASSERT(w.eos());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Object* DescriptorArray::CopyRemove(String* name) {
|
||||
if (!name->IsSymbol()) {
|
||||
Object* result = Heap::LookupSymbol(name);
|
||||
if (result->IsFailure()) return result;
|
||||
name = String::cast(result);
|
||||
}
|
||||
ASSERT(name->IsSymbol());
|
||||
Object* result = Allocate(number_of_descriptors() - 1);
|
||||
if (result->IsFailure()) return result;
|
||||
DescriptorArray* new_descriptors = DescriptorArray::cast(result);
|
||||
|
||||
// Set the enumeration index in the descriptors and set the enumeration index
|
||||
// in the result.
|
||||
new_descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
|
||||
// Write the old content and the descriptor information
|
||||
DescriptorWriter w(new_descriptors);
|
||||
DescriptorReader r(this);
|
||||
while (!r.eos()) {
|
||||
if (r.GetKey() != name) { // Both are symbols; object identity suffices.
|
||||
w.WriteFrom(&r);
|
||||
}
|
||||
r.advance();
|
||||
}
|
||||
ASSERT(w.eos());
|
||||
|
||||
return new_descriptors;
|
||||
}
|
||||
|
||||
|
||||
Object* DescriptorArray::RemoveTransitions() {
|
||||
// Remove all transitions. Return a copy of the array with all transitions
|
||||
// removed, or a Failure object if the new array could not be allocated.
|
||||
|
@ -1303,26 +1303,9 @@ class JSObject: public HeapObject {
|
||||
JSFunction* function,
|
||||
PropertyAttributes attributes);
|
||||
|
||||
Object* ReplaceSlowProperty(String* name,
|
||||
Object* value,
|
||||
PropertyAttributes attributes);
|
||||
|
||||
// Converts a descriptor of any other type to a real field,
|
||||
// backed by the properties array. Descriptors of visible
|
||||
// types, such as CONSTANT_FUNCTION, keep their enumeration order.
|
||||
// Converts the descriptor on the original object's map to a
|
||||
// map transition, and the the new field is on the object's new map.
|
||||
Object* ConvertDescriptorToFieldAndMapTransition(
|
||||
String* name,
|
||||
Object* new_value,
|
||||
PropertyAttributes attributes);
|
||||
|
||||
// Converts a descriptor of any other type to a real field,
|
||||
// backed by the properties array. Descriptors of visible
|
||||
// types, such as CONSTANT_FUNCTION, keep their enumeration order.
|
||||
Object* ConvertDescriptorToField(String* name,
|
||||
Object* new_value,
|
||||
PropertyAttributes attributes);
|
||||
// Replace a constant function property on a fast-case object.
|
||||
Object* ReplaceConstantFunctionProperty(String* name,
|
||||
Object* value);
|
||||
|
||||
// Add a property to a fast-case object.
|
||||
Object* AddFastProperty(String* name,
|
||||
@ -1394,10 +1377,6 @@ class JSObject: public HeapObject {
|
||||
static const uint32_t kMaxGap = 1024;
|
||||
static const int kMaxFastElementsLength = 5000;
|
||||
static const int kMaxFastProperties = 8;
|
||||
// When extending the backing storage for property values, we increase
|
||||
// its size by more than the 1 entry necessary, so sequentially adding fields
|
||||
// to the same object requires fewer allocations and copies.
|
||||
static const int kFieldsAdded = 3;
|
||||
|
||||
// Layout description.
|
||||
static const int kPropertiesOffset = HeapObject::kHeaderSize;
|
||||
@ -1583,6 +1562,7 @@ class DescriptorArray: public FixedArray {
|
||||
inline void Get(int descriptor_number, Descriptor* desc);
|
||||
inline void Set(int descriptor_number, Descriptor* desc);
|
||||
|
||||
void ReplaceConstantFunction(int descriptor_number, JSFunction* value);
|
||||
|
||||
// Copy the descriptor array, insert a new descriptor and optionally
|
||||
// remove map transitions. If the descriptor is already present, it is
|
||||
@ -1592,6 +1572,20 @@ class DescriptorArray: public FixedArray {
|
||||
// a transition, they must not be removed. All null descriptors are removed.
|
||||
Object* CopyInsert(Descriptor* descriptor, TransitionFlag transition_flag);
|
||||
|
||||
// Makes a copy of the descriptor array with the descriptor with key name
|
||||
// removed. If name is the empty string, the descriptor array is copied.
|
||||
// Transitions are removed if TransitionFlag is REMOVE_TRANSITIONS.
|
||||
// All null descriptors are removed.
|
||||
Object* CopyRemove(TransitionFlag remove_transitions, String* name);
|
||||
|
||||
// Copy the descriptor array, replace the property index and attributes
|
||||
// of the named property, but preserve its enumeration index.
|
||||
Object* CopyReplace(String* name, int index, PropertyAttributes attributes);
|
||||
|
||||
// Copy the descriptor array, removing the property index and attributes
|
||||
// of the named property.
|
||||
Object* CopyRemove(String* name);
|
||||
|
||||
// Remove all transitions. Return a copy of the array with all transitions
|
||||
// removed, or a Failure object if the new array could not be allocated.
|
||||
Object* RemoveTransitions();
|
||||
|
6
src/third_party/jscre/pcre.h
vendored
6
src/third_party/jscre/pcre.h
vendored
@ -66,13 +66,15 @@ const int JSRegExpErrorInternal = -4;
|
||||
typedef void* malloc_t(size_t size);
|
||||
typedef void free_t(void* address);
|
||||
|
||||
JSRegExp* jsRegExpCompile(const UChar* pattern, int patternLength,
|
||||
template <typename Char>
|
||||
JSRegExp* jsRegExpCompile(const Char* pattern, int patternLength,
|
||||
JSRegExpIgnoreCaseOption, JSRegExpMultilineOption,
|
||||
unsigned* numSubpatterns, const char** errorMessage,
|
||||
malloc_t* allocate_function, free_t* free_function);
|
||||
|
||||
template <typename Char>
|
||||
int jsRegExpExecute(const JSRegExp*,
|
||||
const UChar* subject, int subjectLength, int startOffset,
|
||||
const Char* subject, int subjectLength, int startOffset,
|
||||
int* offsetsVector, int offsetsVectorLength);
|
||||
|
||||
void jsRegExpFree(JSRegExp*);
|
||||
|
795
src/third_party/jscre/pcre_compile.cpp
vendored
795
src/third_party/jscre/pcre_compile.cpp
vendored
File diff suppressed because it is too large
Load Diff
612
src/third_party/jscre/pcre_exec.cpp
vendored
612
src/third_party/jscre/pcre_exec.cpp
vendored
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user