diff options
Diffstat (limited to 'src/v8/0006-Add-custom-object-compare-callback.patch')
-rw-r--r-- | src/v8/0006-Add-custom-object-compare-callback.patch | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/src/v8/0006-Add-custom-object-compare-callback.patch b/src/v8/0006-Add-custom-object-compare-callback.patch new file mode 100644 index 0000000000..f024009d41 --- /dev/null +++ b/src/v8/0006-Add-custom-object-compare-callback.patch @@ -0,0 +1,548 @@ +From 1c747846df3be9021aff2b0fc402cdd3a239e86b Mon Sep 17 00:00:00 2001 +From: Aaron Kennedy <aaron.kennedy@nokia.com> +Date: Wed, 25 May 2011 10:36:13 +1000 +Subject: [PATCH 06/11] 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 | 22 ++++++++++++++++++++++ + src/arm/code-stubs-arm.cc | 43 +++++++++++++++++++++++++++++++++++++++++-- + src/factory.cc | 8 ++++++++ + src/ia32/code-stubs-ia32.cc | 39 +++++++++++++++++++++++++++++++++++++++ + src/isolate.cc | 7 +++++++ + src/isolate.h | 8 ++++++++ + src/objects-inl.h | 21 ++++++++++++++++++--- + src/objects.cc | 8 ++++---- + src/objects.h | 12 ++++++++++-- + src/runtime.cc | 23 +++++++++++++++++++++++ + src/runtime.h | 1 + + src/x64/code-stubs-x64.cc | 37 +++++++++++++++++++++++++++++++++++++ + 13 files changed, 231 insertions(+), 11 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index c094d08..6baf2b2 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -2501,6 +2501,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); +@@ -2720,6 +2726,10 @@ typedef void (*FailedAccessCheckCallback)(Local<Object> target, + AccessType type, + Local<Value> data); + ++// --- User Object Comparisoa nCallback --- ++typedef bool (*UserObjectComparisonCallback)(Local<Object> lhs, ++ Local<Object> rhs); ++ + // --- AllowCodeGenerationFromStrings callbacks --- + + /** +@@ -3046,6 +3056,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 526aa02..6f6297d 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -1464,6 +1464,17 @@ 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 --- + + +@@ -5106,6 +5117,17 @@ 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 00ac676..a67b3a0 100644 +--- a/src/arm/code-stubs-arm.cc ++++ b/src/arm/code-stubs-arm.cc +@@ -1494,6 +1494,37 @@ 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, ¬_user_equal); ++ ++ __ CompareObjectType(r0, r2, r4, JS_OBJECT_TYPE); ++ __ b(ne, ¬_user_equal); ++ ++ __ CompareObjectType(r1, r3, r4, JS_OBJECT_TYPE); ++ __ b(ne, ¬_user_equal); ++ ++ __ ldrb(r2, FieldMemOperand(r2, Map::kBitField2Offset)); ++ __ and_(r2, r2, Operand(1 << Map::kUseUserObjectComparison)); ++ __ cmp(r2, Operand(1 << Map::kUseUserObjectComparison)); ++ __ b(eq, &user_equal); ++ ++ __ ldrb(r3, FieldMemOperand(r3, Map::kBitField2Offset)); ++ __ and_(r3, r3, Operand(1 << Map::kUseUserObjectComparison)); ++ __ cmp(r3, Operand(1 << Map::kUseUserObjectComparison)); ++ __ b(ne, ¬_user_equal); ++ ++ __ bind(&user_equal); ++ ++ __ Push(r0, r1); ++ __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1); ++ ++ __ bind(¬_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_); +@@ -6544,10 +6575,18 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { + __ and_(r2, r1, Operand(r0)); + __ JumpIfSmi(r2, &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::kBitField2Offset)); ++ __ 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::kBitField2Offset)); ++ __ 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 e3cccae..96d3458 100644 +--- a/src/factory.cc ++++ b/src/factory.cc +@@ -1120,6 +1120,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 = +@@ -1129,6 +1130,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; +@@ -1173,6 +1176,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 6cc80d3..cabf78f 100644 +--- a/src/ia32/code-stubs-ia32.cc ++++ b/src/ia32/code-stubs-ia32.cc +@@ -3931,6 +3931,39 @@ 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; ++ __ test(eax, Immediate(kSmiTagMask)); ++ __ j(zero, ¬_user_equal); ++ __ test(edx, Immediate(kSmiTagMask)); ++ __ j(zero, ¬_user_equal); ++ ++ __ CmpObjectType(eax, JS_OBJECT_TYPE, ebx); ++ __ j(not_equal, ¬_user_equal); ++ ++ __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx); ++ __ j(not_equal, ¬_user_equal); ++ ++ __ test_b(FieldOperand(ebx, Map::kBitField2Offset), ++ 1 << Map::kUseUserObjectComparison); ++ __ j(not_zero, &user_equal); ++ __ test_b(FieldOperand(ecx, Map::kBitField2Offset), ++ 1 << Map::kUseUserObjectComparison); ++ __ j(not_zero, &user_equal); ++ ++ __ jmp(¬_user_equal); ++ ++ __ bind(&user_equal); ++ ++ __ pop(ebx); // Return address. ++ __ push(eax); ++ __ push(edx); ++ __ push(ebx); ++ __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1); ++ ++ __ bind(¬_user_equal); ++ } ++ + // Identical objects can be compared fast, but there are some tricky cases + // for NaN and undefined. + { +@@ -6409,8 +6442,14 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { + + __ CmpObjectType(eax, JS_OBJECT_TYPE, ecx); + __ j(not_equal, &miss, Label::kNear); ++ __ test_b(FieldOperand(ecx, Map::kBitField2Offset), ++ 1 << Map::kUseUserObjectComparison); ++ __ j(not_zero, &miss, Label::kNear); + __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx); + __ j(not_equal, &miss, Label::kNear); ++ __ test_b(FieldOperand(ecx, Map::kBitField2Offset), ++ 1 << Map::kUseUserObjectComparison); ++ __ j(not_zero, &miss, Label::kNear); + + ASSERT(GetCondition() == equal); + __ sub(eax, edx); +diff --git a/src/isolate.cc b/src/isolate.cc +index 951f428..6a86bf1 100644 +--- a/src/isolate.cc ++++ b/src/isolate.cc +@@ -96,6 +96,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; + } +@@ -717,6 +718,12 @@ 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/isolate.h b/src/isolate.h +index 01ab04e..06ef22d 100644 +--- a/src/isolate.h ++++ b/src/isolate.h +@@ -255,6 +255,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_; ++ + // Whether out of memory exceptions should be ignored. + bool ignore_out_of_memory_; + +@@ -701,6 +704,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 375df0f..4657482 100644 +--- a/src/objects-inl.h ++++ b/src/objects-inl.h +@@ -2859,14 +2859,14 @@ bool Map::is_extensible() { + + void Map::set_attached_to_shared_function_info(bool value) { + if (value) { +- set_bit_field2(bit_field2() | (1 << kAttachedToSharedFunctionInfo)); ++ set_bit_field3(bit_field3() | (1 << kAttachedToSharedFunctionInfo)); + } else { +- set_bit_field2(bit_field2() & ~(1 << kAttachedToSharedFunctionInfo)); ++ set_bit_field3(bit_field3() & ~(1 << kAttachedToSharedFunctionInfo)); + } + } + + bool Map::attached_to_shared_function_info() { +- return ((1 << kAttachedToSharedFunctionInfo) & bit_field2()) != 0; ++ return ((1 << kAttachedToSharedFunctionInfo) & bit_field3()) != 0; + } + + +@@ -2896,6 +2896,19 @@ bool Map::has_external_resource() + } + + ++void Map::set_use_user_object_comparison(bool value) { ++ if (value) { ++ set_bit_field2(bit_field2() | (1 << kUseUserObjectComparison)); ++ } else { ++ set_bit_field2(bit_field2() & ~(1 << kUseUserObjectComparison)); ++ } ++} ++ ++bool Map::use_user_object_comparison() { ++ return ((1 << kUseUserObjectComparison) & bit_field2()) != 0; ++} ++ ++ + void Map::set_named_interceptor_is_fallback(bool value) + { + if (value) { +@@ -3429,6 +3442,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.cc b/src/objects.cc +index 8e1773f..28e6d79 100644 +--- a/src/objects.cc ++++ b/src/objects.cc +@@ -7159,8 +7159,8 @@ void SharedFunctionInfo::DetachInitialMap() { + Map* map = reinterpret_cast<Map*>(initial_map()); + + // Make the map remember to restore the link if it survives the GC. +- map->set_bit_field2( +- map->bit_field2() | (1 << Map::kAttachedToSharedFunctionInfo)); ++ map->set_bit_field3( ++ map->bit_field3() | (1 << Map::kAttachedToSharedFunctionInfo)); + + // Undo state changes made by StartInobjectTracking (except the + // construction_count). This way if the initial map does not survive the GC +@@ -7180,8 +7180,8 @@ void SharedFunctionInfo::DetachInitialMap() { + + // Called from GC, hence reinterpret_cast and unchecked accessors. + void SharedFunctionInfo::AttachInitialMap(Map* map) { +- map->set_bit_field2( +- map->bit_field2() & ~(1 << Map::kAttachedToSharedFunctionInfo)); ++ map->set_bit_field3( ++ map->bit_field3() & ~(1 << Map::kAttachedToSharedFunctionInfo)); + + // Resume inobject slack tracking. + set_initial_map(map); +diff --git a/src/objects.h b/src/objects.h +index 71895be..881c24e 100644 +--- a/src/objects.h ++++ b/src/objects.h +@@ -4084,6 +4084,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(); ++ + // [prototype]: implicit prototype object. + DECL_ACCESSORS(prototype, Object) + +@@ -4303,7 +4308,7 @@ class Map: public HeapObject { + static const int kIsExtensible = 0; + static const int kFunctionWithPrototype = 1; + static const int kStringWrapperSafeForDefaultValueOf = 2; +- static const int kAttachedToSharedFunctionInfo = 3; ++ static const int kUseUserObjectComparison = 3; + // No bits can be used after kElementsKindFirstBit, they are all reserved for + // storing ElementKind. + static const int kElementsKindShift = 4; +@@ -4322,6 +4327,7 @@ class Map: public HeapObject { + static const int kIsShared = 0; + static const int kNamedInterceptorIsFallback = 1; + static const int kHasInstanceCallHandler = 2; ++ static const int kAttachedToSharedFunctionInfo = 3; + + // Layout of the default cache. It holds alternating name and code objects. + static const int kCodeCacheEntrySize = 2; +@@ -7306,6 +7312,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); + +@@ -7323,7 +7330,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 0388a77..24b3de0 100644 +--- a/src/runtime.cc ++++ b/src/runtime.cc +@@ -7008,6 +7008,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 284f723..5d6bf5e 100644 +--- a/src/runtime.h ++++ b/src/runtime.h +@@ -157,6 +157,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/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc +index 6ab12fc..3be2c4b 100644 +--- a/src/x64/code-stubs-x64.cc ++++ b/src/x64/code-stubs-x64.cc +@@ -3005,6 +3005,37 @@ 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; ++ __ JumpIfSmi(rax, ¬_user_equal); ++ __ JumpIfSmi(rdx, ¬_user_equal); ++ ++ __ CmpObjectType(rax, JS_OBJECT_TYPE, rbx); ++ __ j(not_equal, ¬_user_equal); ++ ++ __ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx); ++ __ j(not_equal, ¬_user_equal); ++ ++ __ testb(FieldOperand(rbx, Map::kBitField2Offset), ++ Immediate(1 << Map::kUseUserObjectComparison)); ++ __ j(not_zero, &user_equal); ++ __ testb(FieldOperand(rcx, Map::kBitField2Offset), ++ Immediate(1 << Map::kUseUserObjectComparison)); ++ __ j(not_zero, &user_equal); ++ ++ __ jmp(¬_user_equal); ++ ++ __ bind(&user_equal); ++ ++ __ pop(rbx); // Return address. ++ __ push(rax); ++ __ push(rdx); ++ __ push(rbx); ++ __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1); ++ ++ __ bind(¬_user_equal); ++ } ++ + // Two identical objects are equal unless they are both NaN or undefined. + { + Label not_identical; +@@ -5337,8 +5368,14 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { + + __ CmpObjectType(rax, JS_OBJECT_TYPE, rcx); + __ j(not_equal, &miss, Label::kNear); ++ __ testb(FieldOperand(rcx, Map::kBitField2Offset), ++ Immediate(1 << Map::kUseUserObjectComparison)); ++ __ j(not_zero, &miss, Label::kNear); + __ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx); + __ j(not_equal, &miss, Label::kNear); ++ __ testb(FieldOperand(rcx, Map::kBitField2Offset), ++ Immediate(1 << Map::kUseUserObjectComparison)); ++ __ j(not_zero, &miss, Label::kNear); + + ASSERT(GetCondition() == equal); + __ subq(rax, rdx); +-- +1.7.4.4 + |