From 1ce28eda0bf4a469d564d0f108f165291aa4d716 Mon Sep 17 00:00:00 2001 From: "iposva@chromium.org" Date: Tue, 28 Jul 2009 08:43:51 +0000 Subject: [PATCH] - Expose CanvasPixelArray functionality directly in JavaScript 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 --- include/v8.h | 9 + src/api.cc | 21 +- src/arm/ic-arm.cc | 12 +- src/factory.cc | 10 + src/factory.h | 4 + src/globals.h | 1 + src/handles.cc | 8 + src/heap.cc | 24 +- src/heap.h | 9 + src/ia32/codegen-ia32.cc | 4 +- src/ia32/ic-ia32.cc | 86 +++-- src/jsregexp.cc | 6 +- src/objects-debug.cc | 90 +++-- src/objects-inl.h | 79 ++++- src/objects.cc | 884 ++++++++++++++++++++++++++++++----------------- src/objects.h | 54 ++- src/runtime.cc | 130 ++++--- test/cctest/test-api.cc | 131 +++++++ test/cctest/test-heap.cc | 2 +- 19 files changed, 1136 insertions(+), 428 deletions(-) diff --git a/include/v8.h b/include/v8.h index cf8a3bf..382131e 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1169,6 +1169,15 @@ class V8EXPORT Object : public Value { */ Local 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 New(); static Object* Cast(Value* obj); private: diff --git a/src/api.cc b/src/api.cc index 9e3ca9b..edfa0fc 100644 --- a/src/api.cc +++ b/src/api.cc @@ -2194,6 +2194,25 @@ bool v8::Object::DeleteHiddenValue(v8::Handle 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 self = Utils::OpenHandle(this); + if (!ApiCheck(!self->IsJSArray(), + "v8::Object::SetIndexedPropertiesToPixelData()", + "JSArray is not supported")) { + return; + } + i::Handle pixels = i::Factory::NewPixelArray(length, data); + self->set_elements(*pixels); +} + + Local Function::NewInstance() const { return NewInstance(0, NULL); } @@ -3057,7 +3076,7 @@ Local Array::CloneElementAt(uint32_t index) { if (!self->HasFastElements()) { return Local(); } - i::FixedArray* elms = self->elements(); + i::FixedArray* elms = i::FixedArray::cast(self->elements()); i::Object* paragon = elms->get(index); if (!paragon->IsJSObject()) { return Local(); diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index 82a2bec..8781256 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -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. diff --git a/src/factory.cc b/src/factory.cc index 1045a4c..36554df 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -210,6 +210,16 @@ Handle Factory::NewByteArray(int length, PretenureFlag pretenure) { } +Handle Factory::NewPixelArray(int length, + uint8_t* external_pointer, + PretenureFlag pretenure) { + ASSERT(0 <= length); + CALL_HEAP_FUNCTION(Heap::AllocatePixelArray(length, + external_pointer, + pretenure), PixelArray); +} + + Handle Factory::NewMap(InstanceType type, int instance_size) { CALL_HEAP_FUNCTION(Heap::AllocateMap(type, instance_size), Map); } diff --git a/src/factory.h b/src/factory.h index 0afdd76..4db5d4e 100644 --- a/src/factory.h +++ b/src/factory.h @@ -154,6 +154,10 @@ class Factory : public AllStatic { static Handle NewByteArray(int length, PretenureFlag pretenure = NOT_TENURED); + static Handle NewPixelArray(int length, + uint8_t* external_pointer, + PretenureFlag pretenure = NOT_TENURED); + static Handle NewMap(InstanceType type, int instance_size); static Handle NewFunctionPrototype(Handle function); diff --git a/src/globals.h b/src/globals.h index 44bd527..195a2e2 100644 --- a/src/globals.h +++ b/src/globals.h @@ -207,6 +207,7 @@ class HeapObject; class IC; class InterceptorInfo; class IterationStatement; +class Array; class JSArray; class JSFunction; class JSObject; diff --git a/src/handles.cc b/src/handles.cc index 510ea95..872e0d2 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -341,6 +341,14 @@ Handle SubString(Handle str, int start, int end) { Handle SetElement(Handle object, uint32_t index, Handle value) { + if (object->HasPixelElements()) { + if (!value->IsSmi() && !value->IsHeapNumber() && !value->IsUndefined()) { + bool has_exception; + Handle number = Execution::ToNumber(value, &has_exception); + if (has_exception) return Handle(); + value = number; + } + } CALL_HEAP_FUNCTION(object->SetElement(index, *value), Object); } diff --git a/src/heap.cc b/src/heap.cc index 213eec5..233f02f 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -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(result)->set_map(pixel_array_map()); + reinterpret_cast(result)->set_length(length); + reinterpret_cast(result)->set_external_pointer(external_pointer); + + return result; +} + + Object* Heap::CreateCode(const CodeDesc& desc, ZoneScopeInfo* sinfo, Code::Flags flags, diff --git a/src/heap.h b/src/heap.h index 4e2c64c..634c21a 100644 --- a/src/heap.h +++ b/src/heap.h @@ -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. diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 457b22f..6648277 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -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. diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index d9dcb7c..9c0f282 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -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 diff --git a/src/jsregexp.cc b/src/jsregexp.cc index 852d431..0b1205d 100644 --- a/src/jsregexp.cc +++ b/src/jsregexp.cc @@ -254,7 +254,7 @@ Handle RegExpImpl::AtomExec(Handle 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 RegExpImpl::IrregexpExec(Handle jsregexp, if (res != RegExpMacroAssemblerIA32::SUCCESS) return Factory::null_value(); - array = Handle(last_match_info->elements()); + array = Handle(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 RegExpImpl::IrregexpExec(Handle jsregexp, return Factory::null_value(); } - array = Handle(last_match_info->elements()); + array = Handle(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) { diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 8c57afd..cf6aa3c 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -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; } } diff --git a/src/objects-inl.h b/src/objects-inl.h index 7abc7c3..0b5e073 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -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); +} + + +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(ptr); +} + + +void PixelArray::set_external_pointer(uint8_t* value, WriteBarrierMode mode) { + intptr_t ptr = reinterpret_cast(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()); } diff --git a/src/objects.cc b/src/objects.cc index 72412c1..bd52110 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1006,6 +1006,9 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { case BYTE_ARRAY_TYPE: accumulator->Add("", ByteArray::cast(this)->length()); break; + case PIXEL_ARRAY_TYPE: + accumulator->Add("", PixelArray::cast(this)->length()); + break; case SHARED_FUNCTION_INFO_TYPE: accumulator->Add(""); 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(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(Smi::cast(JSArray::cast(this)->length())->value()) : static_cast(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(Smi::cast(JSArray::cast(this)->length())->value()) : static_cast(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(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(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(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(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(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(len->Number()); - if (!HasFastElements()) { - if (IsJSArray()) { - uint32_t old_length = - static_cast(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(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(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(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 self(this); - Handle old_backing(elements()); + Handle 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(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(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( - Smi::cast(JSArray::cast(this)->length())->value()) : - static_cast(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 + (Smi::cast(JSArray::cast(this)->length())->value()) : + static_cast(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(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( - Smi::cast(JSArray::cast(this)->length())->value()) : - static_cast(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 + (Smi::cast(JSArray::cast(this)->length())->value()) : + static_cast(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(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( - Smi::cast(JSArray::cast(this)->length())->value()) : - static_cast(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 + (Smi::cast(JSArray::cast(this)->length())->value()) : + static_cast(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(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 self(this); + Handle key(Factory::NewNumberFromUint(index)); + Handle 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 self(this); - Handle key(Factory::NewNumberFromUint(index)); - Handle 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(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(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(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(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(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( - Smi::cast(JSArray::cast(this)->length())->value()) : - static_cast(FixedArray::cast(elements())->length()); - return (index < length) && - !FixedArray::cast(elements())->get(index)->IsTheHole(); + switch (GetElementsKind()) { + case FAST_ELEMENTS: { + uint32_t length = IsJSArray() ? + static_cast( + Smi::cast(JSArray::cast(this)->length())->value()) : + static_cast(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(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::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(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(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(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(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()); diff --git a/src/objects.h b/src/objects.h index 5e5eb6b..9872477 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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: diff --git a/src/runtime.cc b/src/runtime.cc index 7e6f1f3..e557ae2 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -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 receiver, ArrayConcatVisitor* visitor) { uint32_t num_of_elements = 0; - if (receiver->HasFastElements()) { - Handle elements(FixedArray::cast(receiver->elements())); - uint32_t len = elements->length(); - if (range < len) len = range; + switch (receiver->GetElementsKind()) { + case JSObject::FAST_ELEMENTS: { + Handle elements(FixedArray::cast(receiver->elements())); + uint32_t len = elements->length(); + if (range < len) { + len = range; + } + + for (uint32_t j = 0; j < len; j++) { + Handle e(elements->get(j)); + if (!e->IsTheHole()) { + num_of_elements++; + if (visitor) { + visitor->visit(j, e); + } + } + } + break; + } + case JSObject::PIXEL_ELEMENTS: { + Handle pixels(PixelArray::cast(receiver->elements())); + uint32_t len = pixels->length(); + if (range < len) { + len = range; + } - for (uint32_t j = 0; j < len; j++) { - Handle e(elements->get(j)); - if (!e->IsTheHole()) { + for (uint32_t j = 0; j < len; j++) { num_of_elements++; - if (visitor) + if (visitor != NULL) { + Handle e(Smi::FromInt(pixels->get(j))); visitor->visit(j, e); + } } + break; } - - } else { - Handle dict(receiver->element_dictionary()); - uint32_t capacity = dict->Capacity(); - for (uint32_t j = 0; j < capacity; j++) { - Handle k(dict->KeyAt(j)); - if (dict->IsKey(*k)) { - ASSERT(k->IsNumber()); - uint32_t index = static_cast(k->Number()); - if (index < range) { - num_of_elements++; - if (visitor) { - visitor->visit(index, - Handle(dict->ValueAt(j))); + case JSObject::DICTIONARY_ELEMENTS: { + Handle dict(receiver->element_dictionary()); + uint32_t capacity = dict->Capacity(); + for (uint32_t j = 0; j < capacity; j++) { + Handle k(dict->KeyAt(j)); + if (dict->IsKey(*k)) { + ASSERT(k->IsNumber()); + uint32_t index = static_cast(k->Number()); + if (index < range) { + num_of_elements++; + if (visitor) { + visitor->visit(index, Handle(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); diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 806e711..e0c91c7 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -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(malloc(kElementCount)); + i::Handle 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 obj = v8::Object::New(); + i::Handle 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 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 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); +} diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index 5163ff9..6b5907c 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -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); -- 2.7.4