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);
AccessType type,
Local<Value> data);
+// --- User Object Comparisoa nCallback ---
+typedef bool (*UserObjectComparisonCallback)(Local<Object> lhs,
+ Local<Object> rhs);
+
// --- AllowCodeGenerationFromStrings callbacks ---
/**
/** 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
}
+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 ---
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) {
// 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_);
__ 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));
__ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
__ cmp(r2, Operand(known_map_));
__ b(ne, &miss);
+ __ ldrb(r2, FieldMemOperand(r0, Map::kBitField2Offset));
+ __ and_(r2, r2, Operand(1 << Map::kUseUserObjectComparison));
+ __ cmp(r2, Operand(1 << Map::kUseUserObjectComparison));
+ __ b(eq, &miss);
__ cmp(r3, Operand(known_map_));
__ b(ne, &miss);
+ __ ldrb(r3, FieldMemOperand(r1, Map::kBitField2Offset));
+ __ and_(r3, r2, Operand(1 << Map::kUseUserObjectComparison));
+ __ cmp(r3, Operand(1 << Map::kUseUserObjectComparison));
+ __ b(eq, &miss);
__ sub(r0, r0, Operand(r1));
__ Ret();
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 =
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;
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();
// 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.
{
__ 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);
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
__ cmp(ecx, known_map_);
__ j(not_equal, &miss, Label::kNear);
+ __ test_b(FieldOperand(ecx, Map::kBitField2Offset),
+ 1 << Map::kUseUserObjectComparison);
+ __ j(not_zero, &miss, Label::kNear);
__ cmp(ebx, known_map_);
__ j(not_equal, &miss, Label::kNear);
+ __ test_b(FieldOperand(ebx, Map::kBitField2Offset),
+ 1 << Map::kUseUserObjectComparison);
+ __ j(not_zero, &miss, Label::kNear);
__ sub(eax, edx);
__ ret(0);
thread_id_ = ThreadId::Invalid();
external_caught_exception_ = false;
failed_access_check_callback_ = NULL;
+ user_object_comparison_callback_ = NULL;
save_context_ = NULL;
catcher_ = NULL;
top_lookup_result_ = NULL;
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;
// Head of the list of live LookupResults.
LookupResult* top_lookup_result_;
+ // 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_;
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);
// 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.
+ {
+ // This is optimized for reading the code and not benchmarked for
+ // speed or amount of instructions. The code is not ordered for speed
+ // or anything like this
+ Label miss, user_compare;
+
+ // No global compare if both operands are SMIs
+ __ And(a2, a1, Operand(a0));
+ __ JumpIfSmi(a2, &miss);
+
+
+ // We need to check if lhs and rhs are both objects, if not we are
+ // jumping out of the function. We will keep the 'map' in t0 (lhs) and
+ // t1 (rhs) for later usage.
+ __ GetObjectType(a0, t0, a3);
+ __ Branch(&miss, ne, a3, Operand(JS_OBJECT_TYPE));
+
+ __ GetObjectType(a1, t1, a3);
+ __ Branch(&miss, ne, a3, Operand(JS_OBJECT_TYPE));
+
+ // Check if the UseUserComparison flag is set by using the map of t0 for lhs
+ __ lbu(t0, FieldMemOperand(t0, Map::kBitField2Offset));
+ __ And(t0, t0, Operand(1 << Map::kUseUserObjectComparison));
+ __ Branch(&user_compare, eq, t0, Operand(1 << Map::kUseUserObjectComparison));
+
+
+ // Check if the UseUserComparison flag is _not_ set by using the map of t1 for
+ // rhs and then jump to the miss label.
+ __ lbu(t1, FieldMemOperand(t1, Map::kBitField2Offset));
+ __ And(t1, t1, Operand(1 << Map::kUseUserObjectComparison));
+ __ Branch(&miss, ne, t1, Operand(1 << Map::kUseUserObjectComparison));
+
+ // Invoke the runtime function here
+ __ bind(&user_compare);
+ __ Push(a0, a1);
+ __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1);
+
+ // We exit here without doing anything
+ __ bind(&miss);
+ }
// 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.
__ And(a2, a1, Operand(a0));
__ JumpIfSmi(a2, &miss);
- __ GetObjectType(a0, a2, a2);
- __ Branch(&miss, ne, a2, Operand(JS_OBJECT_TYPE));
- __ GetObjectType(a1, a2, a2);
- __ Branch(&miss, ne, a2, Operand(JS_OBJECT_TYPE));
+ // Compare lhs, a2 holds the map, a3 holds the type_reg
+ __ GetObjectType(a0, a2, a3);
+ __ Branch(&miss, ne, a3, Operand(JS_OBJECT_TYPE));
+ __ lbu(a2, FieldMemOperand(a2, Map::kBitField2Offset));
+ __ And(a2, a2, Operand(1 << Map::kUseUserObjectComparison));
+ __ Branch(&miss, eq, a2, Operand(1 << Map::kUseUserObjectComparison));
+
+
+ // Compare rhs, a2 holds the map, a3 holds the type_reg
+ __ GetObjectType(a1, a2, a3);
+ __ Branch(&miss, ne, a3, Operand(JS_OBJECT_TYPE));
+ __ lbu(a2, FieldMemOperand(a2, Map::kBitField2Offset));
+ __ And(a2, a2, Operand(1 << Map::kUseUserObjectComparison));
+ __ Branch(&miss, eq, a2, Operand(1 << Map::kUseUserObjectComparison));
ASSERT(GetCondition() == eq);
__ Ret(USE_DELAY_SLOT);
__ lw(a2, FieldMemOperand(a0, HeapObject::kMapOffset));
__ lw(a3, FieldMemOperand(a1, HeapObject::kMapOffset));
__ Branch(&miss, ne, a2, Operand(known_map_));
+ __ lbu(a2, FieldMemOperand(a0, Map::kBitField2Offset));
+ __ And(a2, a2, Operand(1 << Map::kUseUserObjectComparison));
+ __ Branch(&miss, eq, a2, Operand(1 << Map::kUseUserObjectComparison));
__ Branch(&miss, ne, a3, Operand(known_map_));
+ __ lbu(a3, FieldMemOperand(a1, Map::kBitField2Offset));
+ __ And(a3, a3, Operand(1 << Map::kUseUserObjectComparison));
+ __ Branch(&miss, eq, a3, Operand(1 << Map::kUseUserObjectComparison));
__ Ret(USE_DELAY_SLOT);
__ subu(v0, a0, a1);
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;
}
}
+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) {
set_bit_field3(bit_field3() | (1 << kNamedInterceptorIsFallback));
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)
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
// 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);
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)
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;
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;
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);
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;
};
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_UserObjectEquals) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(JSObject, lhs, 1);
+ CONVERT_ARG_CHECKED(JSObject, rhs, 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);
/* Comparisons */ \
F(NumberEquals, 2, 1) \
F(StringEquals, 2, 1) \
+ F(UserObjectEquals, 2, 1) \
\
F(NumberCompare, 3, 1) \
F(SmiLexicographicCompare, 2, 1) \
// 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;
__ 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);
__ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset));
__ Cmp(rcx, known_map_);
__ j(not_equal, &miss, Label::kNear);
+ __ testb(FieldOperand(rcx, Map::kBitField2Offset),
+ Immediate(1 << Map::kUseUserObjectComparison));
+ __ j(not_zero, &miss, Label::kNear);
__ Cmp(rbx, known_map_);
__ j(not_equal, &miss, Label::kNear);
+ __ testb(FieldOperand(rbx, Map::kBitField2Offset),
+ Immediate(1 << Map::kUseUserObjectComparison));
+ __ j(not_zero, &miss, Label::kNear);
__ subq(rax, rdx);
__ ret(0);