summaryrefslogtreecommitdiffstats
path: root/src/v8/0008-Add-custom-object-compare-callback.patch
diff options
context:
space:
mode:
Diffstat (limited to 'src/v8/0008-Add-custom-object-compare-callback.patch')
-rw-r--r--src/v8/0008-Add-custom-object-compare-callback.patch489
1 files changed, 489 insertions, 0 deletions
diff --git a/src/v8/0008-Add-custom-object-compare-callback.patch b/src/v8/0008-Add-custom-object-compare-callback.patch
new file mode 100644
index 0000000000..d7ef5c434d
--- /dev/null
+++ b/src/v8/0008-Add-custom-object-compare-callback.patch
@@ -0,0 +1,489 @@
+From bec11b8b7f89d135e7d9a823ac4fe98c70d017cf Mon Sep 17 00:00:00 2001
+From: Aaron Kennedy <aaron.kennedy@nokia.com>
+Date: Mon, 27 Jun 2011 14:57:28 +1000
+Subject: [PATCH 08/13] Add custom object compare callback
+
+A global custom object comparison callback can be set with:
+ V8::SetUserObjectComparisonCallbackFunction()
+When two JSObjects are compared (== or !=), if either one has
+the MarkAsUseUserObjectComparison() bit set, the custom comparison
+callback is invoked to do the actual comparison.
+
+This is useful when you have "value" objects that you want to
+compare as equal, even though they are actually different JS object
+instances.
+---
+ include/v8.h | 13 +++++++++++++
+ src/api.cc | 19 +++++++++++++++++++
+ src/arm/code-stubs-arm.cc | 42 ++++++++++++++++++++++++++++++++++++++++--
+ src/factory.cc | 8 ++++++++
+ src/ia32/code-stubs-ia32.cc | 40 ++++++++++++++++++++++++++++++++++++++++
+ src/isolate.h | 8 ++++++++
+ src/objects-inl.h | 15 +++++++++++++++
+ src/objects.h | 10 +++++++++-
+ src/runtime.cc | 23 +++++++++++++++++++++++
+ src/runtime.h | 1 +
+ src/top.cc | 5 +++++
+ src/x64/code-stubs-x64.cc | 37 +++++++++++++++++++++++++++++++++++++
+ 12 files changed, 218 insertions(+), 3 deletions(-)
+
+diff --git a/include/v8.h b/include/v8.h
+index 8891dab..d5d6972 100644
+--- a/include/v8.h
++++ b/include/v8.h
+@@ -2365,6 +2365,12 @@ class V8EXPORT ObjectTemplate : public Template {
+ bool HasExternalResource();
+ void SetHasExternalResource(bool value);
+
++ /**
++ * Mark object instances of the template as using the user object
++ * comparison callback.
++ */
++ void MarkAsUseUserObjectComparison();
++
+ private:
+ ObjectTemplate();
+ static Local<ObjectTemplate> New(Handle<FunctionTemplate> constructor);
+@@ -2565,6 +2571,10 @@ typedef void (*FailedAccessCheckCallback)(Local<Object> target,
+ AccessType type,
+ Local<Value> data);
+
++// --- U s e r O b j e c t C o m p a r i s o n C a l l b a c k ---
++typedef bool (*UserObjectComparisonCallback)(Local<Object> lhs,
++ Local<Object> rhs);
++
+ // --- G a r b a g e C o l l e c t i o n C a l l b a c k s
+
+ /**
+@@ -2815,6 +2825,9 @@ class V8EXPORT V8 {
+ /** Callback function for reporting failed access checks.*/
+ static void SetFailedAccessCheckCallbackFunction(FailedAccessCheckCallback);
+
++ /** Callback for user object comparisons */
++ static void SetUserObjectComparisonCallbackFunction(UserObjectComparisonCallback);
++
+ /**
+ * Enables the host application to receive a notification before a
+ * garbage collection. Allocations are not allowed in the
+diff --git a/src/api.cc b/src/api.cc
+index ff74efb..2436031 100644
+--- a/src/api.cc
++++ b/src/api.cc
+@@ -1321,6 +1321,16 @@ void ObjectTemplate::SetHasExternalResource(bool value)
+ }
+ }
+
++void ObjectTemplate::MarkAsUseUserObjectComparison()
++{
++ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
++ if (IsDeadCheck(isolate, "v8::ObjectTemplate::MarkAsUseUserObjectComparison()")) {
++ return;
++ }
++ ENTER_V8(isolate);
++ EnsureConstructor(this);
++ Utils::OpenHandle(this)->set_use_user_object_comparison(i::Smi::FromInt(1));
++}
+
+ // --- S c r i p t D a t a ---
+
+@@ -4632,6 +4642,15 @@ void V8::SetFailedAccessCheckCallbackFunction(
+ isolate->SetFailedAccessCheckCallback(callback);
+ }
+
++void V8::SetUserObjectComparisonCallbackFunction(
++ UserObjectComparisonCallback callback) {
++ i::Isolate* isolate = i::Isolate::Current();
++ if (IsDeadCheck(isolate, "v8::V8::SetUserObjectComparisonCallbackFunction()")) {
++ return;
++ }
++ isolate->SetUserObjectComparisonCallback(callback);
++}
++
+ void V8::AddObjectGroup(Persistent<Value>* objects,
+ size_t length,
+ RetainedObjectInfo* info) {
+diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
+index a2626bf..749c9be 100644
+--- a/src/arm/code-stubs-arm.cc
++++ b/src/arm/code-stubs-arm.cc
+@@ -1563,6 +1563,36 @@ void CompareStub::Generate(MacroAssembler* masm) {
+ // NOTICE! This code is only reached after a smi-fast-case check, so
+ // it is certain that at least one operand isn't a smi.
+
++ {
++ Label not_user_equal, user_equal;
++ __ and_(r2, r1, Operand(r0));
++ __ tst(r2, Operand(kSmiTagMask));
++ __ b(eq, &not_user_equal);
++
++ __ CompareObjectType(r0, r2, r4, JS_OBJECT_TYPE);
++ __ b(ne, &not_user_equal);
++
++ __ CompareObjectType(r1, r3, r4, JS_OBJECT_TYPE);
++ __ b(ne, &not_user_equal);
++
++ __ ldrb(r2, FieldMemOperand(r2, Map::kBitField3Offset));
++ __ and_(r2, r2, Operand(1 << Map::kUseUserObjectComparison));
++ __ cmp(r2, Operand(1 << Map::kUseUserObjectComparison));
++ __ b(eq, &user_equal);
++
++ __ ldrb(r3, FieldMemOperand(r3, Map::kBitField3Offset));
++ __ and_(r3, r3, Operand(1 << Map::kUseUserObjectComparison));
++ __ cmp(r3, Operand(1 << Map::kUseUserObjectComparison));
++ __ b(ne, &not_user_equal);
++
++ __ bind(&user_equal);
++
++ __ Push(r0, r1);
++ __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1);
++
++ __ bind(&not_user_equal);
++ }
++
+ // Handle the case where the objects are identical. Either returns the answer
+ // or goes to slow. Only falls through if the objects were not identical.
+ EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_);
+@@ -5802,10 +5832,18 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
+ __ tst(r2, Operand(kSmiTagMask));
+ __ b(eq, &miss);
+
+- __ CompareObjectType(r0, r2, r2, JS_OBJECT_TYPE);
++ __ CompareObjectType(r0, r2, r3, JS_OBJECT_TYPE);
+ __ b(ne, &miss);
+- __ CompareObjectType(r1, r2, r2, JS_OBJECT_TYPE);
++ __ ldrb(r2, FieldMemOperand(r2, Map::kBitField3Offset));
++ __ and_(r2, r2, Operand(1 << Map::kUseUserObjectComparison));
++ __ cmp(r2, Operand(1 << Map::kUseUserObjectComparison));
++ __ b(eq, &miss);
++ __ CompareObjectType(r1, r2, r3, JS_OBJECT_TYPE);
+ __ b(ne, &miss);
++ __ ldrb(r2, FieldMemOperand(r2, Map::kBitField3Offset));
++ __ and_(r2, r2, Operand(1 << Map::kUseUserObjectComparison));
++ __ cmp(r2, Operand(1 << Map::kUseUserObjectComparison));
++ __ b(eq, &miss);
+
+ ASSERT(GetCondition() == eq);
+ __ sub(r0, r0, Operand(r1));
+diff --git a/src/factory.cc b/src/factory.cc
+index d530a75..6f8c7de 100644
+--- a/src/factory.cc
++++ b/src/factory.cc
+@@ -998,6 +998,7 @@ Handle<JSFunction> Factory::CreateApiFunction(
+
+ int internal_field_count = 0;
+ bool has_external_resource = false;
++ bool use_user_object_comparison = false;
+
+ if (!obj->instance_template()->IsUndefined()) {
+ Handle<ObjectTemplateInfo> instance_template =
+@@ -1007,6 +1008,8 @@ Handle<JSFunction> Factory::CreateApiFunction(
+ Smi::cast(instance_template->internal_field_count())->value();
+ has_external_resource =
+ !instance_template->has_external_resource()->IsUndefined();
++ use_user_object_comparison =
++ !instance_template->use_user_object_comparison()->IsUndefined();
+ }
+
+ int instance_size = kPointerSize * internal_field_count;
+@@ -1051,6 +1054,11 @@ Handle<JSFunction> Factory::CreateApiFunction(
+ map->set_has_external_resource(true);
+ }
+
++ // Mark as using user object comparison if needed
++ if (use_user_object_comparison) {
++ map->set_use_user_object_comparison(true);
++ }
++
+ // Mark as undetectable if needed.
+ if (obj->undetectable()) {
+ map->set_is_undetectable();
+diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc
+index afa599e..0964ab9 100644
+--- a/src/ia32/code-stubs-ia32.cc
++++ b/src/ia32/code-stubs-ia32.cc
+@@ -3447,6 +3447,40 @@ void CompareStub::Generate(MacroAssembler* masm) {
+ __ Assert(not_zero, "Unexpected smi operands.");
+ }
+
++ {
++ NearLabel not_user_equal, user_equal;
++ __ test(eax, Immediate(kSmiTagMask));
++ __ j(zero, &not_user_equal);
++ __ test(edx, Immediate(kSmiTagMask));
++ __ j(zero, &not_user_equal);
++
++ __ CmpObjectType(eax, JS_OBJECT_TYPE, ebx);
++ __ j(not_equal, &not_user_equal);
++
++ __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx);
++ __ j(not_equal, &not_user_equal);
++
++ __ test_b(FieldOperand(ebx, Map::kBitField3Offset),
++ 1 << Map::kUseUserObjectComparison);
++ __ j(not_zero, &user_equal);
++ __ test_b(FieldOperand(ecx, Map::kBitField3Offset),
++ 1 << Map::kUseUserObjectComparison);
++ __ j(not_zero, &user_equal);
++
++ __ jmp(&not_user_equal);
++
++ __ bind(&user_equal);
++
++ __ pop(ebx); // Return address.
++ __ push(eax);
++ __ push(edx);
++ __ push(ebx);
++ __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1);
++
++ __ bind(&not_user_equal);
++ }
++
++
+ // NOTICE! This code is only reached after a smi-fast-case check, so
+ // it is certain that at least one operand isn't a smi.
+
+@@ -5592,8 +5626,14 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
+
+ __ CmpObjectType(eax, JS_OBJECT_TYPE, ecx);
+ __ j(not_equal, &miss, not_taken);
++ __ test_b(FieldOperand(ecx, Map::kBitField3Offset),
++ 1 << Map::kUseUserObjectComparison);
++ __ j(not_zero, &miss);
+ __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx);
+ __ j(not_equal, &miss, not_taken);
++ __ test_b(FieldOperand(ecx, Map::kBitField3Offset),
++ 1 << Map::kUseUserObjectComparison);
++ __ j(not_zero, &miss);
+
+ ASSERT(GetCondition() == equal);
+ __ sub(eax, Operand(edx));
+diff --git a/src/isolate.h b/src/isolate.h
+index 35ffcb4..8130397 100644
+--- a/src/isolate.h
++++ b/src/isolate.h
+@@ -267,6 +267,9 @@ class ThreadLocalTop BASE_EMBEDDED {
+ // Call back function to report unsafe JS accesses.
+ v8::FailedAccessCheckCallback failed_access_check_callback_;
+
++ // Call back function for user object comparisons
++ v8::UserObjectComparisonCallback user_object_comparison_callback_;
++
+ private:
+ void InitializeInternal();
+
+@@ -699,6 +702,11 @@ class Isolate {
+ void SetFailedAccessCheckCallback(v8::FailedAccessCheckCallback callback);
+ void ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type);
+
++ void SetUserObjectComparisonCallback(v8::UserObjectComparisonCallback callback);
++ inline v8::UserObjectComparisonCallback UserObjectComparisonCallback() {
++ return thread_local_top()->user_object_comparison_callback_;
++ }
++
+ // Exception throwing support. The caller should use the result
+ // of Throw() as its return value.
+ Failure* Throw(Object* exception, MessageLocation* location = NULL);
+diff --git a/src/objects-inl.h b/src/objects-inl.h
+index 1c7f83e..1765441 100644
+--- a/src/objects-inl.h
++++ b/src/objects-inl.h
+@@ -2552,6 +2552,19 @@ bool Map::has_external_resource()
+ }
+
+
++void Map::set_use_user_object_comparison(bool value) {
++ if (value) {
++ set_bit_field3(bit_field3() | (1 << kUseUserObjectComparison));
++ } else {
++ set_bit_field3(bit_field3() & ~(1 << kUseUserObjectComparison));
++ }
++}
++
++bool Map::use_user_object_comparison() {
++ return ((1 << kUseUserObjectComparison) & bit_field3()) != 0;
++}
++
++
+ void Map::set_named_interceptor_is_fallback(bool value)
+ {
+ if (value) {
+@@ -3050,6 +3063,8 @@ ACCESSORS(ObjectTemplateInfo, internal_field_count, Object,
+ kInternalFieldCountOffset)
+ ACCESSORS(ObjectTemplateInfo, has_external_resource, Object,
+ kHasExternalResourceOffset)
++ACCESSORS(ObjectTemplateInfo, use_user_object_comparison, Object,
++ kUseUserObjectComparisonOffset)
+
+ ACCESSORS(SignatureInfo, receiver, Object, kReceiverOffset)
+ ACCESSORS(SignatureInfo, args, Object, kArgsOffset)
+diff --git a/src/objects.h b/src/objects.h
+index edbc47a..e75e9f1 100644
+--- a/src/objects.h
++++ b/src/objects.h
+@@ -3724,6 +3724,11 @@ class Map: public HeapObject {
+ inline void set_has_external_resource(bool value);
+ inline bool has_external_resource();
+
++
++ // Tells whether the user object comparison callback should be used for
++ // comparisons involving this object
++ inline void set_use_user_object_comparison(bool value);
++ inline bool use_user_object_comparison();
+
+ // Whether the named interceptor is a fallback interceptor or not
+ inline void set_named_interceptor_is_fallback(bool value);
+@@ -3922,6 +3927,7 @@ class Map: public HeapObject {
+ // Bit positions for bit field 3
+ static const int kNamedInterceptorIsFallback = 0;
+ static const int kHasExternalResource = 1;
++ static const int kUseUserObjectComparison = 2;
+
+ // Layout of the default cache. It holds alternating name and code objects.
+ static const int kCodeCacheEntrySize = 2;
+@@ -6442,6 +6448,7 @@ class ObjectTemplateInfo: public TemplateInfo {
+ DECL_ACCESSORS(constructor, Object)
+ DECL_ACCESSORS(internal_field_count, Object)
+ DECL_ACCESSORS(has_external_resource, Object)
++ DECL_ACCESSORS(use_user_object_comparison, Object)
+
+ static inline ObjectTemplateInfo* cast(Object* obj);
+
+@@ -6459,7 +6466,8 @@ class ObjectTemplateInfo: public TemplateInfo {
+ static const int kInternalFieldCountOffset =
+ kConstructorOffset + kPointerSize;
+ static const int kHasExternalResourceOffset = kInternalFieldCountOffset + kPointerSize;
+- static const int kSize = kHasExternalResourceOffset + kPointerSize;
++ static const int kUseUserObjectComparisonOffset = kHasExternalResourceOffset + kPointerSize;
++ static const int kSize = kUseUserObjectComparisonOffset + kPointerSize;
+ };
+
+
+diff --git a/src/runtime.cc b/src/runtime.cc
+index 827d954..d552ddb 100644
+--- a/src/runtime.cc
++++ b/src/runtime.cc
+@@ -6279,6 +6279,29 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringEquals) {
+ }
+
+
++RUNTIME_FUNCTION(MaybeObject*, Runtime_UserObjectEquals) {
++ NoHandleAllocation ha;
++ ASSERT(args.length() == 2);
++
++ CONVERT_CHECKED(JSObject, lhs, args[1]);
++ CONVERT_CHECKED(JSObject, rhs, args[0]);
++
++ bool result;
++
++ v8::UserObjectComparisonCallback callback = isolate->UserObjectComparisonCallback();
++ if (callback) {
++ HandleScope scope(isolate);
++ Handle<JSObject> lhs_handle(lhs);
++ Handle<JSObject> rhs_handle(rhs);
++ result = callback(v8::Utils::ToLocal(lhs_handle), v8::Utils::ToLocal(rhs_handle));
++ } else {
++ result = (lhs == rhs);
++ }
++
++ return Smi::FromInt(result?0:1);
++}
++
++
+ RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberCompare) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 3);
+diff --git a/src/runtime.h b/src/runtime.h
+index 5e97173..0d754f9 100644
+--- a/src/runtime.h
++++ b/src/runtime.h
+@@ -146,6 +146,7 @@ namespace internal {
+ /* Comparisons */ \
+ F(NumberEquals, 2, 1) \
+ F(StringEquals, 2, 1) \
++ F(UserObjectEquals, 2, 1) \
+ \
+ F(NumberCompare, 3, 1) \
+ F(SmiLexicographicCompare, 2, 1) \
+diff --git a/src/top.cc b/src/top.cc
+index e078ee9..c345383 100644
+--- a/src/top.cc
++++ b/src/top.cc
+@@ -68,6 +68,7 @@ void ThreadLocalTop::InitializeInternal() {
+ thread_id_ = ThreadId::Invalid();
+ external_caught_exception_ = false;
+ failed_access_check_callback_ = NULL;
++ user_object_comparison_callback_ = NULL;
+ save_context_ = NULL;
+ catcher_ = NULL;
+ }
+@@ -387,6 +388,10 @@ void Isolate::SetFailedAccessCheckCallback(
+ thread_local_top()->failed_access_check_callback_ = callback;
+ }
+
++void Isolate::SetUserObjectComparisonCallback(
++ v8::UserObjectComparisonCallback callback) {
++ thread_local_top()->user_object_comparison_callback_ = callback;
++}
+
+ void Isolate::ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type) {
+ if (!thread_local_top()->failed_access_check_callback_) return;
+diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
+index d923494..10b9b56 100644
+--- a/src/x64/code-stubs-x64.cc
++++ b/src/x64/code-stubs-x64.cc
+@@ -2443,6 +2443,37 @@ void CompareStub::Generate(MacroAssembler* masm) {
+ __ bind(&ok);
+ }
+
++ {
++ NearLabel not_user_equal, user_equal;
++ __ JumpIfSmi(rax, &not_user_equal);
++ __ JumpIfSmi(rdx, &not_user_equal);
++
++ __ CmpObjectType(rax, JS_OBJECT_TYPE, rbx);
++ __ j(not_equal, &not_user_equal);
++
++ __ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx);
++ __ j(not_equal, &not_user_equal);
++
++ __ testb(FieldOperand(rbx, Map::kBitField3Offset),
++ Immediate(1 << Map::kUseUserObjectComparison));
++ __ j(not_zero, &user_equal);
++ __ testb(FieldOperand(rcx, Map::kBitField3Offset),
++ Immediate(1 << Map::kUseUserObjectComparison));
++ __ j(not_zero, &user_equal);
++
++ __ jmp(&not_user_equal);
++
++ __ bind(&user_equal);
++
++ __ pop(rbx); // Return address.
++ __ push(rax);
++ __ push(rdx);
++ __ push(rbx);
++ __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1);
++
++ __ bind(&not_user_equal);
++ }
++
+ // The compare stub returns a positive, negative, or zero 64-bit integer
+ // value in rax, corresponding to result of comparing the two inputs.
+ // NOTICE! This code is only reached after a smi-fast-case check, so
+@@ -4471,8 +4502,14 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
+
+ __ CmpObjectType(rax, JS_OBJECT_TYPE, rcx);
+ __ j(not_equal, &miss, not_taken);
++ __ testb(FieldOperand(rcx, Map::kBitField3Offset),
++ Immediate(1 << Map::kUseUserObjectComparison));
++ __ j(not_zero, &miss);
+ __ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx);
+ __ j(not_equal, &miss, not_taken);
++ __ testb(FieldOperand(rcx, Map::kBitField3Offset),
++ Immediate(1 << Map::kUseUserObjectComparison));
++ __ j(not_zero, &miss);
+
+ ASSERT(GetCondition() == equal);
+ __ subq(rax, rdx);
+--
+1.7.2.3
+