blob: 1afc5dad5e0400e5e2147a28ee5e4f08d870e8fd [file] [log] [blame]
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001// 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#include "utils.h"
33
34namespace v8 {
35namespace internal {
36
37
38ElementsAccessor** ElementsAccessor::elements_accessors_;
39
40
41bool 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).
72template <typename ElementsAccessorSubclass, typename BackingStoreClass>
73class ElementsAccessorBase : public ElementsAccessor {
74 protected:
75 ElementsAccessorBase() { }
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);
90 }
91 return backing_store->GetHeap()->the_hole_value();
92 }
93
94 virtual MaybeObject* Delete(JSObject* obj,
95 uint32_t key,
96 JSReceiver::DeleteMode mode) = 0;
97
98 virtual MaybeObject* AddElementsToFixedArray(FixedArrayBase* from,
99 FixedArray* to,
100 JSObject* holder,
101 Object* receiver) {
102 int len0 = to->length();
103#ifdef DEBUG
104 if (FLAG_enable_slow_asserts) {
105 for (int i = 0; i < len0; i++) {
106 ASSERT(!to->get(i)->IsTheHole());
107 }
108 }
109#endif
110 BackingStoreClass* backing_store = BackingStoreClass::cast(from);
111 uint32_t len1 = ElementsAccessorSubclass::GetCapacity(backing_store);
112
113 // Optimize if 'other' is empty.
114 // We cannot optimize if 'this' is empty, as other may have holes.
115 if (len1 == 0) return to;
116
117 // Compute how many elements are not in other.
118 int extra = 0;
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 }
135 }
136
137 if (extra == 0) return to;
138
139 // Allocate the result
140 FixedArray* result;
141 MaybeObject* maybe_obj =
142 backing_store->GetHeap()->AllocateFixedArray(len0 + extra);
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++) {
150 Object* e = to->get(i);
151 ASSERT(e->IsString() || e->IsNumber());
152 result->set(i, e, mode);
153 }
154 }
155 // Fill in the extra values.
156 int index = 0;
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 }
172 }
173 }
174 ASSERT(extra == index);
175 return result;
176 }
177
178 protected:
179 static uint32_t GetCapacity(BackingStoreClass* backing_store) {
180 return backing_store->length();
181 }
182
183 virtual uint32_t GetCapacity(FixedArrayBase* backing_store) {
184 return ElementsAccessorSubclass::GetCapacity(
185 BackingStoreClass::cast(backing_store));
186 }
187
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);
218 }
219
220 private:
221 DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase);
222};
223
224
225class FastElementsAccessor
226 : public ElementsAccessorBase<FastElementsAccessor, FixedArray> {
227 public:
228 static MaybeObject* DeleteCommon(JSObject* obj,
229 uint32_t key) {
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());
245 if (key < length) {
246 backing_store->set_the_hole(key);
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) &&
255 ((key > 0 && backing_store->get(key - 1) == hole) ||
256 (key + 1 < length && backing_store->get(key + 1) == hole))) {
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
272 protected:
273 virtual MaybeObject* Delete(JSObject* obj,
274 uint32_t key,
275 JSReceiver::DeleteMode mode) {
276 return DeleteCommon(obj, key);
277 }
278};
279
280
281class FastDoubleElementsAccessor
282 : public ElementsAccessorBase<FastDoubleElementsAccessor,
283 FixedDoubleArray> {
284 protected:
285 friend class ElementsAccessorBase<FastDoubleElementsAccessor,
286 FixedDoubleArray>;
287
288 virtual MaybeObject* Delete(JSObject* obj,
289 uint32_t key,
290 JSReceiver::DeleteMode mode) {
291 int length = obj->IsJSArray()
292 ? Smi::cast(JSArray::cast(obj)->length())->value()
293 : FixedDoubleArray::cast(obj->elements())->length();
294 if (key < static_cast<uint32_t>(length)) {
295 FixedDoubleArray::cast(obj->elements())->set_the_hole(key);
296 }
297 return obj->GetHeap()->true_value();
298 }
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 }
306};
307
308
309// Super class for all external element arrays.
310template<typename ExternalElementsAccessorSubclass,
311 typename ExternalArray>
312class ExternalElementsAccessor
313 : public ElementsAccessorBase<ExternalElementsAccessorSubclass,
314 ExternalArray> {
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);
325 } else {
326 return backing_store->GetHeap()->undefined_value();
327 }
328 }
329
330 virtual MaybeObject* Delete(JSObject* obj,
331 uint32_t key,
332 JSReceiver::DeleteMode mode) {
333 // External arrays always ignore deletes.
334 return obj->GetHeap()->true_value();
335 }
336};
337
338
339class ExternalByteElementsAccessor
340 : public ExternalElementsAccessor<ExternalByteElementsAccessor,
341 ExternalByteArray> {
342};
343
344
345class ExternalUnsignedByteElementsAccessor
346 : public ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor,
347 ExternalUnsignedByteArray> {
348};
349
350
351class ExternalShortElementsAccessor
352 : public ExternalElementsAccessor<ExternalShortElementsAccessor,
353 ExternalShortArray> {
354};
355
356
357class ExternalUnsignedShortElementsAccessor
358 : public ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor,
359 ExternalUnsignedShortArray> {
360};
361
362
363class ExternalIntElementsAccessor
364 : public ExternalElementsAccessor<ExternalIntElementsAccessor,
365 ExternalIntArray> {
366};
367
368
369class ExternalUnsignedIntElementsAccessor
370 : public ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor,
371 ExternalUnsignedIntArray> {
372};
373
374
375class ExternalFloatElementsAccessor
376 : public ExternalElementsAccessor<ExternalFloatElementsAccessor,
377 ExternalFloatArray> {
378};
379
380
381class ExternalDoubleElementsAccessor
382 : public ExternalElementsAccessor<ExternalDoubleElementsAccessor,
383 ExternalDoubleArray> {
384};
385
386
387class PixelElementsAccessor
388 : public ExternalElementsAccessor<PixelElementsAccessor,
389 ExternalPixelArray> {
390};
391
392
393class DictionaryElementsAccessor
394 : public ElementsAccessorBase<DictionaryElementsAccessor,
395 NumberDictionary> {
396 public:
397 static MaybeObject* DeleteCommon(JSObject* obj,
398 uint32_t key,
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);
409 int entry = dictionary->FindEntry(key);
410 if (entry != NumberDictionary::kNotFound) {
411 Object* result = dictionary->DeleteProperty(entry, mode);
412 if (result == heap->true_value()) {
413 MaybeObject* maybe_elements = dictionary->Shrink(key);
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);
430 Handle<Object> name = isolate->factory()->NewNumberFromUint(key);
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
441 protected:
442 friend class ElementsAccessorBase<DictionaryElementsAccessor,
443 NumberDictionary>;
444
445 virtual MaybeObject* Delete(JSObject* obj,
446 uint32_t key,
447 JSReceiver::DeleteMode mode) {
448 return DeleteCommon(obj, key, mode);
449 }
450
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 }
467 }
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();
475 }
476};
477
478
479class NonStrictArgumentsElementsAccessor
480 : public ElementsAccessorBase<NonStrictArgumentsElementsAccessor,
481 FixedArray> {
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()) {
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));
499 return ElementsAccessor::ForArray(arguments)->Get(arguments,
500 key,
501 obj,
502 receiver);
503 }
504 }
505
506 virtual MaybeObject* Delete(JSObject* obj,
507 uint32_t key
508 ,
509 JSReceiver::DeleteMode mode) {
510 FixedArray* parameter_map = FixedArray::cast(obj->elements());
511 Object* probe = GetParameterMapArg(parameter_map, key);
512 if (!probe->IsTheHole()) {
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.
516 parameter_map->set_the_hole(key + 2);
517 } else {
518 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
519 if (arguments->IsDictionary()) {
520 return DictionaryElementsAccessor::DeleteCommon(obj, key, mode);
521 } else {
522 return FastElementsAccessor::DeleteCommon(obj, key);
523 }
524 }
525 return obj->GetHeap()->true_value();
526 }
527
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));
532 }
533
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();
560 }
561};
562
563
564ElementsAccessor* 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 }
594}
595
596
597void ElementsAccessor::InitializeOncePerProcess() {
598 static struct ConcreteElementsAccessors {
599 FastElementsAccessor fast_elements_handler;
600 FastDoubleElementsAccessor fast_double_elements_handler;
601 DictionaryElementsAccessor dictionary_elements_handler;
602 NonStrictArgumentsElementsAccessor non_strict_arguments_elements_handler;
603 ExternalByteElementsAccessor byte_elements_handler;
604 ExternalUnsignedByteElementsAccessor unsigned_byte_elements_handler;
605 ExternalShortElementsAccessor short_elements_handler;
606 ExternalUnsignedShortElementsAccessor unsigned_short_elements_handler;
607 ExternalIntElementsAccessor int_elements_handler;
608 ExternalUnsignedIntElementsAccessor unsigned_int_elements_handler;
609 ExternalFloatElementsAccessor float_elements_handler;
610 ExternalDoubleElementsAccessor double_elements_handler;
611 PixelElementsAccessor pixel_elements_handler;
612 } element_accessors;
613
614 static ElementsAccessor* accessor_array[] = {
615 &element_accessors.fast_elements_handler,
616 &element_accessors.fast_double_elements_handler,
617 &element_accessors.dictionary_elements_handler,
618 &element_accessors.non_strict_arguments_elements_handler,
619 &element_accessors.byte_elements_handler,
620 &element_accessors.unsigned_byte_elements_handler,
621 &element_accessors.short_elements_handler,
622 &element_accessors.unsigned_short_elements_handler,
623 &element_accessors.int_elements_handler,
624 &element_accessors.unsigned_int_elements_handler,
625 &element_accessors.float_elements_handler,
626 &element_accessors.double_elements_handler,
627 &element_accessors.pixel_elements_handler
628 };
629
630 elements_accessors_ = accessor_array;
631}
632
633
634} } // namespace v8::internal