From 25058ddd855b8877ee1078cab022a61677065f86 Mon Sep 17 00:00:00 2001 From: "dcarney@chromium.org" Date: Thu, 7 Mar 2013 11:42:58 +0000 Subject: [PATCH] Runtime version of declarative native accessors. R=svenpanne@chromium.org BUG= Review URL: https://codereview.chromium.org/12297012 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13856 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8.h | 79 +++++- src/api.cc | 256 +++++++++++++++---- src/api.h | 6 +- src/factory.cc | 6 + src/factory.h | 2 + src/objects-debug.cc | 2 +- src/objects-inl.h | 3 +- src/objects-printer.cc | 2 +- src/objects.cc | 213 +++++++++++++++- src/objects.h | 80 +++++- test/cctest/SConscript | 1 + test/cctest/cctest.gyp | 1 + test/cctest/test-declarative-accessors.cc | 294 ++++++++++++++++++++++ 13 files changed, 875 insertions(+), 70 deletions(-) create mode 100644 test/cctest/test-declarative-accessors.cc diff --git a/include/v8.h b/include/v8.h index 1a35b3ad1..7016cb575 100644 --- a/include/v8.h +++ b/include/v8.h @@ -131,6 +131,9 @@ class AccessorInfo; class StackTrace; class StackFrame; class Isolate; +class DeclaredAccessorDescriptor; +class ObjectOperationDescriptor; +class RawOperationDescriptor; namespace internal { @@ -1606,6 +1609,12 @@ class V8EXPORT Object : public Value { AccessControl settings = DEFAULT, PropertyAttribute attribute = None); + // This function is not yet stable and should not be used at this time. + bool SetAccessor(Handle name, + Handle descriptor, + AccessControl settings = DEFAULT, + PropertyAttribute attribute = None); + /** * Returns an array containing the names of the enumerable properties * of this object, including properties from prototype objects. The @@ -2377,13 +2386,6 @@ class V8EXPORT FunctionTemplate : public Template { private: FunctionTemplate(); - void AddInstancePropertyAccessor(Handle name, - AccessorGetter getter, - AccessorSetter setter, - Handle data, - AccessControl settings, - PropertyAttribute attributes, - Handle signature); void SetNamedInstancePropertyHandler(NamedPropertyGetter getter, NamedPropertySetter setter, NamedPropertyQuery query, @@ -2456,6 +2458,14 @@ class V8EXPORT ObjectTemplate : public Template { Handle signature = Handle()); + // This function is not yet stable and should not be used at this time. + bool SetAccessor(Handle name, + Handle descriptor, + AccessControl settings = DEFAULT, + PropertyAttribute attribute = None, + Handle signature = + Handle()); + /** * Sets a named property handler on the object template. * @@ -2585,6 +2595,61 @@ class V8EXPORT AccessorSignature : public Data { }; +class V8EXPORT DeclaredAccessorDescriptor : public Data { + private: + DeclaredAccessorDescriptor(); +}; + + +class V8EXPORT ObjectOperationDescriptor : public Data { + public: + // This function is not yet stable and should not be used at this time. + static Local NewInternalFieldDereference( + Isolate* isolate, + int internal_field); + private: + ObjectOperationDescriptor(); +}; + + +enum DeclaredAccessorDescriptorDataType { + kDescriptorBoolType, + kDescriptorInt8Type, kDescriptorUint8Type, + kDescriptorInt16Type, kDescriptorUint16Type, + kDescriptorInt32Type, kDescriptorUint32Type, + kDescriptorFloatType, kDescriptorDoubleType +}; + + +class V8EXPORT RawOperationDescriptor : public Data { + public: + Local NewHandleDereference(Isolate* isolate); + Local NewRawDereference(Isolate* isolate); + Local NewRawShift(Isolate* isolate, + int16_t byte_offset); + Local NewPointerCompare(Isolate* isolate, + void* compare_value); + Local NewPrimitiveValue( + Isolate* isolate, + DeclaredAccessorDescriptorDataType data_type, + uint8_t bool_offset = 0); + Local NewBitmaskCompare8(Isolate* isolate, + uint8_t bitmask, + uint8_t compare_value); + Local NewBitmaskCompare16( + Isolate* isolate, + uint16_t bitmask, + uint16_t compare_value); + Local NewBitmaskCompare32( + Isolate* isolate, + uint32_t bitmask, + uint32_t compare_value); + + private: + RawOperationDescriptor(); +}; + + /** * A utility for determining the type of objects based on the template * they were constructed from. diff --git a/src/api.cc b/src/api.cc index e58e6c894..4d91e8f46 100644 --- a/src/api.cc +++ b/src/api.cc @@ -1041,6 +1041,124 @@ Local AccessorSignature::New( } +template +static Local NewDescriptor( + Isolate* isolate, + const i::DeclaredAccessorDescriptorData& data, + Data* previous_descriptor + ) { + i::Isolate* internal_isolate = reinterpret_cast(isolate); + i::Handle previous = + i::Handle(); + if (previous_descriptor != NULL) { + previous = Utils::OpenHandle( + static_cast(previous_descriptor)); + } + i::Handle descriptor = + i::DeclaredAccessorDescriptor::Create(internal_isolate, data, previous); + return Local( + reinterpret_cast(*Utils::ToLocal(descriptor))); +} + + +Local + ObjectOperationDescriptor::NewInternalFieldDereference( + Isolate* isolate, + int internal_field) { + i::DeclaredAccessorDescriptorData data; + data.type = i::kDescriptorObjectDereference; + data.object_dereference_descriptor.internal_field = internal_field; + return NewDescriptor(isolate, data, NULL); +} + + +Local RawOperationDescriptor::NewRawShift( + Isolate* isolate, + int16_t byte_offset) { + i::DeclaredAccessorDescriptorData data; + data.type = i::kDescriptorPointerShift; + data.pointer_shift_descriptor.byte_offset = byte_offset; + return NewDescriptor(isolate, data, this); +} + + +Local RawOperationDescriptor::NewHandleDereference( + Isolate* isolate) { + i::DeclaredAccessorDescriptorData data; + data.type = i::kDescriptorReturnObject; + return NewDescriptor(isolate, data, this); +} + + +Local RawOperationDescriptor::NewRawDereference( + Isolate* isolate) { + i::DeclaredAccessorDescriptorData data; + data.type = i::kDescriptorPointerDereference; + return NewDescriptor(isolate, data, this); +} + + +Local RawOperationDescriptor::NewPointerCompare( + Isolate* isolate, + void* compare_value) { + i::DeclaredAccessorDescriptorData data; + data.type = i::kDescriptorPointerCompare; + data.pointer_compare_descriptor.compare_value = compare_value; + return NewDescriptor(isolate, data, this); +} + + +Local RawOperationDescriptor::NewPrimitiveValue( + Isolate* isolate, + DeclaredAccessorDescriptorDataType data_type, + uint8_t bool_offset) { + i::DeclaredAccessorDescriptorData data; + data.type = i::kDescriptorPrimitiveValue; + data.primitive_value_descriptor.data_type = data_type; + data.primitive_value_descriptor.bool_offset = bool_offset; + return NewDescriptor(isolate, data, this); +} + + +template +static Local NewBitmaskCompare( + Isolate* isolate, + T bitmask, + T compare_value, + RawOperationDescriptor* operation) { + i::DeclaredAccessorDescriptorData data; + data.type = i::kDescriptorBitmaskCompare; + data.bitmask_compare_descriptor.bitmask = bitmask; + data.bitmask_compare_descriptor.compare_value = compare_value; + data.bitmask_compare_descriptor.size = sizeof(T); + return NewDescriptor(isolate, data, operation); +} + + +Local RawOperationDescriptor::NewBitmaskCompare8( + Isolate* isolate, + uint8_t bitmask, + uint8_t compare_value) { + return NewBitmaskCompare(isolate, bitmask, compare_value, this); +} + + +Local RawOperationDescriptor::NewBitmaskCompare16( + Isolate* isolate, + uint16_t bitmask, + uint16_t compare_value) { + return NewBitmaskCompare(isolate, bitmask, compare_value, this); +} + + +Local RawOperationDescriptor::NewBitmaskCompare32( + Isolate* isolate, + uint32_t bitmask, + uint32_t compare_value) { + return NewBitmaskCompare(isolate, bitmask, compare_value, this); +} + + Local TypeSwitch::New(Handle type) { Handle types[1] = { type }; return TypeSwitch::New(1, types); @@ -1102,20 +1220,12 @@ void FunctionTemplate::SetCallHandler(InvocationCallback callback, } -static i::Handle MakeAccessorInfo( - v8::Handle name, - AccessorGetter getter, - AccessorSetter setter, - v8::Handle data, - v8::AccessControl settings, - v8::PropertyAttribute attributes, - v8::Handle signature) { - i::Handle obj = - FACTORY->NewExecutableAccessorInfo(); - SET_FIELD_WRAPPED(obj, set_getter, getter); - SET_FIELD_WRAPPED(obj, set_setter, setter); - if (data.IsEmpty()) data = v8::Undefined(); - obj->set_data(*Utils::OpenHandle(*data)); +static i::Handle SetAccessorInfoProperties( + i::Handle obj, + v8::Handle name, + v8::AccessControl settings, + v8::PropertyAttribute attributes, + v8::Handle signature) { obj->set_name(*Utils::OpenHandle(*name)); if (settings & ALL_CAN_READ) obj->set_all_can_read(true); if (settings & ALL_CAN_WRITE) obj->set_all_can_write(true); @@ -1128,7 +1238,7 @@ static i::Handle MakeAccessorInfo( } -void FunctionTemplate::AddInstancePropertyAccessor( +static i::Handle MakeAccessorInfo( v8::Handle name, AccessorGetter getter, AccessorSetter setter, @@ -1136,25 +1246,29 @@ void FunctionTemplate::AddInstancePropertyAccessor( v8::AccessControl settings, v8::PropertyAttribute attributes, v8::Handle signature) { - i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); - if (IsDeadCheck(isolate, - "v8::FunctionTemplate::AddInstancePropertyAccessor()")) { - return; - } - ENTER_V8(isolate); - i::HandleScope scope(isolate); + i::Isolate* isolate = Utils::OpenHandle(*name)->GetIsolate(); + i::Handle obj = + isolate->factory()->NewExecutableAccessorInfo(); + SET_FIELD_WRAPPED(obj, set_getter, getter); + SET_FIELD_WRAPPED(obj, set_setter, setter); + if (data.IsEmpty()) data = v8::Undefined(); + obj->set_data(*Utils::OpenHandle(*data)); + return SetAccessorInfoProperties(obj, name, settings, attributes, signature); +} - i::Handle obj = MakeAccessorInfo(name, getter, setter, data, - settings, attributes, - signature); - i::Handle list(Utils::OpenHandle(this)->property_accessors(), - isolate); - if (list->IsUndefined()) { - list = NeanderArray().value(); - Utils::OpenHandle(this)->set_property_accessors(*list); - } - NeanderArray array(list); - array.add(obj); + +static i::Handle MakeAccessorInfo( + v8::Handle name, + v8::Handle descriptor, + v8::AccessControl settings, + v8::PropertyAttribute attributes, + v8::Handle signature) { + i::Isolate* isolate = Utils::OpenHandle(*name)->GetIsolate(); + if (descriptor.IsEmpty()) return i::Handle(); + i::Handle obj = + isolate->factory()->NewDeclaredAccessorInfo(); + obj->set_descriptor(*Utils::OpenHandle(*descriptor)); + return SetAccessorInfoProperties(obj, name, settings, attributes, signature); } @@ -1335,6 +1449,19 @@ static void EnsureConstructor(ObjectTemplate* object_template) { } +static inline void AddPropertyToFunctionTemplate( + i::Handle cons, + i::Handle obj) { + i::Handle list(cons->property_accessors(), cons->GetIsolate()); + if (list->IsUndefined()) { + list = NeanderArray().value(); + cons->set_property_accessors(*list); + } + NeanderArray array(list); + array.add(obj); +} + + void ObjectTemplate::SetAccessor(v8::Handle name, AccessorGetter getter, AccessorSetter setter, @@ -1350,13 +1477,31 @@ void ObjectTemplate::SetAccessor(v8::Handle name, i::FunctionTemplateInfo* constructor = i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor()); i::Handle cons(constructor); - Utils::ToLocal(cons)->AddInstancePropertyAccessor(name, - getter, - setter, - data, - settings, - attribute, - signature); + i::Handle obj = MakeAccessorInfo(name, getter, setter, data, + settings, attribute, + signature); + AddPropertyToFunctionTemplate(cons, obj); +} + + +bool ObjectTemplate::SetAccessor(Handle name, + Handle descriptor, + AccessControl settings, + PropertyAttribute attribute, + Handle signature) { + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); + if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetAccessor()")) return false; + ENTER_V8(isolate); + i::HandleScope scope(isolate); + EnsureConstructor(this); + i::FunctionTemplateInfo* constructor = + i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor()); + i::Handle cons(constructor); + i::Handle obj = MakeAccessorInfo( + name, descriptor, settings, attribute, signature); + if (obj.is_null()) return false; + AddPropertyToFunctionTemplate(cons, obj); + return true; } @@ -3158,6 +3303,16 @@ bool v8::Object::Has(uint32_t index) { } +static inline bool SetAccessor(Object* obj, i::Handle info) { + if (info.is_null()) return false; + bool fast = Utils::OpenHandle(obj)->HasFastProperties(); + i::Handle result = i::SetAccessor(Utils::OpenHandle(obj), info); + if (result.is_null() || result->IsUndefined()) return false; + if (fast) i::JSObject::TransformToFastProperties(Utils::OpenHandle(obj), 0); + return true; +} + + bool Object::SetAccessor(Handle name, AccessorGetter getter, AccessorSetter setter, @@ -3172,11 +3327,22 @@ bool Object::SetAccessor(Handle name, i::Handle info = MakeAccessorInfo(name, getter, setter, data, settings, attributes, signature); - bool fast = Utils::OpenHandle(this)->HasFastProperties(); - i::Handle result = i::SetAccessor(Utils::OpenHandle(this), info); - if (result.is_null() || result->IsUndefined()) return false; - if (fast) i::JSObject::TransformToFastProperties(Utils::OpenHandle(this), 0); - return true; + return v8::SetAccessor(this, info); +} + + +bool Object::SetAccessor(Handle name, + Handle descriptor, + AccessControl settings, + PropertyAttribute attributes) { + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); + ON_BAILOUT(isolate, "v8::Object::SetAccessor()", return false); + ENTER_V8(isolate); + i::HandleScope scope(isolate); + v8::Handle signature; + i::Handle info = MakeAccessorInfo( + name, descriptor, settings, attributes, signature); + return v8::SetAccessor(this, info); } diff --git a/src/api.h b/src/api.h index ca2240b64..ac6b8342c 100644 --- a/src/api.h +++ b/src/api.h @@ -177,7 +177,8 @@ class RegisteredExtension { V(Context, Context) \ V(External, Foreign) \ V(StackTrace, JSArray) \ - V(StackFrame, JSObject) + V(StackFrame, JSObject) \ + V(DeclaredAccessorDescriptor, DeclaredAccessorDescriptor) class Utils { @@ -225,6 +226,8 @@ class Utils { v8::internal::Handle obj); static inline Local ExternalToLocal( v8::internal::Handle obj); + static inline Local ToLocal( + v8::internal::Handle obj); #define DECLARE_OPEN_HANDLE(From, To) \ static inline v8::internal::Handle \ @@ -280,6 +283,7 @@ MAKE_TO_LOCAL(NumberToLocal, Object, Number) MAKE_TO_LOCAL(IntegerToLocal, Object, Integer) MAKE_TO_LOCAL(Uint32ToLocal, Object, Uint32) MAKE_TO_LOCAL(ExternalToLocal, JSObject, External) +MAKE_TO_LOCAL(ToLocal, DeclaredAccessorDescriptor, DeclaredAccessorDescriptor) #undef MAKE_TO_LOCAL diff --git a/src/factory.cc b/src/factory.cc index 83fb1ffc0..943902e15 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -369,6 +369,12 @@ Handle Factory::NewStruct(InstanceType type) { } +Handle Factory::NewDeclaredAccessorDescriptor() { + return Handle::cast( + NewStruct(DECLARED_ACCESSOR_DESCRIPTOR_TYPE)); +} + + Handle Factory::NewDeclaredAccessorInfo() { Handle info = Handle::cast( diff --git a/src/factory.h b/src/factory.h index 7b8f20000..8695bcd52 100644 --- a/src/factory.h +++ b/src/factory.h @@ -205,6 +205,8 @@ class Factory { // the old generation). Handle NewStruct(InstanceType type); + Handle NewDeclaredAccessorDescriptor(); + Handle NewDeclaredAccessorInfo(); Handle NewExecutableAccessorInfo(); diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 226aeb3f2..3cb3d246b 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -744,7 +744,7 @@ void ExecutableAccessorInfo::ExecutableAccessorInfoVerify() { void DeclaredAccessorDescriptor::DeclaredAccessorDescriptorVerify() { CHECK(IsDeclaredAccessorDescriptor()); - VerifySmiField(kInternalFieldOffset); + VerifyPointer(serialized_data()); } diff --git a/src/objects-inl.h b/src/objects-inl.h index 2139ae95c..f2cdde944 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -4178,7 +4178,8 @@ ACCESSORS_TO_SMI(AccessorInfo, flag, kFlagOffset) ACCESSORS(AccessorInfo, expected_receiver_type, Object, kExpectedReceiverTypeOffset) -ACCESSORS(DeclaredAccessorDescriptor, internal_field, Smi, kInternalFieldOffset) +ACCESSORS(DeclaredAccessorDescriptor, serialized_data, ByteArray, + kSerializedDataOffset) ACCESSORS(DeclaredAccessorInfo, descriptor, DeclaredAccessorDescriptor, kDescriptorOffset) diff --git a/src/objects-printer.cc b/src/objects-printer.cc index 88b2f91da..4522ee43e 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -929,7 +929,7 @@ void DeclaredAccessorInfo::DeclaredAccessorInfoPrint(FILE* out) { void DeclaredAccessorDescriptor::DeclaredAccessorDescriptorPrint(FILE* out) { HeapObject::PrintHeader(out, "DeclaredAccessorDescriptor"); PrintF(out, "\n - internal field: "); - internal_field()->ShortPrint(out); + serialized_data()->ShortPrint(out); } diff --git a/src/objects.cc b/src/objects.cc index 381961be9..00a81756f 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -165,6 +165,146 @@ MaybeObject* Object::GetPropertyWithReceiver(Object* receiver, } +template +static inline To* CheckedCast(void *from) { + uintptr_t temp = reinterpret_cast(from); + ASSERT(temp % sizeof(To) == 0); + return reinterpret_cast(temp); +} + + +static MaybeObject* PerformCompare(const BitmaskCompareDescriptor& descriptor, + char* ptr, + Heap* heap) { + uint32_t bitmask = descriptor.bitmask; + uint32_t compare_value = descriptor.compare_value; + uint32_t value; + switch (descriptor.size) { + case 1: + value = static_cast(*CheckedCast(ptr)); + compare_value &= 0xff; + bitmask &= 0xff; + break; + case 2: + value = static_cast(*CheckedCast(ptr)); + compare_value &= 0xffff; + bitmask &= 0xffff; + break; + case 4: + value = *CheckedCast(ptr); + break; + default: + UNREACHABLE(); + return NULL; + } + return heap->ToBoolean((bitmask & value) == (bitmask & compare_value)); +} + + +static MaybeObject* PerformCompare(const PointerCompareDescriptor& descriptor, + char* ptr, + Heap* heap) { + uintptr_t compare_value = + reinterpret_cast(descriptor.compare_value); + uintptr_t value = *CheckedCast(ptr); + return heap->ToBoolean(compare_value == value); +} + + +static MaybeObject* GetPrimitiveValue( + const PrimitiveValueDescriptor& descriptor, + char* ptr, + Heap* heap) { + int32_t int32_value; + switch (descriptor.data_type) { + case kDescriptorInt8Type: + int32_value = *CheckedCast(ptr); + break; + case kDescriptorUint8Type: + int32_value = *CheckedCast(ptr); + break; + case kDescriptorInt16Type: + int32_value = *CheckedCast(ptr); + break; + case kDescriptorUint16Type: + int32_value = *CheckedCast(ptr); + break; + case kDescriptorInt32Type: + int32_value = *CheckedCast(ptr); + break; + case kDescriptorUint32Type: { + uint32_t value = *CheckedCast(ptr); + return heap->NumberFromUint32(value); + } + case kDescriptorBoolType: { + uint8_t byte = *CheckedCast(ptr); + return heap->ToBoolean(byte & (0x1 << descriptor.bool_offset)); + } + case kDescriptorFloatType: { + float value = *CheckedCast(ptr); + return heap->NumberFromDouble(value); + } + case kDescriptorDoubleType: { + double value = *CheckedCast(ptr); + return heap->NumberFromDouble(value); + } + } + return heap->NumberFromInt32(int32_value); +} + + +static MaybeObject* GetDeclaredAccessorProperty(Object* receiver, + DeclaredAccessorInfo* info, + Isolate* isolate) { + char* current = reinterpret_cast(receiver); + DeclaredAccessorDescriptorIterator iterator(info->descriptor()); + while (true) { + const DeclaredAccessorDescriptorData* data = iterator.Next(); + switch (data->type) { + case kDescriptorReturnObject: { + ASSERT(iterator.Complete()); + current = *CheckedCast(current); + return *CheckedCast(current); + } + case kDescriptorPointerDereference: + ASSERT(!iterator.Complete()); + current = *reinterpret_cast(current); + break; + case kDescriptorPointerShift: + ASSERT(!iterator.Complete()); + current += data->pointer_shift_descriptor.byte_offset; + break; + case kDescriptorObjectDereference: { + ASSERT(!iterator.Complete()); + Object* object = CheckedCast(current); + int field = data->object_dereference_descriptor.internal_field; + Object* smi = JSObject::cast(object)->GetInternalField(field); + ASSERT(smi->IsSmi()); + current = reinterpret_cast(smi); + break; + } + case kDescriptorBitmaskCompare: + ASSERT(iterator.Complete()); + return PerformCompare(data->bitmask_compare_descriptor, + current, + isolate->heap()); + case kDescriptorPointerCompare: + ASSERT(iterator.Complete()); + return PerformCompare(data->pointer_compare_descriptor, + current, + isolate->heap()); + case kDescriptorPrimitiveValue: + ASSERT(iterator.Complete()); + return GetPrimitiveValue(data->primitive_value_descriptor, + current, + isolate->heap()); + } + } + UNREACHABLE(); + return NULL; +} + + MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, Object* structure, Name* name) { @@ -182,9 +322,8 @@ MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, } // api style callbacks. - if (structure->IsExecutableAccessorInfo()) { - ExecutableAccessorInfo* data = ExecutableAccessorInfo::cast(structure); - if (!data->IsCompatibleReceiver(receiver)) { + if (structure->IsAccessorInfo()) { + if (!AccessorInfo::cast(structure)->IsCompatibleReceiver(receiver)) { Handle name_handle(name, isolate); Handle receiver_handle(receiver, isolate); Handle args[2] = { name_handle, receiver_handle }; @@ -197,6 +336,12 @@ MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, // TODO(rossberg): Handling symbols in the API requires changing the API, // so we do not support it for now. if (name->IsSymbol()) return isolate->heap()->undefined_value(); + if (structure->IsDeclaredAccessorInfo()) { + return GetDeclaredAccessorProperty(receiver, + DeclaredAccessorInfo::cast(structure), + isolate); + } + ExecutableAccessorInfo* data = ExecutableAccessorInfo::cast(structure); Object* fun_obj = data->getter(); v8::AccessorGetter call_fun = v8::ToCData(fun_obj); if (call_fun == NULL) return isolate->heap()->undefined_value(); @@ -232,11 +377,6 @@ MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, return isolate->heap()->undefined_value(); } - // TODO(dcarney): Handle correctly. - if (structure->IsDeclaredAccessorInfo()) { - return isolate->heap()->undefined_value(); - } - UNREACHABLE(); return NULL; } @@ -9941,8 +10081,9 @@ MaybeObject* JSObject::GetElementWithCallback(Object* receiver, } if (structure->IsDeclaredAccessorInfo()) { - // TODO(dcarney): Handle correctly. - return isolate->heap()->undefined_value(); + return GetDeclaredAccessorProperty(receiver, + DeclaredAccessorInfo::cast(structure), + isolate); } UNREACHABLE(); @@ -13724,6 +13865,58 @@ void ObjectHashTable::RemoveEntry(int entry) { } +DeclaredAccessorDescriptorIterator::DeclaredAccessorDescriptorIterator( + DeclaredAccessorDescriptor* descriptor) + : array_(descriptor->serialized_data()->GetDataStartAddress()), + length_(descriptor->serialized_data()->length()), + offset_(0) { +} + + +const DeclaredAccessorDescriptorData* + DeclaredAccessorDescriptorIterator::Next() { + ASSERT(offset_ < length_); + uint8_t* ptr = &array_[offset_]; + ASSERT(reinterpret_cast(ptr) % sizeof(uintptr_t) == 0); + const DeclaredAccessorDescriptorData* data = + reinterpret_cast(ptr); + offset_ += sizeof(*data); + ASSERT(offset_ <= length_); + return data; +} + + +Handle DeclaredAccessorDescriptor::Create( + Isolate* isolate, + const DeclaredAccessorDescriptorData& descriptor, + Handle previous) { + int previous_length = + previous.is_null() ? 0 : previous->serialized_data()->length(); + int length = sizeof(descriptor) + previous_length; + Handle serialized_descriptor = + isolate->factory()->NewByteArray(length); + Handle value = + isolate->factory()->NewDeclaredAccessorDescriptor(); + value->set_serialized_data(*serialized_descriptor); + // Copy in the data. + { + AssertNoAllocation no_allocation; + uint8_t* array = serialized_descriptor->GetDataStartAddress(); + if (previous_length != 0) { + uint8_t* previous_array = + previous->serialized_data()->GetDataStartAddress(); + memcpy(array, previous_array, previous_length); + array += previous_length; + } + ASSERT(reinterpret_cast(array) % sizeof(uintptr_t) == 0); + DeclaredAccessorDescriptorData* data = + reinterpret_cast(array); + *data = descriptor; + } + return value; +} + + #ifdef ENABLE_DEBUGGER_SUPPORT // Check if there is a break point at this code position. bool DebugInfo::HasBreakPoint(int code_position) { diff --git a/src/objects.h b/src/objects.h index d90e703f1..59cddc282 100644 --- a/src/objects.h +++ b/src/objects.h @@ -8603,19 +8603,91 @@ class AccessorInfo: public Struct { }; +enum AccessorDescriptorType { + kDescriptorBitmaskCompare, + kDescriptorPointerCompare, + kDescriptorPrimitiveValue, + kDescriptorObjectDereference, + kDescriptorPointerDereference, + kDescriptorPointerShift, + kDescriptorReturnObject +}; + + +struct BitmaskCompareDescriptor { + uint32_t bitmask; + uint32_t compare_value; + uint8_t size; // Must be in {1,2,4}. +}; + + +struct PointerCompareDescriptor { + void* compare_value; +}; + + +struct PrimitiveValueDescriptor { + v8::DeclaredAccessorDescriptorDataType data_type; + uint8_t bool_offset; // Must be in [0,7], used for kDescriptorBoolType. +}; + + +struct ObjectDerefenceDescriptor { + uint8_t internal_field; +}; + + +struct PointerShiftDescriptor { + int16_t byte_offset; +}; + + +struct DeclaredAccessorDescriptorData { + AccessorDescriptorType type; + union { + struct BitmaskCompareDescriptor bitmask_compare_descriptor; + struct PointerCompareDescriptor pointer_compare_descriptor; + struct PrimitiveValueDescriptor primitive_value_descriptor; + struct ObjectDerefenceDescriptor object_dereference_descriptor; + struct PointerShiftDescriptor pointer_shift_descriptor; + }; +}; + + +class DeclaredAccessorDescriptor; + + +class DeclaredAccessorDescriptorIterator { + public: + explicit DeclaredAccessorDescriptorIterator( + DeclaredAccessorDescriptor* descriptor); + const DeclaredAccessorDescriptorData* Next(); + bool Complete() const { return length_ == offset_; } + private: + uint8_t* array_; + const int length_; + int offset_; + DISALLOW_IMPLICIT_CONSTRUCTORS(DeclaredAccessorDescriptorIterator); +}; + + class DeclaredAccessorDescriptor: public Struct { public: - // TODO(dcarney): Fill out this class. - DECL_ACCESSORS(internal_field, Smi) + DECL_ACCESSORS(serialized_data, ByteArray) static inline DeclaredAccessorDescriptor* cast(Object* obj); + static Handle Create( + Isolate* isolate, + const DeclaredAccessorDescriptorData& data, + Handle previous); + // Dispatched behavior. DECLARE_PRINTER(DeclaredAccessorDescriptor) DECLARE_VERIFIER(DeclaredAccessorDescriptor) - static const int kInternalFieldOffset = HeapObject::kHeaderSize; - static const int kSize = kInternalFieldOffset + kPointerSize; + static const int kSerializedDataOffset = HeapObject::kHeaderSize; + static const int kSize = kSerializedDataOffset + kPointerSize; private: DISALLOW_IMPLICIT_CONSTRUCTORS(DeclaredAccessorDescriptor); diff --git a/test/cctest/SConscript b/test/cctest/SConscript index 8ed3f521c..ce42b0d72 100644 --- a/test/cctest/SConscript +++ b/test/cctest/SConscript @@ -64,6 +64,7 @@ SOURCES = { 'test-dataflow.cc', 'test-date.cc', 'test-debug.cc', + 'test-declarative-accessors.cc', 'test-decls.cc', 'test-deoptimization.cc', 'test-dictionary.cc', diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index eb0d90741..ee9995b86 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -59,6 +59,7 @@ 'test-dataflow.cc', 'test-date.cc', 'test-debug.cc', + 'test-declarative-accessors.cc', 'test-decls.cc', 'test-deoptimization.cc', 'test-dictionary.cc', diff --git a/test/cctest/test-declarative-accessors.cc b/test/cctest/test-declarative-accessors.cc new file mode 100644 index 000000000..a3d152dc3 --- /dev/null +++ b/test/cctest/test-declarative-accessors.cc @@ -0,0 +1,294 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "v8.h" + +#include "cctest.h" + +using namespace v8::internal; + + +class HandleArray : public Malloced { + public: + static const unsigned kArraySize = 200; + explicit HandleArray() {} + ~HandleArray() { Reset(v8::Isolate::GetCurrent()); } + void Reset(v8::Isolate* isolate) { + for (unsigned i = 0; i < kArraySize; i++) { + if (handles_[i].IsEmpty()) continue; + handles_[i].Dispose(isolate); + handles_[i].Clear(); + } + } + v8::Persistent handles_[kArraySize]; + private: + DISALLOW_COPY_AND_ASSIGN(HandleArray); +}; + + +// An aligned character array of size 1024. +class AlignedArray : public Malloced { + public: + static const unsigned kArraySize = 1024/sizeof(uint64_t); + AlignedArray() { Reset(); } + + void Reset() { + for (unsigned i = 0; i < kArraySize; i++) { + data_[i] = 0; + } + } + + template + T As() { return reinterpret_cast(data_); } + + private: + uint64_t data_[kArraySize]; + DISALLOW_COPY_AND_ASSIGN(AlignedArray); +}; + + +class DescriptorTestHelper { + public: + DescriptorTestHelper() : + isolate_(NULL), array_(new AlignedArray), handle_array_(new HandleArray) { + v8::V8::Initialize(); + isolate_ = v8::Isolate::GetCurrent(); + } + v8::Isolate* isolate_; + // Data objects. + SmartPointer array_; + SmartPointer handle_array_; + private: + DISALLOW_COPY_AND_ASSIGN(DescriptorTestHelper); +}; + + +static v8::Local CreateConstructor( + v8::Handle context, + const char* class_name, + int internal_field, + const char* descriptor_name = NULL, + v8::Handle descriptor = + v8::Handle()) { + v8::Local constructor = v8::FunctionTemplate::New(); + v8::Local obj_template = constructor->InstanceTemplate(); + // Setup object template. + if (descriptor_name != NULL && !descriptor.IsEmpty()) { + bool added_accessor = + obj_template->SetAccessor(v8_str(descriptor_name), descriptor); + CHECK(added_accessor); + } + obj_template->SetInternalFieldCount((internal_field+1)*2 + 7); + context->Global()->Set(v8_str(class_name), constructor->GetFunction()); + return obj_template; +} + + +static void VerifyRead(v8::Handle descriptor, + int internal_field, + void* internal_object, + v8::Handle expected_value) { + v8::HandleScope scope; + LocalContext local_context; + v8::Handle context = local_context.local(); + CreateConstructor(context, "Accessible", internal_field, "x", descriptor); + // Setup object. + CompileRun("var accessible = new Accessible();"); + v8::Local obj( + v8::Object::Cast(*context->Global()->Get(v8_str("accessible")))); + obj->SetAlignedPointerInInternalField(internal_field, internal_object); + bool added_accessor; + added_accessor = obj->SetAccessor(v8_str("y"), descriptor); + CHECK(added_accessor); + added_accessor = obj->SetAccessor(v8_str("13"), descriptor); + CHECK(added_accessor); + // Test access from template getter. + v8::Local value; + value = CompileRun("accessible.x;"); + CHECK_EQ(expected_value, value); + value = CompileRun("accessible['x'];"); + CHECK_EQ(expected_value, value); + // Test access from object getter. + value = CompileRun("accessible.y;"); + CHECK_EQ(expected_value, value); + value = CompileRun("accessible['y'];"); + CHECK_EQ(expected_value, value); + value = CompileRun("accessible[13];"); + CHECK_EQ(expected_value, value); + value = CompileRun("accessible['13'];"); + CHECK_EQ(expected_value, value); +} + + +static v8::Handle Convert(int32_t value, v8::Isolate* isolate) { + return v8::Integer::New(value, isolate); +} + + +static v8::Handle Convert(float value, v8::Isolate*) { + return v8::Number::New(value); +} + + +static v8::Handle Convert(double value, v8::Isolate*) { + return v8::Number::New(value); +} + + +typedef v8::ObjectOperationDescriptor OOD; + +template +static void TestPrimitiveValue( + T value, + v8::DeclaredAccessorDescriptorDataType data_type, + DescriptorTestHelper* helper) { + v8::HandleScope handle_scope; + int index = 17; + int internal_field = 6; + v8::Handle descriptor = + OOD::NewInternalFieldDereference(helper->isolate_, internal_field) + ->NewRawShift(helper->isolate_, index*sizeof(T)) + ->NewPrimitiveValue(helper->isolate_, data_type, 0); + v8::Handle expected = Convert(value, helper->isolate_); + helper->array_->Reset(); + helper->array_->As()[index] = value; + VerifyRead(descriptor, internal_field, *helper->array_, expected); +} + + +TEST(PrimitiveValueRead) { + DescriptorTestHelper helper; + TestPrimitiveValue(203, v8::kDescriptorInt32Type, &helper); + TestPrimitiveValue(23.7f, v8::kDescriptorFloatType, &helper); + TestPrimitiveValue(23.7, v8::kDescriptorDoubleType, &helper); +} + + +template +static void TestBitmaskCompare(T bitmask, + T compare_value, + DescriptorTestHelper* helper) { + v8::HandleScope handle_scope; + int index = 13; + int internal_field = 4; + v8::Handle raw_descriptor = + OOD::NewInternalFieldDereference(helper->isolate_, internal_field) + ->NewRawShift(helper->isolate_, index*sizeof(T)); + v8::Handle descriptor; + switch (sizeof(T)) { + case 1: + descriptor =raw_descriptor->NewBitmaskCompare8( + helper->isolate_, bitmask, compare_value); + break; + case 2: + descriptor = raw_descriptor->NewBitmaskCompare16( + helper->isolate_, bitmask, compare_value); + break; + case 4: + descriptor = raw_descriptor->NewBitmaskCompare32( + helper->isolate_, bitmask, compare_value); + break; + default: + CHECK(false); + break; + } + AlignedArray* array = *helper->array_; + array->Reset(); + VerifyRead(descriptor, internal_field, array, v8::False(helper->isolate_)); + array->As()[index] = compare_value; + VerifyRead(descriptor, internal_field, array, v8::True(helper->isolate_)); + helper->array_->As()[index] = compare_value & bitmask; + VerifyRead(descriptor, internal_field, array, v8::True(helper->isolate_)); +} + + +TEST(BitmaskCompareRead) { + DescriptorTestHelper helper; + TestBitmaskCompare(0xf3, 0xa8, &helper); + TestBitmaskCompare(0xfefe, 0x7d42, &helper); + TestBitmaskCompare(0xfefeab18, 0x1234fdec, &helper); +} + + +TEST(PointerCompareRead) { + DescriptorTestHelper helper; + v8::HandleScope handle_scope; + int index = 35; + int internal_field = 3; + void* ptr = helper.isolate_; + v8::Handle descriptor = + OOD::NewInternalFieldDereference(helper.isolate_, internal_field) + ->NewRawShift(helper.isolate_, index*sizeof(ptr)) + ->NewPointerCompare(helper.isolate_, ptr); + AlignedArray* array = *helper.array_; + VerifyRead(descriptor, internal_field, array, v8::False(helper.isolate_)); + array->As()[index] = reinterpret_cast(ptr); + VerifyRead(descriptor, internal_field, array, v8::True(helper.isolate_)); +} + + +TEST(PointerDereferenceRead) { + DescriptorTestHelper helper; + v8::HandleScope handle_scope; + int first_index = 13; + int internal_field = 7; + int second_index = 11; + int pointed_to_index = 75; + uint16_t expected = 0x1425; + v8::Handle descriptor = + OOD::NewInternalFieldDereference(helper.isolate_, internal_field) + ->NewRawShift(helper.isolate_, first_index*kPointerSize) + ->NewRawDereference(helper.isolate_) + ->NewRawShift(helper.isolate_, second_index*sizeof(int16_t)) + ->NewPrimitiveValue(helper.isolate_, v8::kDescriptorInt16Type, 0); + AlignedArray* array = *helper.array_; + array->As()[first_index] = + &array->As()[pointed_to_index]; + VerifyRead(descriptor, internal_field, array, v8::Integer::New(0)); + second_index += pointed_to_index*sizeof(uintptr_t)/sizeof(uint16_t); + array->As()[second_index] = expected; + VerifyRead(descriptor, internal_field, array, v8::Integer::New(expected)); +} + + +TEST(HandleDereferenceRead) { + DescriptorTestHelper helper; + v8::HandleScope handle_scope; + int index = 13; + int internal_field = 0; + v8::Handle descriptor = + OOD::NewInternalFieldDereference(helper.isolate_, internal_field) + ->NewRawShift(helper.isolate_, index*kPointerSize) + ->NewHandleDereference(helper.isolate_); + HandleArray* array = *helper.handle_array_; + v8::Handle expected = v8_str("whatever"); + array->handles_[index] = v8::Persistent::New(expected); + VerifyRead(descriptor, internal_field, array, expected); +} + -- 2.34.1