| OLD | NEW |
| (Empty) | |
| 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" |
| 32 |
| 33 namespace v8 { |
| 34 namespace internal { |
| 35 |
| 36 |
| 37 ElementsAccessor** ElementsAccessor::elements_accessors_; |
| 38 |
| 39 |
| 40 // Base class for element handler implementations. Contains the |
| 41 // the common logic for objects with different ElementsKinds. |
| 42 // Subclasses must specialize method for which the element |
| 43 // implementation differs from the base class implementation. |
| 44 // |
| 45 // This class is intended to be used in the following way: |
| 46 // |
| 47 // class SomeElementsAccessor : |
| 48 // public ElementsAccessorBase<SomeElementsAccessor, |
| 49 // BackingStoreClass> { |
| 50 // ... |
| 51 // } |
| 52 // |
| 53 // This is an example of the Curiously Recurring Template Pattern (see |
| 54 // http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). We use |
| 55 // CRTP to guarantee aggressive compile time optimizations (i.e. inlining and |
| 56 // specialization of SomeElementsAccessor methods). |
| 57 template <typename ElementsAccessorSubclass, typename BackingStoreClass> |
| 58 class ElementsAccessorBase : public ElementsAccessor { |
| 59 public: |
| 60 ElementsAccessorBase() { } |
| 61 virtual MaybeObject* GetWithReceiver(JSObject* obj, |
| 62 Object* receiver, |
| 63 uint32_t index) { |
| 64 if (index < ElementsAccessorSubclass::GetLength(obj)) { |
| 65 BackingStoreClass* backing_store = |
| 66 ElementsAccessorSubclass::GetBackingStore(obj); |
| 67 return backing_store->get(index); |
| 68 } |
| 69 return obj->GetHeap()->the_hole_value(); |
| 70 } |
| 71 |
| 72 virtual MaybeObject* Delete(JSObject* obj, |
| 73 uint32_t index, |
| 74 JSReceiver::DeleteMode mode) = 0; |
| 75 |
| 76 protected: |
| 77 static BackingStoreClass* GetBackingStore(JSObject* obj) { |
| 78 return BackingStoreClass::cast(obj->elements()); |
| 79 } |
| 80 |
| 81 static uint32_t GetLength(JSObject* obj) { |
| 82 return ElementsAccessorSubclass::GetBackingStore(obj)->length(); |
| 83 } |
| 84 |
| 85 private: |
| 86 DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase); |
| 87 }; |
| 88 |
| 89 |
| 90 class FastElementsAccessor |
| 91 : public ElementsAccessorBase<FastElementsAccessor, FixedArray> { |
| 92 public: |
| 93 static MaybeObject* DeleteCommon(JSObject* obj, |
| 94 uint32_t index) { |
| 95 ASSERT(obj->HasFastElements() || obj->HasFastArgumentsElements()); |
| 96 Heap* heap = obj->GetHeap(); |
| 97 FixedArray* backing_store = FixedArray::cast(obj->elements()); |
| 98 if (backing_store->map() == heap->non_strict_arguments_elements_map()) { |
| 99 backing_store = FixedArray::cast(backing_store->get(1)); |
| 100 } else { |
| 101 Object* writable; |
| 102 MaybeObject* maybe = obj->EnsureWritableFastElements(); |
| 103 if (!maybe->ToObject(&writable)) return maybe; |
| 104 backing_store = FixedArray::cast(writable); |
| 105 } |
| 106 uint32_t length = static_cast<uint32_t>( |
| 107 obj->IsJSArray() |
| 108 ? Smi::cast(JSArray::cast(obj)->length())->value() |
| 109 : backing_store->length()); |
| 110 if (index < length) { |
| 111 backing_store->set_the_hole(index); |
| 112 // If an old space backing store is larger than a certain size and |
| 113 // has too few used values, normalize it. |
| 114 // To avoid doing the check on every delete we require at least |
| 115 // one adjacent hole to the value being deleted. |
| 116 Object* hole = heap->the_hole_value(); |
| 117 const int kMinLengthForSparsenessCheck = 64; |
| 118 if (backing_store->length() >= kMinLengthForSparsenessCheck && |
| 119 !heap->InNewSpace(backing_store) && |
| 120 ((index > 0 && backing_store->get(index - 1) == hole) || |
| 121 (index + 1 < length && backing_store->get(index + 1) == hole))) { |
| 122 int num_used = 0; |
| 123 for (int i = 0; i < backing_store->length(); ++i) { |
| 124 if (backing_store->get(i) != hole) ++num_used; |
| 125 // Bail out early if more than 1/4 is used. |
| 126 if (4 * num_used > backing_store->length()) break; |
| 127 } |
| 128 if (4 * num_used <= backing_store->length()) { |
| 129 MaybeObject* result = obj->NormalizeElements(); |
| 130 if (result->IsFailure()) return result; |
| 131 } |
| 132 } |
| 133 } |
| 134 return heap->true_value(); |
| 135 } |
| 136 |
| 137 virtual MaybeObject* Delete(JSObject* obj, |
| 138 uint32_t index, |
| 139 JSReceiver::DeleteMode mode) { |
| 140 return DeleteCommon(obj, index); |
| 141 } |
| 142 }; |
| 143 |
| 144 |
| 145 class FastDoubleElementsAccessor |
| 146 : public ElementsAccessorBase<FastDoubleElementsAccessor, |
| 147 FixedDoubleArray> { |
| 148 virtual MaybeObject* Delete(JSObject* obj, |
| 149 uint32_t index, |
| 150 JSReceiver::DeleteMode mode) { |
| 151 int length = obj->IsJSArray() |
| 152 ? Smi::cast(JSArray::cast(obj)->length())->value() |
| 153 : FixedDoubleArray::cast(obj->elements())->length(); |
| 154 if (index < static_cast<uint32_t>(length)) { |
| 155 FixedDoubleArray::cast(obj->elements())->set_the_hole(index); |
| 156 } |
| 157 return obj->GetHeap()->true_value(); |
| 158 } |
| 159 }; |
| 160 |
| 161 |
| 162 // Super class for all external element arrays. |
| 163 template<typename ExternalElementsAccessorSubclass, |
| 164 typename ExternalArray> |
| 165 class ExternalElementsAccessor |
| 166 : public ElementsAccessorBase<ExternalElementsAccessorSubclass, |
| 167 ExternalArray> { |
| 168 public: |
| 169 virtual MaybeObject* GetWithReceiver(JSObject* obj, |
| 170 Object* receiver, |
| 171 uint32_t index) { |
| 172 if (index < ExternalElementsAccessorSubclass::GetLength(obj)) { |
| 173 ExternalArray* backing_store = |
| 174 ExternalElementsAccessorSubclass::GetBackingStore(obj); |
| 175 return backing_store->get(index); |
| 176 } else { |
| 177 return obj->GetHeap()->undefined_value(); |
| 178 } |
| 179 } |
| 180 |
| 181 virtual MaybeObject* Delete(JSObject* obj, |
| 182 uint32_t index, |
| 183 JSReceiver::DeleteMode mode) { |
| 184 // External arrays always ignore deletes. |
| 185 return obj->GetHeap()->true_value(); |
| 186 } |
| 187 }; |
| 188 |
| 189 |
| 190 class ExternalByteElementsAccessor |
| 191 : public ExternalElementsAccessor<ExternalByteElementsAccessor, |
| 192 ExternalByteArray> { |
| 193 }; |
| 194 |
| 195 |
| 196 class ExternalUnsignedByteElementsAccessor |
| 197 : public ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor, |
| 198 ExternalUnsignedByteArray> { |
| 199 }; |
| 200 |
| 201 |
| 202 class ExternalShortElementsAccessor |
| 203 : public ExternalElementsAccessor<ExternalShortElementsAccessor, |
| 204 ExternalShortArray> { |
| 205 }; |
| 206 |
| 207 |
| 208 class ExternalUnsignedShortElementsAccessor |
| 209 : public ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor, |
| 210 ExternalUnsignedShortArray> { |
| 211 }; |
| 212 |
| 213 |
| 214 class ExternalIntElementsAccessor |
| 215 : public ExternalElementsAccessor<ExternalIntElementsAccessor, |
| 216 ExternalIntArray> { |
| 217 }; |
| 218 |
| 219 |
| 220 class ExternalUnsignedIntElementsAccessor |
| 221 : public ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor, |
| 222 ExternalUnsignedIntArray> { |
| 223 }; |
| 224 |
| 225 |
| 226 class ExternalFloatElementsAccessor |
| 227 : public ExternalElementsAccessor<ExternalFloatElementsAccessor, |
| 228 ExternalFloatArray> { |
| 229 }; |
| 230 |
| 231 |
| 232 class ExternalDoubleElementsAccessor |
| 233 : public ExternalElementsAccessor<ExternalDoubleElementsAccessor, |
| 234 ExternalDoubleArray> { |
| 235 }; |
| 236 |
| 237 |
| 238 class PixelElementsAccessor |
| 239 : public ExternalElementsAccessor<PixelElementsAccessor, |
| 240 ExternalPixelArray> { |
| 241 }; |
| 242 |
| 243 |
| 244 class DictionaryElementsAccessor |
| 245 : public ElementsAccessorBase<DictionaryElementsAccessor, |
| 246 NumberDictionary> { |
| 247 public: |
| 248 static MaybeObject* GetNumberDictionaryElement( |
| 249 JSObject* obj, |
| 250 Object* receiver, |
| 251 NumberDictionary* backing_store, |
| 252 uint32_t index) { |
| 253 int entry = backing_store->FindEntry(index); |
| 254 if (entry != NumberDictionary::kNotFound) { |
| 255 Object* element = backing_store->ValueAt(entry); |
| 256 PropertyDetails details = backing_store->DetailsAt(entry); |
| 257 if (details.type() == CALLBACKS) { |
| 258 return obj->GetElementWithCallback(receiver, |
| 259 element, |
| 260 index, |
| 261 obj); |
| 262 } else { |
| 263 return element; |
| 264 } |
| 265 } |
| 266 return obj->GetHeap()->the_hole_value(); |
| 267 } |
| 268 |
| 269 |
| 270 static MaybeObject* DeleteCommon(JSObject* obj, |
| 271 uint32_t index, |
| 272 JSReceiver::DeleteMode mode) { |
| 273 Isolate* isolate = obj->GetIsolate(); |
| 274 Heap* heap = isolate->heap(); |
| 275 FixedArray* backing_store = FixedArray::cast(obj->elements()); |
| 276 bool is_arguments = |
| 277 (obj->GetElementsKind() == JSObject::NON_STRICT_ARGUMENTS_ELEMENTS); |
| 278 if (is_arguments) { |
| 279 backing_store = FixedArray::cast(backing_store->get(1)); |
| 280 } |
| 281 NumberDictionary* dictionary = NumberDictionary::cast(backing_store); |
| 282 int entry = dictionary->FindEntry(index); |
| 283 if (entry != NumberDictionary::kNotFound) { |
| 284 Object* result = dictionary->DeleteProperty(entry, mode); |
| 285 if (result == heap->true_value()) { |
| 286 MaybeObject* maybe_elements = dictionary->Shrink(index); |
| 287 FixedArray* new_elements = NULL; |
| 288 if (!maybe_elements->To(&new_elements)) { |
| 289 return maybe_elements; |
| 290 } |
| 291 if (is_arguments) { |
| 292 FixedArray::cast(obj->elements())->set(1, new_elements); |
| 293 } else { |
| 294 obj->set_elements(new_elements); |
| 295 } |
| 296 } |
| 297 if (mode == JSObject::STRICT_DELETION && |
| 298 result == heap->false_value()) { |
| 299 // In strict mode, attempting to delete a non-configurable property |
| 300 // throws an exception. |
| 301 HandleScope scope(isolate); |
| 302 Handle<Object> holder(obj); |
| 303 Handle<Object> name = isolate->factory()->NewNumberFromUint(index); |
| 304 Handle<Object> args[2] = { name, holder }; |
| 305 Handle<Object> error = |
| 306 isolate->factory()->NewTypeError("strict_delete_property", |
| 307 HandleVector(args, 2)); |
| 308 return isolate->Throw(*error); |
| 309 } |
| 310 } |
| 311 return heap->true_value(); |
| 312 } |
| 313 |
| 314 virtual MaybeObject* Delete(JSObject* obj, |
| 315 uint32_t index, |
| 316 JSReceiver::DeleteMode mode) { |
| 317 return DeleteCommon(obj, index, mode); |
| 318 } |
| 319 |
| 320 virtual MaybeObject* GetWithReceiver(JSObject* obj, |
| 321 Object* receiver, |
| 322 uint32_t index) { |
| 323 return GetNumberDictionaryElement(obj, |
| 324 receiver, |
| 325 obj->element_dictionary(), |
| 326 index); |
| 327 } |
| 328 }; |
| 329 |
| 330 |
| 331 class NonStrictArgumentsElementsAccessor |
| 332 : public ElementsAccessorBase<NonStrictArgumentsElementsAccessor, |
| 333 FixedArray> { |
| 334 public: |
| 335 virtual MaybeObject* GetWithReceiver(JSObject* obj, |
| 336 Object* receiver, |
| 337 uint32_t index) { |
| 338 FixedArray* parameter_map = GetBackingStore(obj); |
| 339 uint32_t length = parameter_map->length(); |
| 340 Object* probe = |
| 341 (index < length - 2) ? parameter_map->get(index + 2) : NULL; |
| 342 if (probe != NULL && !probe->IsTheHole()) { |
| 343 Context* context = Context::cast(parameter_map->get(0)); |
| 344 int context_index = Smi::cast(probe)->value(); |
| 345 ASSERT(!context->get(context_index)->IsTheHole()); |
| 346 return context->get(context_index); |
| 347 } else { |
| 348 // Object is not mapped, defer to the arguments. |
| 349 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| 350 if (arguments->IsDictionary()) { |
| 351 return DictionaryElementsAccessor::GetNumberDictionaryElement( |
| 352 obj, |
| 353 receiver, |
| 354 NumberDictionary::cast(arguments), |
| 355 index); |
| 356 } else if (index < static_cast<uint32_t>(arguments->length())) { |
| 357 return arguments->get(index); |
| 358 } |
| 359 } |
| 360 return obj->GetHeap()->the_hole_value(); |
| 361 } |
| 362 |
| 363 virtual MaybeObject* Delete(JSObject* obj, |
| 364 uint32_t index, |
| 365 JSReceiver::DeleteMode mode) { |
| 366 FixedArray* parameter_map = FixedArray::cast(obj->elements()); |
| 367 uint32_t length = parameter_map->length(); |
| 368 Object* probe = |
| 369 index < (length - 2) ? parameter_map->get(index + 2) : NULL; |
| 370 if (probe != NULL && !probe->IsTheHole()) { |
| 371 // TODO(kmillikin): We could check if this was the last aliased |
| 372 // parameter, and revert to normal elements in that case. That |
| 373 // would enable GC of the context. |
| 374 parameter_map->set_the_hole(index + 2); |
| 375 } else { |
| 376 FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| 377 if (arguments->IsDictionary()) { |
| 378 return DictionaryElementsAccessor::DeleteCommon(obj, index, mode); |
| 379 } else { |
| 380 return FastElementsAccessor::DeleteCommon(obj, index); |
| 381 } |
| 382 } |
| 383 return obj->GetHeap()->true_value(); |
| 384 } |
| 385 }; |
| 386 |
| 387 |
| 388 void ElementsAccessor::InitializeOncePerProcess() { |
| 389 static struct ConcreteElementsAccessors { |
| 390 FastElementsAccessor fast_elements_handler; |
| 391 FastDoubleElementsAccessor fast_double_elements_handler; |
| 392 DictionaryElementsAccessor dictionary_elements_handler; |
| 393 NonStrictArgumentsElementsAccessor non_strict_arguments_elements_handler; |
| 394 ExternalByteElementsAccessor byte_elements_handler; |
| 395 ExternalUnsignedByteElementsAccessor unsigned_byte_elements_handler; |
| 396 ExternalShortElementsAccessor short_elements_handler; |
| 397 ExternalUnsignedShortElementsAccessor unsigned_short_elements_handler; |
| 398 ExternalIntElementsAccessor int_elements_handler; |
| 399 ExternalUnsignedIntElementsAccessor unsigned_int_elements_handler; |
| 400 ExternalFloatElementsAccessor float_elements_handler; |
| 401 ExternalDoubleElementsAccessor double_elements_handler; |
| 402 PixelElementsAccessor pixel_elements_handler; |
| 403 } element_accessors; |
| 404 |
| 405 static ElementsAccessor* accessor_array[] = { |
| 406 &element_accessors.fast_elements_handler, |
| 407 &element_accessors.fast_double_elements_handler, |
| 408 &element_accessors.dictionary_elements_handler, |
| 409 &element_accessors.non_strict_arguments_elements_handler, |
| 410 &element_accessors.byte_elements_handler, |
| 411 &element_accessors.unsigned_byte_elements_handler, |
| 412 &element_accessors.short_elements_handler, |
| 413 &element_accessors.unsigned_short_elements_handler, |
| 414 &element_accessors.int_elements_handler, |
| 415 &element_accessors.unsigned_int_elements_handler, |
| 416 &element_accessors.float_elements_handler, |
| 417 &element_accessors.double_elements_handler, |
| 418 &element_accessors.pixel_elements_handler |
| 419 }; |
| 420 |
| 421 elements_accessors_ = accessor_array; |
| 422 } |
| 423 |
| 424 |
| 425 } } // namespace v8::internal |
| OLD | NEW |