kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 1 | // Copyright 2011 the V8 project authors. All rights reserved. |
| 2 | // Redistribution and use in source and binary forms, with or without |
| 3 | // modification, are permitted provided that the following conditions are |
| 4 | // met: |
| 5 | // |
| 6 | // * Redistributions of source code must retain the above copyright |
| 7 | // notice, this list of conditions and the following disclaimer. |
| 8 | // * Redistributions in binary form must reproduce the above |
| 9 | // copyright notice, this list of conditions and the following |
| 10 | // disclaimer in the documentation and/or other materials provided |
| 11 | // with the distribution. |
| 12 | // * Neither the name of Google Inc. nor the names of its |
| 13 | // contributors may be used to endorse or promote products derived |
| 14 | // from this software without specific prior written permission. |
| 15 | // |
| 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | |
| 28 | #include "v8.h" |
| 29 | |
| 30 | #include "objects.h" |
| 31 | #include "elements.h" |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 32 | #include "utils.h" |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 33 | |
| 34 | namespace v8 { |
| 35 | namespace internal { |
| 36 | |
| 37 | |
| 38 | ElementsAccessor** ElementsAccessor::elements_accessors_; |
| 39 | |
| 40 | |
| 41 | bool HasKey(FixedArray* array, Object* key) { |
| 42 | int len0 = array->length(); |
| 43 | for (int i = 0; i < len0; i++) { |
| 44 | Object* element = array->get(i); |
| 45 | if (element->IsSmi() && element == key) return true; |
| 46 | if (element->IsString() && |
| 47 | key->IsString() && String::cast(element)->Equals(String::cast(key))) { |
| 48 | return true; |
| 49 | } |
| 50 | } |
| 51 | return false; |
| 52 | } |
| 53 | |
| 54 | |
| 55 | // Base class for element handler implementations. Contains the |
| 56 | // the common logic for objects with different ElementsKinds. |
| 57 | // Subclasses must specialize method for which the element |
| 58 | // implementation differs from the base class implementation. |
| 59 | // |
| 60 | // This class is intended to be used in the following way: |
| 61 | // |
| 62 | // class SomeElementsAccessor : |
| 63 | // public ElementsAccessorBase<SomeElementsAccessor, |
| 64 | // BackingStoreClass> { |
| 65 | // ... |
| 66 | // } |
| 67 | // |
| 68 | // This is an example of the Curiously Recurring Template Pattern (see |
| 69 | // http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). We use |
| 70 | // CRTP to guarantee aggressive compile time optimizations (i.e. inlining and |
| 71 | // specialization of SomeElementsAccessor methods). |
| 72 | template <typename ElementsAccessorSubclass, typename BackingStoreClass> |
| 73 | class ElementsAccessorBase : public ElementsAccessor { |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 74 | protected: |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 75 | ElementsAccessorBase() { } |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 76 | virtual MaybeObject* Get(FixedArrayBase* backing_store, |
| 77 | uint32_t key, |
| 78 | JSObject* obj, |
| 79 | Object* receiver) { |
| 80 | return ElementsAccessorSubclass::Get( |
| 81 | BackingStoreClass::cast(backing_store), key, obj, receiver); |
| 82 | } |
| 83 | |
| 84 | static MaybeObject* Get(BackingStoreClass* backing_store, |
| 85 | uint32_t key, |
| 86 | JSObject* obj, |
| 87 | Object* receiver) { |
| 88 | if (key < ElementsAccessorSubclass::GetCapacity(backing_store)) { |
| 89 | return backing_store->get(key); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 90 | } |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 91 | return backing_store->GetHeap()->the_hole_value(); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | virtual MaybeObject* Delete(JSObject* obj, |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 95 | uint32_t key, |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 96 | JSReceiver::DeleteMode mode) = 0; |
| 97 | |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 98 | virtual MaybeObject* AddElementsToFixedArray(FixedArrayBase* from, |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 99 | FixedArray* to, |
| 100 | JSObject* holder, |
| 101 | Object* receiver) { |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 102 | int len0 = to->length(); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 103 | #ifdef DEBUG |
| 104 | if (FLAG_enable_slow_asserts) { |
| 105 | for (int i = 0; i < len0; i++) { |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 106 | ASSERT(!to->get(i)->IsTheHole()); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 107 | } |
| 108 | } |
| 109 | #endif |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 110 | BackingStoreClass* backing_store = BackingStoreClass::cast(from); |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 111 | uint32_t len1 = ElementsAccessorSubclass::GetCapacity(backing_store); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 112 | |
| 113 | // Optimize if 'other' is empty. |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 114 | // We cannot optimize if 'this' is empty, as other may have holes. |
| 115 | if (len1 == 0) return to; |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 116 | |
| 117 | // Compute how many elements are not in other. |
| 118 | int extra = 0; |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 119 | for (uint32_t y = 0; y < len1; y++) { |
| 120 | if (ElementsAccessorSubclass::HasElementAtIndex(backing_store, |
| 121 | y, |
| 122 | holder, |
| 123 | receiver)) { |
| 124 | uint32_t key = |
| 125 | ElementsAccessorSubclass::GetKeyForIndex(backing_store, y); |
| 126 | MaybeObject* maybe_value = |
| 127 | ElementsAccessorSubclass::Get(backing_store, key, holder, receiver); |
| 128 | Object* value; |
| 129 | if (!maybe_value->ToObject(&value)) return maybe_value; |
| 130 | ASSERT(!value->IsTheHole()); |
| 131 | if (!HasKey(to, value)) { |
| 132 | extra++; |
| 133 | } |
| 134 | } |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 135 | } |
| 136 | |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 137 | if (extra == 0) return to; |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 138 | |
| 139 | // Allocate the result |
| 140 | FixedArray* result; |
| 141 | MaybeObject* maybe_obj = |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 142 | backing_store->GetHeap()->AllocateFixedArray(len0 + extra); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 143 | if (!maybe_obj->To<FixedArray>(&result)) return maybe_obj; |
| 144 | |
| 145 | // Fill in the content |
| 146 | { |
| 147 | AssertNoAllocation no_gc; |
| 148 | WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); |
| 149 | for (int i = 0; i < len0; i++) { |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 150 | Object* e = to->get(i); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 151 | ASSERT(e->IsString() || e->IsNumber()); |
| 152 | result->set(i, e, mode); |
| 153 | } |
| 154 | } |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 155 | // Fill in the extra values. |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 156 | int index = 0; |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 157 | for (uint32_t y = 0; y < len1; y++) { |
| 158 | if (ElementsAccessorSubclass::HasElementAtIndex(backing_store, |
| 159 | y, |
| 160 | holder, |
| 161 | receiver)) { |
| 162 | uint32_t key = |
| 163 | ElementsAccessorSubclass::GetKeyForIndex(backing_store, y); |
| 164 | MaybeObject* maybe_value = |
| 165 | ElementsAccessorSubclass::Get(backing_store, key, holder, receiver); |
| 166 | Object* value; |
| 167 | if (!maybe_value->ToObject(&value)) return maybe_value; |
| 168 | if (!value->IsTheHole() && !HasKey(to, value)) { |
| 169 | result->set(len0 + index, value); |
| 170 | index++; |
| 171 | } |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 172 | } |
| 173 | } |
| 174 | ASSERT(extra == index); |
| 175 | return result; |
| 176 | } |
| 177 | |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 178 | protected: |
| 179 | static uint32_t GetCapacity(BackingStoreClass* backing_store) { |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 180 | return backing_store->length(); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 181 | } |
| 182 | |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 183 | virtual uint32_t GetCapacity(FixedArrayBase* backing_store) { |
| 184 | return ElementsAccessorSubclass::GetCapacity( |
| 185 | BackingStoreClass::cast(backing_store)); |
whesse@chromium.org | 4acdc2c | 2011-08-15 13:01:23 +0000 | [diff] [blame] | 186 | } |
| 187 | |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 188 | static bool HasElementAtIndex(BackingStoreClass* backing_store, |
| 189 | uint32_t index, |
| 190 | JSObject* holder, |
| 191 | Object* receiver) { |
| 192 | uint32_t key = |
| 193 | ElementsAccessorSubclass::GetKeyForIndex(backing_store, index); |
| 194 | MaybeObject* element = ElementsAccessorSubclass::Get(backing_store, |
| 195 | key, |
| 196 | holder, |
| 197 | receiver); |
| 198 | return !element->IsTheHole(); |
| 199 | } |
| 200 | |
| 201 | virtual bool HasElementAtIndex(FixedArrayBase* backing_store, |
| 202 | uint32_t index, |
| 203 | JSObject* holder, |
| 204 | Object* receiver) { |
| 205 | return ElementsAccessorSubclass::HasElementAtIndex( |
| 206 | BackingStoreClass::cast(backing_store), index, holder, receiver); |
| 207 | } |
| 208 | |
| 209 | static uint32_t GetKeyForIndex(BackingStoreClass* backing_store, |
| 210 | uint32_t index) { |
| 211 | return index; |
| 212 | } |
| 213 | |
| 214 | virtual uint32_t GetKeyForIndex(FixedArrayBase* backing_store, |
| 215 | uint32_t index) { |
| 216 | return ElementsAccessorSubclass::GetKeyForIndex( |
| 217 | BackingStoreClass::cast(backing_store), index); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 218 | } |
| 219 | |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 220 | private: |
| 221 | DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase); |
| 222 | }; |
| 223 | |
| 224 | |
| 225 | class FastElementsAccessor |
| 226 | : public ElementsAccessorBase<FastElementsAccessor, FixedArray> { |
| 227 | public: |
| 228 | static MaybeObject* DeleteCommon(JSObject* obj, |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 229 | uint32_t key) { |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 230 | ASSERT(obj->HasFastElements() || obj->HasFastArgumentsElements()); |
| 231 | Heap* heap = obj->GetHeap(); |
| 232 | FixedArray* backing_store = FixedArray::cast(obj->elements()); |
| 233 | if (backing_store->map() == heap->non_strict_arguments_elements_map()) { |
| 234 | backing_store = FixedArray::cast(backing_store->get(1)); |
| 235 | } else { |
| 236 | Object* writable; |
| 237 | MaybeObject* maybe = obj->EnsureWritableFastElements(); |
| 238 | if (!maybe->ToObject(&writable)) return maybe; |
| 239 | backing_store = FixedArray::cast(writable); |
| 240 | } |
| 241 | uint32_t length = static_cast<uint32_t>( |
| 242 | obj->IsJSArray() |
| 243 | ? Smi::cast(JSArray::cast(obj)->length())->value() |
| 244 | : backing_store->length()); |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 245 | if (key < length) { |
| 246 | backing_store->set_the_hole(key); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 247 | // If an old space backing store is larger than a certain size and |
| 248 | // has too few used values, normalize it. |
| 249 | // To avoid doing the check on every delete we require at least |
| 250 | // one adjacent hole to the value being deleted. |
| 251 | Object* hole = heap->the_hole_value(); |
| 252 | const int kMinLengthForSparsenessCheck = 64; |
| 253 | if (backing_store->length() >= kMinLengthForSparsenessCheck && |
| 254 | !heap->InNewSpace(backing_store) && |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 255 | ((key > 0 && backing_store->get(key - 1) == hole) || |
| 256 | (key + 1 < length && backing_store->get(key + 1) == hole))) { |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 257 | int num_used = 0; |
| 258 | for (int i = 0; i < backing_store->length(); ++i) { |
| 259 | if (backing_store->get(i) != hole) ++num_used; |
| 260 | // Bail out early if more than 1/4 is used. |
| 261 | if (4 * num_used > backing_store->length()) break; |
| 262 | } |
| 263 | if (4 * num_used <= backing_store->length()) { |
| 264 | MaybeObject* result = obj->NormalizeElements(); |
| 265 | if (result->IsFailure()) return result; |
| 266 | } |
| 267 | } |
| 268 | } |
| 269 | return heap->true_value(); |
| 270 | } |
| 271 | |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 272 | protected: |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 273 | virtual MaybeObject* Delete(JSObject* obj, |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 274 | uint32_t key, |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 275 | JSReceiver::DeleteMode mode) { |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 276 | return DeleteCommon(obj, key); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 277 | } |
| 278 | }; |
| 279 | |
| 280 | |
| 281 | class FastDoubleElementsAccessor |
| 282 | : public ElementsAccessorBase<FastDoubleElementsAccessor, |
| 283 | FixedDoubleArray> { |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 284 | protected: |
| 285 | friend class ElementsAccessorBase<FastDoubleElementsAccessor, |
| 286 | FixedDoubleArray>; |
| 287 | |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 288 | virtual MaybeObject* Delete(JSObject* obj, |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 289 | uint32_t key, |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 290 | JSReceiver::DeleteMode mode) { |
| 291 | int length = obj->IsJSArray() |
| 292 | ? Smi::cast(JSArray::cast(obj)->length())->value() |
| 293 | : FixedDoubleArray::cast(obj->elements())->length(); |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 294 | if (key < static_cast<uint32_t>(length)) { |
| 295 | FixedDoubleArray::cast(obj->elements())->set_the_hole(key); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 296 | } |
| 297 | return obj->GetHeap()->true_value(); |
| 298 | } |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 299 | |
| 300 | static bool HasElementAtIndex(FixedDoubleArray* backing_store, |
| 301 | uint32_t index, |
| 302 | JSObject* holder, |
| 303 | Object* receiver) { |
| 304 | return !backing_store->is_the_hole(index); |
| 305 | } |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 306 | }; |
| 307 | |
| 308 | |
| 309 | // Super class for all external element arrays. |
| 310 | template<typename ExternalElementsAccessorSubclass, |
| 311 | typename ExternalArray> |
| 312 | class ExternalElementsAccessor |
| 313 | : public ElementsAccessorBase<ExternalElementsAccessorSubclass, |
| 314 | ExternalArray> { |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 315 | protected: |
| 316 | friend class ElementsAccessorBase<ExternalElementsAccessorSubclass, |
| 317 | ExternalArray>; |
| 318 | |
| 319 | static MaybeObject* Get(ExternalArray* backing_store, |
| 320 | uint32_t key, |
| 321 | JSObject* obj, |
| 322 | Object* receiver) { |
| 323 | if (key < ExternalElementsAccessorSubclass::GetCapacity(backing_store)) { |
| 324 | return backing_store->get(key); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 325 | } else { |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 326 | return backing_store->GetHeap()->undefined_value(); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 327 | } |
| 328 | } |
| 329 | |
| 330 | virtual MaybeObject* Delete(JSObject* obj, |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 331 | uint32_t key, |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 332 | JSReceiver::DeleteMode mode) { |
| 333 | // External arrays always ignore deletes. |
| 334 | return obj->GetHeap()->true_value(); |
| 335 | } |
| 336 | }; |
| 337 | |
| 338 | |
| 339 | class ExternalByteElementsAccessor |
| 340 | : public ExternalElementsAccessor<ExternalByteElementsAccessor, |
| 341 | ExternalByteArray> { |
| 342 | }; |
| 343 | |
| 344 | |
| 345 | class ExternalUnsignedByteElementsAccessor |
| 346 | : public ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor, |
| 347 | ExternalUnsignedByteArray> { |
| 348 | }; |
| 349 | |
| 350 | |
| 351 | class ExternalShortElementsAccessor |
| 352 | : public ExternalElementsAccessor<ExternalShortElementsAccessor, |
| 353 | ExternalShortArray> { |
| 354 | }; |
| 355 | |
| 356 | |
| 357 | class ExternalUnsignedShortElementsAccessor |
| 358 | : public ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor, |
| 359 | ExternalUnsignedShortArray> { |
| 360 | }; |
| 361 | |
| 362 | |
| 363 | class ExternalIntElementsAccessor |
| 364 | : public ExternalElementsAccessor<ExternalIntElementsAccessor, |
| 365 | ExternalIntArray> { |
| 366 | }; |
| 367 | |
| 368 | |
| 369 | class ExternalUnsignedIntElementsAccessor |
| 370 | : public ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor, |
| 371 | ExternalUnsignedIntArray> { |
| 372 | }; |
| 373 | |
| 374 | |
| 375 | class ExternalFloatElementsAccessor |
| 376 | : public ExternalElementsAccessor<ExternalFloatElementsAccessor, |
| 377 | ExternalFloatArray> { |
| 378 | }; |
| 379 | |
| 380 | |
| 381 | class ExternalDoubleElementsAccessor |
| 382 | : public ExternalElementsAccessor<ExternalDoubleElementsAccessor, |
| 383 | ExternalDoubleArray> { |
| 384 | }; |
| 385 | |
| 386 | |
| 387 | class PixelElementsAccessor |
| 388 | : public ExternalElementsAccessor<PixelElementsAccessor, |
| 389 | ExternalPixelArray> { |
| 390 | }; |
| 391 | |
| 392 | |
| 393 | class DictionaryElementsAccessor |
| 394 | : public ElementsAccessorBase<DictionaryElementsAccessor, |
| 395 | NumberDictionary> { |
| 396 | public: |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 397 | static MaybeObject* DeleteCommon(JSObject* obj, |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 398 | uint32_t key, |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 399 | JSReceiver::DeleteMode mode) { |
| 400 | Isolate* isolate = obj->GetIsolate(); |
| 401 | Heap* heap = isolate->heap(); |
| 402 | FixedArray* backing_store = FixedArray::cast(obj->elements()); |
| 403 | bool is_arguments = |
| 404 | (obj->GetElementsKind() == JSObject::NON_STRICT_ARGUMENTS_ELEMENTS); |
| 405 | if (is_arguments) { |
| 406 | backing_store = FixedArray::cast(backing_store->get(1)); |
| 407 | } |
| 408 | NumberDictionary* dictionary = NumberDictionary::cast(backing_store); |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 409 | int entry = dictionary->FindEntry(key); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 410 | if (entry != NumberDictionary::kNotFound) { |
| 411 | Object* result = dictionary->DeleteProperty(entry, mode); |
| 412 | if (result == heap->true_value()) { |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 413 | MaybeObject* maybe_elements = dictionary->Shrink(key); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 414 | FixedArray* new_elements = NULL; |
| 415 | if (!maybe_elements->To(&new_elements)) { |
| 416 | return maybe_elements; |
| 417 | } |
| 418 | if (is_arguments) { |
| 419 | FixedArray::cast(obj->elements())->set(1, new_elements); |
| 420 | } else { |
| 421 | obj->set_elements(new_elements); |
| 422 | } |
| 423 | } |
| 424 | if (mode == JSObject::STRICT_DELETION && |
| 425 | result == heap->false_value()) { |
| 426 | // In strict mode, attempting to delete a non-configurable property |
| 427 | // throws an exception. |
| 428 | HandleScope scope(isolate); |
| 429 | Handle<Object> holder(obj); |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 430 | Handle<Object> name = isolate->factory()->NewNumberFromUint(key); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 431 | Handle<Object> args[2] = { name, holder }; |
| 432 | Handle<Object> error = |
| 433 | isolate->factory()->NewTypeError("strict_delete_property", |
| 434 | HandleVector(args, 2)); |
| 435 | return isolate->Throw(*error); |
| 436 | } |
| 437 | } |
| 438 | return heap->true_value(); |
| 439 | } |
| 440 | |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 441 | protected: |
| 442 | friend class ElementsAccessorBase<DictionaryElementsAccessor, |
| 443 | NumberDictionary>; |
| 444 | |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 445 | virtual MaybeObject* Delete(JSObject* obj, |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 446 | uint32_t key, |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 447 | JSReceiver::DeleteMode mode) { |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 448 | return DeleteCommon(obj, key, mode); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 449 | } |
| 450 | |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 451 | static MaybeObject* Get(NumberDictionary* backing_store, |
| 452 | uint32_t key, |
| 453 | JSObject* obj, |
| 454 | Object* receiver) { |
| 455 | int entry = backing_store->FindEntry(key); |
| 456 | if (entry != NumberDictionary::kNotFound) { |
| 457 | Object* element = backing_store->ValueAt(entry); |
| 458 | PropertyDetails details = backing_store->DetailsAt(entry); |
| 459 | if (details.type() == CALLBACKS) { |
| 460 | return obj->GetElementWithCallback(receiver, |
| 461 | element, |
| 462 | key, |
| 463 | obj); |
| 464 | } else { |
| 465 | return element; |
| 466 | } |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 467 | } |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 468 | return obj->GetHeap()->the_hole_value(); |
| 469 | } |
| 470 | |
| 471 | static uint32_t GetKeyForIndex(NumberDictionary* dict, |
| 472 | uint32_t index) { |
| 473 | Object* key = dict->KeyAt(index); |
| 474 | return Smi::cast(key)->value(); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 475 | } |
| 476 | }; |
| 477 | |
| 478 | |
| 479 | class NonStrictArgumentsElementsAccessor |
| 480 | : public ElementsAccessorBase<NonStrictArgumentsElementsAccessor, |
| 481 | FixedArray> { |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 482 | protected: |
| 483 | friend class ElementsAccessorBase<NonStrictArgumentsElementsAccessor, |
| 484 | FixedArray>; |
| 485 | |
| 486 | static MaybeObject* Get(FixedArray* parameter_map, |
| 487 | uint32_t key, |
| 488 | JSObject* obj, |
| 489 | Object* receiver) { |
| 490 | Object* probe = GetParameterMapArg(parameter_map, key); |
| 491 | if (!probe->IsTheHole()) { |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 492 | Context* context = Context::cast(parameter_map->get(0)); |
| 493 | int context_index = Smi::cast(probe)->value(); |
| 494 | ASSERT(!context->get(context_index)->IsTheHole()); |
| 495 | return context->get(context_index); |
| 496 | } else { |
| 497 | // Object is not mapped, defer to the arguments. |
| 498 | FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 499 | return ElementsAccessor::ForArray(arguments)->Get(arguments, |
| 500 | key, |
| 501 | obj, |
| 502 | receiver); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 503 | } |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 504 | } |
| 505 | |
| 506 | virtual MaybeObject* Delete(JSObject* obj, |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 507 | uint32_t key |
| 508 | , |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 509 | JSReceiver::DeleteMode mode) { |
| 510 | FixedArray* parameter_map = FixedArray::cast(obj->elements()); |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 511 | Object* probe = GetParameterMapArg(parameter_map, key); |
| 512 | if (!probe->IsTheHole()) { |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 513 | // TODO(kmillikin): We could check if this was the last aliased |
| 514 | // parameter, and revert to normal elements in that case. That |
| 515 | // would enable GC of the context. |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 516 | parameter_map->set_the_hole(key + 2); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 517 | } else { |
| 518 | FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| 519 | if (arguments->IsDictionary()) { |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 520 | return DictionaryElementsAccessor::DeleteCommon(obj, key, mode); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 521 | } else { |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 522 | return FastElementsAccessor::DeleteCommon(obj, key); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 523 | } |
| 524 | } |
| 525 | return obj->GetHeap()->true_value(); |
| 526 | } |
| 527 | |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 528 | static uint32_t GetCapacity(FixedArray* parameter_map) { |
| 529 | FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); |
| 530 | return Max(static_cast<uint32_t>(parameter_map->length() - 2), |
| 531 | ForArray(arguments)->GetCapacity(arguments)); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 532 | } |
| 533 | |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 534 | static uint32_t GetKeyForIndex(FixedArray* dict, |
| 535 | uint32_t index) { |
| 536 | return index; |
| 537 | } |
| 538 | |
| 539 | static bool HasElementAtIndex(FixedArray* parameter_map, |
| 540 | uint32_t index, |
| 541 | JSObject* holder, |
| 542 | Object* receiver) { |
| 543 | Object* probe = GetParameterMapArg(parameter_map, index); |
| 544 | if (!probe->IsTheHole()) { |
| 545 | return true; |
| 546 | } else { |
| 547 | FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); |
| 548 | ElementsAccessor* accessor = ElementsAccessor::ForArray(arguments); |
| 549 | return !accessor->Get(arguments, index, holder, receiver)->IsTheHole(); |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | private: |
| 554 | static Object* GetParameterMapArg(FixedArray* parameter_map, |
| 555 | uint32_t key) { |
| 556 | uint32_t length = parameter_map->length(); |
| 557 | return key < (length - 2 ) |
| 558 | ? parameter_map->get(key + 2) |
| 559 | : parameter_map->GetHeap()->the_hole_value(); |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 560 | } |
| 561 | }; |
| 562 | |
| 563 | |
rossberg@chromium.org | 28a3708 | 2011-08-22 11:03:23 +0000 | [diff] [blame^] | 564 | ElementsAccessor* ElementsAccessor::ForArray(FixedArrayBase* array) { |
| 565 | switch (array->map()->instance_type()) { |
| 566 | case FIXED_ARRAY_TYPE: |
| 567 | if (array->IsDictionary()) { |
| 568 | return elements_accessors_[JSObject::DICTIONARY_ELEMENTS]; |
| 569 | } else { |
| 570 | return elements_accessors_[JSObject::FAST_ELEMENTS]; |
| 571 | } |
| 572 | case EXTERNAL_BYTE_ARRAY_TYPE: |
| 573 | return elements_accessors_[JSObject::EXTERNAL_BYTE_ELEMENTS]; |
| 574 | case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: |
| 575 | return elements_accessors_[JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS]; |
| 576 | case EXTERNAL_SHORT_ARRAY_TYPE: |
| 577 | return elements_accessors_[JSObject::EXTERNAL_SHORT_ELEMENTS]; |
| 578 | case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: |
| 579 | return elements_accessors_[JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS]; |
| 580 | case EXTERNAL_INT_ARRAY_TYPE: |
| 581 | return elements_accessors_[JSObject::EXTERNAL_INT_ELEMENTS]; |
| 582 | case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: |
| 583 | return elements_accessors_[JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS]; |
| 584 | case EXTERNAL_FLOAT_ARRAY_TYPE: |
| 585 | return elements_accessors_[JSObject::EXTERNAL_FLOAT_ELEMENTS]; |
| 586 | case EXTERNAL_DOUBLE_ARRAY_TYPE: |
| 587 | return elements_accessors_[JSObject::EXTERNAL_DOUBLE_ELEMENTS]; |
| 588 | case EXTERNAL_PIXEL_ARRAY_TYPE: |
| 589 | return elements_accessors_[JSObject::EXTERNAL_PIXEL_ELEMENTS]; |
| 590 | default: |
| 591 | UNREACHABLE(); |
| 592 | return NULL; |
| 593 | break; |
| 594 | } |
| 595 | } |
| 596 | |
| 597 | |
kmillikin@chromium.org | 7c2628c | 2011-08-10 11:27:35 +0000 | [diff] [blame] | 598 | void ElementsAccessor::InitializeOncePerProcess() { |
| 599 | static struct ConcreteElementsAccessors { |
| 600 | FastElementsAccessor fast_elements_handler; |
| 601 | FastDoubleElementsAccessor fast_double_elements_handler; |
| 602 | DictionaryElementsAccessor dictionary_elements_handler; |
| 603 | NonStrictArgumentsElementsAccessor non_strict_arguments_elements_handler; |
| 604 | ExternalByteElementsAccessor byte_elements_handler; |
| 605 | ExternalUnsignedByteElementsAccessor unsigned_byte_elements_handler; |
| 606 | ExternalShortElementsAccessor short_elements_handler; |
| 607 | ExternalUnsignedShortElementsAccessor unsigned_short_elements_handler; |
| 608 | ExternalIntElementsAccessor int_elements_handler; |
| 609 | ExternalUnsignedIntElementsAccessor unsigned_int_elements_handler; |
| 610 | ExternalFloatElementsAccessor float_elements_handler; |
| 611 | ExternalDoubleElementsAccessor double_elements_handler; |
| 612 | PixelElementsAccessor pixel_elements_handler; |
| 613 | } element_accessors; |
| 614 | |
| 615 | static ElementsAccessor* accessor_array[] = { |
| 616 | &element_accessors.fast_elements_handler, |
| 617 | &element_accessors.fast_double_elements_handler, |
| 618 | &element_accessors.dictionary_elements_handler, |
| 619 | &element_accessors.non_strict_arguments_elements_handler, |
| 620 | &element_accessors.byte_elements_handler, |
| 621 | &element_accessors.unsigned_byte_elements_handler, |
| 622 | &element_accessors.short_elements_handler, |
| 623 | &element_accessors.unsigned_short_elements_handler, |
| 624 | &element_accessors.int_elements_handler, |
| 625 | &element_accessors.unsigned_int_elements_handler, |
| 626 | &element_accessors.float_elements_handler, |
| 627 | &element_accessors.double_elements_handler, |
| 628 | &element_accessors.pixel_elements_handler |
| 629 | }; |
| 630 | |
| 631 | elements_accessors_ = accessor_array; |
| 632 | } |
| 633 | |
| 634 | |
| 635 | } } // namespace v8::internal |