diff --git a/src/binding.cc b/src/binding.cc index 68d5562d..02bbe861 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -12,6 +12,7 @@ #include "v8/src/execution/isolate-utils.h" #include "v8/src/objects/objects-inl.h" #include "v8/src/objects/objects.h" +#include "v8/src/objects/smi.h" using namespace support; @@ -621,6 +622,10 @@ long std__shared_ptr__v8__ArrayBuffer__Allocator__use_count( return ptr.use_count(); } +int v8__Name__GetIdentityHash(const v8::Name& self) { + return ptr_to_local(&self)->GetIdentityHash(); +} + const v8::String* v8__String__Empty(v8::Isolate* isolate) { return local_to_ptr(v8::String::Empty(isolate)); } @@ -1730,6 +1735,17 @@ v8::Isolate* v8__internal__GetIsolateFromHeapObject(const v8::Data& data) { ? reinterpret_cast(isolate) : nullptr; } + +int v8__internal__Object__GetHash(const v8::Data& data) { + namespace i = v8::internal; + i::Object object(reinterpret_cast(data)); + i::Isolate* isolate; + int hash = object.IsHeapObject() && i::GetIsolateFromHeapObject( + object.GetHeapObject(), &isolate) + ? object.GetOrCreateHash(isolate).value() + : i::Smi::ToInt(object.GetHash()); + assert(hash != 0); + return hash; } } // extern "C" diff --git a/src/data.rs b/src/data.rs index a259a891..7a0640d6 100644 --- a/src/data.rs +++ b/src/data.rs @@ -6,12 +6,34 @@ use std::error::Error; use std::fmt; use std::fmt::Display; use std::fmt::Formatter; +use std::hash::Hash; +use std::hash::Hasher; use std::mem::transmute; use std::ops::Deref; +use crate::support::int; use crate::support::Opaque; use crate::Local; +extern "C" { + fn v8__Data__EQ(this: *const Data, other: *const Data) -> bool; + fn v8__internal__Object__GetHash(this: *const Data) -> int; + + fn v8__Value__SameValue(this: *const Value, other: *const Value) -> bool; + fn v8__Value__StrictEquals(this: *const Value, other: *const Value) -> bool; +} + +impl Data { + /// Returns the V8 hash value for this value. The current implementation + /// uses a hidden property to store the identity hash on some object types. + /// + /// The return value will never be 0. Also, it is not guaranteed to be + /// unique. + pub fn get_hash(&self) -> int { + unsafe { v8__internal__Object__GetHash(self) } + } +} + macro_rules! impl_deref { { $target:ident for $type:ident } => { impl Deref for $type { @@ -49,14 +71,18 @@ macro_rules! impl_try_from { macro_rules! impl_eq { { for $type:ident } => { - impl<'s> Eq for $type {} + impl Eq for $type {} }; } -extern "C" { - fn v8__Data__EQ(this: *const Data, other: *const Data) -> bool; - fn v8__Value__SameValue(this: *const Value, other: *const Value) -> bool; - fn v8__Value__StrictEquals(this: *const Value, other: *const Value) -> bool; +macro_rules! impl_hash { + { for $type:ident } => { + impl Hash for $type { + fn hash(&self, state: &mut H) { + self.get_hash().hash(state) + } + } + }; } macro_rules! impl_partial_eq { @@ -236,6 +262,7 @@ pub struct AccessorSignature(Opaque); impl_deref! { Data for AccessorSignature } impl_eq! { for AccessorSignature } +impl_hash! { for AccessorSignature } impl_partial_eq! { Data for AccessorSignature use identity } impl_partial_eq! { AccessorSignature for AccessorSignature use identity } @@ -246,6 +273,7 @@ pub struct Context(Opaque); impl_deref! { Data for Context } impl_eq! { for Context } +impl_hash! { for Context } impl_partial_eq! { Data for Context use identity } impl_partial_eq! { Context for Context use identity } @@ -255,6 +283,7 @@ pub struct Message(Opaque); impl_deref! { Data for Message } impl_eq! { for Message } +impl_hash! { for Message } impl_partial_eq! { Data for Message use identity } impl_partial_eq! { Message for Message use identity } @@ -264,6 +293,7 @@ pub struct Module(Opaque); impl_deref! { Data for Module } impl_eq! { for Module } +impl_hash! { for Module } impl_partial_eq! { Data for Module use identity } impl_partial_eq! { Module for Module use identity } @@ -277,6 +307,7 @@ pub struct PrimitiveArray(Opaque); impl_deref! { Data for PrimitiveArray } impl_eq! { for PrimitiveArray } +impl_hash! { for PrimitiveArray } impl_partial_eq! { Data for PrimitiveArray use identity } impl_partial_eq! { PrimitiveArray for PrimitiveArray use identity } @@ -288,6 +319,7 @@ pub struct Private(Opaque); impl_deref! { Data for Private } impl_eq! { for Private } +impl_hash! { for Private } impl_partial_eq! { Data for Private use identity } impl_partial_eq! { Private for Private use identity } @@ -298,6 +330,7 @@ pub struct Script(Opaque); impl_deref! { Data for Script } impl_eq! { for Script } +impl_hash! { for Script } impl_partial_eq! { Data for Script use identity } impl_partial_eq! { Script for Script use identity } @@ -310,6 +343,7 @@ pub struct ScriptOrModule(Opaque); impl_deref! { Data for ScriptOrModule } impl_eq! { for ScriptOrModule } +impl_hash! { for ScriptOrModule } impl_partial_eq! { Data for ScriptOrModule use identity } impl_partial_eq! { ScriptOrModule for ScriptOrModule use identity } @@ -324,6 +358,7 @@ pub struct Signature(Opaque); impl_deref! { Data for Signature } impl_eq! { for Signature } +impl_hash! { for Signature } impl_partial_eq! { Data for Signature use identity } impl_partial_eq! { Signature for Signature use identity } @@ -333,6 +368,7 @@ pub struct StackFrame(Opaque); impl_deref! { Data for StackFrame } impl_eq! { for StackFrame } +impl_hash! { for StackFrame } impl_partial_eq! { Data for StackFrame use identity } impl_partial_eq! { StackFrame for StackFrame use identity } @@ -344,6 +380,7 @@ pub struct StackTrace(Opaque); impl_deref! { Data for StackTrace } impl_eq! { for StackTrace } +impl_hash! { for StackTrace } impl_partial_eq! { Data for StackTrace use identity } impl_partial_eq! { StackTrace for StackTrace use identity } @@ -355,6 +392,7 @@ impl_deref! { Data for Template } impl_from! { FunctionTemplate for Template } impl_from! { ObjectTemplate for Template } impl_eq! { for Template } +impl_hash! { for Template } impl_partial_eq! { Data for Template use identity } impl_partial_eq! { Template for Template use identity } impl_partial_eq! { FunctionTemplate for Template use identity } @@ -470,6 +508,7 @@ pub struct FunctionTemplate(Opaque); impl_deref! { Template for FunctionTemplate } impl_eq! { for FunctionTemplate } +impl_hash! { for FunctionTemplate } impl_partial_eq! { Data for FunctionTemplate use identity } impl_partial_eq! { Template for FunctionTemplate use identity } impl_partial_eq! { FunctionTemplate for FunctionTemplate use identity } @@ -483,6 +522,7 @@ pub struct ObjectTemplate(Opaque); impl_deref! { Template for ObjectTemplate } impl_eq! { for ObjectTemplate } +impl_hash! { for ObjectTemplate } impl_partial_eq! { Data for ObjectTemplate use identity } impl_partial_eq! { Template for ObjectTemplate use identity } impl_partial_eq! { ObjectTemplate for ObjectTemplate use identity } @@ -493,6 +533,7 @@ pub struct UnboundModuleScript(Opaque); impl_deref! { Data for UnboundModuleScript } impl_eq! { for UnboundModuleScript } +impl_hash! { for UnboundModuleScript } impl_partial_eq! { Data for UnboundModuleScript use identity } impl_partial_eq! { UnboundModuleScript for UnboundModuleScript use identity } @@ -502,6 +543,7 @@ pub struct UnboundScript(Opaque); impl_deref! { Data for UnboundScript } impl_eq! { for UnboundScript } +impl_hash! { for UnboundScript } impl_partial_eq! { Data for UnboundScript use identity } impl_partial_eq! { UnboundScript for UnboundScript use identity } @@ -554,6 +596,7 @@ impl_from! { Integer for Value } impl_from! { Int32 for Value } impl_from! { Uint32 for Value } impl_eq! { for Value } +impl_hash! { for Value } impl_partial_eq! { Value for Value use same_value_zero } impl_partial_eq! { External for Value use identity } impl_partial_eq! { Object for Value use identity } @@ -607,6 +650,7 @@ pub struct External(Opaque); impl_deref! { Value for External } impl_try_from! { Value for External if v => v.is_external() } impl_eq! { for External } +impl_hash! { for External } impl_partial_eq! { Data for External use identity } impl_partial_eq! { Value for External use identity } impl_partial_eq! { External for External use identity } @@ -649,6 +693,7 @@ impl_from! { StringObject for Object } impl_from! { SymbolObject for Object } impl_from! { WasmModuleObject for Object } impl_eq! { for Object } +impl_hash! { for Object } impl_partial_eq! { Data for Object use identity } impl_partial_eq! { Value for Object use identity } impl_partial_eq! { Object for Object use identity } @@ -692,6 +737,7 @@ impl_deref! { Object for Array } impl_try_from! { Value for Array if v => v.is_array() } impl_try_from! { Object for Array if v => v.is_array() } impl_eq! { for Array } +impl_hash! { for Array } impl_partial_eq! { Data for Array use identity } impl_partial_eq! { Value for Array use identity } impl_partial_eq! { Object for Array use identity } @@ -705,6 +751,7 @@ impl_deref! { Object for ArrayBuffer } impl_try_from! { Value for ArrayBuffer if v => v.is_array_buffer() } impl_try_from! { Object for ArrayBuffer if v => v.is_array_buffer() } impl_eq! { for ArrayBuffer } +impl_hash! { for ArrayBuffer } impl_partial_eq! { Data for ArrayBuffer use identity } impl_partial_eq! { Value for ArrayBuffer use identity } impl_partial_eq! { Object for ArrayBuffer use identity } @@ -732,6 +779,7 @@ impl_from! { Uint32Array for ArrayBufferView } impl_from! { Uint8Array for ArrayBufferView } impl_from! { Uint8ClampedArray for ArrayBufferView } impl_eq! { for ArrayBufferView } +impl_hash! { for ArrayBufferView } impl_partial_eq! { Data for ArrayBufferView use identity } impl_partial_eq! { Value for ArrayBufferView use identity } impl_partial_eq! { Object for ArrayBufferView use identity } @@ -759,6 +807,7 @@ impl_try_from! { Value for DataView if v => v.is_data_view() } impl_try_from! { Object for DataView if v => v.is_data_view() } impl_try_from! { ArrayBufferView for DataView if v => v.is_data_view() } impl_eq! { for DataView } +impl_hash! { for DataView } impl_partial_eq! { Data for DataView use identity } impl_partial_eq! { Value for DataView use identity } impl_partial_eq! { Object for DataView use identity } @@ -786,6 +835,7 @@ impl_from! { Uint32Array for TypedArray } impl_from! { Uint8Array for TypedArray } impl_from! { Uint8ClampedArray for TypedArray } impl_eq! { for TypedArray } +impl_hash! { for TypedArray } impl_partial_eq! { Data for TypedArray use identity } impl_partial_eq! { Value for TypedArray use identity } impl_partial_eq! { Object for TypedArray use identity } @@ -813,6 +863,7 @@ impl_try_from! { Object for BigInt64Array if v => v.is_big_int64_array() } impl_try_from! { ArrayBufferView for BigInt64Array if v => v.is_big_int64_array() } impl_try_from! { TypedArray for BigInt64Array if v => v.is_big_int64_array() } impl_eq! { for BigInt64Array } +impl_hash! { for BigInt64Array } impl_partial_eq! { Data for BigInt64Array use identity } impl_partial_eq! { Value for BigInt64Array use identity } impl_partial_eq! { Object for BigInt64Array use identity } @@ -830,6 +881,7 @@ impl_try_from! { Object for BigUint64Array if v => v.is_big_uint64_array() } impl_try_from! { ArrayBufferView for BigUint64Array if v => v.is_big_uint64_array() } impl_try_from! { TypedArray for BigUint64Array if v => v.is_big_uint64_array() } impl_eq! { for BigUint64Array } +impl_hash! { for BigUint64Array } impl_partial_eq! { Data for BigUint64Array use identity } impl_partial_eq! { Value for BigUint64Array use identity } impl_partial_eq! { Object for BigUint64Array use identity } @@ -847,6 +899,7 @@ impl_try_from! { Object for Float32Array if v => v.is_float32_array() } impl_try_from! { ArrayBufferView for Float32Array if v => v.is_float32_array() } impl_try_from! { TypedArray for Float32Array if v => v.is_float32_array() } impl_eq! { for Float32Array } +impl_hash! { for Float32Array } impl_partial_eq! { Data for Float32Array use identity } impl_partial_eq! { Value for Float32Array use identity } impl_partial_eq! { Object for Float32Array use identity } @@ -864,6 +917,7 @@ impl_try_from! { Object for Float64Array if v => v.is_float64_array() } impl_try_from! { ArrayBufferView for Float64Array if v => v.is_float64_array() } impl_try_from! { TypedArray for Float64Array if v => v.is_float64_array() } impl_eq! { for Float64Array } +impl_hash! { for Float64Array } impl_partial_eq! { Data for Float64Array use identity } impl_partial_eq! { Value for Float64Array use identity } impl_partial_eq! { Object for Float64Array use identity } @@ -881,6 +935,7 @@ impl_try_from! { Object for Int16Array if v => v.is_int16_array() } impl_try_from! { ArrayBufferView for Int16Array if v => v.is_int16_array() } impl_try_from! { TypedArray for Int16Array if v => v.is_int16_array() } impl_eq! { for Int16Array } +impl_hash! { for Int16Array } impl_partial_eq! { Data for Int16Array use identity } impl_partial_eq! { Value for Int16Array use identity } impl_partial_eq! { Object for Int16Array use identity } @@ -898,6 +953,7 @@ impl_try_from! { Object for Int32Array if v => v.is_int32_array() } impl_try_from! { ArrayBufferView for Int32Array if v => v.is_int32_array() } impl_try_from! { TypedArray for Int32Array if v => v.is_int32_array() } impl_eq! { for Int32Array } +impl_hash! { for Int32Array } impl_partial_eq! { Data for Int32Array use identity } impl_partial_eq! { Value for Int32Array use identity } impl_partial_eq! { Object for Int32Array use identity } @@ -915,6 +971,7 @@ impl_try_from! { Object for Int8Array if v => v.is_int8_array() } impl_try_from! { ArrayBufferView for Int8Array if v => v.is_int8_array() } impl_try_from! { TypedArray for Int8Array if v => v.is_int8_array() } impl_eq! { for Int8Array } +impl_hash! { for Int8Array } impl_partial_eq! { Data for Int8Array use identity } impl_partial_eq! { Value for Int8Array use identity } impl_partial_eq! { Object for Int8Array use identity } @@ -932,6 +989,7 @@ impl_try_from! { Object for Uint16Array if v => v.is_uint16_array() } impl_try_from! { ArrayBufferView for Uint16Array if v => v.is_uint16_array() } impl_try_from! { TypedArray for Uint16Array if v => v.is_uint16_array() } impl_eq! { for Uint16Array } +impl_hash! { for Uint16Array } impl_partial_eq! { Data for Uint16Array use identity } impl_partial_eq! { Value for Uint16Array use identity } impl_partial_eq! { Object for Uint16Array use identity } @@ -949,6 +1007,7 @@ impl_try_from! { Object for Uint32Array if v => v.is_uint32_array() } impl_try_from! { ArrayBufferView for Uint32Array if v => v.is_uint32_array() } impl_try_from! { TypedArray for Uint32Array if v => v.is_uint32_array() } impl_eq! { for Uint32Array } +impl_hash! { for Uint32Array } impl_partial_eq! { Data for Uint32Array use identity } impl_partial_eq! { Value for Uint32Array use identity } impl_partial_eq! { Object for Uint32Array use identity } @@ -966,6 +1025,7 @@ impl_try_from! { Object for Uint8Array if v => v.is_uint8_array() } impl_try_from! { ArrayBufferView for Uint8Array if v => v.is_uint8_array() } impl_try_from! { TypedArray for Uint8Array if v => v.is_uint8_array() } impl_eq! { for Uint8Array } +impl_hash! { for Uint8Array } impl_partial_eq! { Data for Uint8Array use identity } impl_partial_eq! { Value for Uint8Array use identity } impl_partial_eq! { Object for Uint8Array use identity } @@ -983,6 +1043,7 @@ impl_try_from! { Object for Uint8ClampedArray if v => v.is_uint8_clamped_array() impl_try_from! { ArrayBufferView for Uint8ClampedArray if v => v.is_uint8_clamped_array() } impl_try_from! { TypedArray for Uint8ClampedArray if v => v.is_uint8_clamped_array() } impl_eq! { for Uint8ClampedArray } +impl_hash! { for Uint8ClampedArray } impl_partial_eq! { Data for Uint8ClampedArray use identity } impl_partial_eq! { Value for Uint8ClampedArray use identity } impl_partial_eq! { Object for Uint8ClampedArray use identity } @@ -998,6 +1059,7 @@ impl_deref! { Object for BigIntObject } impl_try_from! { Value for BigIntObject if v => v.is_big_int_object() } impl_try_from! { Object for BigIntObject if v => v.is_big_int_object() } impl_eq! { for BigIntObject } +impl_hash! { for BigIntObject } impl_partial_eq! { Data for BigIntObject use identity } impl_partial_eq! { Value for BigIntObject use identity } impl_partial_eq! { Object for BigIntObject use identity } @@ -1011,6 +1073,7 @@ impl_deref! { Object for BooleanObject } impl_try_from! { Value for BooleanObject if v => v.is_boolean_object() } impl_try_from! { Object for BooleanObject if v => v.is_boolean_object() } impl_eq! { for BooleanObject } +impl_hash! { for BooleanObject } impl_partial_eq! { Data for BooleanObject use identity } impl_partial_eq! { Value for BooleanObject use identity } impl_partial_eq! { Object for BooleanObject use identity } @@ -1024,6 +1087,7 @@ impl_deref! { Object for Date } impl_try_from! { Value for Date if v => v.is_date() } impl_try_from! { Object for Date if v => v.is_date() } impl_eq! { for Date } +impl_hash! { for Date } impl_partial_eq! { Data for Date use identity } impl_partial_eq! { Value for Date use identity } impl_partial_eq! { Object for Date use identity } @@ -1037,6 +1101,7 @@ impl_deref! { Object for Function } impl_try_from! { Value for Function if v => v.is_function() } impl_try_from! { Object for Function if v => v.is_function() } impl_eq! { for Function } +impl_hash! { for Function } impl_partial_eq! { Data for Function use identity } impl_partial_eq! { Value for Function use identity } impl_partial_eq! { Object for Function use identity } @@ -1050,6 +1115,7 @@ impl_deref! { Object for Map } impl_try_from! { Value for Map if v => v.is_map() } impl_try_from! { Object for Map if v => v.is_map() } impl_eq! { for Map } +impl_hash! { for Map } impl_partial_eq! { Data for Map use identity } impl_partial_eq! { Value for Map use identity } impl_partial_eq! { Object for Map use identity } @@ -1063,6 +1129,7 @@ impl_deref! { Object for NumberObject } impl_try_from! { Value for NumberObject if v => v.is_number_object() } impl_try_from! { Object for NumberObject if v => v.is_number_object() } impl_eq! { for NumberObject } +impl_hash! { for NumberObject } impl_partial_eq! { Data for NumberObject use identity } impl_partial_eq! { Value for NumberObject use identity } impl_partial_eq! { Object for NumberObject use identity } @@ -1076,6 +1143,7 @@ impl_deref! { Object for Promise } impl_try_from! { Value for Promise if v => v.is_promise() } impl_try_from! { Object for Promise if v => v.is_promise() } impl_eq! { for Promise } +impl_hash! { for Promise } impl_partial_eq! { Data for Promise use identity } impl_partial_eq! { Value for Promise use identity } impl_partial_eq! { Object for Promise use identity } @@ -1086,6 +1154,7 @@ pub struct PromiseResolver(Opaque); impl_deref! { Object for PromiseResolver } impl_eq! { for PromiseResolver } +impl_hash! { for PromiseResolver } impl_partial_eq! { Data for PromiseResolver use identity } impl_partial_eq! { Value for PromiseResolver use identity } impl_partial_eq! { Object for PromiseResolver use identity } @@ -1100,6 +1169,7 @@ impl_deref! { Object for Proxy } impl_try_from! { Value for Proxy if v => v.is_proxy() } impl_try_from! { Object for Proxy if v => v.is_proxy() } impl_eq! { for Proxy } +impl_hash! { for Proxy } impl_partial_eq! { Data for Proxy use identity } impl_partial_eq! { Value for Proxy use identity } impl_partial_eq! { Object for Proxy use identity } @@ -1113,6 +1183,7 @@ impl_deref! { Object for RegExp } impl_try_from! { Value for RegExp if v => v.is_reg_exp() } impl_try_from! { Object for RegExp if v => v.is_reg_exp() } impl_eq! { for RegExp } +impl_hash! { for RegExp } impl_partial_eq! { Data for RegExp use identity } impl_partial_eq! { Value for RegExp use identity } impl_partial_eq! { Object for RegExp use identity } @@ -1126,6 +1197,7 @@ impl_deref! { Object for Set } impl_try_from! { Value for Set if v => v.is_set() } impl_try_from! { Object for Set if v => v.is_set() } impl_eq! { for Set } +impl_hash! { for Set } impl_partial_eq! { Data for Set use identity } impl_partial_eq! { Value for Set use identity } impl_partial_eq! { Object for Set use identity } @@ -1139,6 +1211,7 @@ impl_deref! { Object for SharedArrayBuffer } impl_try_from! { Value for SharedArrayBuffer if v => v.is_shared_array_buffer() } impl_try_from! { Object for SharedArrayBuffer if v => v.is_shared_array_buffer() } impl_eq! { for SharedArrayBuffer } +impl_hash! { for SharedArrayBuffer } impl_partial_eq! { Data for SharedArrayBuffer use identity } impl_partial_eq! { Value for SharedArrayBuffer use identity } impl_partial_eq! { Object for SharedArrayBuffer use identity } @@ -1152,6 +1225,7 @@ impl_deref! { Object for StringObject } impl_try_from! { Value for StringObject if v => v.is_string_object() } impl_try_from! { Object for StringObject if v => v.is_string_object() } impl_eq! { for StringObject } +impl_hash! { for StringObject } impl_partial_eq! { Data for StringObject use identity } impl_partial_eq! { Value for StringObject use identity } impl_partial_eq! { Object for StringObject use identity } @@ -1165,6 +1239,7 @@ impl_deref! { Object for SymbolObject } impl_try_from! { Value for SymbolObject if v => v.is_symbol_object() } impl_try_from! { Object for SymbolObject if v => v.is_symbol_object() } impl_eq! { for SymbolObject } +impl_hash! { for SymbolObject } impl_partial_eq! { Data for SymbolObject use identity } impl_partial_eq! { Value for SymbolObject use identity } impl_partial_eq! { Object for SymbolObject use identity } @@ -1177,6 +1252,7 @@ impl_deref! { Object for WasmModuleObject } impl_try_from! { Value for WasmModuleObject if v => v.is_wasm_module_object() } impl_try_from! { Object for WasmModuleObject if v => v.is_wasm_module_object() } impl_eq! { for WasmModuleObject } +impl_hash! { for WasmModuleObject } impl_partial_eq! { Data for WasmModuleObject use identity } impl_partial_eq! { Value for WasmModuleObject use identity } impl_partial_eq! { Object for WasmModuleObject use identity } @@ -1198,6 +1274,7 @@ impl_from! { Integer for Primitive } impl_from! { Int32 for Primitive } impl_from! { Uint32 for Primitive } impl_eq! { for Primitive } +impl_hash! { for Primitive } impl_partial_eq! { Value for Primitive use same_value_zero } impl_partial_eq! { Primitive for Primitive use same_value_zero } impl_partial_eq! { BigInt for Primitive use same_value_zero } @@ -1218,6 +1295,7 @@ impl_deref! { Primitive for BigInt } impl_try_from! { Value for BigInt if v => v.is_big_int() } impl_try_from! { Primitive for BigInt if v => v.is_big_int() } impl_eq! { for BigInt } +impl_hash! { for BigInt } impl_partial_eq! { Value for BigInt use same_value_zero } impl_partial_eq! { Primitive for BigInt use same_value_zero } impl_partial_eq! { BigInt for BigInt use strict_equals } @@ -1231,6 +1309,7 @@ impl_deref! { Primitive for Boolean } impl_try_from! { Value for Boolean if v => v.is_boolean() } impl_try_from! { Primitive for Boolean if v => v.is_boolean() } impl_eq! { for Boolean } +impl_hash! { for Boolean } impl_partial_eq! { Data for Boolean use identity } impl_partial_eq! { Value for Boolean use identity } impl_partial_eq! { Primitive for Boolean use identity } @@ -1246,6 +1325,7 @@ impl_try_from! { Primitive for Name if v => v.is_name() } impl_from! { String for Name } impl_from! { Symbol for Name } impl_eq! { for Name } +impl_hash! { for Name } impl_partial_eq! { Value for Name use same_value_zero } impl_partial_eq! { Primitive for Name use same_value_zero } impl_partial_eq! { Name for Name use strict_equals } @@ -1261,6 +1341,7 @@ impl_try_from! { Value for String if v => v.is_string() } impl_try_from! { Primitive for String if v => v.is_string() } impl_try_from! { Name for String if v => v.is_string() } impl_eq! { for String } +impl_hash! { for String } impl_partial_eq! { Value for String use same_value_zero } impl_partial_eq! { Primitive for String use same_value_zero } impl_partial_eq! { Name for String use strict_equals } @@ -1275,6 +1356,7 @@ impl_try_from! { Value for Symbol if v => v.is_symbol() } impl_try_from! { Primitive for Symbol if v => v.is_symbol() } impl_try_from! { Name for Symbol if v => v.is_symbol() } impl_eq! { for Symbol } +impl_hash! { for Symbol } impl_partial_eq! { Data for Symbol use identity } impl_partial_eq! { Value for Symbol use identity } impl_partial_eq! { Primitive for Symbol use identity } @@ -1292,6 +1374,7 @@ impl_from! { Integer for Number } impl_from! { Int32 for Number } impl_from! { Uint32 for Number } impl_eq! { for Number } +impl_hash! { for Number } impl_partial_eq! { Value for Number use same_value_zero } impl_partial_eq! { Primitive for Number use same_value_zero } impl_partial_eq! { Number for Number use same_value_zero } @@ -1310,6 +1393,7 @@ impl_try_from! { Number for Integer if v => v.is_int32() || v.is_uint32() } impl_from! { Int32 for Integer } impl_from! { Uint32 for Integer } impl_eq! { for Integer } +impl_hash! { for Integer } impl_partial_eq! { Value for Integer use same_value_zero } impl_partial_eq! { Primitive for Integer use same_value_zero } impl_partial_eq! { Number for Integer use same_value_zero } @@ -1327,6 +1411,7 @@ impl_try_from! { Primitive for Int32 if v => v.is_int32() } impl_try_from! { Number for Int32 if v => v.is_int32() } impl_try_from! { Integer for Int32 if v => v.is_int32() } impl_eq! { for Int32 } +impl_hash! { for Int32 } impl_partial_eq! { Value for Int32 use same_value_zero } impl_partial_eq! { Primitive for Int32 use same_value_zero } impl_partial_eq! { Number for Int32 use same_value_zero } @@ -1343,6 +1428,7 @@ impl_try_from! { Primitive for Uint32 if v => v.is_uint32() } impl_try_from! { Number for Uint32 if v => v.is_uint32() } impl_try_from! { Integer for Uint32 if v => v.is_uint32() } impl_eq! { for Uint32 } +impl_hash! { for Uint32 } impl_partial_eq! { Value for Uint32 use same_value_zero } impl_partial_eq! { Primitive for Uint32 use same_value_zero } impl_partial_eq! { Number for Uint32 use same_value_zero } diff --git a/src/handle.rs b/src/handle.rs index 1e42b524..466fd591 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -1,3 +1,6 @@ +use std::borrow::Borrow; +use std::hash::Hash; +use std::hash::Hasher; use std::marker::PhantomData; use std::mem::transmute; use std::ops::Deref; @@ -237,9 +240,42 @@ impl<'a, T> Handle for &'a Global { } } +impl<'s, T> Borrow for Local<'s, T> { + fn borrow(&self) -> &T { + &**self + } +} + +impl Borrow for Global { + fn borrow(&self) -> &T { + let HandleInfo { data, host } = self.get_handle_info(); + if let HandleHost::DisposedIsolate = host { + panic!("attempt to access Handle hosted by disposed Isolate"); + } + unsafe { &*data.as_ptr() } + } +} + impl<'s, T> Eq for Local<'s, T> where T: Eq {} impl Eq for Global where T: Eq {} +impl<'s, T: Hash> Hash for Local<'s, T> { + fn hash(&self, state: &mut H) { + (&**self).hash(state) + } +} + +impl Hash for Global { + fn hash(&self, state: &mut H) { + unsafe { + if self.isolate_handle.get_isolate_ptr().is_null() { + panic!("can't hash Global after its host Isolate has been disposed"); + } + self.data.as_ref().hash(state); + } + } +} + impl<'s, T, Rhs: Handle> PartialEq for Local<'s, T> where T: PartialEq, diff --git a/src/lib.rs b/src/lib.rs index 9fefbc7c..bfe418be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,7 @@ mod handle; mod isolate; mod isolate_create_params; mod module; +mod name; mod number; mod object; mod platform; diff --git a/src/module.rs b/src/module.rs index 7fb03562..e51a4ba1 100644 --- a/src/module.rs +++ b/src/module.rs @@ -183,7 +183,9 @@ impl Module { } } - /// Returns the identity hash for this object. + /// The `Module` specific equivalent of `Data::get_hash()`. + /// This function is kept around for testing purposes only. + #[doc(hidden)] pub fn get_identity_hash(&self) -> int { unsafe { v8__Module__GetIdentityHash(self) } } diff --git a/src/name.rs b/src/name.rs new file mode 100644 index 00000000..990e9eba --- /dev/null +++ b/src/name.rs @@ -0,0 +1,17 @@ +// Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. + +use crate::support::int; +use crate::Name; + +extern "C" { + fn v8__Name__GetIdentityHash(this: *const Name) -> int; +} + +impl Name { + /// The `String` or `Symbol` specific equivalent of `Data::get_hash()`. + /// This function is kept around for testing purposes only. + #[doc(hidden)] + pub fn get_identity_hash(&self) -> int { + unsafe { v8__Name__GetIdentityHash(self) } + } +} diff --git a/src/object.rs b/src/object.rs index 68631e6c..b0349b4d 100644 --- a/src/object.rs +++ b/src/object.rs @@ -286,11 +286,9 @@ impl Object { .into() } - /// Returns the identity hash for this object. The current implementation - /// uses a hidden property on the object to store the identity hash. - /// - /// The return value will never be 0. Also, it is not guaranteed to be - /// unique. + /// The `Object` specific equivalent of `Data::get_hash()`. + /// This function is kept around for testing purposes only. + #[doc(hidden)] pub fn get_identity_hash(&self) -> int { unsafe { v8__Object__GetIdentityHash(self) } } diff --git a/tests/test_api.rs b/tests/test_api.rs index 46f3bb0e..0e62be39 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -1799,26 +1799,31 @@ fn equality_edge_cases() { assert!(pos_zero.same_value(pos_zero)); assert!(pos_zero.same_value_zero(pos_zero)); assert!(pos_zero.strict_equals(pos_zero)); + assert_eq!(pos_zero.get_hash(), pos_zero.get_hash()); assert!(neg_zero == neg_zero); assert!(neg_zero.same_value(neg_zero)); assert!(neg_zero.same_value_zero(neg_zero)); assert!(neg_zero.strict_equals(neg_zero)); + assert_eq!(neg_zero.get_hash(), neg_zero.get_hash()); assert!(pos_zero == neg_zero); assert!(!pos_zero.same_value(neg_zero)); assert!(pos_zero.same_value_zero(neg_zero)); assert!(pos_zero.strict_equals(neg_zero)); + assert_eq!(pos_zero.get_hash(), neg_zero.get_hash()); assert!(neg_zero == pos_zero); assert!(!neg_zero.same_value(pos_zero)); assert!(neg_zero.same_value_zero(pos_zero)); - assert!(pos_zero.strict_equals(pos_zero)); + assert!(neg_zero.strict_equals(pos_zero)); + assert_eq!(neg_zero.get_hash(), pos_zero.get_hash()); assert!(nan == nan); assert!(nan.same_value(nan)); assert!(nan.same_value_zero(nan)); assert!(!nan.strict_equals(nan)); + assert_eq!(nan.get_hash(), nan.get_hash()); assert!(nan != pos_zero); assert!(!nan.same_value(pos_zero)); @@ -1831,6 +1836,126 @@ fn equality_edge_cases() { assert!(!neg_zero.strict_equals(nan)); } +#[test] +fn get_hash() { + use std::collections::HashMap; + use std::collections::HashSet; + use std::iter::once; + + let _setup_guard = setup(); + let isolate = &mut v8::Isolate::new(Default::default()); + + let scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context); + + // Note: the set with hashes and the collition counter is used below in both + // the 'primitives' and the 'objects' section. + let mut hashes = HashSet::new(); + let mut collision_count = 0; + + let mut get_primitives = || -> v8::Local { + eval( + scope, + r#"[ + undefined, + null, + false, + true, + 0, + 123, + 12345e67, + 123456789012345678901234567890123456789012345678901234567890n, + NaN, + -Infinity, + "", + "hello metaverse!", + Symbol.isConcatSpreadable + ]"#, + ) + .unwrap() + .try_into() + .unwrap() + }; + + let primitives1 = get_primitives(); + let primitives2 = get_primitives(); + + let len = primitives1.length(); + assert!(len > 10); + assert_eq!(len, primitives2.length()); + + let mut name_count = 0; + + for i in 0..len { + let pri1 = primitives1.get_index(scope, i).unwrap(); + let pri2 = primitives2.get_index(scope, i).unwrap(); + let hash = pri1.get_hash(); + assert_ne!(hash, 0); + assert_eq!(hash, pri2.get_hash()); + if let Ok(name) = v8::Local::::try_from(pri1) { + assert_eq!(hash, name.get_identity_hash()); + name_count += 1; + } + if !hashes.insert(hash) { + collision_count += 1; + } + let map = + once((v8::Global::new(scope, pri1), i)).collect::>(); + assert_eq!(map[&*pri2], i); + } + + assert_eq!(name_count, 3); + assert!(collision_count <= 2); + + for _ in 0..1 { + let objects: v8::Local:: = eval( + scope, + r#"[ + [1, 2, 3], + (function() { return arguments; })(1, 2, 3), + { a: 1, b: 2, c: 3 }, + Object.create(null), + new Map([[null, 1], ["2", 3n]]), + new Set(), + function f() {}, + function* f() {}, + async function f() {}, + async function* f() {}, + foo => foo, + async bar => bar, + class Custom extends Object { method(p) { return -p; } }, + new class MyString extends String { constructor() { super("yeaeaeah"); } }, + (() => { try { not_defined } catch(e) { return e; } })() + ]"#) + .unwrap() + .try_into() + .unwrap(); + + let len = objects.length(); + assert!(len > 10); + + for i in 0..len { + let val = objects.get_index(scope, i).unwrap(); + let hash = val.get_hash(); + assert_ne!(hash, 0); + let obj = v8::Local::::try_from(val).unwrap(); + assert_eq!(hash, obj.get_identity_hash()); + if !hashes.insert(hash) { + collision_count += 1; + } + let map = + once((v8::Global::new(scope, obj), i)).collect::>(); + assert_eq!(map[&*obj], i); + } + + assert!(collision_count <= 2); + } + + // TODO: add tests for `External` and for types that are not derived from + // `v8::Value`, like `Module`, `Function/ObjectTemplate` etc. +} + #[test] fn array_buffer_view() { let _setup_guard = setup();