summaryrefslogtreecommitdiffstats
path: root/src/v8/0008-Add-custom-object-compare-callback.patch
blob: d7ef5c434d43f2093de7183030117faf071b5737 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
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