Commit bedff67b authored by whesse@chromium.org's avatar whesse@chromium.org

Make Array.sort safely generic on JSObject types. Fix bug 346...

Make Array.sort safely generic on JSObject types.  Fix bug 346 http://code.google.com/p/v8/issues/detail?id=346
Review URL: http://codereview.chromium.org/119357

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2133 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 8bd85d8c
...@@ -769,6 +769,63 @@ function ArraySort(comparefn) { ...@@ -769,6 +769,63 @@ function ArraySort(comparefn) {
} }
} }
function SafeRemoveArrayHoles(obj) {
// Copy defined elements from the end to fill in all holes and undefineds
// in the beginning of the array. Write undefineds and holes at the end
// after loop is finished.
var first_undefined = 0;
var last_defined = length - 1;
var num_holes = 0;
while (first_undefined < last_defined) {
// Find first undefined element.
while (first_undefined < last_defined &&
!IS_UNDEFINED(obj[first_undefined])) {
first_undefined++;
}
// Maintain the invariant num_holes = the number of holes in the original
// array with indices <= first_undefined or > last_defined.
if (!obj.hasOwnProperty(first_undefined)) {
num_holes++;
}
// Find last defined element.
while (first_undefined < last_defined &&
IS_UNDEFINED(obj[last_defined])) {
if (!obj.hasOwnProperty(last_defined)) {
num_holes++;
}
last_defined--;
}
if (first_undefined < last_defined) {
// Fill in hole or undefined.
obj[first_undefined] = obj[last_defined];
obj[last_defined] = void 0;
}
}
// If there were any undefineds in the entire array, first_undefined
// points to one past the last defined element. Make this true if
// there were no undefineds, as well, so that first_undefined == number
// of defined elements.
if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
// Fill in the undefineds and the holes. There may be a hole where
// an undefined should be and vice versa.
var i;
for (i = first_undefined; i < length - num_holes; i++) {
obj[i] = void 0;
}
for (i = length - num_holes; i < length; i++) {
// For compatability with Webkit, do not expose elements in the prototype.
if (i in obj.__proto__) {
obj[i] = void 0;
} else {
delete obj[i];
}
}
// Return the number of defined elements.
return first_undefined;
}
var length = ToUint32(this.length); var length = ToUint32(this.length);
if (length < 2) return this; if (length < 2) return this;
...@@ -787,6 +844,12 @@ function ArraySort(comparefn) { ...@@ -787,6 +844,12 @@ function ArraySort(comparefn) {
} }
var num_non_undefined = %RemoveArrayHoles(this, length); var num_non_undefined = %RemoveArrayHoles(this, length);
if (num_non_undefined == -1) {
// There were indexed accessors in the array. Move array holes and
// undefineds to the end using a Javascript function that is safe
// in the presence of accessors.
num_non_undefined = SafeRemoveArrayHoles(this);
}
QuickSort(this, 0, num_non_undefined); QuickSort(this, 0, num_non_undefined);
......
...@@ -6436,10 +6436,6 @@ Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) { ...@@ -6436,10 +6436,6 @@ Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
AssertNoAllocation no_alloc; AssertNoAllocation no_alloc;
// Loose all details on properties when moving them around.
// Elements do not have special details like properties.
PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
uint32_t pos = 0; uint32_t pos = 0;
uint32_t undefs = 0; uint32_t undefs = 0;
for (int i = 0; i < capacity; i++) { for (int i = 0; i < capacity; i++) {
...@@ -6450,21 +6446,27 @@ Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) { ...@@ -6450,21 +6446,27 @@ Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0); ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32); ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
Object* value = dict->ValueAt(i); Object* value = dict->ValueAt(i);
PropertyDetails details = dict->DetailsAt(i);
if (details.type() == CALLBACKS) {
// Bail out and do the sorting of undefineds and array holes in JS.
return Smi::FromInt(-1);
}
uint32_t key = NumberToUint32(k); uint32_t key = NumberToUint32(k);
if (key < limit) { if (key < limit) {
if (value->IsUndefined()) { if (value->IsUndefined()) {
undefs++; undefs++;
} else { } else {
new_dict->AddNumberEntry(pos, value, no_details); new_dict->AddNumberEntry(pos, value, details);
pos++; pos++;
} }
} else { } else {
new_dict->AddNumberEntry(key, value, no_details); new_dict->AddNumberEntry(key, value, details);
} }
} }
} }
uint32_t result = pos; uint32_t result = pos;
PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
while (undefs > 0) { while (undefs > 0) {
new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details); new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details);
pos++; pos++;
......
...@@ -2483,7 +2483,7 @@ class Map: public HeapObject { ...@@ -2483,7 +2483,7 @@ class Map: public HeapObject {
return ((1 << kIsHiddenPrototype) & bit_field()) != 0; return ((1 << kIsHiddenPrototype) & bit_field()) != 0;
} }
// Tells whether the instance has a named interceptor. // Records and queries whether the instance has a named interceptor.
inline void set_has_named_interceptor() { inline void set_has_named_interceptor() {
set_bit_field(bit_field() | (1 << kHasNamedInterceptor)); set_bit_field(bit_field() | (1 << kHasNamedInterceptor));
} }
...@@ -2492,7 +2492,7 @@ class Map: public HeapObject { ...@@ -2492,7 +2492,7 @@ class Map: public HeapObject {
return ((1 << kHasNamedInterceptor) & bit_field()) != 0; return ((1 << kHasNamedInterceptor) & bit_field()) != 0;
} }
// Tells whether the instance has a named interceptor. // Records and queries whether the instance has an indexed interceptor.
inline void set_has_indexed_interceptor() { inline void set_has_indexed_interceptor() {
set_bit_field(bit_field() | (1 << kHasIndexedInterceptor)); set_bit_field(bit_field() | (1 << kHasIndexedInterceptor));
} }
...@@ -4021,10 +4021,9 @@ class JSArray: public JSObject { ...@@ -4021,10 +4021,9 @@ class JSArray: public JSObject {
// If an accessor was found and it does not have a setter, // If an accessor was found and it does not have a setter,
// the request is ignored. // the request is ignored.
// //
// To allow shadow an accessor property, the accessor can // If the accessor in the prototype has the READ_ONLY property attribute, then
// have READ_ONLY property attribute so that a new value // a new value is added to the local object when the property is set.
// is added to the local object to shadow the accessor // This shadows the accessor in the prototype.
// in prototypes.
class AccessorInfo: public Struct { class AccessorInfo: public Struct {
public: public:
DECL_ACCESSORS(getter, Object) DECL_ACCESSORS(getter, Object)
......
...@@ -214,6 +214,30 @@ TestNonArrayLongerLength(500000); ...@@ -214,6 +214,30 @@ TestNonArrayLongerLength(500000);
TestNonArrayLongerLength(Math.pow(2,32) - 1); TestNonArrayLongerLength(Math.pow(2,32) - 1);
function TestNonArrayWithAccessors() {
// Regression test for issue 346, more info at URL
// http://code.google.com/p/v8/issues/detail?id=346
// Reported by nth10sd, test based on this report.
var x = {};
x[0] = 42;
x.__defineGetter__("1", function(){return this.foo;});
x.__defineSetter__("1", function(val){this.foo = val;});
x[1] = 49
x[3] = 37;
x.length = 4;
Array.prototype.sort.call(x);
// Behavior of sort with accessors is undefined. This accessor is
// well-behaved (acts like a normal property), so it should work.
assertEquals(4, x.length, "sortaccessors length");
assertEquals(37, x[0], "sortaccessors first");
assertEquals(42, x[1], "sortaccessors second");
assertEquals(49, x[2], "sortaccessors third")
assertFalse(3 in x, "sortaccessors fourth");
}
TestNonArrayWithAccessors();
function TestInheritedElementSort(depth) { function TestInheritedElementSort(depth) {
var length = depth * 2 + 3; var length = depth * 2 + 3;
var obj = {length: length}; var obj = {length: length};
...@@ -268,7 +292,7 @@ function TestSparseInheritedElementSort(scale) { ...@@ -268,7 +292,7 @@ function TestSparseInheritedElementSort(scale) {
assertEquals(i, y[i], name + "value" + i); assertEquals(i, y[i], name + "value" + i);
} }
for (var i = 10; i < length; i++) { for (var i = 10; i < length; i++) {
assertEquals(x.hasOwnProperty(i), y.hasOwnProperty(i), assertEquals(x.hasOwnProperty(i), y.hasOwnProperty(i),
name + "hasundef" + i); name + "hasundef" + i);
assertEquals(undefined, y[i], name+"undefined"+i); assertEquals(undefined, y[i], name+"undefined"+i);
if (x.hasOwnProperty(i)) { if (x.hasOwnProperty(i)) {
...@@ -282,7 +306,7 @@ TestSparseInheritedElementSort(100); ...@@ -282,7 +306,7 @@ TestSparseInheritedElementSort(100);
TestSparseInheritedElementSort(1000); TestSparseInheritedElementSort(1000);
function TestSpecialCasesInheritedElementSort() { function TestSpecialCasesInheritedElementSort() {
var x = { var x = {
1:"d1", 1:"d1",
2:"c1", 2:"c1",
...@@ -309,11 +333,11 @@ function TestSpecialCasesInheritedElementSort() { ...@@ -309,11 +333,11 @@ function TestSpecialCasesInheritedElementSort() {
} }
}; };
Array.prototype.sort.call(x); Array.prototype.sort.call(x);
var name = "SpecialInherit-"; var name = "SpecialInherit-";
assertEquals(10000, x.length, name + "length"); assertEquals(10000, x.length, name + "length");
var sorted = ["a2", "a3", "b1", "b2", "c1", "c2", "d1", "d2", "e3", var sorted = ["a2", "a3", "b1", "b2", "c1", "c2", "d1", "d2", "e3",
undefined, undefined, undefined]; undefined, undefined, undefined];
for (var i = 0; i < sorted.length; i++) { for (var i = 0; i < sorted.length; i++) {
assertTrue(x.hasOwnProperty(i), name + "has" + i) assertTrue(x.hasOwnProperty(i), name + "has" + i)
...@@ -321,7 +345,6 @@ function TestSpecialCasesInheritedElementSort() { ...@@ -321,7 +345,6 @@ function TestSpecialCasesInheritedElementSort() {
} }
assertFalse(x.hasOwnProperty(sorted.length), name + "haspost"); assertFalse(x.hasOwnProperty(sorted.length), name + "haspost");
assertFalse(sorted.length in x, name + "haspost2"); assertFalse(sorted.length in x, name + "haspost2");
assertTrue(x.hasOwnProperty(10), name + "hasundefined10"); assertTrue(x.hasOwnProperty(10), name + "hasundefined10");
assertEquals(undefined, x[10], name + "undefined10"); assertEquals(undefined, x[10], name + "undefined10");
assertTrue(x.hasOwnProperty(100), name + "hasundefined100"); assertTrue(x.hasOwnProperty(100), name + "hasundefined100");
...@@ -332,11 +355,8 @@ function TestSpecialCasesInheritedElementSort() { ...@@ -332,11 +355,8 @@ function TestSpecialCasesInheritedElementSort() {
assertEquals(undefined, x[2000], name + "undefined2000"); assertEquals(undefined, x[2000], name + "undefined2000");
assertTrue(x.hasOwnProperty(8000), name + "hasundefined8000"); assertTrue(x.hasOwnProperty(8000), name + "hasundefined8000");
assertEquals(undefined, x[8000], name + "undefined8000"); assertEquals(undefined, x[8000], name + "undefined8000");
assertFalse(x.hasOwnProperty(12000), name + "has12000"); assertFalse(x.hasOwnProperty(12000), name + "has12000");
assertEquals("XX", x[12000], name + "XX12000"); assertEquals("XX", x[12000], name + "XX12000");
} }
TestSpecialCasesInheritedElementSort(); TestSpecialCasesInheritedElementSort();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment