Commit 68f1c73a authored by lrn@chromium.org's avatar lrn@chromium.org

Fix array concat to follow the specification in the presence of element getters.

Also fix issue 1175 and 1177.

BUG=v8:1175

Review URL: http://codereview.chromium.org/6568007

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6934 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent ef0f8985
......@@ -418,7 +418,6 @@ function ArrayPush() {
function ArrayConcat(arg1) { // length == 1
// TODO: can we just use arguments?
var arg_count = %_ArgumentsLength();
var arrays = new $Array(1 + arg_count);
arrays[0] = this;
......
......@@ -36,14 +36,14 @@
namespace v8 {
namespace internal {
template<class T>
template<typename T>
Handle<T>::Handle(T* obj) {
ASSERT(!obj->IsFailure());
location_ = HandleScope::CreateHandle(obj);
}
template <class T>
template <typename T>
inline T* Handle<T>::operator*() const {
ASSERT(location_ != NULL);
ASSERT(reinterpret_cast<Address>(*location_) != kHandleZapValue);
......@@ -51,6 +51,16 @@ inline T* Handle<T>::operator*() const {
}
template <typename T>
HandleCell<T>::HandleCell(T* value)
: location_(HandleScope::CreateHandle(value)) { }
template <typename T>
HandleCell<T>::HandleCell(Handle<T> value)
: location_(HandleScope::CreateHandle(*value)) { }
#ifdef DEBUG
inline NoHandleAllocation::NoHandleAllocation() {
v8::ImplementationUtilities::HandleScopeData* current =
......
......@@ -39,7 +39,7 @@ namespace internal {
// Handles are only valid within a HandleScope.
// When a handle is created for an object a cell is allocated in the heap.
template<class T>
template<typename T>
class Handle {
public:
INLINE(explicit Handle(T** location)) { location_ = location; }
......@@ -93,6 +93,55 @@ class Handle {
};
// A handle-scope based variable. The value stored in the variable can change
// over time. The value stored in the variable at any time is a root
// for garbage collection.
// The variable is backed by the current HandleScope.
template <typename T>
class HandleCell {
public:
// Create a new HandleCell holding the given value.
explicit HandleCell(Handle<T> value);
explicit HandleCell(T* value);
// Create an alias of an existing HandleCell.
explicit HandleCell(const HandleCell<T>& value)
: location_(value.location_) { }
INLINE(T* operator->() const) { return operator*(); }
INLINE(T* operator*() const) {
return *location_;
}
INLINE(void operator=(T* value)) {
*location_ = value;
}
INLINE(void operator=(Handle<T> value)) {
*location_ = *value;
}
INLINE(void operator=(const HandleCell<T>& value)) {
*location_ = *value.location_;
}
// Extract the value of the variable and cast it to a give type.
// This is typically used for calling methods on a more specialized type.
template <typename S>
inline S* cast() {
S::cast(*location_);
return *reinterpret_cast<S**>(location_);
}
Handle<T> ToHandle() const {
return Handle<T>(*location_);
}
private:
// Prevent implicit constructor from being created.
HandleCell();
T** location_;
};
// A stack-allocated class that governs a number of local handles.
// After a handle scope has been created, all local handles will be
// allocated within that handle scope until either the handle scope is
......
......@@ -769,6 +769,10 @@ bool Object::HasSpecificClassOf(String* name) {
MaybeObject* Object::GetElement(uint32_t index) {
// GetElement can trigger a getter which can cause allocation.
// This was not always the case. This ASSERT is here to catch
// leftover incorrect uses.
ASSERT(Heap::IsAllocationAllowed());
return GetElementWithReceiver(this, index);
}
......
This diff is collapsed.
......@@ -101,7 +101,6 @@ while (pos = poses.shift()) {
assertEquals("undefined", typeof(c[-1]));
assertEquals("undefined", typeof(c[0xffffffff]));
assertEquals(c.length, a.length + 1);
}
poses = [140, 4000000000];
......@@ -193,3 +192,46 @@ for (var i = 0; i < holey.length; i++) {
assertTrue(i in holey);
}
}
// Polluted prototype from prior tests.
delete Array.prototype[123];
// Check that concat reads getters in the correct order.
var arr1 = [,2];
var arr2 = [1,3];
var r1 = [].concat(arr1, arr2); // [,2,1,3]
assertEquals([,2,1,3], r1);
// Make first array change length of second array.
Object.defineProperty(arr1, 0, {get: function() {
arr2.push("X");
return undefined;
}, configurable: true})
var r2 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"]
assertEquals([undefined,2,1,3,"X"], r2);
// Make first array change length of second array massively.
arr2.length = 2;
Object.defineProperty(arr1, 0, {get: function() {
arr2[500000] = "X";
return undefined;
}, configurable: true})
var r3 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"]
var expected = [undefined,2,1,3];
expected[500000 + 2] = "X";
assertEquals(expected, r3);
var arr3 = [];
var trace = [];
var expectedTrace = []
function mkGetter(i) { return function() { trace.push(i); }; }
arr3.length = 10000;
for (var i = 0; i < 100; i++) {
Object.defineProperty(arr3, i * i, {get: mkGetter(i)});
expectedTrace[i] = i;
expectedTrace[100 + i] = i;
}
var r4 = [0].concat(arr3, arr3);
assertEquals(1 + arr3.length * 2, r4.length);
assertEquals(expectedTrace, trace);
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