Hi all!

We've found and fixed a bug in the ExternalResource patch.
The problem was in heap-inl.h in the FinalizeExternalString()
function:

if (value->IsSmi()) {
      resource = reinterpret_cast<v8::Object::ExternalResource*>(
          Internals::GetExternalPointerFromSmi(value));
    } else if (value->IsForeign()) {
      resource = reinterpret_cast<v8::Object::ExternalResource*>(
          Foreign::cast(value)->foreign_address());

The last line of the original patch was:

  Foreign::cast(value)->address());

It caused segfault on 64bit systems with ExternalResource objects
with high memory address.
The fixed patch has been attached to this mail. It has been modified
to meet V8's style guide and three tests have been added as well.
Now that the test are working I'm gonna start the upstreaming process
to V8.

Best regards,
Gabor Ballabas

----------------------------------------------------------------
This message was sent using IMP, the Internet Messaging Program.

diff --git a/include/v8.h b/include/v8.h
index 9c8b9c7..2396437 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -1552,6 +1552,25 @@ class Object : public Value {
   /** Sets a native pointer in an internal field. */
   V8EXPORT void SetPointerInInternalField(int index, void* value);
 
+  class V8EXPORT ExternalResource { // NOLINT
+   public:
+    ExternalResource() {}
+    virtual ~ExternalResource() {}
+
+   protected:
+    virtual void Dispose() { delete this; }
+
+   private:
+    // Disallow copying and assigning.
+    ExternalResource(const ExternalResource&);
+    void operator=(const ExternalResource&);
+
+    friend class v8::internal::Heap;
+  };
+
+  V8EXPORT void SetExternalResource(ExternalResource* resource);
+  V8EXPORT ExternalResource* GetExternalResource();
+
   // Testers for local properties.
   V8EXPORT bool HasOwnProperty(Handle<String> key);
   V8EXPORT bool HasRealNamedProperty(Handle<String> key);
@@ -2421,6 +2440,12 @@ class V8EXPORT ObjectTemplate : public Template {
    */
   void SetInternalFieldCount(int value);
 
+  /**
+   * Sets whether the object can store an "external resource" object.
+   */
+  bool HasExternalResource();
+  void SetHasExternalResource(bool value);
+
  private:
   ObjectTemplate();
   static Local<ObjectTemplate> New(Handle<FunctionTemplate> constructor);
diff --git a/src/api.cc b/src/api.cc
index d3a6f0b..7dc29b6 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -1407,6 +1407,33 @@ void ObjectTemplate::SetInternalFieldCount(int value) {
 }
 
 
+bool ObjectTemplate::HasExternalResource() {
+  if (IsDeadCheck(Utils::OpenHandle(this)->GetIsolate(),
+                  "v8::ObjectTemplate::HasExternalResource()")) {
+    return 0;
+  }
+  return !Utils::OpenHandle(this)->has_external_resource()->IsUndefined();
+}
+
+
+void ObjectTemplate::SetHasExternalResource(bool value) {
+  i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+  if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetHasExternalResource()")) {
+    return;
+  }
+  ENTER_V8(isolate);
+  if (value) {
+    EnsureConstructor(this);
+  }
+  if (value) {
+      Utils::OpenHandle(this)->set_has_external_resource(i::Smi::FromInt(1));
+  } else {
+      Utils::OpenHandle(this)->set_has_external_resource(
+          Utils::OpenHandle(this)->GetHeap()->undefined_value());
+  }
+}
+
+
 // --- S c r i p t D a t a ---
 
 
@@ -3966,6 +3993,37 @@ void v8::Object::SetPointerInInternalField(int index, void* value) {
 }
 
 
+void v8::Object::SetExternalResource(v8::Object::ExternalResource* resource) {
+  i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+  ENTER_V8(isolate);
+  i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+  if (CanBeEncodedAsSmi(resource)) {
+    obj->SetExternalResourceObject(EncodeAsSmi(resource));
+  } else {
+    obj->SetExternalResourceObject(*isolate->factory()->NewForeign(
+        static_cast<i::Address>(reinterpret_cast<void*>(resource))));
+  }
+  if (!obj->IsSymbol()) {
+    isolate->heap()->external_string_table()->AddObject(*obj);
+  }
+}
+
+
+v8::Object::ExternalResource* v8::Object::GetExternalResource() {
+  i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+  i::Object* value = obj->GetExternalResourceObject();
+  if (value->IsSmi()) {
+    return reinterpret_cast<v8::Object::ExternalResource*>(
+        i::Internals::GetExternalPointerFromSmi(value));
+  } else if (value->IsForeign()) {
+    return reinterpret_cast<v8::Object::ExternalResource*>(
+        i::Foreign::cast(value)->address());
+  } else {
+    return NULL;
+  }
+}
+
+
 // --- E n v i r o n m e n t ---
 
 
diff --git a/src/factory.cc b/src/factory.cc
index f1042a4..0483099 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -1155,15 +1155,21 @@ Handle<JSFunction> Factory::CreateApiFunction(
   Handle<Code> construct_stub = isolate()->builtins()->JSConstructStubApi();
 
   int internal_field_count = 0;
+  bool has_external_resource = false;
+
   if (!obj->instance_template()->IsUndefined()) {
     Handle<ObjectTemplateInfo> instance_template =
         Handle<ObjectTemplateInfo>(
             ObjectTemplateInfo::cast(obj->instance_template()));
     internal_field_count =
         Smi::cast(instance_template->internal_field_count())->value();
+    has_external_resource =
+        !instance_template->has_external_resource()->IsUndefined();
   }
 
   int instance_size = kPointerSize * internal_field_count;
+  if (has_external_resource) instance_size += kPointerSize;
+
   InstanceType type = INVALID_TYPE;
   switch (instance_type) {
     case JavaScriptObject:
@@ -1198,6 +1204,11 @@ Handle<JSFunction> Factory::CreateApiFunction(
 
   Handle<Map> map = Handle<Map>(result->initial_map());
 
+  // Mark as having external data object if needed
+  if (has_external_resource) {
+    map->set_has_external_resource(true);
+  }
+
   // Mark as undetectable if needed.
   if (obj->undetectable()) {
     map->set_is_undetectable();
diff --git a/src/heap-inl.h b/src/heap-inl.h
index 8977cdb..c111831 100644
--- a/src/heap-inl.h
+++ b/src/heap-inl.h
@@ -247,19 +247,39 @@ MaybeObject* Heap::NumberFromUint32(
 }
 
 
-void Heap::FinalizeExternalString(String* string) {
-  ASSERT(string->IsExternalString());
-  v8::String::ExternalStringResourceBase** resource_addr =
-      reinterpret_cast<v8::String::ExternalStringResourceBase**>(
-          reinterpret_cast<byte*>(string) +
-          ExternalString::kResourceOffset -
-          kHeapObjectTag);
-
-  // Dispose of the C++ object if it has not already been disposed.
-  if (*resource_addr != NULL) {
-    (*resource_addr)->Dispose();
-    *resource_addr = NULL;
-  }
+void Heap::FinalizeExternalString(HeapObject* string) {
+  ASSERT(string->IsExternalString() || string->map()->has_external_resource());
+
+  if (string->IsExternalString()) {
+    v8::String::ExternalStringResourceBase** resource_addr =
+        reinterpret_cast<v8::String::ExternalStringResourceBase**>(
+            reinterpret_cast<byte*>(string) +
+            ExternalString::kResourceOffset -
+            kHeapObjectTag);
+
+    // Dispose of the C++ object if it has not already been disposed.
+    if (*resource_addr != NULL) {
+      (*resource_addr)->Dispose();
+      *resource_addr = NULL;
+    }
+
+  } else {
+    JSObject* object = JSObject::cast(string);
+    Object* value = object->GetExternalResourceObject();
+    v8::Object::ExternalResource *resource = 0;
+
+    if (value->IsSmi()) {
+      resource = reinterpret_cast<v8::Object::ExternalResource*>(
+          Internals::GetExternalPointerFromSmi(value));
+    } else if (value->IsForeign()) {
+      resource = reinterpret_cast<v8::Object::ExternalResource*>(
+          Foreign::cast(value)->foreign_address());
+    }
+
+    if (resource) {
+      resource->Dispose();
+    }
+ }
 }
 
 
@@ -578,6 +598,16 @@ void ExternalStringTable::AddString(String* string) {
 }
 
 
+void ExternalStringTable::AddObject(HeapObject* object) {
+  ASSERT(object->map()->has_external_resource());
+  if (heap_->InNewSpace(object)) {
+    new_space_strings_.Add(object);
+  } else {
+    old_space_strings_.Add(object);
+  }
+}
+
+
 void ExternalStringTable::Iterate(ObjectVisitor* v) {
   if (!new_space_strings_.is_empty()) {
     Object** start = &new_space_strings_[0];
@@ -606,14 +636,14 @@ void ExternalStringTable::Verify() {
 }
 
 
-void ExternalStringTable::AddOldString(String* string) {
-  ASSERT(string->IsExternalString());
-  ASSERT(!heap_->InNewSpace(string));
-  old_space_strings_.Add(string);
+void ExternalStringTable::AddOldObject(HeapObject* object) {
+  ASSERT(object->IsExternalString() || object->map()->has_external_resource());
+  ASSERT(!heap_->InNewSpace(object));
+  old_space_strings_.Add(object);
 }
 
 
-void ExternalStringTable::ShrinkNewStrings(int position) {
+void ExternalStringTable::ShrinkNewObjects(int position) {
   new_space_strings_.Rewind(position);
   if (FLAG_verify_heap) {
     Verify();
diff --git a/src/heap.cc b/src/heap.cc
index 3d6db92..46c92c8 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -1140,18 +1140,19 @@ void Heap::Scavenge() {
 }
 
 
-String* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap,
-                                                                Object** p) {
+HeapObject* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(
+    Heap* heap,
+    Object** p) {
   MapWord first_word = HeapObject::cast(*p)->map_word();
 
   if (!first_word.IsForwardingAddress()) {
     // Unreachable external string can be finalized.
-    heap->FinalizeExternalString(String::cast(*p));
+    heap->FinalizeExternalString(HeapObject::cast(*p));
     return NULL;
   }
 
   // String is still reachable.
-  return String::cast(first_word.ToForwardingAddress());
+  return HeapObject::cast(first_word.ToForwardingAddress());
 }
 
 
@@ -1169,11 +1170,12 @@ void Heap::UpdateNewSpaceReferencesInExternalStringTable(
 
   for (Object** p = start; p < end; ++p) {
     ASSERT(InFromSpace(*p));
-    String* target = updater_func(this, p);
+    HeapObject* target = updater_func(this, p);
 
     if (target == NULL) continue;
 
-    ASSERT(target->IsExternalString());
+    ASSERT(target->IsExternalString() ||
+           target->map()->has_external_resource());
 
     if (InNewSpace(target)) {
       // String is still in new space.  Update the table entry.
@@ -1181,12 +1183,12 @@ void Heap::UpdateNewSpaceReferencesInExternalStringTable(
       ++last;
     } else {
       // String got promoted.  Move it to the old string list.
-      external_string_table_.AddOldString(target);
+      external_string_table_.AddOldObject(target);
     }
   }
 
   ASSERT(last <= end);
-  external_string_table_.ShrinkNewStrings(static_cast<int>(last - start));
+  external_string_table_.ShrinkNewObjects(static_cast<int>(last - start));
 }
 
 
@@ -6401,6 +6403,19 @@ void ExternalStringTable::CleanUp() {
 
 
 void ExternalStringTable::TearDown() {
+  for (int i = 0; i < new_space_strings_.length(); ++i) {
+    if (new_space_strings_[i] == heap_->raw_unchecked_null_value()) continue;
+    HeapObject* object = HeapObject::cast(new_space_strings_[i]);
+    if (!object->IsExternalString())
+        heap_->FinalizeExternalString(object);
+  }
+  for (int i = 0; i < old_space_strings_.length(); ++i) {
+    if (old_space_strings_[i] == heap_->raw_unchecked_null_value()) continue;
+    HeapObject* object = HeapObject::cast(old_space_strings_[i]);
+    if (!object->IsExternalString())
+        heap_->FinalizeExternalString(object);
+  }
+
   new_space_strings_.Free();
   old_space_strings_.Free();
 }
diff --git a/src/heap.h b/src/heap.h
index 9f4f505..3347f91 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -255,8 +255,8 @@ class Isolate;
 class WeakObjectRetainer;
 
 
-typedef String* (*ExternalStringTableUpdaterCallback)(Heap* heap,
-                                                      Object** pointer);
+typedef HeapObject* (*ExternalStringTableUpdaterCallback)(Heap* heap,
+                                                          Object** pointer);
 
 class StoreBufferRebuilder {
  public:
@@ -392,10 +392,14 @@ typedef void (*ScavengingCallback)(Map* map,
 // External strings table is a place where all external strings are
 // registered.  We need to keep track of such strings to properly
 // finalize them.
+// The ExternalStringTable can contain both strings and objects with
+// external resources.  It was not renamed to make the patch simpler.
 class ExternalStringTable {
  public:
   // Registers an external string.
   inline void AddString(String* string);
+  // Registers an external object.
+  inline void AddObject(HeapObject* string);
 
   inline void Iterate(ObjectVisitor* v);
 
@@ -413,10 +417,10 @@ class ExternalStringTable {
 
   inline void Verify();
 
-  inline void AddOldString(String* string);
+  inline void AddOldObject(HeapObject* string);
 
   // Notifies the table that only a prefix of the new list is valid.
-  inline void ShrinkNewStrings(int position);
+  inline void ShrinkNewObjects(int position);
 
   // To speed up scavenge collections new space string are kept
   // separate from old space strings.
@@ -914,7 +918,7 @@ class Heap {
 
   // Finalizes an external string by deleting the associated external
   // data and clearing the resource pointer.
-  inline void FinalizeExternalString(String* string);
+  inline void FinalizeExternalString(HeapObject* string);
 
   // Allocates an uninitialized object.  The memory is non-executable if the
   // hardware and OS allow.
@@ -1721,7 +1725,7 @@ class Heap {
   // Performs a minor collection in new generation.
   void Scavenge();
 
-  static String* UpdateNewSpaceReferenceInExternalStringTableEntry(
+  static HeapObject* UpdateNewSpaceReferenceInExternalStringTableEntry(
       Heap* heap,
       Object** pointer);
 
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index 74c0777..02ade60 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -1515,8 +1515,10 @@ class SymbolTableCleaner : public ObjectVisitor {
 
         // Since no objects have yet been moved we can safely access the map of
         // the object.
-        if (o->IsExternalString()) {
-          heap_->FinalizeExternalString(String::cast(*p));
+        if (o->IsExternalString() ||
+            (o->IsHeapObject() &&
+             HeapObject::cast(o)->map()->has_external_resource())) {
+          heap_->FinalizeExternalString(HeapObject::cast(*p));
         }
         // Set the entry to the_hole_value (as deleted).
         *p = heap_->the_hole_value();
@@ -2488,15 +2490,15 @@ static void UpdatePointer(HeapObject** p, HeapObject* object) {
 }
 
 
-static String* UpdateReferenceInExternalStringTableEntry(Heap* heap,
-                                                         Object** p) {
+static HeapObject* UpdateReferenceInExternalStringTableEntry(Heap* heap,
+                                                             Object** p) {
   MapWord map_word = HeapObject::cast(*p)->map_word();
 
   if (map_word.IsForwardingAddress()) {
-    return String::cast(map_word.ToForwardingAddress());
+    return HeapObject::cast(map_word.ToForwardingAddress());
   }
 
-  return String::cast(*p);
+  return HeapObject::cast(*p);
 }
 
 
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 8b5f1d0..5154cd0 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -1355,7 +1355,8 @@ int JSObject::GetInternalFieldCount() {
   // Make sure to adjust for the number of in-object properties. These
   // properties do contribute to the size, but are not internal fields.
   return ((Size() - GetHeaderSize()) >> kPointerSizeLog2) -
-         map()->inobject_properties();
+         map()->inobject_properties() -
+            (map()->has_external_resource() ? 1 : 0);
 }
 
 
@@ -1374,6 +1375,24 @@ Object* JSObject::GetInternalField(int index) {
 }
 
 
+void JSObject::SetExternalResourceObject(Object* value) {
+  ASSERT(map()->has_external_resource());
+  int offset = GetHeaderSize() + kPointerSize * GetInternalFieldCount();
+  WRITE_FIELD(this, offset, value);
+  WRITE_BARRIER(GetHeap(), this, offset, value);
+}
+
+
+Object *JSObject::GetExternalResourceObject() {
+  if (map()->has_external_resource()) {
+    return READ_FIELD(this, GetHeaderSize() +
+        kPointerSize * GetInternalFieldCount());
+  } else {
+    return GetHeap()->undefined_value();
+  }
+}
+
+
 void JSObject::SetInternalField(int index, Object* value) {
   ASSERT(index < GetInternalFieldCount() && index >= 0);
   // Internal objects do follow immediately after the header, whereas in-object
@@ -2828,6 +2847,19 @@ bool Map::is_shared() {
 }
 
 
+void Map::set_has_external_resource(bool value) {
+  if (value) {
+    set_bit_field(bit_field() | (1 << kHasExternalResource));
+  } else {
+    set_bit_field(bit_field() & ~(1 << kHasExternalResource));
+  }
+}
+
+bool Map::has_external_resource() {
+    return ((1 << kHasExternalResource) & bit_field()) != 0;
+}
+
+
 JSFunction* Map::unchecked_constructor() {
   return reinterpret_cast<JSFunction*>(READ_FIELD(this, kConstructorOffset));
 }
@@ -3358,6 +3390,8 @@ ACCESSORS(FunctionTemplateInfo, flag, Smi, kFlagOffset)
 ACCESSORS(ObjectTemplateInfo, constructor, Object, kConstructorOffset)
 ACCESSORS(ObjectTemplateInfo, internal_field_count, Object,
           kInternalFieldCountOffset)
+ACCESSORS(ObjectTemplateInfo, has_external_resource, Object,
+          kHasExternalResourceOffset)
 
 ACCESSORS(SignatureInfo, receiver, Object, kReceiverOffset)
 ACCESSORS(SignatureInfo, args, Object, kArgsOffset)
diff --git a/src/objects.h b/src/objects.h
index 5267c0b..5573478 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -1734,6 +1734,9 @@ class JSObject: public JSReceiver {
   inline void SetInternalField(int index, Object* value);
   inline void SetInternalField(int index, Smi* value);
 
+  inline void SetExternalResourceObject(Object* value);
+  inline Object *GetExternalResourceObject();
+
   // The following lookup functions skip interceptors.
   void LocalLookupRealNamedProperty(String* name, LookupResult* result);
   void LookupRealNamedProperty(String* name, LookupResult* result);
@@ -4282,11 +4285,11 @@ class Map: public HeapObject {
 
   // Tells whether the instance has a call-as-function handler.
   inline void set_has_instance_call_handler() {
-    set_bit_field(bit_field() | (1 << kHasInstanceCallHandler));
+    set_bit_field3(bit_field3() | (1 << kHasInstanceCallHandler));
   }
 
   inline bool has_instance_call_handler() {
-    return ((1 << kHasInstanceCallHandler) & bit_field()) != 0;
+    return ((1 << kHasInstanceCallHandler) & bit_field3()) != 0;
   }
 
   inline void set_is_extensible(bool value);
@@ -4354,6 +4357,11 @@ class Map: public HeapObject {
   inline void set_is_access_check_needed(bool access_check_needed);
   inline bool is_access_check_needed();
 
+  // Tells whether the instance has the space for an external resource
+  // object
+  inline void set_has_external_resource(bool value);
+  inline bool has_external_resource();
+
   // [prototype]: implicit prototype object.
   DECL_ACCESSORS(prototype, Object)
 
@@ -4594,7 +4602,7 @@ class Map: public HeapObject {
   static const int kHasNamedInterceptor = 3;
   static const int kHasIndexedInterceptor = 4;
   static const int kIsUndetectable = 5;
-  static const int kHasInstanceCallHandler = 6;
+  static const int kHasExternalResource = 6;
   static const int kIsAccessCheckNeeded = 7;
 
   // Bit positions for bit field 2
@@ -4618,6 +4626,7 @@ class Map: public HeapObject {
 
   // Bit positions for bit field 3
   static const int kIsShared = 0;
+  static const int kHasInstanceCallHandler = 2;
 
   // Layout of the default cache. It holds alternating name and code objects.
   static const int kCodeCacheEntrySize = 2;
@@ -7677,6 +7686,7 @@ class ObjectTemplateInfo: public TemplateInfo {
  public:
   DECL_ACCESSORS(constructor, Object)
   DECL_ACCESSORS(internal_field_count, Object)
+  DECL_ACCESSORS(has_external_resource, Object)
 
   static inline ObjectTemplateInfo* cast(Object* obj);
 
@@ -7693,7 +7703,9 @@ class ObjectTemplateInfo: public TemplateInfo {
   static const int kConstructorOffset = TemplateInfo::kHeaderSize;
   static const int kInternalFieldCountOffset =
       kConstructorOffset + kPointerSize;
-  static const int kSize = kInternalFieldCountOffset + kPointerSize;
+  static const int kHasExternalResourceOffset =
+      kInternalFieldCountOffset + kPointerSize;
+  static const int kSize = kHasExternalResourceOffset + kPointerSize;
 };
 
 
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index cc20b6f..4db616d 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -710,6 +710,179 @@ TEST(ExternalStringWithDisposeHandling) {
 }
 
 
+class TestPoint {
+public:
+  TestPoint(int x, int y)
+    : x_(x), y_(y) { }
+
+  int32_t x_, y_;
+};
+
+
+class TestPointResource : public v8::Object::ExternalResource {
+public:
+  TestPointResource(TestPoint* tpoint, int* counter = NULL)
+    : tpoint_(tpoint), counter_(counter) {}
+
+  ~TestPointResource() {
+    delete tpoint_;
+    if (counter_ != NULL) ++*counter_;
+  }
+
+  TestPoint* tpoint_;
+  int* counter_;
+};
+
+
+static Handle<Value> GetTestPointX(Local<String>, const AccessorInfo& info) {
+  Local<Object> self = info.Holder();
+  TestPointResource* test_point_resource =
+      static_cast<TestPointResource*>(self->GetExternalResource());
+  CHECK(test_point_resource);
+  return v8::Int32::New(test_point_resource->tpoint_->x_);
+}
+
+
+static Handle<Value> GetTestPointY(Local<String>, const AccessorInfo& info) {
+  Local<Object> self = info.Holder();
+  TestPointResource* test_point_resource =
+      static_cast<TestPointResource*>(self->GetExternalResource());
+  CHECK(test_point_resource);
+  return v8::Int32::New(test_point_resource->tpoint_->y_);
+}
+
+
+THREADED_TEST(ExternalResource) {
+  int dispose_count = 0;
+  TestPoint* test_point = new TestPoint(3, 4);
+  TestPointResource* test_point_resource =
+      new TestPointResource(test_point, &dispose_count);
+  {
+    v8::HandleScope scope;
+    LocalContext env;
+    Local<ObjectTemplate> resource_template = ObjectTemplate::New();
+    resource_template->SetHasExternalResource(true);
+    resource_template->SetAccessor(String::New("x"), GetTestPointX);
+    resource_template->SetAccessor(String::New("y"), GetTestPointY);
+    CHECK(resource_template->HasExternalResource());
+    Local<Object> resource_obj = resource_template->NewInstance();
+    resource_obj->SetExternalResource(test_point_resource);
+    CHECK_EQ(test_point_resource,
+      static_cast<TestPointResource*>(resource_obj->GetExternalResource()));
+    // There was some problem with InternalFieldCount() in an erlier version.
+    CHECK_EQ(0, resource_obj->InternalFieldCount());
+    CHECK_EQ(test_point->x_, resource_obj->Get(String::New("x"))->Int32Value());
+    CHECK_EQ(test_point->y_, resource_obj->Get(String::New("y"))->Int32Value());
+    HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
+    CHECK_EQ(0, dispose_count);
+  }
+  HEAP->CollectAllAvailableGarbage();
+  CHECK_EQ(1, dispose_count);
+}
+
+
+THREADED_TEST(ScavengeExternalResource) {
+  int dispose_count = 0;
+  TestPoint* test_point = new TestPoint(3, 4);
+  TestPointResource* test_point_resource =
+      new TestPointResource(test_point, &dispose_count);
+  bool in_new_space;
+  {
+    v8::HandleScope scope;
+    LocalContext env;
+    Local<ObjectTemplate> resource_template = ObjectTemplate::New();
+    resource_template->SetHasExternalResource(true);
+    resource_template->SetAccessor(String::New("x"), GetTestPointX);
+    resource_template->SetAccessor(String::New("y"), GetTestPointY);
+    CHECK(resource_template->HasExternalResource());
+    Local<Object> resource_obj = resource_template->NewInstance();
+    resource_obj->SetExternalResource(test_point_resource);
+    i::Handle<i::JSObject> iobject = v8::Utils::OpenHandle(*resource_obj);
+    HEAP->CollectGarbage(i::NEW_SPACE);
+    in_new_space = HEAP->InNewSpace(*iobject);
+    CHECK(in_new_space || HEAP->old_data_space()->Contains(*iobject));
+    CHECK_EQ(0, dispose_count);
+  }
+  HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
+  CHECK_EQ(1, dispose_count);
+}
+
+
+class TestPointResourceWithDisposeControl: public TestPointResource {
+ public:
+  // Only used by non-threaded tests, so it can use static fields.
+  static int dispose_calls;
+  static int dispose_count;
+
+  TestPointResourceWithDisposeControl(TestPoint* tpoint, bool dispose)
+      : TestPointResource(tpoint, &dispose_count),
+        dispose_(dispose) { }
+
+  void Dispose() {
+    ++dispose_calls;
+    if (dispose_) delete this;
+  }
+
+ private:
+  bool dispose_;
+};
+
+
+int TestPointResourceWithDisposeControl::dispose_count = 0;
+int TestPointResourceWithDisposeControl::dispose_calls = 0;
+
+
+TEST(ExternalResourceWithDisposeHandling) {
+  TestPoint* test_point_one = new TestPoint(3, 4);
+
+  // Use a stack allocated external resource allocated object.
+  TestPointResourceWithDisposeControl::dispose_count = 0;
+  TestPointResourceWithDisposeControl::dispose_calls = 0;
+  TestPointResourceWithDisposeControl res_stack(test_point_one, false);
+  {
+    v8::HandleScope scope;
+    LocalContext env;
+    Local<ObjectTemplate> resource_template = ObjectTemplate::New();
+    resource_template->SetHasExternalResource(true);
+    resource_template->SetAccessor(String::New("x"), GetTestPointX);
+    resource_template->SetAccessor(String::New("y"), GetTestPointY);
+    CHECK(resource_template->HasExternalResource());
+    Local<Object> resource_obj = resource_template->NewInstance();
+    resource_obj->SetExternalResource(&res_stack);
+
+    HEAP->CollectAllAvailableGarbage();
+    CHECK_EQ(0, TestPointResourceWithDisposeControl::dispose_count);
+  }
+  HEAP->CollectAllAvailableGarbage();
+  CHECK_EQ(1, TestPointResourceWithDisposeControl::dispose_calls);
+  CHECK_EQ(0, TestPointResourceWithDisposeControl::dispose_count);
+
+  // Use a heap allocated external resource allocated object.
+  TestPointResourceWithDisposeControl::dispose_count = 0;
+  TestPointResourceWithDisposeControl::dispose_calls = 0;
+  TestPoint* test_point_two = new TestPoint(5, 6);
+  TestPointResource* res_heap =
+      new TestPointResourceWithDisposeControl(test_point_two, true);
+  {
+    v8::HandleScope scope;
+    LocalContext env;
+    Local<ObjectTemplate> resource_template = ObjectTemplate::New();
+    resource_template->SetHasExternalResource(true);
+    resource_template->SetAccessor(String::New("x"), GetTestPointX);
+    resource_template->SetAccessor(String::New("y"), GetTestPointY);
+    CHECK(resource_template->HasExternalResource());
+    Local<Object> resource_obj = resource_template->NewInstance();
+    resource_obj->SetExternalResource(res_heap);
+
+    HEAP->CollectAllAvailableGarbage();
+    CHECK_EQ(0, TestPointResourceWithDisposeControl::dispose_count);
+  }
+  HEAP->CollectAllAvailableGarbage();
+  CHECK_EQ(1, TestPointResourceWithDisposeControl::dispose_calls);
+  CHECK_EQ(1, TestPointResourceWithDisposeControl::dispose_count);
+}
+
+
 THREADED_TEST(StringConcat) {
   {
     v8::HandleScope scope;
_______________________________________________
Qt-script mailing list
[email protected]
http://lists.qt.nokia.com/mailman/listinfo/qt-script

Reply via email to