| // Copyright 2011 the V8 project authors. All rights reserved. |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "v8.h" |
| |
| #include "objects.h" |
| #include "elements.h" |
| #include "utils.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| |
| ElementsAccessor** ElementsAccessor::elements_accessors_; |
| |
| |
| bool HasKey(FixedArray* array, Object* key) { |
| int len0 = array->length(); |
| for (int i = 0; i < len0; i++) { |
| Object* element = array->get(i); |
| if (element->IsSmi() && element == key) return true; |
| if (element->IsString() && |
| key->IsString() && String::cast(element)->Equals(String::cast(key))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| // Base class for element handler implementations. Contains the |
| // the common logic for objects with different ElementsKinds. |
| // Subclasses must specialize method for which the element |
| // implementation differs from the base class implementation. |
| // |
| // This class is intended to be used in the following way: |
| // |
| // class SomeElementsAccessor : |
| // public ElementsAccessorBase<SomeElementsAccessor, |
| // BackingStoreClass> { |
| // ... |
| // } |
| // |
| // This is an example of the Curiously Recurring Template Pattern (see |
| // http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). We use |
| // CRTP to guarantee aggressive compile time optimizations (i.e. inlining and |
| // specialization of SomeElementsAccessor methods). |
| template <typename ElementsAccessorSubclass, typename BackingStoreClass> |
| class ElementsAccessorBase : public ElementsAccessor { |
| protected: |
| ElementsAccessorBase() { } |
| virtual MaybeObject* Get(FixedArrayBase* backing_store, |
| uint32_t key, |
| JSObject* obj, |
| Object* receiver) { |
| return ElementsAccessorSubclass::Get( |
| BackingStoreClass::cast(backing_store), key, obj, receiver); |
| } |
| |
| static MaybeObject* Get(BackingStoreClass* backing_store, |
| uint32_t key, |
| JSObject* obj, |
| Object* receiver) { |
| if (key < ElementsAccessorSubclass::GetCapacity(backing_store)) { |
| return backing_store->get(key); |
| } |
| return backing_store->GetHeap()->the_hole_value(); |
| } |
| |
| virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) = 0; |
| |
| virtual MaybeObject* AddElementsToFixedArray(FixedArrayBase* from, |
| FixedArray* to, |
| JSObject* holder, |
| Object* receiver) { |
| int len0 = to->length(); |
| #ifdef DEBUG |
| if (FLAG_enable_slow_asserts) { |
| for (int i = 0; i < len0; i++) { |
| ASSERT(!to->get(i)->IsTheHole()); |
| } |
| } |
| #endif |
| BackingStoreClass* backing_store = BackingStoreClass::cast(from); |
| uint32_t len1 = ElementsAccessorSubclass::GetCapacity(backing_store); |
| |
| // Optimize if 'other' is empty. |
| // We cannot optimize if 'this' is empty, as other may have holes. |
| if (len1 == 0) return to; |
| |
| // Compute how many elements are not in other. |
| int extra = 0; |
| for (uint32_t y = 0; y < len1; y++) { |
| if (ElementsAccessorSubclass::HasElementAtIndex(backing_store, |
| y, |
| holder, |
| receiver)) { |
| uint32_t key = |
| ElementsAccessorSubclass::GetKeyForIndex(backing_store, y); |
| MaybeObject* maybe_value = |
| ElementsAccessorSubclass::Get(backing_store, key, holder, receiver); |
| Object* value; |
| if (!maybe_value->ToObject(&value)) return maybe_value; |
| ASSERT(!value->IsTheHole()); |
| if (!HasKey(to, value)) { |
| extra++; |
| } |
| } |
| } |
| |
| if (extra == 0) return to; |
| |
| // Allocate the result |
| FixedArray* result; |
| MaybeObject* maybe_obj = |
| backing_store->GetHeap()->AllocateFixedArray(len0 + extra); |
| if (!maybe_obj->To<FixedArray>(&result)) return maybe_obj; |
| |
| // Fill in the content |
| { |
| AssertNoAllocation no_gc; |
| WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); |
| for (int i = 0; i < len0; i++) { |
| Object* e = to->get(i); |
| ASSERT(e->IsString() || e->IsNumber()); |
| result->set(i, e, mode); |
| } |
| } |
| // Fill in the extra values. |
| int index = 0; |
| for (uint32_t y = 0; y < len1; y++) { |
| if (ElementsAccessorSubclass::HasElementAtIndex(backing_store, |
| y, |
| holder, |
| receiver)) { |
| uint32_t key = |
| ElementsAccessorSubclass::GetKeyForIndex(backing_store, y); |
| MaybeObject* maybe_value = |
| ElementsAccessorSubclass::Get(backing_store, key, holder, receiver); |
| Object* value; |
| if (!maybe_value->ToObject(&value)) return maybe_value; |
| if (!value->IsTheHole() && !HasKey(to, value)) { |
| result->set(len0 + index, value); |
| index++; |
| } |
| } |
| } |
| ASSERT(extra == index); |
| return result; |
| } |
| |
| protected: |
| static uint32_t GetCapacity(BackingStoreClass* backing_store) { |
| return backing_store->length(); |
| } |
| |
| virtual uint32_t GetCapacity(FixedArrayBase* backing_store) { |
| return ElementsAccessorSubclass::GetCapacity( |
| BackingStoreClass::cast(backing_store)); |
| } |
| |
| static bool HasElementAtIndex(BackingStoreClass* backing_store, |
| uint32_t index, |
| JSObject* holder, |
| Object* receiver) { |
| uint32_t key = |
| ElementsAccessorSubclass::GetKeyForIndex(backing_store, index); |
| MaybeObject* element = ElementsAccessorSubclass::Get(backing_store, |
| key, |
| holder, |
| receiver); |
| return !element->IsTheHole(); |
| } |
| |
| virtual bool HasElementAtIndex(FixedArrayBase* backing_store, |
| uint32_t index, |
| JSObject* holder, |
| Object* receiver) { |
| return ElementsAccessorSubclass::HasElementAtIndex( |
| BackingStoreClass::cast(backing_store), index, holder, receiver); |
| } |
| |
| static uint32_t GetKeyForIndex(BackingStoreClass* backing_store, |
| uint32_t index) { |
| return index; |
| } |
| |
| virtual uint32_t GetKeyForIndex(FixedArrayBase* backing_store, |
| uint32_t index) { |
| return ElementsAccessorSubclass::GetKeyForIndex( |
| BackingStoreClass::cast(backing_store), index); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase); |
| }; |
| |
| |
| class FastElementsAccessor |
| : public ElementsAccessorBase<FastElementsAccessor, FixedArray> { |
| public: |
| static MaybeObject* DeleteCommon(JSObject* obj, |
| uint32_t key) { |
| ASSERT(obj->HasFastElements() || obj->HasFastArgumentsElements()); |
| Heap* heap = obj->GetHeap(); |
| FixedArray* backing_store = FixedArray::cast(obj->elements()); |
| if (backing_store->map() == heap->non_strict_arguments_elements_map()) { |
| backing_store = FixedArray::cast(backing_store->get(1)); |
| } else { |
| Object* writable; |
| MaybeObject* maybe = obj->EnsureWritableFastElements(); |
| if (!maybe->ToObject(&writable)) return maybe; |
| backing_store = FixedArray::cast(writable); |
| } |
| uint32_t length = static_cast<uint32_t>( |
| obj->IsJSArray() |
| ? Smi::cast(JSArray::cast(obj)->length())->value() |
| : backing_store->length()); |
| if (key < length) { |
| backing_store->set_the_hole(key); |
| // If an old space backing store is larger than a certain size and |
| // has too few used values, normalize it. |
| // To avoid doing the check on every delete we require at least |
| // one adjacent hole to the value being deleted. |
| Object* hole = heap->the_hole_value(); |
| const int kMinLengthForSparsenessCheck = 64; |
| if (backing_store->length() >= kMinLengthForSparsenessCheck && |
| !heap->InNewSpace(backing_store) && |
| ((key > 0 && backing_store->get(key - 1) == hole) || |
| (key + 1 < length && backing_store->get(key + 1) == hole))) { |
| int num_used = 0; |
| for (int i = 0; i < backing_store->length(); ++i) { |
| if (backing_store->get(i) != hole) ++num_used; |
| // Bail out early if more than 1/4 is used. |
| if (4 * num_used > backing_store->length()) break; |
| } |
| if (4 * num_used <= backing_store->length()) { |
| MaybeObject* result = obj->NormalizeElements(); |
| if (result->IsFailure()) return result; |
| } |
| } |
| } |
| return heap->true_value(); |
| } |
| |
| protected: |
| virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| return DeleteCommon(obj, key); |
| } |
| }; |
| |
| |
| class FastDoubleElementsAccessor |
| : public ElementsAccessorBase<FastDoubleElementsAccessor, |
| FixedDoubleArray> { |
| protected: |
| friend class ElementsAccessorBase<FastDoubleElementsAccessor, |
| FixedDoubleArray>; |
| |
| virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| int length = obj->IsJSArray() |
| ? Smi::cast(JSArray::cast(obj)->length())->value() |
| : FixedDoubleArray::cast(obj->elements())->length(); |
| if (key < static_cast<uint32_t>(length)) { |
| FixedDoubleArray::cast(obj->elements())->set_the_hole(key); |
| } |
| return obj->GetHeap()->true_value(); |
| } |
| |
| static bool HasElementAtIndex(FixedDoubleArray* backing_store, |
| uint32_t index, |
| JSObject* holder, |
| Object* receiver) { |
| return !backing_store->is_the_hole(index); |
| } |
| }; |
| |
| |
| // Super class for all external element arrays. |
| template<typename ExternalElementsAccessorSubclass, |
| typename ExternalArray> |
| class ExternalElementsAccessor |
| : public ElementsAccessorBase<ExternalElementsAccessorSubclass, |
| ExternalArray> { |
| protected: |
| friend class ElementsAccessorBase<ExternalElementsAccessorSubclass, |
| ExternalArray>; |
| |
| static MaybeObject* Get(ExternalArray* backing_store, |
| uint32_t key, |
| JSObject* obj, |
| Object* receiver) { |
| if (key < ExternalElementsAccessorSubclass::GetCapacity(backing_store)) { |
| return backing_store->get(key); |
| } else { |
| return backing_store->GetHeap()->undefined_value(); |
| } |
| } |
| |
| virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| // External arrays always ignore deletes. |
| return obj->GetHeap()->true_value(); |
| } |
| }; |
| |
| |
| class ExternalByteElementsAccessor |
| : public ExternalElementsAccessor<ExternalByteElementsAccessor, |
| ExternalByteArray> { |
| }; |
| |
| |
| class ExternalUnsignedByteElementsAccessor |
| : public ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor, |
| ExternalUnsignedByteArray> { |
| }; |
| |
| |
| class ExternalShortElementsAccessor |
| : public ExternalElementsAccessor<ExternalShortElementsAccessor, |
| ExternalShortArray> { |
| }; |
| |
| |
| class ExternalUnsignedShortElementsAccessor |
| : public ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor, |
| ExternalUnsignedShortArray> { |
| }; |
| |
| |
| class ExternalIntElementsAccessor |
| : public ExternalElementsAccessor<ExternalIntElementsAccessor, |
| ExternalIntArray> { |
| }; |
| |
| |
| class ExternalUnsignedIntElementsAccessor |
| : public ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor, |
| ExternalUnsignedIntArray> { |
| }; |
| |
| |
| class ExternalFloatElementsAccessor |
| : public ExternalElementsAccessor<ExternalFloatElementsAccessor, |
| ExternalFloatArray> { |
| }; |
| |
| |
| class ExternalDoubleElementsAccessor |
| : public ExternalElementsAccessor<ExternalDoubleElementsAccessor, |
| ExternalDoubleArray> { |
| }; |
| |
| |
| class PixelElementsAccessor |
| : public ExternalElementsAccessor<PixelElementsAccessor, |
| ExternalPixelArray> { |
| }; |
| |
| |
| class DictionaryElementsAccessor |
| : public ElementsAccessorBase<DictionaryElementsAccessor, |
| NumberDictionary> { |
| public: |
| static MaybeObject* DeleteCommon(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| Isolate* isolate = obj->GetIsolate(); |
| Heap* heap = isolate->heap(); |
| FixedArray* backing_store = FixedArray::cast(obj->elements()); |
| bool is_arguments = |
| (obj->GetElementsKind() == JSObject::NON_STRICT_ARGUMENTS_ELEMENTS); |
| if (is_arguments) { |
| backing_store = FixedArray::cast(backing_store->get(1)); |
| } |
| NumberDictionary* dictionary = NumberDictionary::cast(backing_store); |
| int entry = dictionary->FindEntry(key); |
| if (entry != NumberDictionary::kNotFound) { |
| Object* result = dictionary->DeleteProperty(entry, mode); |
| if (result == heap->true_value()) { |
| MaybeObject* maybe_elements = dictionary->Shrink(key); |
| FixedArray* new_elements = NULL; |
| if (!maybe_elements->To(&new_elements)) { |
| return maybe_elements; |
| } |
| if (is_arguments) { |
| FixedArray::cast(obj->elements())->set(1, new_elements); |
| } else { |
| obj->set_elements(new_elements); |
| } |
| } |
| if (mode == JSObject::STRICT_DELETION && |
| result == heap->false_value()) { |
| // In strict mode, attempting to delete a non-configurable property |
| // throws an exception. |
| HandleScope scope(isolate); |
| Handle<Object> holder(obj); |
| Handle<Object> name = isolate->factory()->NewNumberFromUint(key); |
| Handle<Object> args[2] = { name, holder }; |
| Handle<Object> error = |
| isolate->factory()->NewTypeError("strict_delete_property", |
| HandleVector(args, 2)); |
| return isolate->Throw(*error); |
| } |
| } |
| return heap->true_value(); |
| } |
| |
| protected: |
| friend class ElementsAccessorBase<DictionaryElementsAccessor, |
| NumberDictionary>; |
| |
| virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key, |
| JSReceiver::DeleteMode mode) { |
| return DeleteCommon(obj, key, mode); |
| } |
| |
| static MaybeObject* Get(NumberDictionary* backing_store, |
| uint32_t key, |
| JSObject* obj, |
| Object* receiver) { |
| int entry = backing_store->FindEntry(key); |
| if (entry != NumberDictionary::kNotFound) { |
| Object* element = backing_store->ValueAt(entry); |
| PropertyDetails details = backing_store->DetailsAt(entry); |
| if (details.type() == CALLBACKS) { |
| return obj->GetElementWithCallback(receiver, |
| element, |
| key, |
| obj); |
| } else { |
| return element; |
| } |
| } |
| return obj->GetHeap()->the_hole_value(); |
| } |
| |
| static uint32_t GetKeyForIndex(NumberDictionary* dict, |
| uint32_t index) { |
| Object* key = dict->KeyAt(index); |
| return Smi::cast(key)->value(); |
| } |
| }; |
| |
| |
| class NonStrictArgumentsElementsAccessor |
| : public ElementsAccessorBase<NonStrictArgumentsElementsAccessor, |
| FixedArray> { |
| protected: |
| friend class ElementsAccessorBase<NonStrictArgumentsElementsAccessor, |
| FixedArray>; |
| |
| static MaybeObject* Get(FixedArray* parameter_map, |
| uint32_t key, |
| JSObject* obj, |
| Object* receiver) { |
| Object* probe = GetParameterMapArg(parameter_map, key); |
| if (!probe->IsTheHole()) { |
| Context* context = Context::cast(parameter_map->get(0)); |
| int context_index = Smi::cast(probe)->value(); |
| ASSERT(!context->get(context_index)->IsTheHole()); |
| return context->get(context_index); |
| } else { |
| // Object is not mapped, defer to the arguments. |
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| return ElementsAccessor::ForArray(arguments)->Get(arguments, |
| key, |
| obj, |
| receiver); |
| } |
| } |
| |
| virtual MaybeObject* Delete(JSObject* obj, |
| uint32_t key |
| , |
| JSReceiver::DeleteMode mode) { |
| FixedArray* parameter_map = FixedArray::cast(obj->elements()); |
| Object* probe = GetParameterMapArg(parameter_map, key); |
| if (!probe->IsTheHole()) { |
| // TODO(kmillikin): We could check if this was the last aliased |
| // parameter, and revert to normal elements in that case. That |
| // would enable GC of the context. |
| parameter_map->set_the_hole(key + 2); |
| } else { |
| FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); |
| if (arguments->IsDictionary()) { |
| return DictionaryElementsAccessor::DeleteCommon(obj, key, mode); |
| } else { |
| return FastElementsAccessor::DeleteCommon(obj, key); |
| } |
| } |
| return obj->GetHeap()->true_value(); |
| } |
| |
| static uint32_t GetCapacity(FixedArray* parameter_map) { |
| FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); |
| return Max(static_cast<uint32_t>(parameter_map->length() - 2), |
| ForArray(arguments)->GetCapacity(arguments)); |
| } |
| |
| static uint32_t GetKeyForIndex(FixedArray* dict, |
| uint32_t index) { |
| return index; |
| } |
| |
| static bool HasElementAtIndex(FixedArray* parameter_map, |
| uint32_t index, |
| JSObject* holder, |
| Object* receiver) { |
| Object* probe = GetParameterMapArg(parameter_map, index); |
| if (!probe->IsTheHole()) { |
| return true; |
| } else { |
| FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1)); |
| ElementsAccessor* accessor = ElementsAccessor::ForArray(arguments); |
| return !accessor->Get(arguments, index, holder, receiver)->IsTheHole(); |
| } |
| } |
| |
| private: |
| static Object* GetParameterMapArg(FixedArray* parameter_map, |
| uint32_t key) { |
| uint32_t length = parameter_map->length(); |
| return key < (length - 2 ) |
| ? parameter_map->get(key + 2) |
| : parameter_map->GetHeap()->the_hole_value(); |
| } |
| }; |
| |
| |
| ElementsAccessor* ElementsAccessor::ForArray(FixedArrayBase* array) { |
| switch (array->map()->instance_type()) { |
| case FIXED_ARRAY_TYPE: |
| if (array->IsDictionary()) { |
| return elements_accessors_[JSObject::DICTIONARY_ELEMENTS]; |
| } else { |
| return elements_accessors_[JSObject::FAST_ELEMENTS]; |
| } |
| case EXTERNAL_BYTE_ARRAY_TYPE: |
| return elements_accessors_[JSObject::EXTERNAL_BYTE_ELEMENTS]; |
| case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: |
| return elements_accessors_[JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS]; |
| case EXTERNAL_SHORT_ARRAY_TYPE: |
| return elements_accessors_[JSObject::EXTERNAL_SHORT_ELEMENTS]; |
| case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: |
| return elements_accessors_[JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS]; |
| case EXTERNAL_INT_ARRAY_TYPE: |
| return elements_accessors_[JSObject::EXTERNAL_INT_ELEMENTS]; |
| case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: |
| return elements_accessors_[JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS]; |
| case EXTERNAL_FLOAT_ARRAY_TYPE: |
| return elements_accessors_[JSObject::EXTERNAL_FLOAT_ELEMENTS]; |
| case EXTERNAL_DOUBLE_ARRAY_TYPE: |
| return elements_accessors_[JSObject::EXTERNAL_DOUBLE_ELEMENTS]; |
| case EXTERNAL_PIXEL_ARRAY_TYPE: |
| return elements_accessors_[JSObject::EXTERNAL_PIXEL_ELEMENTS]; |
| default: |
| UNREACHABLE(); |
| return NULL; |
| } |
| } |
| |
| |
| void ElementsAccessor::InitializeOncePerProcess() { |
| static struct ConcreteElementsAccessors { |
| FastElementsAccessor fast_elements_handler; |
| FastDoubleElementsAccessor fast_double_elements_handler; |
| DictionaryElementsAccessor dictionary_elements_handler; |
| NonStrictArgumentsElementsAccessor non_strict_arguments_elements_handler; |
| ExternalByteElementsAccessor byte_elements_handler; |
| ExternalUnsignedByteElementsAccessor unsigned_byte_elements_handler; |
| ExternalShortElementsAccessor short_elements_handler; |
| ExternalUnsignedShortElementsAccessor unsigned_short_elements_handler; |
| ExternalIntElementsAccessor int_elements_handler; |
| ExternalUnsignedIntElementsAccessor unsigned_int_elements_handler; |
| ExternalFloatElementsAccessor float_elements_handler; |
| ExternalDoubleElementsAccessor double_elements_handler; |
| PixelElementsAccessor pixel_elements_handler; |
| } element_accessors; |
| |
| static ElementsAccessor* accessor_array[] = { |
| &element_accessors.fast_elements_handler, |
| &element_accessors.fast_double_elements_handler, |
| &element_accessors.dictionary_elements_handler, |
| &element_accessors.non_strict_arguments_elements_handler, |
| &element_accessors.byte_elements_handler, |
| &element_accessors.unsigned_byte_elements_handler, |
| &element_accessors.short_elements_handler, |
| &element_accessors.unsigned_short_elements_handler, |
| &element_accessors.int_elements_handler, |
| &element_accessors.unsigned_int_elements_handler, |
| &element_accessors.float_elements_handler, |
| &element_accessors.double_elements_handler, |
| &element_accessors.pixel_elements_handler |
| }; |
| |
| elements_accessors_ = accessor_array; |
| } |
| |
| |
| } } // namespace v8::internal |