- Expose CanvasPixelArray functionality directly in JavaScript
authoriposva@chromium.org <iposva@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 28 Jul 2009 08:43:51 +0000 (08:43 +0000)
committeriposva@chromium.org <iposva@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 28 Jul 2009 08:43:51 +0000 (08:43 +0000)
  indexed property accesses.
- The IC stubs have not been updated to handle these directly, but
  at least we do not have to leave the VM to access bytes.

Review URL: http://codereview.chromium.org/159263

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2549 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

19 files changed:
include/v8.h
src/api.cc
src/arm/ic-arm.cc
src/factory.cc
src/factory.h
src/globals.h
src/handles.cc
src/heap.cc
src/heap.h
src/ia32/codegen-ia32.cc
src/ia32/ic-ia32.cc
src/jsregexp.cc
src/objects-debug.cc
src/objects-inl.h
src/objects.cc
src/objects.h
src/runtime.cc
test/cctest/test-api.cc
test/cctest/test-heap.cc

index cf8a3bf063bf33007f49a83ee91abb2d70dd58bd..382131eedea9ef6cc6d89d9ae8f3be8a258a52d1 100644 (file)
@@ -1169,6 +1169,15 @@ class V8EXPORT Object : public Value {
    */
   Local<Object> Clone();
 
+  /**
+   * Set the backing store of the indexed properties to be managed by the
+   * embedding layer. Access to the indexed properties will follow the rules
+   * spelled out in CanvasPixelArray.
+   * Note: The embedding program still owns the data and needs to ensure that
+   *       the backing store is preserved while V8 has a reference.
+   */
+  void SetIndexedPropertiesToPixelData(uint8_t* data, int length);
+
   static Local<Object> New();
   static Object* Cast(Value* obj);
  private:
index 9e3ca9b9df23d32f11a59fca24924402ed6f09ae..edfa0fcef492c3721544be4781e2a3006c2eb716 100644 (file)
@@ -2194,6 +2194,25 @@ bool v8::Object::DeleteHiddenValue(v8::Handle<v8::String> key) {
 }
 
 
+void v8::Object::SetIndexedPropertiesToPixelData(uint8_t* data, int length) {
+  ON_BAILOUT("v8::SetElementsToPixelData()", return);
+  ENTER_V8;
+  if (!ApiCheck(i::Smi::IsValid(length),
+                "v8::Object::SetIndexedPropertiesToPixelData()",
+                "length exceeds max acceptable value")) {
+    return;
+  }
+  i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+  if (!ApiCheck(!self->IsJSArray(),
+                "v8::Object::SetIndexedPropertiesToPixelData()",
+                "JSArray is not supported")) {
+    return;
+  }
+  i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(length, data);
+  self->set_elements(*pixels);
+}
+
+
 Local<v8::Object> Function::NewInstance() const {
   return NewInstance(0, NULL);
 }
@@ -3057,7 +3076,7 @@ Local<Object> Array::CloneElementAt(uint32_t index) {
   if (!self->HasFastElements()) {
     return Local<Object>();
   }
-  i::FixedArray* elms = self->elements();
+  i::FixedArray* elms = i::FixedArray::cast(self->elements());
   i::Object* paragon = elms->get(index);
   if (!paragon->IsJSObject()) {
     return Local<Object>();
index 82a2bec02ebddc93fe2e4c7155b29198f3656721..878125664368bfb75c481845cca484f05b5dfe4b 100644 (file)
@@ -582,8 +582,8 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
   __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
   // Check that the object is in fast mode (not dictionary).
   __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
-  __ cmp(r3, Operand(Factory::hash_table_map()));
-  __ b(eq, &slow);
+  __ cmp(r3, Operand(Factory::fixed_array_map()));
+  __ b(ne, &slow);
   // Check that the key (index) is within bounds.
   __ ldr(r3, FieldMemOperand(r1, Array::kLengthOffset));
   __ cmp(r0, Operand(r3));
@@ -661,8 +661,8 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
   __ ldr(r3, FieldMemOperand(r3, JSObject::kElementsOffset));
   // Check that the object is in fast mode (not dictionary).
   __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
-  __ cmp(r2, Operand(Factory::hash_table_map()));
-  __ b(eq, &slow);
+  __ cmp(r2, Operand(Factory::fixed_array_map()));
+  __ b(ne, &slow);
   // Untag the key (for checking against untagged length in the fixed array).
   __ mov(r1, Operand(r1, ASR, kSmiTagSize));
   // Compute address to store into and check array bounds.
@@ -710,8 +710,8 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
   __ bind(&array);
   __ ldr(r2, FieldMemOperand(r3, JSObject::kElementsOffset));
   __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
-  __ cmp(r1, Operand(Factory::hash_table_map()));
-  __ b(eq, &slow);
+  __ cmp(r1, Operand(Factory::fixed_array_map()));
+  __ b(ne, &slow);
 
   // Check the key against the length in the array, compute the
   // address to store into and fall through to fast case.
index 1045a4c3c8ea5cc4b005a2a2e8376e7cf12823a4..36554df73232eb801cd6ddde4eda6ffc1c0e272f 100644 (file)
@@ -210,6 +210,16 @@ Handle<ByteArray> Factory::NewByteArray(int length, PretenureFlag pretenure) {
 }
 
 
+Handle<PixelArray> Factory::NewPixelArray(int length,
+                                          uint8_t* external_pointer,
+                                          PretenureFlag pretenure) {
+  ASSERT(0 <= length);
+  CALL_HEAP_FUNCTION(Heap::AllocatePixelArray(length,
+                                              external_pointer,
+                                              pretenure), PixelArray);
+}
+
+
 Handle<Map> Factory::NewMap(InstanceType type, int instance_size) {
   CALL_HEAP_FUNCTION(Heap::AllocateMap(type, instance_size), Map);
 }
index 0afdd76a49b19c5072de7d0e5fa2d7281fb64575..4db5d4e71b1ba72f46428502060183ac68a5716c 100644 (file)
@@ -154,6 +154,10 @@ class Factory : public AllStatic {
   static Handle<ByteArray> NewByteArray(int length,
                                         PretenureFlag pretenure = NOT_TENURED);
 
+  static Handle<PixelArray> NewPixelArray(int length,
+      uint8_t* external_pointer,
+      PretenureFlag pretenure = NOT_TENURED);
+
   static Handle<Map> NewMap(InstanceType type, int instance_size);
 
   static Handle<JSObject> NewFunctionPrototype(Handle<JSFunction> function);
index 44bd527574acb3516083560f357bacf9da0feca0..195a2e2fc32dcadc05e87bd44dcfdcf791c79925 100644 (file)
@@ -207,6 +207,7 @@ class HeapObject;
 class IC;
 class InterceptorInfo;
 class IterationStatement;
+class Array;
 class JSArray;
 class JSFunction;
 class JSObject;
index 510ea95b8c4c51b629be6196fcf9d4d45d24e686..872e0d20eec756374937df208f2bd0caf3b7ff6d 100644 (file)
@@ -341,6 +341,14 @@ Handle<String> SubString(Handle<String> str, int start, int end) {
 Handle<Object> SetElement(Handle<JSObject> object,
                           uint32_t index,
                           Handle<Object> value) {
+  if (object->HasPixelElements()) {
+    if (!value->IsSmi() && !value->IsHeapNumber() && !value->IsUndefined()) {
+      bool has_exception;
+      Handle<Object> number = Execution::ToNumber(value, &has_exception);
+      if (has_exception) return Handle<Object>();
+      value = number;
+    }
+  }
   CALL_HEAP_FUNCTION(object->SetElement(index, *value), Object);
 }
 
index 213eec5abd9f17929b8d5bf5e10552339670ee58..233f02f8a3daebb51467811fa4eb1823f4bc56a1 100644 (file)
@@ -1191,6 +1191,10 @@ bool Heap::CreateInitialMaps() {
   if (obj->IsFailure()) return false;
   set_byte_array_map(Map::cast(obj));
 
+  obj = AllocateMap(PIXEL_ARRAY_TYPE, PixelArray::kAlignedSize);
+  if (obj->IsFailure()) return false;
+  set_pixel_array_map(Map::cast(obj));
+
   obj = AllocateMap(CODE_TYPE, Code::kHeaderSize);
   if (obj->IsFailure()) return false;
   set_code_map(Map::cast(obj));
@@ -1576,8 +1580,7 @@ Object* Heap::NumberFromDouble(double value, PretenureFlag pretenure) {
 Object* Heap::AllocateProxy(Address proxy, PretenureFlag pretenure) {
   // Statically ensure that it is safe to allocate proxies in paged spaces.
   STATIC_ASSERT(Proxy::kSize <= Page::kMaxHeapObjectSize);
-  AllocationSpace space =
-      (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
+  AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
   Object* result = Allocate(proxy_map(), space);
   if (result->IsFailure()) return result;
 
@@ -1859,6 +1862,23 @@ void Heap::CreateFillerObjectAt(Address addr, int size) {
 }
 
 
+Object* Heap::AllocatePixelArray(int length,
+                                 uint8_t* external_pointer,
+                                 PretenureFlag pretenure) {
+  AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
+
+  Object* result = AllocateRaw(PixelArray::kAlignedSize, space, OLD_DATA_SPACE);
+
+  if (result->IsFailure()) return result;
+
+  reinterpret_cast<PixelArray*>(result)->set_map(pixel_array_map());
+  reinterpret_cast<PixelArray*>(result)->set_length(length);
+  reinterpret_cast<PixelArray*>(result)->set_external_pointer(external_pointer);
+
+  return result;
+}
+
+
 Object* Heap::CreateCode(const CodeDesc& desc,
                          ZoneScopeInfo* sinfo,
                          Code::Flags flags,
index 4e2c64cd418f826d6d32ebece64b8b991c3dde25..634c21aaf3dbad498b1e9ef573048bcddc41d9bc 100644 (file)
@@ -94,6 +94,7 @@ namespace internal {
     UndetectableMediumAsciiStringMap)                                          \
   V(Map, undetectable_long_ascii_string_map, UndetectableLongAsciiStringMap)   \
   V(Map, byte_array_map, ByteArrayMap)                                         \
+  V(Map, pixel_array_map, PixelArrayMap)                                       \
   V(Map, fixed_array_map, FixedArrayMap)                                       \
   V(Map, hash_table_map, HashTableMap)                                         \
   V(Map, context_map, ContextMap)                                              \
@@ -418,6 +419,14 @@ class Heap : public AllStatic {
   // Please note this does not perform a garbage collection.
   static Object* AllocateByteArray(int length);
 
+  // Allocate a pixel array of the specified length
+  // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+  // failed.
+  // Please note this does not perform a garbage collection.
+  static Object* AllocatePixelArray(int length,
+                                    uint8_t* external_pointer,
+                                    PretenureFlag pretenure);
+
   // Allocate a tenured JS global property cell.
   // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
   // failed.
index 457b22f18d9b742fbde38be42f370ce07d49a6c7..664827719bb99a77b8c6e251508b5c9803b20f76 100644 (file)
@@ -6297,8 +6297,8 @@ void Reference::GetValue(TypeofState typeof_state) {
         __ mov(elements.reg(),
                FieldOperand(receiver.reg(), JSObject::kElementsOffset));
         __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset),
-               Immediate(Factory::hash_table_map()));
-        deferred->Branch(equal);
+               Immediate(Factory::fixed_array_map()));
+        deferred->Branch(not_equal);
 
         // Shift the key to get the actual index value and check that
         // it is within bounds.
index d9dcb7c912c4a286892ea45fa38717fbc966d01f..9c0f282a6be53c2db5ae0caa7d35d7374ffa3dea 100644 (file)
@@ -234,7 +234,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
   //  -- esp[4] : name
   //  -- esp[8] : receiver
   // -----------------------------------
-  Label slow, fast, check_string, index_int, index_string;
+  Label slow, check_string, index_int, index_string, check_pixel_array;
 
   // Load name and receiver.
   __ mov(eax, Operand(esp, kPointerSize));
@@ -269,11 +269,36 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
   __ mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset));
   // Check that the object is in fast mode (not dictionary).
   __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
-         Immediate(Factory::hash_table_map()));
-  __ j(equal, &slow, not_taken);
+         Immediate(Factory::fixed_array_map()));
+  __ j(not_equal, &check_pixel_array);
   // Check that the key (index) is within bounds.
   __ cmp(eax, FieldOperand(ecx, FixedArray::kLengthOffset));
-  __ j(below, &fast, taken);
+  __ j(above_equal, &slow);
+  // Fast case: Do the load.
+  __ mov(eax,
+         Operand(ecx, eax, times_4, FixedArray::kHeaderSize - kHeapObjectTag));
+  __ cmp(Operand(eax), Immediate(Factory::the_hole_value()));
+  // In case the loaded value is the_hole we have to consult GetProperty
+  // to ensure the prototype chain is searched.
+  __ j(equal, &slow);
+  __ IncrementCounter(&Counters::keyed_load_generic_smi, 1);
+  __ ret(0);
+
+  // Check whether the elements is a pixel array.
+  // eax: untagged index
+  // ecx: elements array
+  __ bind(&check_pixel_array);
+  __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
+         Immediate(Factory::pixel_array_map()));
+  __ j(not_equal, &slow);
+  __ cmp(eax, FieldOperand(ecx, PixelArray::kLengthOffset));
+  __ j(above_equal, &slow);
+  __ mov(ecx, FieldOperand(ecx, PixelArray::kExternalPointerOffset));
+  __ movzx_b(eax, Operand(ecx, eax, times_1, 0));
+  __ shl(eax, kSmiTagSize);
+  __ ret(0);
+  
+
   // Slow case: Load name and receiver from stack and jump to runtime.
   __ bind(&slow);
   __ IncrementCounter(&Counters::keyed_load_generic_slow, 1);
@@ -315,16 +340,6 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
   __ and_(eax, (1 << String::kShortLengthShift) - 1);
   __ shr(eax, String::kLongLengthShift);
   __ jmp(&index_int);
-  // Fast case: Do the load.
-  __ bind(&fast);
-  __ mov(eax,
-         Operand(ecx, eax, times_4, FixedArray::kHeaderSize - kHeapObjectTag));
-  __ cmp(Operand(eax), Immediate(Factory::the_hole_value()));
-  // In case the loaded value is the_hole we have to consult GetProperty
-  // to ensure the prototype chain is searched.
-  __ j(equal, &slow, not_taken);
-  __ IncrementCounter(&Counters::keyed_load_generic_smi, 1);
-  __ ret(0);
 }
 
 
@@ -335,7 +350,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
   //  -- esp[4] : key
   //  -- esp[8] : receiver
   // -----------------------------------
-  Label slow, fast, array, extra;
+  Label slow, fast, array, extra, check_pixel_array;
 
   // Get the receiver from the stack.
   __ mov(edx, Operand(esp, 2 * kPointerSize));  // 2 ~ return address, key
@@ -370,8 +385,8 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
   __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
   // Check that the object is in fast mode (not dictionary).
   __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
-         Immediate(Factory::hash_table_map()));
-  __ j(equal, &slow, not_taken);
+         Immediate(Factory::fixed_array_map()));
+  __ j(not_equal, &check_pixel_array, not_taken);
   // Untag the key (for checking against untagged length in the fixed array).
   __ mov(edx, Operand(ebx));
   __ sar(edx, kSmiTagSize);  // untag the index and use it for the comparison
@@ -381,7 +396,6 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
   // ebx: index (as a smi)
   __ j(below, &fast, taken);
 
-
   // Slow case: Push extra copies of the arguments (3).
   __ bind(&slow);
   __ pop(ecx);
@@ -392,6 +406,37 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
   // Do tail-call to runtime routine.
   __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3);
 
+  // Check whether the elements is a pixel array.
+  // eax: value
+  // ecx: elements array
+  // ebx: index (as a smi)
+  __ bind(&check_pixel_array);
+  __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
+         Immediate(Factory::pixel_array_map()));
+  __ j(not_equal, &slow);
+  // Check that the value is a smi. If a conversion is needed call into the
+  // runtime to convert and clamp.
+  __ test(eax, Immediate(kSmiTagMask));
+  __ j(not_zero, &slow);
+  __ sar(ebx, kSmiTagSize);  // Untag the index.
+  __ cmp(ebx, FieldOperand(ecx, PixelArray::kLengthOffset));
+  __ j(above_equal, &slow);
+  __ sar(eax, kSmiTagSize);  // Untag the value.
+  {  // Clamp the value to [0..255].
+    Label done, check_255;
+    __ cmp(eax, 0);
+    __ j(greater_equal, &check_255);
+    __ mov(eax, Immediate(0));
+    __ jmp(&done);
+    __ bind(&check_255);
+    __ cmp(eax, 255);
+    __ j(less_equal, &done);
+    __ mov(eax, Immediate(255));
+    __ bind(&done);
+  }
+  __ mov(ecx, FieldOperand(ecx, PixelArray::kExternalPointerOffset));
+  __ mov_b(Operand(ecx, ebx, times_1, 0), eax);
+  __ ret(0);
 
   // Extra capacity case: Check if there is extra capacity to
   // perform the store and update the length. Used for adding one
@@ -422,15 +467,14 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
   // ebx: index (as a smi)
   __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
   __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
-         Immediate(Factory::hash_table_map()));
-  __ j(equal, &slow, not_taken);
+         Immediate(Factory::fixed_array_map()));
+  __ j(not_equal, &check_pixel_array);
 
   // Check the key against the length in the array, compute the
   // address to store into and fall through to fast case.
   __ cmp(ebx, FieldOperand(edx, JSArray::kLengthOffset));
   __ j(above_equal, &extra, not_taken);
 
-
   // Fast case: Do the store.
   __ bind(&fast);
   // eax: value
index 852d431be0cbd13312567ade88c8cacad25d2729..0b1205deb4fb9f22a416f0962cef628677c32552 100644 (file)
@@ -254,7 +254,7 @@ Handle<Object> RegExpImpl::AtomExec(Handle<JSRegExp> re,
 
   {
     NoHandleAllocation no_handles;
-    FixedArray* array = last_match_info->elements();
+    FixedArray* array = FixedArray::cast(last_match_info->elements());
     SetAtomLastCapture(array, *subject, value, value + needle->length());
   }
   return last_match_info;
@@ -442,7 +442,7 @@ Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> jsregexp,
 
   if (res != RegExpMacroAssemblerIA32::SUCCESS) return Factory::null_value();
 
-  array = Handle<FixedArray>(last_match_info->elements());
+  array = Handle<FixedArray>(FixedArray::cast(last_match_info->elements()));
   ASSERT(array->length() >= number_of_capture_registers + kLastMatchOverhead);
   // The captures come in (start, end+1) pairs.
   for (int i = 0; i < number_of_capture_registers; i += 2) {
@@ -475,7 +475,7 @@ Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> jsregexp,
     return Factory::null_value();
   }
 
-  array = Handle<FixedArray>(last_match_info->elements());
+  array = Handle<FixedArray>(FixedArray::cast(last_match_info->elements()));
   ASSERT(array->length() >= number_of_capture_registers + kLastMatchOverhead);
   // The captures come in (start, end+1) pairs.
   for (int i = 0; i < number_of_capture_registers; i += 2) {
index 8c57afdbfddcb59fbbe4a01843edf0eecb62931f..cf6aa3c0af17dd5278774a398b57d25bca89a139 100644 (file)
@@ -115,6 +115,9 @@ void HeapObject::HeapObjectPrint() {
     case BYTE_ARRAY_TYPE:
       ByteArray::cast(this)->ByteArrayPrint();
       break;
+    case PIXEL_ARRAY_TYPE:
+      PixelArray::cast(this)->PixelArrayPrint();
+      break;
     case FILLER_TYPE:
       PrintF("filler");
       break;
@@ -191,6 +194,9 @@ void HeapObject::HeapObjectVerify() {
     case BYTE_ARRAY_TYPE:
       ByteArray::cast(this)->ByteArrayVerify();
       break;
+    case PIXEL_ARRAY_TYPE:
+      PixelArray::cast(this)->PixelArrayVerify();
+      break;
     case CODE_TYPE:
       Code::cast(this)->CodeVerify();
       break;
@@ -264,11 +270,21 @@ void ByteArray::ByteArrayPrint() {
 }
 
 
+void PixelArray::PixelArrayPrint() {
+  PrintF("pixel array");
+}
+
+
 void ByteArray::ByteArrayVerify() {
   ASSERT(IsByteArray());
 }
 
 
+void PixelArray::PixelArrayVerify() {
+  ASSERT(IsPixelArray());
+}
+
+
 void JSObject::PrintProperties() {
   if (HasFastProperties()) {
     DescriptorArray* descs = map()->instance_descriptors();
@@ -312,15 +328,30 @@ void JSObject::PrintProperties() {
 
 
 void JSObject::PrintElements() {
-  if (HasFastElements()) {
-    FixedArray* p = FixedArray::cast(elements());
-    for (int i = 0; i < p->length(); i++) {
-      PrintF("   %d: ", i);
-      p->get(i)->ShortPrint();
-      PrintF("\n");
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      // Print in array notation for non-sparse arrays.
+      FixedArray* p = FixedArray::cast(elements());
+      for (int i = 0; i < p->length(); i++) {
+        PrintF("   %d: ", i);
+        p->get(i)->ShortPrint();
+        PrintF("\n");
+      }
+      break;
     }
-  } else {
-    elements()->Print();
+    case PIXEL_ELEMENTS: {
+      PixelArray* p = PixelArray::cast(elements());
+      for (int i = 0; i < p->length(); i++) {
+        PrintF("   %d: %d\n", i, p->get(i));
+      }
+      break;
+    }
+    case DICTIONARY_ELEMENTS:
+      elements()->Print();
+      break;
+    default:
+      UNREACHABLE();
+      break;
   }
 }
 
@@ -402,6 +433,7 @@ static const char* TypeToString(InstanceType type) {
     case LONG_EXTERNAL_STRING_TYPE: return "EXTERNAL_STRING";
     case FIXED_ARRAY_TYPE: return "FIXED_ARRAY";
     case BYTE_ARRAY_TYPE: return "BYTE_ARRAY";
+    case PIXEL_ARRAY_TYPE: return "PIXEL_ARRAY";
     case FILLER_TYPE: return "FILLER";
     case JS_OBJECT_TYPE: return "JS_OBJECT";
     case JS_CONTEXT_EXTENSION_OBJECT_TYPE: return "JS_CONTEXT_EXTENSION_OBJECT";
@@ -1015,21 +1047,35 @@ void JSObject::IncrementSpillStatistics(SpillInformation* info) {
         dict->Capacity() - dict->NumberOfElements();
   }
   // Indexed properties
-  if (HasFastElements()) {
-    info->number_of_objects_with_fast_elements_++;
-    int holes = 0;
-    FixedArray* e = FixedArray::cast(elements());
-    int len = e->length();
-    for (int i = 0; i < len; i++) {
-      if (e->get(i) == Heap::the_hole_value()) holes++;
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      info->number_of_objects_with_fast_elements_++;
+      int holes = 0;
+      FixedArray* e = FixedArray::cast(elements());
+      int len = e->length();
+      for (int i = 0; i < len; i++) {
+        if (e->get(i) == Heap::the_hole_value()) holes++;
+      }
+      info->number_of_fast_used_elements_   += len - holes;
+      info->number_of_fast_unused_elements_ += holes;
+      break;
     }
-    info->number_of_fast_used_elements_   += len - holes;
-    info->number_of_fast_unused_elements_ += holes;
-  } else {
-    NumberDictionary* dict = element_dictionary();
-    info->number_of_slow_used_elements_ += dict->NumberOfElements();
-    info->number_of_slow_unused_elements_ +=
-        dict->Capacity() - dict->NumberOfElements();
+    case PIXEL_ELEMENTS: {
+      info->number_of_objects_with_fast_elements_++;
+      PixelArray* e = PixelArray::cast(elements());
+      info->number_of_fast_used_elements_ += e->length();
+      break;
+    }
+    case DICTIONARY_ELEMENTS: {
+      NumberDictionary* dict = element_dictionary();
+      info->number_of_slow_used_elements_ += dict->NumberOfElements();
+      info->number_of_slow_unused_elements_ +=
+          dict->Capacity() - dict->NumberOfElements();
+      break;
+    }
+    default:
+      UNREACHABLE();
+      break;
   }
 }
 
index 7abc7c3eeaf075e3b7c22d1d34739ba4a974afe6..0b5e073405692e52692bb56acc97c2e8e70e5bb6 100644 (file)
@@ -321,6 +321,12 @@ bool Object::IsByteArray() {
 }
 
 
+bool Object::IsPixelArray() {
+  return Object::IsHeapObject() &&
+      HeapObject::cast(this)->map()->instance_type() == PIXEL_ARRAY_TYPE;
+}
+
+
 bool Object::IsFailure() {
   return HAS_FAILURE_TAG(this);
 }
@@ -1043,7 +1049,22 @@ void HeapNumber::set_value(double value) {
 
 
 ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset)
-ACCESSORS(JSObject, elements, FixedArray, kElementsOffset)
+
+
+Array* JSObject::elements() {
+  Object* array = READ_FIELD(this, kElementsOffset);
+  // In the assert below Dictionary is covered under FixedArray.
+  ASSERT(array->IsFixedArray() || array->IsPixelArray());
+  return reinterpret_cast<Array*>(array);
+}
+
+
+void JSObject::set_elements(Array* value, WriteBarrierMode mode) {
+  // In the assert below Dictionary is covered under FixedArray.
+  ASSERT(value->IsFixedArray() || value->IsPixelArray());
+  WRITE_FIELD(this, kElementsOffset, value);
+  CONDITIONAL_WRITE_BARRIER(this, kElementsOffset, mode);
+}
 
 
 void JSObject::initialize_properties() {
@@ -1502,6 +1523,7 @@ CAST_ACCESSOR(JSArray)
 CAST_ACCESSOR(JSRegExp)
 CAST_ACCESSOR(Proxy)
 CAST_ACCESSOR(ByteArray)
+CAST_ACCESSOR(PixelArray)
 CAST_ACCESSOR(Struct)
 
 
@@ -1860,6 +1882,32 @@ Address ByteArray::GetDataStartAddress() {
 }
 
 
+uint8_t* PixelArray::external_pointer() {
+  intptr_t ptr = READ_INTPTR_FIELD(this, kExternalPointerOffset);
+  return reinterpret_cast<uint8_t*>(ptr);
+}
+
+
+void PixelArray::set_external_pointer(uint8_t* value, WriteBarrierMode mode) {
+  intptr_t ptr = reinterpret_cast<intptr_t>(value);
+  WRITE_INTPTR_FIELD(this, kExternalPointerOffset, ptr);
+}
+
+
+uint8_t PixelArray::get(int index) {
+  ASSERT((index >= 0) && (index < this->length()));
+  uint8_t* ptr = external_pointer();
+  return ptr[index];
+}
+
+
+void PixelArray::set(int index, uint8_t value) {
+  ASSERT((index >= 0) && (index < this->length()));
+  uint8_t* ptr = external_pointer();
+  ptr[index] = value;
+}
+
+
 int Map::instance_size() {
   return READ_BYTE_FIELD(this, kInstanceSizeOffset) << kPointerSizeLog2;
 }
@@ -2523,8 +2571,33 @@ void JSRegExp::SetDataAt(int index, Object* value) {
 }
 
 
+JSObject::ElementsKind JSObject::GetElementsKind() {
+  Array* array = elements();
+  if (array->IsFixedArray()) {
+    // FAST_ELEMENTS or DICTIONARY_ELEMENTS are both stored in a FixedArray.
+    if (array->map() == Heap::fixed_array_map()) {
+      return FAST_ELEMENTS;
+    }
+    ASSERT(array->IsDictionary());
+    return DICTIONARY_ELEMENTS;
+  }
+  ASSERT(array->IsPixelArray());
+  return PIXEL_ELEMENTS;
+}
+
+
 bool JSObject::HasFastElements() {
-  return !elements()->IsDictionary();
+  return GetElementsKind() == FAST_ELEMENTS;
+}
+
+
+bool JSObject::HasDictionaryElements() {
+  return GetElementsKind() == DICTIONARY_ELEMENTS;
+}
+
+
+bool JSObject::HasPixelElements() {
+  return GetElementsKind() == PIXEL_ELEMENTS;
 }
 
 
@@ -2545,7 +2618,7 @@ StringDictionary* JSObject::property_dictionary() {
 
 
 NumberDictionary* JSObject::element_dictionary() {
-  ASSERT(!HasFastElements());
+  ASSERT(HasDictionaryElements());
   return NumberDictionary::cast(elements());
 }
 
index 72412c15c75031bfcfdfd25cba7b068a9732d357..bd521107c9256ad3894c928726c9db555578c395 100644 (file)
@@ -1006,6 +1006,9 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
     case BYTE_ARRAY_TYPE:
       accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
       break;
+    case PIXEL_ARRAY_TYPE:
+      accumulator->Add("<PixelArray[%u]>", PixelArray::cast(this)->length());
+      break;
     case SHARED_FUNCTION_INFO_TYPE:
       accumulator->Add("<SharedFunctionInfo>");
       break;
@@ -1147,6 +1150,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
     case HEAP_NUMBER_TYPE:
     case FILLER_TYPE:
     case BYTE_ARRAY_TYPE:
+    case PIXEL_ARRAY_TYPE:
       break;
     case SHARED_FUNCTION_INFO_TYPE: {
       SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(this);
@@ -1669,7 +1673,9 @@ Object* JSObject::LookupCallbackSetterInPrototypes(uint32_t index) {
   for (Object* pt = GetPrototype();
        pt != Heap::null_value();
        pt = pt->GetPrototype()) {
-    if (JSObject::cast(pt)->HasFastElements()) continue;
+    if (!JSObject::cast(pt)->HasDictionaryElements()) {
+        continue;
+    }
     NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
     int entry = dictionary->FindEntry(index);
     if (entry != NumberDictionary::kNotFound) {
@@ -2231,7 +2237,8 @@ Object* JSObject::TransformToFastProperties(int unused_property_fields) {
 
 
 Object* JSObject::NormalizeElements() {
-  if (!HasFastElements()) return this;
+  ASSERT(!HasPixelElements());
+  if (HasDictionaryElements()) return this;
 
   // Get number of entries.
   FixedArray* array = FixedArray::cast(elements());
@@ -2317,20 +2324,28 @@ Object* JSObject::DeletePropertyWithInterceptor(String* name) {
 
 Object* JSObject::DeleteElementPostInterceptor(uint32_t index,
                                                DeleteMode mode) {
-  if (HasFastElements()) {
-    uint32_t length = IsJSArray() ?
+  ASSERT(!HasPixelElements());
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      uint32_t length = IsJSArray() ?
       static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
       static_cast<uint32_t>(FixedArray::cast(elements())->length());
-    if (index < length) {
-      FixedArray::cast(elements())->set_the_hole(index);
+      if (index < length) {
+        FixedArray::cast(elements())->set_the_hole(index);
+      }
+      break;
     }
-    return Heap::true_value();
-  }
-  ASSERT(!HasFastElements());
-  NumberDictionary* dictionary = element_dictionary();
-  int entry = dictionary->FindEntry(index);
-  if (entry != NumberDictionary::kNotFound) {
-    return dictionary->DeleteProperty(entry, mode);
+    case DICTIONARY_ELEMENTS: {
+      NumberDictionary* dictionary = element_dictionary();
+      int entry = dictionary->FindEntry(index);
+      if (entry != NumberDictionary::kNotFound) {
+        return dictionary->DeleteProperty(entry, mode);
+      }
+      break;
+    }
+    default:
+      UNREACHABLE();
+      break;
   }
   return Heap::true_value();
 }
@@ -2392,20 +2407,31 @@ Object* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
     return DeleteElementWithInterceptor(index);
   }
 
-  if (HasFastElements()) {
-    uint32_t length = IsJSArray() ?
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      uint32_t length = IsJSArray() ?
       static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
       static_cast<uint32_t>(FixedArray::cast(elements())->length());
-    if (index < length) {
-      FixedArray::cast(elements())->set_the_hole(index);
+      if (index < length) {
+        FixedArray::cast(elements())->set_the_hole(index);
+      }
+      break;
     }
-    return Heap::true_value();
-  } else {
-    NumberDictionary* dictionary = element_dictionary();
-    int entry = dictionary->FindEntry(index);
-    if (entry != NumberDictionary::kNotFound) {
-      return dictionary->DeleteProperty(entry, mode);
+    case PIXEL_ELEMENTS: {
+      // Pixel elements cannot be deleted. Just silently ignore here.
+      break;
+    }
+    case DICTIONARY_ELEMENTS: {
+      NumberDictionary* dictionary = element_dictionary();
+      int entry = dictionary->FindEntry(index);
+      if (entry != NumberDictionary::kNotFound) {
+        return dictionary->DeleteProperty(entry, mode);
+      }
+      break;
     }
+    default:
+      UNREACHABLE();
+      break;
   }
   return Heap::true_value();
 }
@@ -2483,21 +2509,32 @@ bool JSObject::ReferencesObject(Object* obj) {
   }
 
   // Check if the object is among the indexed properties.
-  if (HasFastElements()) {
-    int length = IsJSArray()
-        ? Smi::cast(JSArray::cast(this)->length())->value()
-        : FixedArray::cast(elements())->length();
-    for (int i = 0; i < length; i++) {
-      Object* element = FixedArray::cast(elements())->get(i);
-      if (!element->IsTheHole() && element == obj) {
-        return true;
+  switch (GetElementsKind()) {
+    case PIXEL_ELEMENTS:
+      // Raw pixels do not reference other objects.
+      break;
+    case FAST_ELEMENTS: {
+      int length = IsJSArray() ?
+          Smi::cast(JSArray::cast(this)->length())->value() :
+          FixedArray::cast(elements())->length();
+      for (int i = 0; i < length; i++) {
+        Object* element = FixedArray::cast(elements())->get(i);
+        if (!element->IsTheHole() && element == obj) {
+          return true;
+        }
       }
+      break;
     }
-  } else {
-    key = element_dictionary()->SlowReverseLookup(obj);
-    if (key != Heap::undefined_value()) {
-      return true;
+    case DICTIONARY_ELEMENTS: {
+      key = element_dictionary()->SlowReverseLookup(obj);
+      if (key != Heap::undefined_value()) {
+        return true;
+      }
+      break;
     }
+    default:
+      UNREACHABLE();
+      break;
   }
 
   // For functions check the context. Boilerplate functions do
@@ -2715,20 +2752,31 @@ Object* JSObject::DefineGetterSetter(String* name,
   if (is_element && IsJSArray()) return Heap::undefined_value();
 
   if (is_element) {
-    // Lookup the index.
-    if (!HasFastElements()) {
-      NumberDictionary* dictionary = element_dictionary();
-      int entry = dictionary->FindEntry(index);
-      if (entry != NumberDictionary::kNotFound) {
-        Object* result = dictionary->ValueAt(entry);
-        PropertyDetails details = dictionary->DetailsAt(entry);
-        if (details.IsReadOnly()) return Heap::undefined_value();
-        if (details.type() == CALLBACKS) {
-          // Only accessors allowed as elements.
-          ASSERT(result->IsFixedArray());
-          return result;
+    switch (GetElementsKind()) {
+      case FAST_ELEMENTS:
+        break;
+      case PIXEL_ELEMENTS:
+        // Ignore getters and setters on pixel elements.
+        return Heap::undefined_value();
+      case DICTIONARY_ELEMENTS: {
+        // Lookup the index.
+        NumberDictionary* dictionary = element_dictionary();
+        int entry = dictionary->FindEntry(index);
+        if (entry != NumberDictionary::kNotFound) {
+          Object* result = dictionary->ValueAt(entry);
+          PropertyDetails details = dictionary->DetailsAt(entry);
+          if (details.IsReadOnly()) return Heap::undefined_value();
+          if (details.type() == CALLBACKS) {
+            // Only accessors allowed as elements.
+            ASSERT(result->IsFixedArray());
+            return result;
+          }
         }
+        break;
       }
+      default:
+        UNREACHABLE();
+        break;
     }
   } else {
     // Lookup the name.
@@ -2827,9 +2875,9 @@ Object* JSObject::LookupAccessor(String* name, bool is_getter) {
     for (Object* obj = this;
          obj != Heap::null_value();
          obj = JSObject::cast(obj)->GetPrototype()) {
-      JSObject* jsObject = JSObject::cast(obj);
-      if (!jsObject->HasFastElements()) {
-        NumberDictionary* dictionary = jsObject->element_dictionary();
+      JSObject* js_object = JSObject::cast(obj);
+      if (js_object->HasDictionaryElements()) {
+        NumberDictionary* dictionary = js_object->element_dictionary();
         int entry = dictionary->FindEntry(index);
         if (entry != NumberDictionary::kNotFound) {
           Object* element = dictionary->ValueAt(entry);
@@ -3029,28 +3077,35 @@ static bool HasKey(FixedArray* array, Object* key) {
 
 
 Object* FixedArray::AddKeysFromJSArray(JSArray* array) {
-  if (array->HasFastElements()) {
-    return UnionOfKeys(array->elements());
-  }
-  ASSERT(!array->HasFastElements());
-  NumberDictionary* dict = array->element_dictionary();
-  int size = dict->NumberOfElements();
-
-  // Allocate a temporary fixed array.
-  Object* object = Heap::AllocateFixedArray(size);
-  if (object->IsFailure()) return object;
-  FixedArray* key_array = FixedArray::cast(object);
-
-  int capacity = dict->Capacity();
-  int pos = 0;
-  // Copy the elements from the JSArray to the temporary fixed array.
-  for (int i = 0; i < capacity; i++) {
-    if (dict->IsKey(dict->KeyAt(i))) {
-      key_array->set(pos++, dict->ValueAt(i));
+  ASSERT(!array->HasPixelElements());
+  switch (array->GetElementsKind()) {
+    case JSObject::FAST_ELEMENTS:
+      return UnionOfKeys(FixedArray::cast(array->elements()));
+    case JSObject::DICTIONARY_ELEMENTS: {
+      NumberDictionary* dict = array->element_dictionary();
+      int size = dict->NumberOfElements();
+      
+      // Allocate a temporary fixed array.
+      Object* object = Heap::AllocateFixedArray(size);
+      if (object->IsFailure()) return object;
+      FixedArray* key_array = FixedArray::cast(object);
+      
+      int capacity = dict->Capacity();
+      int pos = 0;
+      // Copy the elements from the JSArray to the temporary fixed array.
+      for (int i = 0; i < capacity; i++) {
+        if (dict->IsKey(dict->KeyAt(i))) {
+          key_array->set(pos++, dict->ValueAt(i));
+        }
+      }
+      // Compute the union of this and the temporary fixed array.
+      return UnionOfKeys(key_array);
     }
+    default:
+      UNREACHABLE();
   }
-  // Compute the union of this and the temporary fixed array.
-  return UnionOfKeys(key_array);
+  UNREACHABLE();
+  return Heap::null_value();  // Failure case needs to "return" a value.
 }
 
 
@@ -5089,54 +5144,74 @@ void Code::Disassemble(const char* name) {
 
 
 void JSObject::SetFastElements(FixedArray* elems) {
+  // We should never end in here with a pixel array.
+  ASSERT(!HasPixelElements());
 #ifdef DEBUG
   // Check the provided array is filled with the_hole.
   uint32_t len = static_cast<uint32_t>(elems->length());
   for (uint32_t i = 0; i < len; i++) ASSERT(elems->get(i)->IsTheHole());
 #endif
   WriteBarrierMode mode = elems->GetWriteBarrierMode();
-  if (HasFastElements()) {
-    FixedArray* old_elements = FixedArray::cast(elements());
-    uint32_t old_length = static_cast<uint32_t>(old_elements->length());
-    // Fill out the new array with this content and array holes.
-    for (uint32_t i = 0; i < old_length; i++) {
-      elems->set(i, old_elements->get(i), mode);
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      FixedArray* old_elements = FixedArray::cast(elements());
+      uint32_t old_length = static_cast<uint32_t>(old_elements->length());
+      // Fill out the new array with this content and array holes.
+      for (uint32_t i = 0; i < old_length; i++) {
+        elems->set(i, old_elements->get(i), mode);
+      }
+      break;
     }
-  } else {
-    NumberDictionary* dictionary = NumberDictionary::cast(elements());
-    for (int i = 0; i < dictionary->Capacity(); i++) {
-      Object* key = dictionary->KeyAt(i);
-      if (key->IsNumber()) {
-        uint32_t entry = static_cast<uint32_t>(key->Number());
-        elems->set(entry, dictionary->ValueAt(i), mode);
+    case DICTIONARY_ELEMENTS: {
+      NumberDictionary* dictionary = NumberDictionary::cast(elements());
+      for (int i = 0; i < dictionary->Capacity(); i++) {
+        Object* key = dictionary->KeyAt(i);
+        if (key->IsNumber()) {
+          uint32_t entry = static_cast<uint32_t>(key->Number());
+          elems->set(entry, dictionary->ValueAt(i), mode);
+        }
       }
+      break;
     }
+    default:
+      UNREACHABLE();
+      break;
   }
   set_elements(elems);
 }
 
 
 Object* JSObject::SetSlowElements(Object* len) {
+  // We should never end in here with a pixel array.
+  ASSERT(!HasPixelElements());
+
   uint32_t new_length = static_cast<uint32_t>(len->Number());
 
-  if (!HasFastElements()) {
-    if (IsJSArray()) {
-      uint32_t old_length =
-          static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
-      element_dictionary()->RemoveNumberEntries(new_length, old_length),
-      JSArray::cast(this)->set_length(len);
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      // Make sure we never try to shrink dense arrays into sparse arrays.
+      ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
+                                   new_length);
+      Object* obj = NormalizeElements();
+      if (obj->IsFailure()) return obj;
+      
+      // Update length for JSArrays.
+      if (IsJSArray()) JSArray::cast(this)->set_length(len);
+      break;
     }
-    return this;
+    case DICTIONARY_ELEMENTS: {
+      if (IsJSArray()) {
+        uint32_t old_length =
+        static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
+        element_dictionary()->RemoveNumberEntries(new_length, old_length),
+        JSArray::cast(this)->set_length(len);
+      }
+      break;
+    }
+    default:
+      UNREACHABLE();
+      break;
   }
-
-  // Make sure we never try to shrink dense arrays into sparse arrays.
-  ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
-                               new_length);
-  Object* obj = NormalizeElements();
-  if (obj->IsFailure()) return obj;
-
-  // Update length for JSArrays.
-  if (IsJSArray()) JSArray::cast(this)->set_length(len);
   return this;
 }
 
@@ -5159,7 +5234,7 @@ Object* JSArray::Initialize(int capacity) {
 
 void JSArray::Expand(int required_size) {
   Handle<JSArray> self(this);
-  Handle<FixedArray> old_backing(elements());
+  Handle<FixedArray> old_backing(FixedArray::cast(elements()));
   int old_size = old_backing->length();
   // Doubling in size would be overkill, but leave some slack to avoid
   // constantly growing.
@@ -5186,52 +5261,62 @@ static Object* ArrayLengthRangeError() {
 
 
 Object* JSObject::SetElementsLength(Object* len) {
+  // We should never end in here with a pixel array.
+  ASSERT(!HasPixelElements());
+
   Object* smi_length = len->ToSmi();
   if (smi_length->IsSmi()) {
     int value = Smi::cast(smi_length)->value();
     if (value < 0) return ArrayLengthRangeError();
-    if (HasFastElements()) {
-      int old_capacity = FixedArray::cast(elements())->length();
-      if (value <= old_capacity) {
+    switch (GetElementsKind()) {
+      case FAST_ELEMENTS: {
+        int old_capacity = FixedArray::cast(elements())->length();
+        if (value <= old_capacity) {
+          if (IsJSArray()) {
+            int old_length = FastD2I(JSArray::cast(this)->length()->Number());
+            // NOTE: We may be able to optimize this by removing the
+            // last part of the elements backing storage array and
+            // setting the capacity to the new size.
+            for (int i = value; i < old_length; i++) {
+              FixedArray::cast(elements())->set_the_hole(i);
+            }
+            JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
+          }
+          return this;
+        }
+        int min = NewElementsCapacity(old_capacity);
+        int new_capacity = value > min ? value : min;
+        if (new_capacity <= kMaxFastElementsLength ||
+            !ShouldConvertToSlowElements(new_capacity)) {
+          Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity);
+          if (obj->IsFailure()) return obj;
+          if (IsJSArray()) JSArray::cast(this)->set_length(smi_length,
+                                                           SKIP_WRITE_BARRIER);
+          SetFastElements(FixedArray::cast(obj));
+          return this;
+        }
+        break;
+      }
+      case DICTIONARY_ELEMENTS: {
         if (IsJSArray()) {
-          int old_length = FastD2I(JSArray::cast(this)->length()->Number());
-          // NOTE: We may be able to optimize this by removing the
-          // last part of the elements backing storage array and
-          // setting the capacity to the new size.
-          for (int i = value; i < old_length; i++) {
-            FixedArray::cast(elements())->set_the_hole(i);
+          if (value == 0) {
+            // If the length of a slow array is reset to zero, we clear
+            // the array and flush backing storage. This has the added
+            // benefit that the array returns to fast mode.
+            initialize_elements();
+          } else {
+            // Remove deleted elements.
+            uint32_t old_length =
+            static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
+            element_dictionary()->RemoveNumberEntries(value, old_length);
           }
           JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
         }
         return this;
       }
-      int min = NewElementsCapacity(old_capacity);
-      int new_capacity = value > min ? value : min;
-      if (new_capacity <= kMaxFastElementsLength ||
-          !ShouldConvertToSlowElements(new_capacity)) {
-        Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity);
-        if (obj->IsFailure()) return obj;
-        if (IsJSArray()) JSArray::cast(this)->set_length(smi_length,
-                                                         SKIP_WRITE_BARRIER);
-        SetFastElements(FixedArray::cast(obj));
-        return this;
-      }
-    } else {
-      if (IsJSArray()) {
-        if (value == 0) {
-          // If the length of a slow array is reset to zero, we clear
-          // the array and flush backing storage. This has the added
-          // benefit that the array returns to fast mode.
-          initialize_elements();
-        } else {
-          // Remove deleted elements.
-          uint32_t old_length =
-              static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
-          element_dictionary()->RemoveNumberEntries(value, old_length);
-        }
-        JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
-      }
-      return this;
+      default:
+        UNREACHABLE();
+        break;
     }
   }
 
@@ -5258,20 +5343,36 @@ Object* JSObject::SetElementsLength(Object* len) {
 
 
 bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
-  if (HasFastElements()) {
-    uint32_t length = IsJSArray() ?
-        static_cast<uint32_t>(
-            Smi::cast(JSArray::cast(this)->length())->value()) :
-        static_cast<uint32_t>(FixedArray::cast(elements())->length());
-    if ((index < length) &&
-        !FixedArray::cast(elements())->get(index)->IsTheHole()) {
-      return true;
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      uint32_t length = IsJSArray() ?
+          static_cast<uint32_t>
+              (Smi::cast(JSArray::cast(this)->length())->value()) :
+          static_cast<uint32_t>(FixedArray::cast(elements())->length());
+      if ((index < length) &&
+          !FixedArray::cast(elements())->get(index)->IsTheHole()) {
+        return true;
+      }
+      break;
     }
-  } else {
-    if (element_dictionary()->FindEntry(index)
-        != NumberDictionary::kNotFound) {
-      return true;
+    case PIXEL_ELEMENTS: {
+      // TODO(iposva): Add testcase.
+      PixelArray* pixels = PixelArray::cast(elements());
+      if (index < static_cast<uint32_t>(pixels->length())) {
+        return true;
+      }
+      break;
+    }
+    case DICTIONARY_ELEMENTS: {
+      if (element_dictionary()->FindEntry(index)
+          != NumberDictionary::kNotFound) {
+        return true;
+      }
+      break;
     }
+    default:
+      UNREACHABLE();
+      break;
   }
 
   // Handle [] on String objects.
@@ -5338,17 +5439,29 @@ bool JSObject::HasLocalElement(uint32_t index) {
   // Handle [] on String objects.
   if (this->IsStringObjectWithCharacterAt(index)) return true;
 
-  if (HasFastElements()) {
-    uint32_t length = IsJSArray() ?
-        static_cast<uint32_t>(
-            Smi::cast(JSArray::cast(this)->length())->value()) :
-        static_cast<uint32_t>(FixedArray::cast(elements())->length());
-    return (index < length) &&
-           !FixedArray::cast(elements())->get(index)->IsTheHole();
-  } else {
-    return element_dictionary()->FindEntry(index)
-        != NumberDictionary::kNotFound;
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      uint32_t length = IsJSArray() ?
+          static_cast<uint32_t>
+              (Smi::cast(JSArray::cast(this)->length())->value()) :
+          static_cast<uint32_t>(FixedArray::cast(elements())->length());
+      return (index < length) &&
+          !FixedArray::cast(elements())->get(index)->IsTheHole();
+    }
+    case PIXEL_ELEMENTS: {
+      PixelArray* pixels = PixelArray::cast(elements());
+      return (index < static_cast<uint32_t>(pixels->length()));
+    }
+    case DICTIONARY_ELEMENTS: {
+      return element_dictionary()->FindEntry(index)
+          != NumberDictionary::kNotFound;
+    }
+    default:
+      UNREACHABLE();
+      break;
   }
+  UNREACHABLE();
+  return Heap::null_value();
 }
 
 
@@ -5365,18 +5478,33 @@ bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
     return HasElementWithInterceptor(receiver, index);
   }
 
-  if (HasFastElements()) {
-    uint32_t length = IsJSArray() ?
-        static_cast<uint32_t>(
-            Smi::cast(JSArray::cast(this)->length())->value()) :
-        static_cast<uint32_t>(FixedArray::cast(elements())->length());
-    if ((index < length) &&
-        !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
-  } else {
-    if (element_dictionary()->FindEntry(index)
-        != NumberDictionary::kNotFound) {
-      return true;
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      uint32_t length = IsJSArray() ?
+          static_cast<uint32_t>
+              (Smi::cast(JSArray::cast(this)->length())->value()) :
+          static_cast<uint32_t>(FixedArray::cast(elements())->length());
+      if ((index < length) &&
+          !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
+      break;
+    }
+    case PIXEL_ELEMENTS: {
+      PixelArray* pixels = PixelArray::cast(elements());
+      if (index < static_cast<uint32_t>(pixels->length())) {
+        return true;
+      }
+      break;
+    }
+    case DICTIONARY_ELEMENTS: {
+      if (element_dictionary()->FindEntry(index)
+          != NumberDictionary::kNotFound) {
+        return true;
+      }
+      break;
     }
+    default:
+      UNREACHABLE();
+      break;
   }
 
   // Handle [] on String objects.
@@ -5472,7 +5600,7 @@ Object* JSObject::SetFastElement(uint32_t index, Object* value) {
   // Otherwise default to slow case.
   Object* obj = NormalizeElements();
   if (obj->IsFailure()) return obj;
-  ASSERT(!HasFastElements());
+  ASSERT(HasDictionaryElements());
   return SetElement(index, value);
 }
 
@@ -5501,80 +5629,95 @@ Object* JSObject::SetElement(uint32_t index, Object* value) {
 
 
 Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) {
-  // Fast case.
-  if (HasFastElements()) return SetFastElement(index, value);
-
-  // Dictionary case.
-  ASSERT(!HasFastElements());
-
-  // Insert element in the dictionary.
-  FixedArray* elms = FixedArray::cast(elements());
-  NumberDictionary* dictionary = NumberDictionary::cast(elms);
-
-  int entry = dictionary->FindEntry(index);
-  if (entry != NumberDictionary::kNotFound) {
-    Object* element = dictionary->ValueAt(entry);
-    PropertyDetails details = dictionary->DetailsAt(entry);
-    if (details.type() == CALLBACKS) {
-      // Only accessors allowed as elements.
-      FixedArray* structure = FixedArray::cast(element);
-      if (structure->get(kSetterIndex)->IsJSFunction()) {
-        JSFunction* setter = JSFunction::cast(structure->get(kSetterIndex));
-        return SetPropertyWithDefinedSetter(setter, value);
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS:
+      // Fast case.
+      return SetFastElement(index, value);
+    case PIXEL_ELEMENTS: {
+      PixelArray* pixels = PixelArray::cast(elements());
+      return pixels->SetValue(index, value);
+    }
+    case DICTIONARY_ELEMENTS: {
+      // Insert element in the dictionary.
+      FixedArray* elms = FixedArray::cast(elements());
+      NumberDictionary* dictionary = NumberDictionary::cast(elms);
+      
+      int entry = dictionary->FindEntry(index);
+      if (entry != NumberDictionary::kNotFound) {
+        Object* element = dictionary->ValueAt(entry);
+        PropertyDetails details = dictionary->DetailsAt(entry);
+        if (details.type() == CALLBACKS) {
+          // Only accessors allowed as elements.
+          FixedArray* structure = FixedArray::cast(element);
+          if (structure->get(kSetterIndex)->IsJSFunction()) {
+            JSFunction* setter = JSFunction::cast(structure->get(kSetterIndex));
+            return SetPropertyWithDefinedSetter(setter, value);
+          } else {
+            Handle<Object> self(this);
+            Handle<Object> key(Factory::NewNumberFromUint(index));
+            Handle<Object> args[2] = { key, self };
+            return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
+                                                     HandleVector(args, 2)));
+          }
+        } else {
+          dictionary->UpdateMaxNumberKey(index);
+          dictionary->ValueAtPut(entry, value);
+        }
       } else {
-        Handle<Object> self(this);
-        Handle<Object> key(Factory::NewNumberFromUint(index));
-        Handle<Object> args[2] = { key, self };
-        return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
-                                                 HandleVector(args, 2)));
+        // Index not already used. Look for an accessor in the prototype chain.
+        if (!IsJSArray()) {
+          Object* setter = LookupCallbackSetterInPrototypes(index);
+          if (setter->IsJSFunction()) {
+            return SetPropertyWithDefinedSetter(JSFunction::cast(setter),
+                                                value);
+          }
+        }
+        Object* result = dictionary->AtNumberPut(index, value);
+        if (result->IsFailure()) return result;
+        if (elms != FixedArray::cast(result)) {
+          set_elements(FixedArray::cast(result));
+        }
       }
-    } else {
-      dictionary->UpdateMaxNumberKey(index);
-      dictionary->ValueAtPut(entry, value);
-    }
-  } else {
-    // Index not already used. Look for an accessor in the prototype chain.
-    if (!IsJSArray()) {
-      Object* setter = LookupCallbackSetterInPrototypes(index);
-      if (setter->IsJSFunction()) {
-        return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
+      
+      // Update the array length if this JSObject is an array.
+      if (IsJSArray()) {
+        JSArray* array = JSArray::cast(this);
+        Object* return_value = array->JSArrayUpdateLengthFromIndex(index,
+                                                                   value);
+        if (return_value->IsFailure()) return return_value;
       }
-    }
-    Object* result = dictionary->AtNumberPut(index, value);
-    if (result->IsFailure()) return result;
-    if (elms != FixedArray::cast(result)) {
-      set_elements(FixedArray::cast(result));
-    }
-  }
-
-  // Update the array length if this JSObject is an array.
-  if (IsJSArray()) {
-    JSArray* array = JSArray::cast(this);
-    Object* return_value = array->JSArrayUpdateLengthFromIndex(index, value);
-    if (return_value->IsFailure()) return return_value;
-  }
-
-  // Attempt to put this object back in fast case.
-  if (ShouldConvertToFastElements()) {
-    uint32_t new_length = 0;
-    if (IsJSArray()) {
-      CHECK(Array::IndexFromObject(JSArray::cast(this)->length(), &new_length));
-      JSArray::cast(this)->set_length(Smi::FromInt(new_length));
-    } else {
-      new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
-    }
-    Object* obj = Heap::AllocateFixedArrayWithHoles(new_length);
-    if (obj->IsFailure()) return obj;
-    SetFastElements(FixedArray::cast(obj));
+      
+      // Attempt to put this object back in fast case.
+      if (ShouldConvertToFastElements()) {
+        uint32_t new_length = 0;
+        if (IsJSArray()) {
+          CHECK(Array::IndexFromObject(JSArray::cast(this)->length(),
+                                       &new_length));
+          JSArray::cast(this)->set_length(Smi::FromInt(new_length));
+        } else {
+          new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
+        }
+        Object* obj = Heap::AllocateFixedArrayWithHoles(new_length);
+        if (obj->IsFailure()) return obj;
+        SetFastElements(FixedArray::cast(obj));
 #ifdef DEBUG
-    if (FLAG_trace_normalization) {
-      PrintF("Object elements are fast case again:\n");
-      Print();
-    }
+        if (FLAG_trace_normalization) {
+          PrintF("Object elements are fast case again:\n");
+          Print();
+        }
 #endif
+      }
+      
+      return value;
+    }
+    default:
+      UNREACHABLE();
+      break;
   }
-
-  return value;
+  // All possible cases have been handled above. Add a return to avoid the
+  // complaints from the compiler.
+  UNREACHABLE();
+  return Heap::null_value();
 }
 
 
@@ -5597,32 +5740,45 @@ Object* JSObject::GetElementPostInterceptor(JSObject* receiver,
                                             uint32_t index) {
   // Get element works for both JSObject and JSArray since
   // JSArray::length cannot change.
-  if (HasFastElements()) {
-    FixedArray* elms = FixedArray::cast(elements());
-    if (index < static_cast<uint32_t>(elms->length())) {
-      Object* value = elms->get(index);
-      if (!value->IsTheHole()) return value;
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      FixedArray* elms = FixedArray::cast(elements());
+      if (index < static_cast<uint32_t>(elms->length())) {
+        Object* value = elms->get(index);
+        if (!value->IsTheHole()) return value;
+      }
+      break;
     }
-  } else {
-    NumberDictionary* dictionary = element_dictionary();
-    int entry = dictionary->FindEntry(index);
-    if (entry != NumberDictionary::kNotFound) {
-      Object* element = dictionary->ValueAt(entry);
-      PropertyDetails details = dictionary->DetailsAt(entry);
-      if (details.type() == CALLBACKS) {
-        // Only accessors allowed as elements.
-        FixedArray* structure = FixedArray::cast(element);
-        Object* getter = structure->get(kGetterIndex);
-        if (getter->IsJSFunction()) {
-          return GetPropertyWithDefinedGetter(receiver,
-                                              JSFunction::cast(getter));
-        } else {
-          // Getter is not a function.
-          return Heap::undefined_value();
+    case PIXEL_ELEMENTS: {
+      // TODO(iposva): Add testcase and implement.
+      UNIMPLEMENTED();
+      break;
+    }
+    case DICTIONARY_ELEMENTS: {
+      NumberDictionary* dictionary = element_dictionary();
+      int entry = dictionary->FindEntry(index);
+      if (entry != NumberDictionary::kNotFound) {
+        Object* element = dictionary->ValueAt(entry);
+        PropertyDetails details = dictionary->DetailsAt(entry);
+        if (details.type() == CALLBACKS) {
+          // Only accessors allowed as elements.
+          FixedArray* structure = FixedArray::cast(element);
+          Object* getter = structure->get(kGetterIndex);
+          if (getter->IsJSFunction()) {
+            return GetPropertyWithDefinedGetter(receiver,
+                                                JSFunction::cast(getter));
+          } else {
+            // Getter is not a function.
+            return Heap::undefined_value();
+          }
         }
+        return element;
       }
-      return element;
+      break;
     }
+    default:
+      UNREACHABLE();
+      break;
   }
 
   // Continue searching via the prototype chain.
@@ -5681,31 +5837,44 @@ Object* JSObject::GetElementWithReceiver(JSObject* receiver, uint32_t index) {
 
   // Get element works for both JSObject and JSArray since
   // JSArray::length cannot change.
-  if (HasFastElements()) {
-    FixedArray* elms = FixedArray::cast(elements());
-    if (index < static_cast<uint32_t>(elms->length())) {
-      Object* value = elms->get(index);
-      if (!value->IsTheHole()) return value;
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      FixedArray* elms = FixedArray::cast(elements());
+      if (index < static_cast<uint32_t>(elms->length())) {
+        Object* value = elms->get(index);
+        if (!value->IsTheHole()) return value;
+      }
+      break;
     }
-  } else {
-    NumberDictionary* dictionary = element_dictionary();
-    int entry = dictionary->FindEntry(index);
-    if (entry != NumberDictionary::kNotFound) {
-      Object* element = dictionary->ValueAt(entry);
-      PropertyDetails details = dictionary->DetailsAt(entry);
-      if (details.type() == CALLBACKS) {
-        // Only accessors allowed as elements.
-        FixedArray* structure = FixedArray::cast(element);
-        Object* getter = structure->get(kGetterIndex);
-        if (getter->IsJSFunction()) {
-          return GetPropertyWithDefinedGetter(receiver,
-                                              JSFunction::cast(getter));
-        } else {
-          // Getter is not a function.
-          return Heap::undefined_value();
+    case PIXEL_ELEMENTS: {
+      PixelArray* pixels = PixelArray::cast(elements());
+      if (index < static_cast<uint32_t>(pixels->length())) {
+        uint8_t value = pixels->get(index);
+        return Smi::FromInt(value);
+      }
+      break;
+    }
+    case DICTIONARY_ELEMENTS: {
+      NumberDictionary* dictionary = element_dictionary();
+      int entry = dictionary->FindEntry(index);
+      if (entry != NumberDictionary::kNotFound) {
+        Object* element = dictionary->ValueAt(entry);
+        PropertyDetails details = dictionary->DetailsAt(entry);
+        if (details.type() == CALLBACKS) {
+          // Only accessors allowed as elements.
+          FixedArray* structure = FixedArray::cast(element);
+          Object* getter = structure->get(kGetterIndex);
+          if (getter->IsJSFunction()) {
+            return GetPropertyWithDefinedGetter(receiver,
+                                                JSFunction::cast(getter));
+          } else {
+            // Getter is not a function.
+            return Heap::undefined_value();
+          }
         }
+        return element;
       }
-      return element;
+      break;
     }
   }
 
@@ -5719,16 +5888,27 @@ bool JSObject::HasDenseElements() {
   int capacity = 0;
   int number_of_elements = 0;
 
-  if (HasFastElements()) {
-    FixedArray* elms = FixedArray::cast(elements());
-    capacity = elms->length();
-    for (int i = 0; i < capacity; i++) {
-      if (!elms->get(i)->IsTheHole()) number_of_elements++;
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      FixedArray* elms = FixedArray::cast(elements());
+      capacity = elms->length();
+      for (int i = 0; i < capacity; i++) {
+        if (!elms->get(i)->IsTheHole()) number_of_elements++;
+      }
+      break;
     }
-  } else {
-    NumberDictionary* dictionary = NumberDictionary::cast(elements());
-    capacity = dictionary->Capacity();
-    number_of_elements = dictionary->NumberOfElements();
+    case PIXEL_ELEMENTS: {
+      return true;
+    }
+    case DICTIONARY_ELEMENTS: {
+      NumberDictionary* dictionary = NumberDictionary::cast(elements());
+      capacity = dictionary->Capacity();
+      number_of_elements = dictionary->NumberOfElements();
+      break;
+    }
+    default:
+      UNREACHABLE();
+      break;
   }
 
   if (capacity == 0) return true;
@@ -5747,7 +5927,7 @@ bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
 
 
 bool JSObject::ShouldConvertToFastElements() {
-  ASSERT(!HasFastElements());
+  ASSERT(HasDictionaryElements());
   NumberDictionary* dictionary = NumberDictionary::cast(elements());
   // If the elements are sparse, we should not go back to fast case.
   if (!HasDenseElements()) return false;
@@ -6001,16 +6181,30 @@ bool JSObject::HasRealElementProperty(uint32_t index) {
   // Handle [] on String objects.
   if (this->IsStringObjectWithCharacterAt(index)) return true;
 
-  if (HasFastElements()) {
-    uint32_t length = IsJSArray() ?
-        static_cast<uint32_t>(
-            Smi::cast(JSArray::cast(this)->length())->value()) :
-        static_cast<uint32_t>(FixedArray::cast(elements())->length());
-    return (index < length) &&
-        !FixedArray::cast(elements())->get(index)->IsTheHole();
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      uint32_t length = IsJSArray() ?
+          static_cast<uint32_t>(
+              Smi::cast(JSArray::cast(this)->length())->value()) :
+          static_cast<uint32_t>(FixedArray::cast(elements())->length());
+      return (index < length) &&
+          !FixedArray::cast(elements())->get(index)->IsTheHole();
+    }
+    case PIXEL_ELEMENTS: {
+      PixelArray* pixels = PixelArray::cast(elements());
+      return index < static_cast<uint32_t>(pixels->length());
+    }
+    case DICTIONARY_ELEMENTS: {
+      return element_dictionary()->FindEntry(index)
+          != NumberDictionary::kNotFound;
+    }
+    default:
+      UNREACHABLE();
+      break;
   }
-  return element_dictionary()->FindEntry(index)
-      != NumberDictionary::kNotFound;
+  // All possibilities have been handled above already.
+  UNREACHABLE();
+  return Heap::null_value();
 }
 
 
@@ -6193,24 +6387,43 @@ int JSObject::NumberOfEnumElements() {
 int JSObject::GetLocalElementKeys(FixedArray* storage,
                                   PropertyAttributes filter) {
   int counter = 0;
-  if (HasFastElements()) {
-    int length = IsJSArray()
-        ? Smi::cast(JSArray::cast(this)->length())->value()
-        : FixedArray::cast(elements())->length();
-    for (int i = 0; i < length; i++) {
-      if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
-        if (storage) {
-          storage->set(counter, Smi::FromInt(i), SKIP_WRITE_BARRIER);
+  switch (GetElementsKind()) {
+    case FAST_ELEMENTS: {
+      int length = IsJSArray() ?
+          Smi::cast(JSArray::cast(this)->length())->value() :
+          FixedArray::cast(elements())->length();
+      for (int i = 0; i < length; i++) {
+        if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
+          if (storage != NULL) {
+            storage->set(counter, Smi::FromInt(i), SKIP_WRITE_BARRIER);
+          }
+          counter++;
+        }
+      }
+      ASSERT(!storage || storage->length() >= counter);
+      break;
+    }
+    case PIXEL_ELEMENTS: {
+      int length = PixelArray::cast(elements())->length();
+      while (counter < length) {
+        if (storage != NULL) {
+          storage->set(counter, Smi::FromInt(counter), SKIP_WRITE_BARRIER);
         }
         counter++;
       }
+      ASSERT(!storage || storage->length() >= counter);
+      break;
     }
-    ASSERT(!storage || storage->length() >= counter);
-  } else {
-    if (storage) {
-      element_dictionary()->CopyKeysTo(storage, filter);
+    case DICTIONARY_ELEMENTS: {
+      if (storage != NULL) {
+        element_dictionary()->CopyKeysTo(storage, filter);
+      }
+      counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
+      break;
     }
-    counter = element_dictionary()->NumberOfElementsFilterAttributes(filter);
+    default:
+      UNREACHABLE();
+      break;
   }
 
   if (this->IsJSValue()) {
@@ -6669,7 +6882,7 @@ int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
 // Collates undefined and unexisting elements below limit from position
 // zero of the elements. The object stays in Dictionary mode.
 Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
-  ASSERT(!HasFastElements());
+  ASSERT(HasDictionaryElements());
   // Must stay in dictionary mode, either because of requires_slow_elements,
   // or because we are not going to sort (and therefore compact) all of the
   // elements.
@@ -6743,7 +6956,9 @@ Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
 // If the object is in dictionary mode, it is converted to fast elements
 // mode.
 Object* JSObject::PrepareElementsForSort(uint32_t limit) {
-  if (!HasFastElements()) {
+  ASSERT(!HasPixelElements());
+
+  if (HasDictionaryElements()) {
     // Convert to fast elements containing only the existing properties.
     // Ordering is irrelevant, since we are going to sort anyway.
     NumberDictionary* dict = element_dictionary();
@@ -6768,7 +6983,7 @@ Object* JSObject::PrepareElementsForSort(uint32_t limit) {
   // Collect holes at the end, undefined before that and the rest at the
   // start, and return the number of non-hole, non-undefined values.
 
-  FixedArray* elements = this->elements();
+  FixedArray* elements = FixedArray::cast(this->elements());
   uint32_t elements_length = static_cast<uint32_t>(elements->length());
   if (limit > elements_length) {
     limit = elements_length ;
@@ -6838,6 +7053,41 @@ Object* JSObject::PrepareElementsForSort(uint32_t limit) {
 }
 
 
+Object* PixelArray::SetValue(uint32_t index, Object* value) {
+  uint8_t clamped_value = 0;
+  if (index < static_cast<uint32_t>(length())) {
+    int int_value = 0;
+    if (value->IsSmi()) {
+      int_value = Smi::cast(value)->value();
+    } else if (value->IsHeapNumber()) {
+      static const DoubleRepresentation nan(OS::nan_value());
+      DoubleRepresentation double_value = HeapNumber::cast(value)->value();
+      if (nan.bits != double_value.bits) {
+        int_value = static_cast<int>(double_value.value + 0.5);
+      } else {
+        // NaN clamps to zero.
+        int_value = 0;
+      }
+    } else if (value->IsUndefined()) {
+      int_value = 0;
+    } else {
+      // All other types have been converted to a number type further up in the
+      // call chain.
+      UNREACHABLE();
+    }
+    if (int_value < 0) {
+      clamped_value = 0;
+    } else if (int_value > 255) {
+      clamped_value = 255;
+    } else {
+      clamped_value = static_cast<uint8_t>(int_value);
+    }
+    set(index, clamped_value);
+  }
+  return Smi::FromInt(clamped_value);
+}
+
+
 Object* GlobalObject::GetPropertyCell(LookupResult* result) {
   ASSERT(!HasFastProperties());
   Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
index 5e5eb6bab04a2d657c18ad4cb29f860e3a1a8882..9872477310f85566b89db7e8679675e824644e5f 100644 (file)
@@ -52,6 +52,7 @@
 //         - JSValue
 //       - Array
 //         - ByteArray
+//         - PixelArray
 //         - FixedArray
 //           - DescriptorArray
 //           - HashTable
@@ -95,7 +96,6 @@
 //  HeapObject: [32 bit direct pointer] (4 byte aligned) | 01
 //  Failure:    [30 bit signed int] 11
 
-
 // Ecma-262 3rd 8.6.1
 enum PropertyAttributes {
   NONE              = v8::None,
@@ -270,6 +270,7 @@ enum PropertyNormalizationMode {
   V(ODDBALL_TYPE)                               \
   V(PROXY_TYPE)                                 \
   V(BYTE_ARRAY_TYPE)                            \
+  V(PIXEL_ARRAY_TYPE)                           \
   V(FILLER_TYPE)                                \
                                                 \
   V(ACCESSOR_INFO_TYPE)                         \
@@ -659,6 +660,7 @@ enum InstanceType {
   JS_GLOBAL_PROPERTY_CELL_TYPE,
   PROXY_TYPE,
   BYTE_ARRAY_TYPE,
+  PIXEL_ARRAY_TYPE,
   FILLER_TYPE,
   SMI_TYPE,
 
@@ -760,6 +762,7 @@ class Object BASE_EMBEDDED {
 
   inline bool IsNumber();
   inline bool IsByteArray();
+  inline bool IsPixelArray();
   inline bool IsFailure();
   inline bool IsRetryAfterGC();
   inline bool IsOutOfMemoryFailure();
@@ -1302,6 +1305,11 @@ class HeapNumber: public HeapObject {
 class JSObject: public HeapObject {
  public:
   enum DeleteMode { NORMAL_DELETION, FORCE_DELETION };
+  enum ElementsKind {
+    FAST_ELEMENTS,
+    DICTIONARY_ELEMENTS,
+    PIXEL_ELEMENTS
+  };
 
   // [properties]: Backing storage for properties.
   // properties is a FixedArray in the fast case, and a Dictionary in the
@@ -1313,10 +1321,13 @@ class JSObject: public HeapObject {
 
   // [elements]: The elements (properties with names that are integers).
   // elements is a FixedArray in the fast case, and a Dictionary in the slow
-  // case.
-  DECL_ACCESSORS(elements, FixedArray)  // Get and set fast elements.
+  // case or a PixelArray in a special case.
+  DECL_ACCESSORS(elements, Array)  // Get and set fast elements.
   inline void initialize_elements();
+  inline ElementsKind GetElementsKind();
   inline bool HasFastElements();
+  inline bool HasDictionaryElements();
+  inline bool HasPixelElements();
   inline NumberDictionary* element_dictionary();  // Gets slow elements.
 
   // Collects elements starting at index 0.
@@ -2440,6 +2451,43 @@ class ByteArray: public Array {
 };
 
 
+// PixelArray represents a fixed size byte array with special sematics used for
+// implementing the CanvasPixelArray object. Please see the specification at:
+// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#canvaspixelarray
+// In particular write access clamps the values to 0 or 255 if the value
+// used is outside this range.
+class PixelArray: public Array {
+ public:
+  // [external_pointer]: The pointer to the external memory area backing this
+  // pixel array.
+  DECL_ACCESSORS(external_pointer, uint8_t)  // Pointer to the data store.
+  
+  // Setter and getter.
+  inline uint8_t get(int index);
+  inline void set(int index, uint8_t value);
+
+  // This accessor applies the correct conversion from Smi, HeapNumber and
+  // undefined and clamps the converted value between 0 and 255.
+  Object* SetValue(uint32_t index, Object* value);
+
+  // Casting.
+  static inline PixelArray* cast(Object* obj);
+
+#ifdef DEBUG
+  void PixelArrayPrint();
+  void PixelArrayVerify();
+#endif  // DEBUG
+
+  // PixelArray headers are not quadword aligned.
+  static const int kExternalPointerOffset = Array::kAlignedSize;
+  static const int kHeaderSize = kExternalPointerOffset + kPointerSize;
+  static const int kAlignedSize = OBJECT_SIZE_ALIGN(kHeaderSize);
+  
+private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(PixelArray);
+};
+
+
 // Code describes objects with on-the-fly generated machine code.
 class Code: public HeapObject {
  public:
index 7e6f1f3aa3193248083b298e2d824bf5a4506f8d..e557ae24a20787a81b00d6877caff138177f07b5 100644 (file)
@@ -155,33 +155,43 @@ static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
   }
 
   // Deep copy local elements.
-  if (copy->HasFastElements()) {
-    FixedArray* elements = copy->elements();
-    WriteBarrierMode mode = elements->GetWriteBarrierMode();
-    for (int i = 0; i < elements->length(); i++) {
-      Object* value = elements->get(i);
-      if (value->IsJSObject()) {
-        JSObject* jsObject = JSObject::cast(value);
-        result = DeepCopyBoilerplate(jsObject);
-        if (result->IsFailure()) return result;
-        elements->set(i, result, mode);
-      }
-    }
-  } else {
-    NumberDictionary* element_dictionary = copy->element_dictionary();
-    int capacity = element_dictionary->Capacity();
-    for (int i = 0; i < capacity; i++) {
-      Object* k = element_dictionary->KeyAt(i);
-      if (element_dictionary->IsKey(k)) {
-        Object* value = element_dictionary->ValueAt(i);
+  // Pixel elements cannot be created using an object literal.
+  ASSERT(!copy->HasPixelElements());
+  switch (copy->GetElementsKind()) {
+    case JSObject::FAST_ELEMENTS: {
+      FixedArray* elements = FixedArray::cast(copy->elements());
+      WriteBarrierMode mode = elements->GetWriteBarrierMode();
+      for (int i = 0; i < elements->length(); i++) {
+        Object* value = elements->get(i);
         if (value->IsJSObject()) {
           JSObject* jsObject = JSObject::cast(value);
           result = DeepCopyBoilerplate(jsObject);
           if (result->IsFailure()) return result;
-          element_dictionary->ValueAtPut(i, result);
+          elements->set(i, result, mode);
+        }
+      }
+      break;
+    }
+    case JSObject::DICTIONARY_ELEMENTS: {
+      NumberDictionary* element_dictionary = copy->element_dictionary();
+      int capacity = element_dictionary->Capacity();
+      for (int i = 0; i < capacity; i++) {
+        Object* k = element_dictionary->KeyAt(i);
+        if (element_dictionary->IsKey(k)) {
+          Object* value = element_dictionary->ValueAt(i);
+          if (value->IsJSObject()) {
+            JSObject* jsObject = JSObject::cast(value);
+            result = DeepCopyBoilerplate(jsObject);
+            if (result->IsFailure()) return result;
+            element_dictionary->ValueAtPut(i, result);
+          }
         }
       }
+      break;
     }
+    default:
+      UNREACHABLE();
+      break;
   }
   return copy;
 }
@@ -1637,7 +1647,7 @@ void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
       }
       case SUBJECT_CAPTURE: {
         int capture = part.data;
-        FixedArray* match_info = last_match_info->elements();
+        FixedArray* match_info = FixedArray::cast(last_match_info->elements());
         int from = RegExpImpl::GetCapture(match_info, capture * 2);
         int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
         if (from >= 0 && to > from) {
@@ -1717,7 +1727,8 @@ static Object* StringReplaceRegExpWithString(String* subject,
     int start, end;
     {
       AssertNoAllocation match_info_array_is_not_in_a_handle;
-      FixedArray* match_info_array = last_match_info_handle->elements();
+      FixedArray* match_info_array =
+          FixedArray::cast(last_match_info_handle->elements());
 
       ASSERT_EQ(capture_count * 2 + 2,
                 RegExpImpl::GetLastCaptureCount(match_info_array));
@@ -2345,7 +2356,7 @@ static Object* Runtime_StringMatch(Arguments args) {
     int end;
     {
       AssertNoAllocation no_alloc;
-      FixedArray* elements = regexp_info->elements();
+      FixedArray* elements = FixedArray::cast(regexp_info->elements());
       start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
       end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
     }
@@ -4885,7 +4896,7 @@ static Object* Runtime_DateParseString(Arguments args) {
 
   AssertNoAllocation no_allocation;
 
-  FixedArray* output_array = output->elements();
+  FixedArray* output_array = FixedArray::cast(output->elements());
   RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
   bool result;
   if (str->IsAsciiRepresentation()) {
@@ -5173,37 +5184,62 @@ static uint32_t IterateElements(Handle<JSObject> receiver,
                                 ArrayConcatVisitor* visitor) {
   uint32_t num_of_elements = 0;
 
-  if (receiver->HasFastElements()) {
-    Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
-    uint32_t len = elements->length();
-    if (range < len) len = range;
+  switch (receiver->GetElementsKind()) {
+    case JSObject::FAST_ELEMENTS: {
+      Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
+      uint32_t len = elements->length();
+      if (range < len) {
+        len = range;
+      }
+      
+      for (uint32_t j = 0; j < len; j++) {
+        Handle<Object> e(elements->get(j));
+        if (!e->IsTheHole()) {
+          num_of_elements++;
+          if (visitor) {
+            visitor->visit(j, e);
+          }
+        }
+      }
+      break;
+    }
+    case JSObject::PIXEL_ELEMENTS: {
+      Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
+      uint32_t len = pixels->length();
+      if (range < len) {
+        len = range;
+      }
 
-    for (uint32_t j = 0; j < len; j++) {
-      Handle<Object> e(elements->get(j));
-      if (!e->IsTheHole()) {
+      for (uint32_t j = 0; j < len; j++) {
         num_of_elements++;
-        if (visitor)
+        if (visitor != NULL) {
+          Handle<Smi> e(Smi::FromInt(pixels->get(j)));
           visitor->visit(j, e);
+        }
       }
+      break;
     }
-
-  } else {
-    Handle<NumberDictionary> dict(receiver->element_dictionary());
-    uint32_t capacity = dict->Capacity();
-    for (uint32_t j = 0; j < capacity; j++) {
-      Handle<Object> k(dict->KeyAt(j));
-      if (dict->IsKey(*k)) {
-        ASSERT(k->IsNumber());
-        uint32_t index = static_cast<uint32_t>(k->Number());
-        if (index < range) {
-          num_of_elements++;
-          if (visitor) {
-            visitor->visit(index,
-                           Handle<Object>(dict->ValueAt(j)));
+    case JSObject::DICTIONARY_ELEMENTS: {
+      Handle<NumberDictionary> dict(receiver->element_dictionary());
+      uint32_t capacity = dict->Capacity();
+      for (uint32_t j = 0; j < capacity; j++) {
+        Handle<Object> k(dict->KeyAt(j));
+        if (dict->IsKey(*k)) {
+          ASSERT(k->IsNumber());
+          uint32_t index = static_cast<uint32_t>(k->Number());
+          if (index < range) {
+            num_of_elements++;
+            if (visitor) {
+              visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
+            }
           }
         }
       }
+      break;
     }
+    default:
+      UNREACHABLE();
+      break;
   }
 
   return num_of_elements;
@@ -7449,7 +7485,7 @@ static Object* Runtime_CollectStackTrace(Arguments args) {
       Address pc = frame->pc();
       Address start = frame->code()->address();
       Smi* offset = Smi::FromInt(pc - start);
-      FixedArray* elements = result->elements();
+      FixedArray* elements = FixedArray::cast(result->elements());
       if (cursor + 2 < elements->length()) {
         elements->set(cursor++, recv);
         elements->set(cursor++, fun);
index 806e7116be106ac7c95c81bfd89d8ad153f67dfd..e0c91c7b7f501077da14ef77d8edb764b2f5f749 100644 (file)
@@ -7548,3 +7548,134 @@ THREADED_TEST(Regress16276) {
   context->DetachGlobal();
   CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
 }
+
+
+THREADED_TEST(PixelArray) {
+  v8::HandleScope scope;
+  LocalContext context;
+  const int kElementCount = 40;
+  uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
+  i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
+                                                              pixel_data);
+  i::Heap::CollectAllGarbage();  // Force GC to trigger verification.
+  for (int i = 0; i < kElementCount; i++) {
+    pixels->set(i, i);
+  }
+  i::Heap::CollectAllGarbage();  // Force GC to trigger verification.
+  for (int i = 0; i < kElementCount; i++) {
+    CHECK_EQ(i, pixels->get(i));
+    CHECK_EQ(i, pixel_data[i]);
+  }
+
+  v8::Handle<v8::Object> obj = v8::Object::New();
+  i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
+  // Set the elements to be the pixels.
+  // jsobj->set_elements(*pixels);
+  obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
+  CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
+  obj->Set(v8_str("field"), v8::Int32::New(1503));
+  context->Global()->Set(v8_str("pixels"), obj);
+  v8::Handle<v8::Value> result = CompileRun("pixels.field");
+  CHECK_EQ(1503, result->Int32Value());
+  result = CompileRun("pixels[1]");
+  CHECK_EQ(1, result->Int32Value());
+  result = CompileRun("var sum = 0;"
+                      "for (var i = 0; i < 8; i++) {"
+                      "  sum += pixels[i];"
+                      "}"
+                      "sum;");
+  CHECK_EQ(28, result->Int32Value());
+
+  i::Handle<i::Smi> value(i::Smi::FromInt(2));
+  i::SetElement(jsobj, 1, value);
+  CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
+  *value.location() = i::Smi::FromInt(256);
+  i::SetElement(jsobj, 1, value);
+  CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
+  *value.location() = i::Smi::FromInt(-1);
+  i::SetElement(jsobj, 1, value);
+  CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
+
+  result = CompileRun("for (var i = 0; i < 8; i++) {"
+                      "  pixels[i] = (i * 65) - 109;"
+                      "}"
+                      "pixels[1] + pixels[6];");
+  CHECK_EQ(255, result->Int32Value());
+  CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
+  CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
+  CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
+  CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
+  CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
+  CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
+  CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
+  CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
+  result = CompileRun("var sum = 0;"
+                      "for (var i = 0; i < 8; i++) {"
+                      "  sum += pixels[i];"
+                      "}"
+                      "sum;");
+  CHECK_EQ(984, result->Int32Value());
+
+  result = CompileRun("for (var i = 0; i < 8; i++) {"
+                      "  pixels[i] = (i * 1.1);"
+                      "}"
+                      "pixels[1] + pixels[6];");
+  CHECK_EQ(8, result->Int32Value());
+  CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
+  CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
+  CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
+  CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
+  CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
+  CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
+  CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
+  CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
+
+  result = CompileRun("for (var i = 0; i < 8; i++) {"
+                      "  pixels[7] = undefined;"
+                      "}"
+                      "pixels[7];");
+  CHECK_EQ(0, result->Int32Value());
+  CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
+
+  result = CompileRun("for (var i = 0; i < 8; i++) {"
+                      "  pixels[6] = '2.3';"
+                      "}"
+                      "pixels[6];");
+  CHECK_EQ(2, result->Int32Value());
+  CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
+
+  result = CompileRun("var nan = 0/0;"
+                      "for (var i = 0; i < 8; i++) {"
+                      "  pixels[5] = nan;"
+                      "}"
+                      "pixels[5];");
+  CHECK_EQ(0, result->Int32Value());
+  CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
+  
+  result = CompileRun("pixels[3] = 33;"
+                      "delete pixels[3];"
+                      "pixels[3];");
+  CHECK_EQ(33, result->Int32Value());
+
+  result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
+                      "pixels[2] = 12; pixels[3] = 13;"
+                      "pixels.__defineGetter__('2',"
+                                                "function() { return 120; });"
+                      "pixels[2];");
+  CHECK_EQ(12, result->Int32Value());
+
+  result = CompileRun("var js_array = new Array(40);"
+                      "js_array[0] = 77;"
+                      "js_array;"
+  );
+  CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
+
+  result = CompileRun("pixels[1] = 23;"
+                      "pixels.__proto__ = [];"
+                      "js_array.__proto__ = pixels;"
+                      "js_array.concat(pixels);");
+  CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
+  CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
+
+  free(pixel_data);
+}
index 5163ff95cb8169154b43ad8612c539b09df11ef2..6b5907c2e8ecd4a2e3a94bac1ef810aa53f1b536 100644 (file)
@@ -653,7 +653,7 @@ TEST(JSArray) {
   uint32_t int_length = 0;
   CHECK(Array::IndexFromObject(length, &int_length));
   CHECK_EQ(length, array->length());
-  CHECK(!array->HasFastElements());  // Must be in slow mode.
+  CHECK(array->HasDictionaryElements());  // Must be in slow mode.
 
   // array[length] = name.
   array->SetElement(int_length, name);