Runtime version of declarative native accessors.
authordcarney@chromium.org <dcarney@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 7 Mar 2013 11:42:58 +0000 (11:42 +0000)
committerdcarney@chromium.org <dcarney@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 7 Mar 2013 11:42:58 +0000 (11:42 +0000)
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

13 files changed:
include/v8.h
src/api.cc
src/api.h
src/factory.cc
src/factory.h
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects.cc
src/objects.h
test/cctest/SConscript
test/cctest/cctest.gyp
test/cctest/test-declarative-accessors.cc [new file with mode: 0644]

index 1a35b3a..7016cb5 100644 (file)
@@ -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<String> name,
+                   Handle<DeclaredAccessorDescriptor> 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<String> name,
-                                   AccessorGetter getter,
-                                   AccessorSetter setter,
-                                   Handle<Value> data,
-                                   AccessControl settings,
-                                   PropertyAttribute attributes,
-                                   Handle<AccessorSignature> signature);
   void SetNamedInstancePropertyHandler(NamedPropertyGetter getter,
                                        NamedPropertySetter setter,
                                        NamedPropertyQuery query,
@@ -2456,6 +2458,14 @@ class V8EXPORT ObjectTemplate : public Template {
                    Handle<AccessorSignature> signature =
                        Handle<AccessorSignature>());
 
+  // This function is not yet stable and should not be used at this time.
+  bool SetAccessor(Handle<String> name,
+                   Handle<DeclaredAccessorDescriptor> descriptor,
+                   AccessControl settings = DEFAULT,
+                   PropertyAttribute attribute = None,
+                   Handle<AccessorSignature> signature =
+                       Handle<AccessorSignature>());
+
   /**
    * 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<RawOperationDescriptor> 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<DeclaredAccessorDescriptor> NewHandleDereference(Isolate* isolate);
+  Local<RawOperationDescriptor> NewRawDereference(Isolate* isolate);
+  Local<RawOperationDescriptor> NewRawShift(Isolate* isolate,
+                                            int16_t byte_offset);
+  Local<DeclaredAccessorDescriptor> NewPointerCompare(Isolate* isolate,
+                                                      void* compare_value);
+  Local<DeclaredAccessorDescriptor> NewPrimitiveValue(
+      Isolate* isolate,
+      DeclaredAccessorDescriptorDataType data_type,
+      uint8_t bool_offset = 0);
+  Local<DeclaredAccessorDescriptor> NewBitmaskCompare8(Isolate* isolate,
+                                                       uint8_t bitmask,
+                                                       uint8_t compare_value);
+  Local<DeclaredAccessorDescriptor> NewBitmaskCompare16(
+      Isolate* isolate,
+      uint16_t bitmask,
+      uint16_t compare_value);
+  Local<DeclaredAccessorDescriptor> 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.
index e58e6c8..4d91e8f 100644 (file)
@@ -1041,6 +1041,124 @@ Local<AccessorSignature> AccessorSignature::New(
 }
 
 
+template<typename Operation>
+static Local<Operation> NewDescriptor(
+    Isolate* isolate,
+    const i::DeclaredAccessorDescriptorData& data,
+    Data* previous_descriptor
+    ) {
+  i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+  i::Handle<i::DeclaredAccessorDescriptor> previous =
+      i::Handle<i::DeclaredAccessorDescriptor>();
+  if (previous_descriptor != NULL) {
+    previous = Utils::OpenHandle(
+      static_cast<DeclaredAccessorDescriptor*>(previous_descriptor));
+  }
+  i::Handle<i::DeclaredAccessorDescriptor> descriptor =
+      i::DeclaredAccessorDescriptor::Create(internal_isolate, data, previous);
+  return Local<Operation>(
+      reinterpret_cast<Operation*>(*Utils::ToLocal(descriptor)));
+}
+
+
+Local<RawOperationDescriptor>
+  ObjectOperationDescriptor::NewInternalFieldDereference(
+    Isolate* isolate,
+    int internal_field) {
+  i::DeclaredAccessorDescriptorData data;
+  data.type = i::kDescriptorObjectDereference;
+  data.object_dereference_descriptor.internal_field = internal_field;
+  return NewDescriptor<RawOperationDescriptor>(isolate, data, NULL);
+}
+
+
+Local<RawOperationDescriptor> 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<RawOperationDescriptor>(isolate, data, this);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewHandleDereference(
+    Isolate* isolate) {
+  i::DeclaredAccessorDescriptorData data;
+  data.type = i::kDescriptorReturnObject;
+  return NewDescriptor<DeclaredAccessorDescriptor>(isolate, data, this);
+}
+
+
+Local<RawOperationDescriptor> RawOperationDescriptor::NewRawDereference(
+    Isolate* isolate) {
+  i::DeclaredAccessorDescriptorData data;
+  data.type = i::kDescriptorPointerDereference;
+  return NewDescriptor<RawOperationDescriptor>(isolate, data, this);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewPointerCompare(
+    Isolate* isolate,
+    void* compare_value) {
+  i::DeclaredAccessorDescriptorData data;
+  data.type = i::kDescriptorPointerCompare;
+  data.pointer_compare_descriptor.compare_value = compare_value;
+  return NewDescriptor<DeclaredAccessorDescriptor>(isolate, data, this);
+}
+
+
+Local<DeclaredAccessorDescriptor> 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<DeclaredAccessorDescriptor>(isolate, data, this);
+}
+
+
+template<typename T>
+static Local<DeclaredAccessorDescriptor> 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<DeclaredAccessorDescriptor>(isolate, data, operation);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewBitmaskCompare8(
+    Isolate* isolate,
+    uint8_t bitmask,
+    uint8_t compare_value) {
+  return NewBitmaskCompare(isolate, bitmask, compare_value, this);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewBitmaskCompare16(
+    Isolate* isolate,
+    uint16_t bitmask,
+    uint16_t compare_value) {
+  return NewBitmaskCompare(isolate, bitmask, compare_value, this);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewBitmaskCompare32(
+    Isolate* isolate,
+    uint32_t bitmask,
+    uint32_t compare_value) {
+  return NewBitmaskCompare(isolate, bitmask, compare_value, this);
+}
+
+
 Local<TypeSwitch> TypeSwitch::New(Handle<FunctionTemplate> type) {
   Handle<FunctionTemplate> types[1] = { type };
   return TypeSwitch::New(1, types);
@@ -1102,20 +1220,12 @@ void FunctionTemplate::SetCallHandler(InvocationCallback callback,
 }
 
 
-static i::Handle<i::AccessorInfo> MakeAccessorInfo(
-      v8::Handle<String> name,
-      AccessorGetter getter,
-      AccessorSetter setter,
-      v8::Handle<Value> data,
-      v8::AccessControl settings,
-      v8::PropertyAttribute attributes,
-      v8::Handle<AccessorSignature> signature) {
-  i::Handle<i::ExecutableAccessorInfo> 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<i::AccessorInfo> SetAccessorInfoProperties(
+    i::Handle<i::AccessorInfo> obj,
+    v8::Handle<String> name,
+    v8::AccessControl settings,
+    v8::PropertyAttribute attributes,
+    v8::Handle<AccessorSignature> 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<i::AccessorInfo> MakeAccessorInfo(
 }
 
 
-void FunctionTemplate::AddInstancePropertyAccessor(
+static i::Handle<i::AccessorInfo> MakeAccessorInfo(
       v8::Handle<String> name,
       AccessorGetter getter,
       AccessorSetter setter,
@@ -1136,25 +1246,29 @@ void FunctionTemplate::AddInstancePropertyAccessor(
       v8::AccessControl settings,
       v8::PropertyAttribute attributes,
       v8::Handle<AccessorSignature> 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<i::ExecutableAccessorInfo> 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<i::AccessorInfo> obj = MakeAccessorInfo(name, getter, setter, data,
-                                                    settings, attributes,
-                                                    signature);
-  i::Handle<i::Object> 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<i::AccessorInfo> MakeAccessorInfo(
+      v8::Handle<String> name,
+      v8::Handle<v8::DeclaredAccessorDescriptor> descriptor,
+      v8::AccessControl settings,
+      v8::PropertyAttribute attributes,
+      v8::Handle<AccessorSignature> signature) {
+  i::Isolate* isolate = Utils::OpenHandle(*name)->GetIsolate();
+  if (descriptor.IsEmpty()) return i::Handle<i::DeclaredAccessorInfo>();
+  i::Handle<i::DeclaredAccessorInfo> 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<i::FunctionTemplateInfo> cons,
+    i::Handle<i::AccessorInfo> obj) {
+  i::Handle<i::Object> 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<String> name,
                                  AccessorGetter getter,
                                  AccessorSetter setter,
@@ -1350,13 +1477,31 @@ void ObjectTemplate::SetAccessor(v8::Handle<String> name,
   i::FunctionTemplateInfo* constructor =
       i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor());
   i::Handle<i::FunctionTemplateInfo> cons(constructor);
-  Utils::ToLocal(cons)->AddInstancePropertyAccessor(name,
-                                                    getter,
-                                                    setter,
-                                                    data,
-                                                    settings,
-                                                    attribute,
-                                                    signature);
+  i::Handle<i::AccessorInfo> obj = MakeAccessorInfo(name, getter, setter, data,
+                                                      settings, attribute,
+                                                      signature);
+  AddPropertyToFunctionTemplate(cons, obj);
+}
+
+
+bool ObjectTemplate::SetAccessor(Handle<String> name,
+                                 Handle<DeclaredAccessorDescriptor> descriptor,
+                                 AccessControl settings,
+                                 PropertyAttribute attribute,
+                                 Handle<AccessorSignature> 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<i::FunctionTemplateInfo> cons(constructor);
+  i::Handle<i::AccessorInfo> 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<i::AccessorInfo> info) {
+  if (info.is_null()) return false;
+  bool fast = Utils::OpenHandle(obj)->HasFastProperties();
+  i::Handle<i::Object> 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<String> name,
                          AccessorGetter getter,
                          AccessorSetter setter,
@@ -3172,11 +3327,22 @@ bool Object::SetAccessor(Handle<String> name,
   i::Handle<i::AccessorInfo> info = MakeAccessorInfo(name, getter, setter, data,
                                                      settings, attributes,
                                                      signature);
-  bool fast = Utils::OpenHandle(this)->HasFastProperties();
-  i::Handle<i::Object> 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<String> name,
+                         Handle<DeclaredAccessorDescriptor> 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<AccessorSignature> signature;
+  i::Handle<i::AccessorInfo> info = MakeAccessorInfo(
+      name, descriptor, settings, attributes, signature);
+  return v8::SetAccessor(this, info);
 }
 
 
index ca2240b..ac6b834 100644 (file)
--- 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<v8::internal::TypeSwitchInfo> obj);
   static inline Local<External> ExternalToLocal(
       v8::internal::Handle<v8::internal::JSObject> obj);
+  static inline Local<DeclaredAccessorDescriptor> ToLocal(
+      v8::internal::Handle<v8::internal::DeclaredAccessorDescriptor> obj);
 
 #define DECLARE_OPEN_HANDLE(From, To) \
   static inline v8::internal::Handle<v8::internal::To> \
@@ -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
 
index 83fb1ff..943902e 100644 (file)
@@ -369,6 +369,12 @@ Handle<Struct> Factory::NewStruct(InstanceType type) {
 }
 
 
+Handle<DeclaredAccessorDescriptor> Factory::NewDeclaredAccessorDescriptor() {
+  return Handle<DeclaredAccessorDescriptor>::cast(
+      NewStruct(DECLARED_ACCESSOR_DESCRIPTOR_TYPE));
+}
+
+
 Handle<DeclaredAccessorInfo> Factory::NewDeclaredAccessorInfo() {
   Handle<DeclaredAccessorInfo> info =
       Handle<DeclaredAccessorInfo>::cast(
index 7b8f200..8695bcd 100644 (file)
@@ -205,6 +205,8 @@ class Factory {
   // the old generation).
   Handle<Struct> NewStruct(InstanceType type);
 
+  Handle<DeclaredAccessorDescriptor> NewDeclaredAccessorDescriptor();
+
   Handle<DeclaredAccessorInfo> NewDeclaredAccessorInfo();
 
   Handle<ExecutableAccessorInfo> NewExecutableAccessorInfo();
index 226aeb3..3cb3d24 100644 (file)
@@ -744,7 +744,7 @@ void ExecutableAccessorInfo::ExecutableAccessorInfoVerify() {
 
 void DeclaredAccessorDescriptor::DeclaredAccessorDescriptorVerify() {
   CHECK(IsDeclaredAccessorDescriptor());
-  VerifySmiField(kInternalFieldOffset);
+  VerifyPointer(serialized_data());
 }
 
 
index 2139ae9..f2cdde9 100644 (file)
@@ -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)
index 88b2f91..4522ee4 100644 (file)
@@ -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);
 }
 
 
index 381961b..00a8175 100644 (file)
@@ -165,6 +165,146 @@ MaybeObject* Object::GetPropertyWithReceiver(Object* receiver,
 }
 
 
+template<typename To>
+static inline To* CheckedCast(void *from) {
+  uintptr_t temp = reinterpret_cast<uintptr_t>(from);
+  ASSERT(temp % sizeof(To) == 0);
+  return reinterpret_cast<To*>(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<uint32_t>(*CheckedCast<uint8_t>(ptr));
+      compare_value &= 0xff;
+      bitmask &= 0xff;
+      break;
+    case 2:
+      value = static_cast<uint32_t>(*CheckedCast<uint16_t>(ptr));
+      compare_value &= 0xffff;
+      bitmask &= 0xffff;
+      break;
+    case 4:
+      value = *CheckedCast<uint32_t>(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<uintptr_t>(descriptor.compare_value);
+  uintptr_t value = *CheckedCast<uintptr_t>(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<int8_t>(ptr);
+      break;
+    case kDescriptorUint8Type:
+      int32_value = *CheckedCast<uint8_t>(ptr);
+      break;
+    case kDescriptorInt16Type:
+      int32_value = *CheckedCast<int16_t>(ptr);
+      break;
+    case kDescriptorUint16Type:
+      int32_value = *CheckedCast<uint16_t>(ptr);
+      break;
+    case kDescriptorInt32Type:
+      int32_value = *CheckedCast<int32_t>(ptr);
+      break;
+    case kDescriptorUint32Type: {
+      uint32_t value = *CheckedCast<uint32_t>(ptr);
+      return heap->NumberFromUint32(value);
+    }
+    case kDescriptorBoolType: {
+      uint8_t byte = *CheckedCast<uint8_t>(ptr);
+      return heap->ToBoolean(byte & (0x1 << descriptor.bool_offset));
+    }
+    case kDescriptorFloatType: {
+      float value = *CheckedCast<float>(ptr);
+      return heap->NumberFromDouble(value);
+    }
+    case kDescriptorDoubleType: {
+      double value = *CheckedCast<double>(ptr);
+      return heap->NumberFromDouble(value);
+    }
+  }
+  return heap->NumberFromInt32(int32_value);
+}
+
+
+static MaybeObject* GetDeclaredAccessorProperty(Object* receiver,
+                                                DeclaredAccessorInfo* info,
+                                                Isolate* isolate) {
+  char* current = reinterpret_cast<char*>(receiver);
+  DeclaredAccessorDescriptorIterator iterator(info->descriptor());
+  while (true) {
+    const DeclaredAccessorDescriptorData* data = iterator.Next();
+    switch (data->type) {
+      case kDescriptorReturnObject: {
+        ASSERT(iterator.Complete());
+        current = *CheckedCast<char*>(current);
+        return *CheckedCast<Object*>(current);
+      }
+      case kDescriptorPointerDereference:
+        ASSERT(!iterator.Complete());
+        current = *reinterpret_cast<char**>(current);
+        break;
+      case kDescriptorPointerShift:
+        ASSERT(!iterator.Complete());
+        current += data->pointer_shift_descriptor.byte_offset;
+        break;
+      case kDescriptorObjectDereference: {
+        ASSERT(!iterator.Complete());
+        Object* object = CheckedCast<Object>(current);
+        int field = data->object_dereference_descriptor.internal_field;
+        Object* smi = JSObject::cast(object)->GetInternalField(field);
+        ASSERT(smi->IsSmi());
+        current = reinterpret_cast<char*>(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<Object> name_handle(name, isolate);
       Handle<Object> receiver_handle(receiver, isolate);
       Handle<Object> 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<v8::AccessorGetter>(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<uintptr_t>(ptr) % sizeof(uintptr_t) == 0);
+  const DeclaredAccessorDescriptorData* data =
+      reinterpret_cast<const DeclaredAccessorDescriptorData*>(ptr);
+  offset_ += sizeof(*data);
+  ASSERT(offset_ <= length_);
+  return data;
+}
+
+
+Handle<DeclaredAccessorDescriptor> DeclaredAccessorDescriptor::Create(
+    Isolate* isolate,
+    const DeclaredAccessorDescriptorData& descriptor,
+    Handle<DeclaredAccessorDescriptor> previous) {
+  int previous_length =
+      previous.is_null() ? 0 : previous->serialized_data()->length();
+  int length = sizeof(descriptor) + previous_length;
+  Handle<ByteArray> serialized_descriptor =
+      isolate->factory()->NewByteArray(length);
+  Handle<DeclaredAccessorDescriptor> 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<uintptr_t>(array) % sizeof(uintptr_t) == 0);
+    DeclaredAccessorDescriptorData* data =
+        reinterpret_cast<DeclaredAccessorDescriptorData*>(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) {
index d90e703..59cddc2 100644 (file)
@@ -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<DeclaredAccessorDescriptor> Create(
+      Isolate* isolate,
+      const DeclaredAccessorDescriptorData& data,
+      Handle<DeclaredAccessorDescriptor> 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);
index 8ed3f52..ce42b0d 100644 (file)
@@ -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',
index eb0d907..ee9995b 100644 (file)
@@ -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 (file)
index 0000000..a3d152d
--- /dev/null
@@ -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 <stdlib.h>
+
+#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<v8::Value> 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<typename T>
+  T As() { return reinterpret_cast<T>(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<AlignedArray> array_;
+  SmartPointer<HandleArray> handle_array_;
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DescriptorTestHelper);
+};
+
+
+static v8::Local<v8::ObjectTemplate> CreateConstructor(
+    v8::Handle<v8::Context> context,
+    const char* class_name,
+    int internal_field,
+    const char* descriptor_name = NULL,
+    v8::Handle<v8::DeclaredAccessorDescriptor> descriptor =
+        v8::Handle<v8::DeclaredAccessorDescriptor>()) {
+  v8::Local<v8::FunctionTemplate> constructor = v8::FunctionTemplate::New();
+  v8::Local<v8::ObjectTemplate> 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<v8::DeclaredAccessorDescriptor> descriptor,
+                       int internal_field,
+                       void* internal_object,
+                       v8::Handle<v8::Value> expected_value) {
+  v8::HandleScope scope;
+  LocalContext local_context;
+  v8::Handle<v8::Context> context = local_context.local();
+  CreateConstructor(context, "Accessible", internal_field, "x", descriptor);
+  // Setup object.
+  CompileRun("var accessible = new Accessible();");
+  v8::Local<v8::Object> 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<v8::Value> 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<v8::Value> Convert(int32_t value, v8::Isolate* isolate) {
+  return v8::Integer::New(value, isolate);
+}
+
+
+static v8::Handle<v8::Value> Convert(float value, v8::Isolate*) {
+  return v8::Number::New(value);
+}
+
+
+static v8::Handle<v8::Value> Convert(double value, v8::Isolate*) {
+  return v8::Number::New(value);
+}
+
+
+typedef v8::ObjectOperationDescriptor OOD;
+
+template<typename T>
+static void TestPrimitiveValue(
+    T value,
+    v8::DeclaredAccessorDescriptorDataType data_type,
+    DescriptorTestHelper* helper) {
+  v8::HandleScope handle_scope;
+  int index = 17;
+  int internal_field = 6;
+  v8::Handle<v8::DeclaredAccessorDescriptor> descriptor =
+      OOD::NewInternalFieldDereference(helper->isolate_, internal_field)
+      ->NewRawShift(helper->isolate_, index*sizeof(T))
+      ->NewPrimitiveValue(helper->isolate_, data_type, 0);
+  v8::Handle<v8::Value> expected = Convert(value, helper->isolate_);
+  helper->array_->Reset();
+  helper->array_->As<T*>()[index] = value;
+  VerifyRead(descriptor, internal_field, *helper->array_, expected);
+}
+
+
+TEST(PrimitiveValueRead) {
+  DescriptorTestHelper helper;
+  TestPrimitiveValue<int32_t>(203, v8::kDescriptorInt32Type, &helper);
+  TestPrimitiveValue<float>(23.7f, v8::kDescriptorFloatType, &helper);
+  TestPrimitiveValue<double>(23.7, v8::kDescriptorDoubleType, &helper);
+}
+
+
+template<typename T>
+static void TestBitmaskCompare(T bitmask,
+                               T compare_value,
+                               DescriptorTestHelper* helper) {
+  v8::HandleScope handle_scope;
+  int index = 13;
+  int internal_field = 4;
+  v8::Handle<v8::RawOperationDescriptor> raw_descriptor =
+      OOD::NewInternalFieldDereference(helper->isolate_, internal_field)
+      ->NewRawShift(helper->isolate_, index*sizeof(T));
+  v8::Handle<v8::DeclaredAccessorDescriptor> 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<T*>()[index] = compare_value;
+  VerifyRead(descriptor, internal_field, array, v8::True(helper->isolate_));
+  helper->array_->As<T*>()[index] = compare_value & bitmask;
+  VerifyRead(descriptor, internal_field, array, v8::True(helper->isolate_));
+}
+
+
+TEST(BitmaskCompareRead) {
+  DescriptorTestHelper helper;
+  TestBitmaskCompare<uint8_t>(0xf3, 0xa8, &helper);
+  TestBitmaskCompare<uint16_t>(0xfefe, 0x7d42, &helper);
+  TestBitmaskCompare<uint32_t>(0xfefeab18, 0x1234fdec, &helper);
+}
+
+
+TEST(PointerCompareRead) {
+  DescriptorTestHelper helper;
+  v8::HandleScope handle_scope;
+  int index = 35;
+  int internal_field = 3;
+  void* ptr = helper.isolate_;
+  v8::Handle<v8::DeclaredAccessorDescriptor> 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<uintptr_t*>()[index] = reinterpret_cast<uintptr_t>(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<v8::DeclaredAccessorDescriptor> 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<uintptr_t**>()[first_index] =
+      &array->As<uintptr_t*>()[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<uint16_t*>()[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<v8::DeclaredAccessorDescriptor> descriptor =
+      OOD::NewInternalFieldDereference(helper.isolate_, internal_field)
+      ->NewRawShift(helper.isolate_, index*kPointerSize)
+      ->NewHandleDereference(helper.isolate_);
+  HandleArray* array = *helper.handle_array_;
+  v8::Handle<v8::String> expected = v8_str("whatever");
+  array->handles_[index] = v8::Persistent<v8::Value>::New(expected);
+  VerifyRead(descriptor, internal_field, array, expected);
+}
+