blob: 9927cd5ced891948443d78eab9d76e6434b350f6 [file] [log] [blame]
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +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
33namespace v8 {
34namespace internal {
35
36
37ElementsAccessor** ElementsAccessor::elements_accessors_;
38
39
40bool HasKey(FixedArray* array, Object* key) {
41 int len0 = array->length();
42 for (int i = 0; i < len0; i++) {
43 Object* element = array->get(i);
44 if (element->IsSmi() && element == key) return true;
45 if (element->IsString() &&
46 key->IsString() && String::cast(element)->Equals(String::cast(key))) {
47 return true;
48 }
49 }
50 return false;
51}
52
53
54// Base class for element handler implementations. Contains the
55// the common logic for objects with different ElementsKinds.
56// Subclasses must specialize method for which the element
57// implementation differs from the base class implementation.
58//
59// This class is intended to be used in the following way:
60//
61// class SomeElementsAccessor :
62// public ElementsAccessorBase<SomeElementsAccessor,
63// BackingStoreClass> {
64// ...
65// }
66//
67// This is an example of the Curiously Recurring Template Pattern (see
68// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). We use
69// CRTP to guarantee aggressive compile time optimizations (i.e. inlining and
70// specialization of SomeElementsAccessor methods).
71template <typename ElementsAccessorSubclass, typename BackingStoreClass>
72class ElementsAccessorBase : public ElementsAccessor {
73 public:
74 ElementsAccessorBase() { }
75 virtual MaybeObject* GetWithReceiver(JSObject* obj,
76 Object* receiver,
77 uint32_t index) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +000078 BackingStoreClass* backing_store = BackingStoreClass::cast(obj->elements());
79 if (index < ElementsAccessorSubclass::GetLength(backing_store)) {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000080 return backing_store->get(index);
81 }
82 return obj->GetHeap()->the_hole_value();
83 }
84
85 virtual MaybeObject* Delete(JSObject* obj,
86 uint32_t index,
87 JSReceiver::DeleteMode mode) = 0;
88
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +000089 virtual MaybeObject* AddElementsToFixedArray(FixedArrayBase* from,
90 FixedArray* to) {
91 int len0 = to->length();
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000092#ifdef DEBUG
93 if (FLAG_enable_slow_asserts) {
94 for (int i = 0; i < len0; i++) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +000095 ASSERT(!to->get(i)->IsTheHole());
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000096 }
97 }
98#endif
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +000099 BackingStoreClass* backing_store = BackingStoreClass::cast(from);
100 int len1 = ElementsAccessorSubclass::GetCapacity(backing_store);
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000101
102 // Optimize if 'other' is empty.
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000103 // We cannot optimize if 'this' is empty, as other may have holes.
104 if (len1 == 0) return to;
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000105
106 // Compute how many elements are not in other.
107 int extra = 0;
108 for (int y = 0; y < len1; y++) {
109 Object* value;
110 MaybeObject* maybe_value =
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000111 ElementsAccessorSubclass::GetElementAtCapacityIndex(backing_store, y);
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000112 if (!maybe_value->ToObject(&value)) return maybe_value;
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000113 if (!value->IsTheHole() && !HasKey(to, value)) extra++;
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000114 }
115
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000116 if (extra == 0) return to;
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000117
118 // Allocate the result
119 FixedArray* result;
120 MaybeObject* maybe_obj =
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000121 backing_store->GetHeap()->AllocateFixedArray(len0 + extra);
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000122 if (!maybe_obj->To<FixedArray>(&result)) return maybe_obj;
123
124 // Fill in the content
125 {
126 AssertNoAllocation no_gc;
127 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
128 for (int i = 0; i < len0; i++) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000129 Object* e = to->get(i);
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000130 ASSERT(e->IsString() || e->IsNumber());
131 result->set(i, e, mode);
132 }
133 }
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000134 // Fill in the extra values.
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000135 int index = 0;
136 for (int y = 0; y < len1; y++) {
137 MaybeObject* maybe_value =
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000138 ElementsAccessorSubclass::GetElementAtCapacityIndex(backing_store, y);
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000139 Object* value;
140 if (!maybe_value->ToObject(&value)) return maybe_value;
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000141 if (!value->IsTheHole() && !HasKey(to, value)) {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000142 result->set(len0 + index, value);
143 index++;
144 }
145 }
146 ASSERT(extra == index);
147 return result;
148 }
149
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000150 static uint32_t GetLength(BackingStoreClass* backing_store) {
151 return backing_store->length();
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000152 }
153
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000154 static uint32_t GetCapacity(BackingStoreClass* backing_store) {
155 return GetLength(backing_store);
156 }
157
158 static MaybeObject* GetElementAtCapacityIndex(
159 BackingStoreClass* backing_store,
160 int index) {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000161 return backing_store->get(index);
162 }
163
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000164 private:
165 DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase);
166};
167
168
169class FastElementsAccessor
170 : public ElementsAccessorBase<FastElementsAccessor, FixedArray> {
171 public:
172 static MaybeObject* DeleteCommon(JSObject* obj,
173 uint32_t index) {
174 ASSERT(obj->HasFastElements() || obj->HasFastArgumentsElements());
175 Heap* heap = obj->GetHeap();
176 FixedArray* backing_store = FixedArray::cast(obj->elements());
177 if (backing_store->map() == heap->non_strict_arguments_elements_map()) {
178 backing_store = FixedArray::cast(backing_store->get(1));
179 } else {
180 Object* writable;
181 MaybeObject* maybe = obj->EnsureWritableFastElements();
182 if (!maybe->ToObject(&writable)) return maybe;
183 backing_store = FixedArray::cast(writable);
184 }
185 uint32_t length = static_cast<uint32_t>(
186 obj->IsJSArray()
187 ? Smi::cast(JSArray::cast(obj)->length())->value()
188 : backing_store->length());
189 if (index < length) {
190 backing_store->set_the_hole(index);
191 // If an old space backing store is larger than a certain size and
192 // has too few used values, normalize it.
193 // To avoid doing the check on every delete we require at least
194 // one adjacent hole to the value being deleted.
195 Object* hole = heap->the_hole_value();
196 const int kMinLengthForSparsenessCheck = 64;
197 if (backing_store->length() >= kMinLengthForSparsenessCheck &&
198 !heap->InNewSpace(backing_store) &&
199 ((index > 0 && backing_store->get(index - 1) == hole) ||
200 (index + 1 < length && backing_store->get(index + 1) == hole))) {
201 int num_used = 0;
202 for (int i = 0; i < backing_store->length(); ++i) {
203 if (backing_store->get(i) != hole) ++num_used;
204 // Bail out early if more than 1/4 is used.
205 if (4 * num_used > backing_store->length()) break;
206 }
207 if (4 * num_used <= backing_store->length()) {
208 MaybeObject* result = obj->NormalizeElements();
209 if (result->IsFailure()) return result;
210 }
211 }
212 }
213 return heap->true_value();
214 }
215
216 virtual MaybeObject* Delete(JSObject* obj,
217 uint32_t index,
218 JSReceiver::DeleteMode mode) {
219 return DeleteCommon(obj, index);
220 }
221};
222
223
224class FastDoubleElementsAccessor
225 : public ElementsAccessorBase<FastDoubleElementsAccessor,
226 FixedDoubleArray> {
227 virtual MaybeObject* Delete(JSObject* obj,
228 uint32_t index,
229 JSReceiver::DeleteMode mode) {
230 int length = obj->IsJSArray()
231 ? Smi::cast(JSArray::cast(obj)->length())->value()
232 : FixedDoubleArray::cast(obj->elements())->length();
233 if (index < static_cast<uint32_t>(length)) {
234 FixedDoubleArray::cast(obj->elements())->set_the_hole(index);
235 }
236 return obj->GetHeap()->true_value();
237 }
238};
239
240
241// Super class for all external element arrays.
242template<typename ExternalElementsAccessorSubclass,
243 typename ExternalArray>
244class ExternalElementsAccessor
245 : public ElementsAccessorBase<ExternalElementsAccessorSubclass,
246 ExternalArray> {
247 public:
248 virtual MaybeObject* GetWithReceiver(JSObject* obj,
249 Object* receiver,
250 uint32_t index) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000251 ExternalArray* backing_store = ExternalArray::cast(obj->elements());
252 if (index < ExternalElementsAccessorSubclass::GetLength(backing_store)) {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000253 return backing_store->get(index);
254 } else {
255 return obj->GetHeap()->undefined_value();
256 }
257 }
258
259 virtual MaybeObject* Delete(JSObject* obj,
260 uint32_t index,
261 JSReceiver::DeleteMode mode) {
262 // External arrays always ignore deletes.
263 return obj->GetHeap()->true_value();
264 }
265};
266
267
268class ExternalByteElementsAccessor
269 : public ExternalElementsAccessor<ExternalByteElementsAccessor,
270 ExternalByteArray> {
271};
272
273
274class ExternalUnsignedByteElementsAccessor
275 : public ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor,
276 ExternalUnsignedByteArray> {
277};
278
279
280class ExternalShortElementsAccessor
281 : public ExternalElementsAccessor<ExternalShortElementsAccessor,
282 ExternalShortArray> {
283};
284
285
286class ExternalUnsignedShortElementsAccessor
287 : public ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor,
288 ExternalUnsignedShortArray> {
289};
290
291
292class ExternalIntElementsAccessor
293 : public ExternalElementsAccessor<ExternalIntElementsAccessor,
294 ExternalIntArray> {
295};
296
297
298class ExternalUnsignedIntElementsAccessor
299 : public ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor,
300 ExternalUnsignedIntArray> {
301};
302
303
304class ExternalFloatElementsAccessor
305 : public ExternalElementsAccessor<ExternalFloatElementsAccessor,
306 ExternalFloatArray> {
307};
308
309
310class ExternalDoubleElementsAccessor
311 : public ExternalElementsAccessor<ExternalDoubleElementsAccessor,
312 ExternalDoubleArray> {
313};
314
315
316class PixelElementsAccessor
317 : public ExternalElementsAccessor<PixelElementsAccessor,
318 ExternalPixelArray> {
319};
320
321
322class DictionaryElementsAccessor
323 : public ElementsAccessorBase<DictionaryElementsAccessor,
324 NumberDictionary> {
325 public:
326 static MaybeObject* GetNumberDictionaryElement(
327 JSObject* obj,
328 Object* receiver,
329 NumberDictionary* backing_store,
330 uint32_t index) {
331 int entry = backing_store->FindEntry(index);
332 if (entry != NumberDictionary::kNotFound) {
333 Object* element = backing_store->ValueAt(entry);
334 PropertyDetails details = backing_store->DetailsAt(entry);
335 if (details.type() == CALLBACKS) {
336 return obj->GetElementWithCallback(receiver,
337 element,
338 index,
339 obj);
340 } else {
341 return element;
342 }
343 }
344 return obj->GetHeap()->the_hole_value();
345 }
346
347
348 static MaybeObject* DeleteCommon(JSObject* obj,
349 uint32_t index,
350 JSReceiver::DeleteMode mode) {
351 Isolate* isolate = obj->GetIsolate();
352 Heap* heap = isolate->heap();
353 FixedArray* backing_store = FixedArray::cast(obj->elements());
354 bool is_arguments =
355 (obj->GetElementsKind() == JSObject::NON_STRICT_ARGUMENTS_ELEMENTS);
356 if (is_arguments) {
357 backing_store = FixedArray::cast(backing_store->get(1));
358 }
359 NumberDictionary* dictionary = NumberDictionary::cast(backing_store);
360 int entry = dictionary->FindEntry(index);
361 if (entry != NumberDictionary::kNotFound) {
362 Object* result = dictionary->DeleteProperty(entry, mode);
363 if (result == heap->true_value()) {
364 MaybeObject* maybe_elements = dictionary->Shrink(index);
365 FixedArray* new_elements = NULL;
366 if (!maybe_elements->To(&new_elements)) {
367 return maybe_elements;
368 }
369 if (is_arguments) {
370 FixedArray::cast(obj->elements())->set(1, new_elements);
371 } else {
372 obj->set_elements(new_elements);
373 }
374 }
375 if (mode == JSObject::STRICT_DELETION &&
376 result == heap->false_value()) {
377 // In strict mode, attempting to delete a non-configurable property
378 // throws an exception.
379 HandleScope scope(isolate);
380 Handle<Object> holder(obj);
381 Handle<Object> name = isolate->factory()->NewNumberFromUint(index);
382 Handle<Object> args[2] = { name, holder };
383 Handle<Object> error =
384 isolate->factory()->NewTypeError("strict_delete_property",
385 HandleVector(args, 2));
386 return isolate->Throw(*error);
387 }
388 }
389 return heap->true_value();
390 }
391
392 virtual MaybeObject* Delete(JSObject* obj,
393 uint32_t index,
394 JSReceiver::DeleteMode mode) {
395 return DeleteCommon(obj, index, mode);
396 }
397
398 virtual MaybeObject* GetWithReceiver(JSObject* obj,
399 Object* receiver,
400 uint32_t index) {
401 return GetNumberDictionaryElement(obj,
402 receiver,
403 obj->element_dictionary(),
404 index);
405 }
406
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000407 static uint32_t GetCapacity(NumberDictionary* dict) {
408 return dict->Capacity();
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000409 }
410
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000411 static MaybeObject* GetElementAtCapacityIndex(NumberDictionary* dict,
412 int index) {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000413 if (dict->IsKey(dict->KeyAt(index))) {
414 return dict->ValueAt(index);
415 } else {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000416 return dict->GetHeap()->the_hole_value();
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000417 }
418 }
419};
420
421
422class NonStrictArgumentsElementsAccessor
423 : public ElementsAccessorBase<NonStrictArgumentsElementsAccessor,
424 FixedArray> {
425 public:
426 virtual MaybeObject* GetWithReceiver(JSObject* obj,
427 Object* receiver,
428 uint32_t index) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000429 FixedArray* parameter_map = FixedArray::cast(obj->elements());
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000430 uint32_t length = parameter_map->length();
431 Object* probe =
432 (index < length - 2) ? parameter_map->get(index + 2) : NULL;
433 if (probe != NULL && !probe->IsTheHole()) {
434 Context* context = Context::cast(parameter_map->get(0));
435 int context_index = Smi::cast(probe)->value();
436 ASSERT(!context->get(context_index)->IsTheHole());
437 return context->get(context_index);
438 } else {
439 // Object is not mapped, defer to the arguments.
440 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
441 if (arguments->IsDictionary()) {
442 return DictionaryElementsAccessor::GetNumberDictionaryElement(
443 obj,
444 receiver,
445 NumberDictionary::cast(arguments),
446 index);
447 } else if (index < static_cast<uint32_t>(arguments->length())) {
448 return arguments->get(index);
449 }
450 }
451 return obj->GetHeap()->the_hole_value();
452 }
453
454 virtual MaybeObject* Delete(JSObject* obj,
455 uint32_t index,
456 JSReceiver::DeleteMode mode) {
457 FixedArray* parameter_map = FixedArray::cast(obj->elements());
458 uint32_t length = parameter_map->length();
459 Object* probe =
460 index < (length - 2) ? parameter_map->get(index + 2) : NULL;
461 if (probe != NULL && !probe->IsTheHole()) {
462 // TODO(kmillikin): We could check if this was the last aliased
463 // parameter, and revert to normal elements in that case. That
464 // would enable GC of the context.
465 parameter_map->set_the_hole(index + 2);
466 } else {
467 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
468 if (arguments->IsDictionary()) {
469 return DictionaryElementsAccessor::DeleteCommon(obj, index, mode);
470 } else {
471 return FastElementsAccessor::DeleteCommon(obj, index);
472 }
473 }
474 return obj->GetHeap()->true_value();
475 }
476
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000477 static uint32_t GetCapacity(FixedArray* obj) {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000478 // TODO(danno): Return max of parameter map length or backing store
479 // capacity.
480 return 0;
481 }
482
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000483 static MaybeObject* GetElementAtCapacityIndex(FixedArray* obj, int index) {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000484 // TODO(danno): Return either value from parameter map of backing
485 // store value at index.
486 return obj->GetHeap()->the_hole_value();
487 }
488};
489
490
491void ElementsAccessor::InitializeOncePerProcess() {
492 static struct ConcreteElementsAccessors {
493 FastElementsAccessor fast_elements_handler;
494 FastDoubleElementsAccessor fast_double_elements_handler;
495 DictionaryElementsAccessor dictionary_elements_handler;
496 NonStrictArgumentsElementsAccessor non_strict_arguments_elements_handler;
497 ExternalByteElementsAccessor byte_elements_handler;
498 ExternalUnsignedByteElementsAccessor unsigned_byte_elements_handler;
499 ExternalShortElementsAccessor short_elements_handler;
500 ExternalUnsignedShortElementsAccessor unsigned_short_elements_handler;
501 ExternalIntElementsAccessor int_elements_handler;
502 ExternalUnsignedIntElementsAccessor unsigned_int_elements_handler;
503 ExternalFloatElementsAccessor float_elements_handler;
504 ExternalDoubleElementsAccessor double_elements_handler;
505 PixelElementsAccessor pixel_elements_handler;
506 } element_accessors;
507
508 static ElementsAccessor* accessor_array[] = {
509 &element_accessors.fast_elements_handler,
510 &element_accessors.fast_double_elements_handler,
511 &element_accessors.dictionary_elements_handler,
512 &element_accessors.non_strict_arguments_elements_handler,
513 &element_accessors.byte_elements_handler,
514 &element_accessors.unsigned_byte_elements_handler,
515 &element_accessors.short_elements_handler,
516 &element_accessors.unsigned_short_elements_handler,
517 &element_accessors.int_elements_handler,
518 &element_accessors.unsigned_int_elements_handler,
519 &element_accessors.float_elements_handler,
520 &element_accessors.double_elements_handler,
521 &element_accessors.pixel_elements_handler
522 };
523
524 elements_accessors_ = accessor_array;
525}
526
527
528} } // namespace v8::internal