blob: 9cfcceec24cadeea63fd15c452cff71a3b08caf8 [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) {
78 if (index < ElementsAccessorSubclass::GetLength(obj)) {
79 BackingStoreClass* backing_store =
80 ElementsAccessorSubclass::GetBackingStore(obj);
81 return backing_store->get(index);
82 }
83 return obj->GetHeap()->the_hole_value();
84 }
85
86 virtual MaybeObject* Delete(JSObject* obj,
87 uint32_t index,
88 JSReceiver::DeleteMode mode) = 0;
89
90 virtual MaybeObject* AddJSArrayKeysToFixedArray(JSArray* other,
91 FixedArray* keys) {
92 int len0 = keys->length();
93#ifdef DEBUG
94 if (FLAG_enable_slow_asserts) {
95 for (int i = 0; i < len0; i++) {
96 ASSERT(keys->get(i)->IsString() || keys->get(i)->IsNumber());
97 }
98 }
99#endif
100 int len1 = ElementsAccessorSubclass::GetCapacity(other);
101
102 // Optimize if 'other' is empty.
103 // We cannot optimize if 'this' is empty, as other may have holes
104 // or non keys.
105 if (len1 == 0) return keys;
106
107 // Compute how many elements are not in other.
108 int extra = 0;
109 for (int y = 0; y < len1; y++) {
110 Object* value;
111 MaybeObject* maybe_value =
112 ElementsAccessorSubclass::GetElementAtCapacityIndex(other, y);
113 if (!maybe_value->ToObject(&value)) return maybe_value;
114 if (!value->IsTheHole() && !HasKey(keys, value)) extra++;
115 }
116
117 if (extra == 0) return keys;
118
119 // Allocate the result
120 FixedArray* result;
121 MaybeObject* maybe_obj =
122 other->GetHeap()->AllocateFixedArray(len0 + extra);
123 if (!maybe_obj->To<FixedArray>(&result)) return maybe_obj;
124
125 // Fill in the content
126 {
127 AssertNoAllocation no_gc;
128 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
129 for (int i = 0; i < len0; i++) {
130 Object* e = keys->get(i);
131 ASSERT(e->IsString() || e->IsNumber());
132 result->set(i, e, mode);
133 }
134 }
135 // Fill in the extra keys.
136 int index = 0;
137 for (int y = 0; y < len1; y++) {
138 MaybeObject* maybe_value =
139 ElementsAccessorSubclass::GetElementAtCapacityIndex(other, y);
140 Object* value;
141 if (!maybe_value->ToObject(&value)) return maybe_value;
142 if (!value->IsTheHole() && !HasKey(keys, value)) {
143 ASSERT(value->IsString() || value->IsNumber());
144 result->set(len0 + index, value);
145 index++;
146 }
147 }
148 ASSERT(extra == index);
149 return result;
150 }
151
152 static uint32_t GetCapacity(JSObject* obj) {
153 return ElementsAccessorSubclass::GetBackingStore(obj)->length();
154 }
155
156 static MaybeObject* GetElementAtCapacityIndex(JSObject* obj, int index) {
157 BackingStoreClass* backing_store =
158 ElementsAccessorSubclass::GetBackingStore(obj);
159 return backing_store->get(index);
160 }
161
162 protected:
163 static BackingStoreClass* GetBackingStore(JSObject* obj) {
164 return BackingStoreClass::cast(obj->elements());
165 }
166
167 static uint32_t GetLength(JSObject* obj) {
168 return ElementsAccessorSubclass::GetBackingStore(obj)->length();
169 }
170
171 private:
172 DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase);
173};
174
175
176class FastElementsAccessor
177 : public ElementsAccessorBase<FastElementsAccessor, FixedArray> {
178 public:
179 static MaybeObject* DeleteCommon(JSObject* obj,
180 uint32_t index) {
181 ASSERT(obj->HasFastElements() || obj->HasFastArgumentsElements());
182 Heap* heap = obj->GetHeap();
183 FixedArray* backing_store = FixedArray::cast(obj->elements());
184 if (backing_store->map() == heap->non_strict_arguments_elements_map()) {
185 backing_store = FixedArray::cast(backing_store->get(1));
186 } else {
187 Object* writable;
188 MaybeObject* maybe = obj->EnsureWritableFastElements();
189 if (!maybe->ToObject(&writable)) return maybe;
190 backing_store = FixedArray::cast(writable);
191 }
192 uint32_t length = static_cast<uint32_t>(
193 obj->IsJSArray()
194 ? Smi::cast(JSArray::cast(obj)->length())->value()
195 : backing_store->length());
196 if (index < length) {
197 backing_store->set_the_hole(index);
198 // If an old space backing store is larger than a certain size and
199 // has too few used values, normalize it.
200 // To avoid doing the check on every delete we require at least
201 // one adjacent hole to the value being deleted.
202 Object* hole = heap->the_hole_value();
203 const int kMinLengthForSparsenessCheck = 64;
204 if (backing_store->length() >= kMinLengthForSparsenessCheck &&
205 !heap->InNewSpace(backing_store) &&
206 ((index > 0 && backing_store->get(index - 1) == hole) ||
207 (index + 1 < length && backing_store->get(index + 1) == hole))) {
208 int num_used = 0;
209 for (int i = 0; i < backing_store->length(); ++i) {
210 if (backing_store->get(i) != hole) ++num_used;
211 // Bail out early if more than 1/4 is used.
212 if (4 * num_used > backing_store->length()) break;
213 }
214 if (4 * num_used <= backing_store->length()) {
215 MaybeObject* result = obj->NormalizeElements();
216 if (result->IsFailure()) return result;
217 }
218 }
219 }
220 return heap->true_value();
221 }
222
223 virtual MaybeObject* Delete(JSObject* obj,
224 uint32_t index,
225 JSReceiver::DeleteMode mode) {
226 return DeleteCommon(obj, index);
227 }
228};
229
230
231class FastDoubleElementsAccessor
232 : public ElementsAccessorBase<FastDoubleElementsAccessor,
233 FixedDoubleArray> {
234 virtual MaybeObject* Delete(JSObject* obj,
235 uint32_t index,
236 JSReceiver::DeleteMode mode) {
237 int length = obj->IsJSArray()
238 ? Smi::cast(JSArray::cast(obj)->length())->value()
239 : FixedDoubleArray::cast(obj->elements())->length();
240 if (index < static_cast<uint32_t>(length)) {
241 FixedDoubleArray::cast(obj->elements())->set_the_hole(index);
242 }
243 return obj->GetHeap()->true_value();
244 }
245};
246
247
248// Super class for all external element arrays.
249template<typename ExternalElementsAccessorSubclass,
250 typename ExternalArray>
251class ExternalElementsAccessor
252 : public ElementsAccessorBase<ExternalElementsAccessorSubclass,
253 ExternalArray> {
254 public:
255 virtual MaybeObject* GetWithReceiver(JSObject* obj,
256 Object* receiver,
257 uint32_t index) {
258 if (index < ExternalElementsAccessorSubclass::GetLength(obj)) {
259 ExternalArray* backing_store =
260 ExternalElementsAccessorSubclass::GetBackingStore(obj);
261 return backing_store->get(index);
262 } else {
263 return obj->GetHeap()->undefined_value();
264 }
265 }
266
267 virtual MaybeObject* Delete(JSObject* obj,
268 uint32_t index,
269 JSReceiver::DeleteMode mode) {
270 // External arrays always ignore deletes.
271 return obj->GetHeap()->true_value();
272 }
273};
274
275
276class ExternalByteElementsAccessor
277 : public ExternalElementsAccessor<ExternalByteElementsAccessor,
278 ExternalByteArray> {
279};
280
281
282class ExternalUnsignedByteElementsAccessor
283 : public ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor,
284 ExternalUnsignedByteArray> {
285};
286
287
288class ExternalShortElementsAccessor
289 : public ExternalElementsAccessor<ExternalShortElementsAccessor,
290 ExternalShortArray> {
291};
292
293
294class ExternalUnsignedShortElementsAccessor
295 : public ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor,
296 ExternalUnsignedShortArray> {
297};
298
299
300class ExternalIntElementsAccessor
301 : public ExternalElementsAccessor<ExternalIntElementsAccessor,
302 ExternalIntArray> {
303};
304
305
306class ExternalUnsignedIntElementsAccessor
307 : public ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor,
308 ExternalUnsignedIntArray> {
309};
310
311
312class ExternalFloatElementsAccessor
313 : public ExternalElementsAccessor<ExternalFloatElementsAccessor,
314 ExternalFloatArray> {
315};
316
317
318class ExternalDoubleElementsAccessor
319 : public ExternalElementsAccessor<ExternalDoubleElementsAccessor,
320 ExternalDoubleArray> {
321};
322
323
324class PixelElementsAccessor
325 : public ExternalElementsAccessor<PixelElementsAccessor,
326 ExternalPixelArray> {
327};
328
329
330class DictionaryElementsAccessor
331 : public ElementsAccessorBase<DictionaryElementsAccessor,
332 NumberDictionary> {
333 public:
334 static MaybeObject* GetNumberDictionaryElement(
335 JSObject* obj,
336 Object* receiver,
337 NumberDictionary* backing_store,
338 uint32_t index) {
339 int entry = backing_store->FindEntry(index);
340 if (entry != NumberDictionary::kNotFound) {
341 Object* element = backing_store->ValueAt(entry);
342 PropertyDetails details = backing_store->DetailsAt(entry);
343 if (details.type() == CALLBACKS) {
344 return obj->GetElementWithCallback(receiver,
345 element,
346 index,
347 obj);
348 } else {
349 return element;
350 }
351 }
352 return obj->GetHeap()->the_hole_value();
353 }
354
355
356 static MaybeObject* DeleteCommon(JSObject* obj,
357 uint32_t index,
358 JSReceiver::DeleteMode mode) {
359 Isolate* isolate = obj->GetIsolate();
360 Heap* heap = isolate->heap();
361 FixedArray* backing_store = FixedArray::cast(obj->elements());
362 bool is_arguments =
363 (obj->GetElementsKind() == JSObject::NON_STRICT_ARGUMENTS_ELEMENTS);
364 if (is_arguments) {
365 backing_store = FixedArray::cast(backing_store->get(1));
366 }
367 NumberDictionary* dictionary = NumberDictionary::cast(backing_store);
368 int entry = dictionary->FindEntry(index);
369 if (entry != NumberDictionary::kNotFound) {
370 Object* result = dictionary->DeleteProperty(entry, mode);
371 if (result == heap->true_value()) {
372 MaybeObject* maybe_elements = dictionary->Shrink(index);
373 FixedArray* new_elements = NULL;
374 if (!maybe_elements->To(&new_elements)) {
375 return maybe_elements;
376 }
377 if (is_arguments) {
378 FixedArray::cast(obj->elements())->set(1, new_elements);
379 } else {
380 obj->set_elements(new_elements);
381 }
382 }
383 if (mode == JSObject::STRICT_DELETION &&
384 result == heap->false_value()) {
385 // In strict mode, attempting to delete a non-configurable property
386 // throws an exception.
387 HandleScope scope(isolate);
388 Handle<Object> holder(obj);
389 Handle<Object> name = isolate->factory()->NewNumberFromUint(index);
390 Handle<Object> args[2] = { name, holder };
391 Handle<Object> error =
392 isolate->factory()->NewTypeError("strict_delete_property",
393 HandleVector(args, 2));
394 return isolate->Throw(*error);
395 }
396 }
397 return heap->true_value();
398 }
399
400 virtual MaybeObject* Delete(JSObject* obj,
401 uint32_t index,
402 JSReceiver::DeleteMode mode) {
403 return DeleteCommon(obj, index, mode);
404 }
405
406 virtual MaybeObject* GetWithReceiver(JSObject* obj,
407 Object* receiver,
408 uint32_t index) {
409 return GetNumberDictionaryElement(obj,
410 receiver,
411 obj->element_dictionary(),
412 index);
413 }
414
415 static uint32_t GetCapacity(JSObject* obj) {
416 return obj->element_dictionary()->Capacity();
417 }
418
419 static MaybeObject* GetElementAtCapacityIndex(JSObject* obj, int index) {
420 NumberDictionary* dict = obj->element_dictionary();
421 if (dict->IsKey(dict->KeyAt(index))) {
422 return dict->ValueAt(index);
423 } else {
424 return obj->GetHeap()->the_hole_value();
425 }
426 }
427};
428
429
430class NonStrictArgumentsElementsAccessor
431 : public ElementsAccessorBase<NonStrictArgumentsElementsAccessor,
432 FixedArray> {
433 public:
434 virtual MaybeObject* GetWithReceiver(JSObject* obj,
435 Object* receiver,
436 uint32_t index) {
437 FixedArray* parameter_map = GetBackingStore(obj);
438 uint32_t length = parameter_map->length();
439 Object* probe =
440 (index < length - 2) ? parameter_map->get(index + 2) : NULL;
441 if (probe != NULL && !probe->IsTheHole()) {
442 Context* context = Context::cast(parameter_map->get(0));
443 int context_index = Smi::cast(probe)->value();
444 ASSERT(!context->get(context_index)->IsTheHole());
445 return context->get(context_index);
446 } else {
447 // Object is not mapped, defer to the arguments.
448 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
449 if (arguments->IsDictionary()) {
450 return DictionaryElementsAccessor::GetNumberDictionaryElement(
451 obj,
452 receiver,
453 NumberDictionary::cast(arguments),
454 index);
455 } else if (index < static_cast<uint32_t>(arguments->length())) {
456 return arguments->get(index);
457 }
458 }
459 return obj->GetHeap()->the_hole_value();
460 }
461
462 virtual MaybeObject* Delete(JSObject* obj,
463 uint32_t index,
464 JSReceiver::DeleteMode mode) {
465 FixedArray* parameter_map = FixedArray::cast(obj->elements());
466 uint32_t length = parameter_map->length();
467 Object* probe =
468 index < (length - 2) ? parameter_map->get(index + 2) : NULL;
469 if (probe != NULL && !probe->IsTheHole()) {
470 // TODO(kmillikin): We could check if this was the last aliased
471 // parameter, and revert to normal elements in that case. That
472 // would enable GC of the context.
473 parameter_map->set_the_hole(index + 2);
474 } else {
475 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
476 if (arguments->IsDictionary()) {
477 return DictionaryElementsAccessor::DeleteCommon(obj, index, mode);
478 } else {
479 return FastElementsAccessor::DeleteCommon(obj, index);
480 }
481 }
482 return obj->GetHeap()->true_value();
483 }
484
485 static uint32_t GetCapacity(JSObject* obj) {
486 // TODO(danno): Return max of parameter map length or backing store
487 // capacity.
488 return 0;
489 }
490
491 static MaybeObject* GetElementAtCapacityIndex(JSObject* obj, int index) {
492 // TODO(danno): Return either value from parameter map of backing
493 // store value at index.
494 return obj->GetHeap()->the_hole_value();
495 }
496};
497
498
499void ElementsAccessor::InitializeOncePerProcess() {
500 static struct ConcreteElementsAccessors {
501 FastElementsAccessor fast_elements_handler;
502 FastDoubleElementsAccessor fast_double_elements_handler;
503 DictionaryElementsAccessor dictionary_elements_handler;
504 NonStrictArgumentsElementsAccessor non_strict_arguments_elements_handler;
505 ExternalByteElementsAccessor byte_elements_handler;
506 ExternalUnsignedByteElementsAccessor unsigned_byte_elements_handler;
507 ExternalShortElementsAccessor short_elements_handler;
508 ExternalUnsignedShortElementsAccessor unsigned_short_elements_handler;
509 ExternalIntElementsAccessor int_elements_handler;
510 ExternalUnsignedIntElementsAccessor unsigned_int_elements_handler;
511 ExternalFloatElementsAccessor float_elements_handler;
512 ExternalDoubleElementsAccessor double_elements_handler;
513 PixelElementsAccessor pixel_elements_handler;
514 } element_accessors;
515
516 static ElementsAccessor* accessor_array[] = {
517 &element_accessors.fast_elements_handler,
518 &element_accessors.fast_double_elements_handler,
519 &element_accessors.dictionary_elements_handler,
520 &element_accessors.non_strict_arguments_elements_handler,
521 &element_accessors.byte_elements_handler,
522 &element_accessors.unsigned_byte_elements_handler,
523 &element_accessors.short_elements_handler,
524 &element_accessors.unsigned_short_elements_handler,
525 &element_accessors.int_elements_handler,
526 &element_accessors.unsigned_int_elements_handler,
527 &element_accessors.float_elements_handler,
528 &element_accessors.double_elements_handler,
529 &element_accessors.pixel_elements_handler
530 };
531
532 elements_accessors_ = accessor_array;
533}
534
535
536} } // namespace v8::internal