Commit 1ce28eda authored by iposva@chromium.org's avatar iposva@chromium.org

- 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
parent 60bdcf9e
......@@ -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:
......
......@@ -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>();
......
......@@ -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.
......
......@@ -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);
}
......
......@@ -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);
......
......@@ -207,6 +207,7 @@ class HeapObject;
class IC;
class InterceptorInfo;
class IterationStatement;
class Array;
class JSArray;
class JSFunction;
class JSObject;
......
......@@ -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);
}
......
......@@ -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,
......
......@@ -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.
......
......@@ -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.
......
......@@ -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
......
......@@ -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) {
......
......@@ -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;
}
}
......
......@@ -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());
}
......
This diff is collapsed.
......@@ -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:
......
......@@ -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);
......
......@@ -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);
}
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment