Push revisions 1527, 1539, 1540, 1541, 1550, 1551 from bleeding_edge to trunk.
git-svn-id: http://v8.googlecode.com/svn/trunk@1555 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/include/v8.h b/include/v8.h
index 44d0307..ab886cd 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -1075,6 +1075,28 @@
* access check info, the object cannot be accessed by anyone.
*/
void TurnOnAccessCheck();
+
+ /**
+ * Returns the identity hash for this object. The current implemenation uses
+ * a hidden property on the object to store the identity hash.
+ */
+ int GetIdentityHash();
+
+ /**
+ * Access hidden properties on JavaScript objects. These properties are
+ * hidden from the executing JavaScript and only accessible through the V8
+ * C++ API. Hidden properties introduced by V8 internally (for example the
+ * identity hash) are prefixed with "v8::".
+ */
+ bool SetHiddenValue(Handle<String> key, Handle<Value> value);
+ Local<Value> GetHiddenValue(Handle<String> key);
+ bool DeleteHiddenValue(Handle<String> key);
+
+ /**
+ * Clone this object with a fast but shallow copy. Values will point
+ * to the same values as the original object.
+ */
+ Local<Object> Clone();
static Local<Object> New();
static Object* Cast(Value* obj);
diff --git a/src/api.cc b/src/api.cc
index 2a37310..785aa20 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -1919,6 +1919,88 @@
}
+Local<v8::Object> v8::Object::Clone() {
+ ON_BAILOUT("v8::Object::Clone()", return Local<Object>());
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ EXCEPTION_PREAMBLE();
+ i::Handle<i::JSObject> result = i::Copy(self);
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(Local<Object>());
+ return Utils::ToLocal(result);
+}
+
+
+int v8::Object::GetIdentityHash() {
+ ON_BAILOUT("v8::Object::GetIdentityHash()", return 0);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> hidden_props(i::GetHiddenProperties(self, true));
+ i::Handle<i::Object> hash_symbol = i::Factory::identity_hash_symbol();
+ i::Handle<i::Object> hash = i::GetProperty(hidden_props, hash_symbol);
+ int hash_value;
+ if (hash->IsSmi()) {
+ hash_value = i::Smi::cast(*hash)->value();
+ } else {
+ hash_value = random() & i::Smi::kMaxValue; // Limit range to fit a smi.
+ i::SetProperty(hidden_props,
+ hash_symbol,
+ i::Handle<i::Object>(i::Smi::FromInt(hash_value)),
+ static_cast<PropertyAttributes>(None));
+ }
+ return hash_value;
+}
+
+
+bool v8::Object::SetHiddenValue(v8::Handle<v8::String> key,
+ v8::Handle<v8::Value> value) {
+ ON_BAILOUT("v8::Object::SetHiddenValue()", return false);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> hidden_props(i::GetHiddenProperties(self, true));
+ i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+ i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
+ EXCEPTION_PREAMBLE();
+ i::Handle<i::Object> obj = i::SetProperty(
+ hidden_props,
+ key_obj,
+ value_obj,
+ static_cast<PropertyAttributes>(None));
+ has_pending_exception = obj.is_null();
+ EXCEPTION_BAILOUT_CHECK(false);
+ return true;
+}
+
+
+v8::Local<v8::Value> v8::Object::GetHiddenValue(v8::Handle<v8::String> key) {
+ ON_BAILOUT("v8::Object::GetHiddenValue()", return Local<v8::Value>());
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> hidden_props(i::GetHiddenProperties(self, false));
+ if (hidden_props->IsUndefined()) {
+ return v8::Local<v8::Value>();
+ }
+ i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
+ EXCEPTION_PREAMBLE();
+ i::Handle<i::Object> result = i::GetProperty(hidden_props, key_obj);
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(v8::Local<v8::Value>());
+ if (result->IsUndefined()) {
+ return v8::Local<v8::Value>();
+ }
+ return Utils::ToLocal(result);
+}
+
+
+bool v8::Object::DeleteHiddenValue(v8::Handle<v8::String> key) {
+ ON_BAILOUT("v8::DeleteHiddenValue()", return false);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::JSObject> hidden_props(
+ i::JSObject::cast(*i::GetHiddenProperties(self, false)));
+ if (hidden_props->IsUndefined()) {
+ return false;
+ }
+ i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
+ return i::DeleteProperty(hidden_props, key_obj)->IsTrue();
+}
+
+
Local<v8::Object> Function::NewInstance() const {
return NewInstance(0, NULL);
}
@@ -2200,7 +2282,7 @@
const char* v8::V8::GetVersion() {
- return "1.1.1.2";
+ return "1.1.1.3";
}
diff --git a/src/factory.h b/src/factory.h
index 754c6da..1388de1 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -302,6 +302,10 @@
SYMBOL_LIST(SYMBOL_ACCESSOR)
#undef SYMBOL_ACCESSOR
+ static Handle<String> hidden_symbol() {
+ return Handle<String>(&Heap::hidden_symbol_);
+ }
+
static Handle<SharedFunctionInfo> NewSharedFunctionInfo(Handle<String> name);
static Handle<Dictionary> DictionaryAtNumberPut(Handle<Dictionary>,
diff --git a/src/handles.cc b/src/handles.cc
index 48065dd..b6c59b0 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -52,44 +52,39 @@
}
-void** HandleScope::CreateHandle(void* value) {
+void** HandleScope::Extend() {
void** result = current_.next;
- if (result == current_.limit) {
- // Make sure there's at least one scope on the stack and that the
- // top of the scope stack isn't a barrier.
- if (current_.extensions < 0) {
- Utils::ReportApiFailure("v8::HandleScope::CreateHandle()",
- "Cannot create a handle without a HandleScope");
- return NULL;
- }
- HandleScopeImplementer* impl = HandleScopeImplementer::instance();
- // If there's more room in the last block, we use that. This is used
- // for fast creation of scopes after scope barriers.
- if (!impl->Blocks()->is_empty()) {
- void** limit = &impl->Blocks()->last()[kHandleBlockSize];
- if (current_.limit != limit) {
- current_.limit = limit;
- }
- }
- // If we still haven't found a slot for the handle, we extend the
- // current handle scope by allocating a new handle block.
- if (result == current_.limit) {
- // If there's a spare block, use it for growing the current scope.
- result = impl->GetSpareOrNewBlock();
- // Add the extension to the global list of blocks, but count the
- // extension as part of the current scope.
- impl->Blocks()->Add(result);
- current_.extensions++;
- current_.limit = &result[kHandleBlockSize];
+ ASSERT(result == current_.limit);
+ // Make sure there's at least one scope on the stack and that the
+ // top of the scope stack isn't a barrier.
+ if (current_.extensions < 0) {
+ Utils::ReportApiFailure("v8::HandleScope::CreateHandle()",
+ "Cannot create a handle without a HandleScope");
+ return NULL;
+ }
+ HandleScopeImplementer* impl = HandleScopeImplementer::instance();
+ // If there's more room in the last block, we use that. This is used
+ // for fast creation of scopes after scope barriers.
+ if (!impl->Blocks()->is_empty()) {
+ void** limit = &impl->Blocks()->last()[kHandleBlockSize];
+ if (current_.limit != limit) {
+ current_.limit = limit;
}
}
- // Update the current next field, set the value in the created
- // handle, and return the result.
- ASSERT(result < current_.limit);
- current_.next = result + 1;
- *result = value;
+ // If we still haven't found a slot for the handle, we extend the
+ // current handle scope by allocating a new handle block.
+ if (result == current_.limit) {
+ // If there's a spare block, use it for growing the current scope.
+ result = impl->GetSpareOrNewBlock();
+ // Add the extension to the global list of blocks, but count the
+ // extension as part of the current scope.
+ impl->Blocks()->Add(result);
+ current_.extensions++;
+ current_.limit = &result[kHandleBlockSize];
+ }
+
return result;
}
@@ -266,6 +261,12 @@
}
+Handle<Object> GetHiddenProperties(Handle<JSObject> obj,
+ bool create_if_needed) {
+ CALL_HEAP_FUNCTION(obj->GetHiddenProperties(create_if_needed), Object);
+}
+
+
Handle<Object> DeleteElement(Handle<JSObject> obj,
uint32_t index) {
CALL_HEAP_FUNCTION(obj->DeleteElement(index), Object);
diff --git a/src/handles.h b/src/handles.h
index 21af7fc..c8e534e 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -118,7 +118,16 @@
static int NumberOfHandles();
// Creates a new handle with the given value.
- static void** CreateHandle(void* value);
+ static inline void** CreateHandle(void* value) {
+ void** result = current_.next;
+ if (result == current_.limit) result = Extend();
+ // Update the current next field, set the value in the created
+ // handle, and return the result.
+ ASSERT(result < current_.limit);
+ current_.next = result + 1;
+ *result = value;
+ return result;
+ }
private:
// Prevent heap allocation or illegal handle scopes.
@@ -150,6 +159,9 @@
#endif
}
+ // Extend the handle scope making room for more handles.
+ static void** Extend();
+
// Deallocates any extensions used by the current scope.
static void DeleteExtensions();
@@ -211,6 +223,8 @@
Handle<Object> GetPrototype(Handle<Object> obj);
+Handle<Object> GetHiddenProperties(Handle<JSObject> obj, bool create_if_needed);
+
Handle<Object> DeleteElement(Handle<JSObject> obj, uint32_t index);
Handle<Object> DeleteProperty(Handle<JSObject> obj, Handle<String> prop);
diff --git a/src/heap.cc b/src/heap.cc
index 8159311..f384297 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -57,6 +57,8 @@
SYMBOL_LIST(SYMBOL_ALLOCATION)
#undef SYMBOL_ALLOCATION
+String* Heap::hidden_symbol_;
+
NewSpace Heap::new_space_;
OldSpace* Heap::old_pointer_space_ = NULL;
OldSpace* Heap::old_data_space_ = NULL;
@@ -1206,6 +1208,16 @@
SYMBOL_LIST(SYMBOL_INITIALIZE)
#undef SYMBOL_INITIALIZE
+ // Allocate the hidden symbol which is used to identify the hidden properties
+ // in JSObjects. The hash code has a special value so that it will not match
+ // the empty string when searching for the property. It cannot be part of the
+ // SYMBOL_LIST because it needs to be allocated manually with the special
+ // hash code in place. The hash code for the hidden_symbol is zero to ensure
+ // that it will always be at the first entry in property descriptors.
+ obj = AllocateSymbol(CStrVector(""), 0, String::kHashComputedMask);
+ if (obj->IsFailure()) return false;
+ hidden_symbol_ = String::cast(obj);
+
// Allocate the proxy for __proto__.
obj = AllocateProxy((Address) &Accessors::ObjectPrototype);
if (obj->IsFailure()) return false;
@@ -2650,6 +2662,7 @@
v->VisitPointer(bit_cast<Object**, String**>(&name##_));
SYMBOL_LIST(SYMBOL_ITERATE)
#undef SYMBOL_ITERATE
+ v->VisitPointer(bit_cast<Object**, String**>(&hidden_symbol_));
SYNCHRONIZE_TAG("symbol");
Bootstrapper::Iterate(v);
diff --git a/src/heap.h b/src/heap.h
index 082bbb2..a1dd81e 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -195,7 +195,8 @@
V(space_symbol, " ") \
V(exec_symbol, "exec") \
V(zero_symbol, "0") \
- V(global_eval_symbol, "GlobalEval")
+ V(global_eval_symbol, "GlobalEval") \
+ V(identity_hash_symbol, "v8::IdentityHash")
// Forward declaration of the GCTracer class.
@@ -645,6 +646,10 @@
SYMBOL_LIST(SYMBOL_ACCESSOR)
#undef SYMBOL_ACCESSOR
+ // The hidden_symbol is special because it is the empty string, but does
+ // not match the empty string.
+ static String* hidden_symbol() { return hidden_symbol_; }
+
// Iterates over all roots in the heap.
static void IterateRoots(ObjectVisitor* v);
// Iterates over all strong roots in the heap.
@@ -888,6 +893,10 @@
SYMBOL_LIST(SYMBOL_DECLARATION)
#undef SYMBOL_DECLARATION
+ // The special hidden symbol which is an empty string, but does not match
+ // any string when looked up in properties.
+ static String* hidden_symbol_;
+
// GC callback function, called before and after mark-compact GC.
// Allocations in the callback function are disallowed.
static GCCallback global_gc_prologue_callback_;
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 9aee342..941b84c 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -2438,10 +2438,15 @@
uint32_t StringHasher::GetHash() {
+ // Get the calculated raw hash value and do some more bit ops to distribute
+ // the hash further. Ensure that we never return zero as the hash value.
uint32_t result = raw_running_hash_;
result += (result << 3);
result ^= (result >> 11);
result += (result << 15);
+ if (result == 0) {
+ result = 27;
+ }
return result;
}
diff --git a/src/objects.cc b/src/objects.cc
index b8228f0..44ebb3f 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -3113,8 +3113,12 @@
int DescriptorArray::LinearSearch(String* name, int len) {
+ uint32_t hash = name->Hash();
for (int number = 0; number < len; number++) {
- if (name->Equals(GetKey(number)) && !is_null_descriptor(number)) {
+ String* entry = GetKey(number);
+ if ((entry->Hash() == hash) &&
+ name->Equals(entry) &&
+ !is_null_descriptor(number)) {
return number;
}
}
@@ -4187,7 +4191,7 @@
uint32_t String::ComputeAndSetHash() {
- // Should only be call if hash code has not yet been computed.
+ // Should only be called if hash code has not yet been computed.
ASSERT(!(length_field() & kHashComputedMask));
// Compute the hash code.
@@ -4199,7 +4203,9 @@
// Check the hash code is there.
ASSERT(length_field() & kHashComputedMask);
- return field >> kHashShift;
+ uint32_t result = field >> kHashShift;
+ ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
+ return result;
}
@@ -5081,6 +5087,45 @@
}
+Object* JSObject::GetHiddenProperties(bool create_if_needed) {
+ String* key = Heap::hidden_symbol();
+ if (this->HasFastProperties()) {
+ // If the object has fast properties, check whether the first slot in the
+ // descriptor array matches the hidden symbol. Since the hidden symbols
+ // hash code is zero it will always occupy the first entry if present.
+ DescriptorArray* descriptors = this->map()->instance_descriptors();
+ if (descriptors->number_of_descriptors() > 0) {
+ if (descriptors->GetKey(0) == key) {
+#ifdef DEBUG
+ PropertyDetails details(descriptors->GetDetails(0));
+ ASSERT(details.type() == FIELD);
+#endif // DEBUG
+ Object* value = descriptors->GetValue(0);
+ return FastPropertyAt(Descriptor::IndexFromValue(value));
+ }
+ }
+ }
+
+ // Only attempt to find the hidden properties in the local object and not
+ // in the prototype chain.
+ if (!this->HasLocalProperty(key)) {
+ // Hidden properties object not found. Allocate a new hidden properties
+ // object if requested. Otherwise return the undefined value.
+ if (create_if_needed) {
+ Object* obj = Heap::AllocateJSObject(
+ Top::context()->global_context()->object_function());
+ if (obj->IsFailure()) {
+ return obj;
+ }
+ return this->SetProperty(key, obj, DONT_ENUM);
+ } else {
+ return Heap::undefined_value();
+ }
+ }
+ return this->GetProperty(key);
+}
+
+
bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
// Check access rights if needed.
if (IsAccessCheckNeeded() &&
@@ -5927,13 +5972,20 @@
// StringKey simply carries a string object as key.
class StringKey : public HashTableKey {
public:
- explicit StringKey(String* string) : string_(string) { }
+ explicit StringKey(String* string) :
+ string_(string),
+ hash_(StringHash(string)) { }
bool IsMatch(Object* string) {
+ // We know that all entries in a hash table had their hash keys created.
+ // Use that knowledge to have fast failure.
+ if (hash_ != StringHash(string)) {
+ return false;
+ }
return string_->Equals(String::cast(string));
}
- uint32_t Hash() { return StringHash(string_); }
+ uint32_t Hash() { return hash_; }
HashFunction GetHashFunction() { return StringHash; }
@@ -5946,6 +5998,7 @@
bool IsStringKey() { return true; }
String* string_;
+ uint32_t hash_;
};
@@ -6072,7 +6125,9 @@
static_cast<unsigned>(string_.length()));
chars_ = buffer.Length();
length_field_ = String::ComputeLengthAndHashField(&buffer, chars_);
- return length_field_ >> String::kHashShift;
+ uint32_t result = length_field_ >> String::kHashShift;
+ ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
+ return result;
}
Object* GetObject() {
diff --git a/src/objects.h b/src/objects.h
index 28df02a..c38aa38 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -1243,6 +1243,11 @@
// Return the object's prototype (might be Heap::null_value()).
inline Object* GetPrototype();
+ // Return the object's hidden properties object. If the object has no hidden
+ // properties and create_if_needed is true, then a new hidden property object
+ // will be allocated. Otherwise the Heap::undefined_value is returned.
+ Object* GetHiddenProperties(bool create_if_needed);
+
// Tells whether the index'th element is present.
inline bool HasElement(uint32_t index);
bool HasElementWithReceiver(JSObject* receiver, uint32_t index);
diff --git a/src/spaces.h b/src/spaces.h
index 3d13219..8a3dc5b 100644
--- a/src/spaces.h
+++ b/src/spaces.h
@@ -1481,7 +1481,7 @@
MapSpaceFreeList free_list_;
// An array of page start address in a map space.
- Address page_addresses_[kMaxMapPageIndex];
+ Address page_addresses_[kMaxMapPageIndex + 1];
public:
TRACK_MEMORY("MapSpace")
diff --git a/src/stub-cache.cc b/src/stub-cache.cc
index b7ef311..b472eab 100644
--- a/src/stub-cache.cc
+++ b/src/stub-cache.cc
@@ -656,9 +656,8 @@
Object* LoadCallbackProperty(Arguments args) {
Handle<JSObject> recv = args.at<JSObject>(0);
AccessorInfo* callback = AccessorInfo::cast(args[1]);
- v8::AccessorGetter fun =
- FUNCTION_CAST<v8::AccessorGetter>(
- v8::ToCData<Address>(callback->getter()));
+ Address getter_address = v8::ToCData<Address>(callback->getter());
+ v8::AccessorGetter fun = FUNCTION_CAST<v8::AccessorGetter>(getter_address);
ASSERT(fun != NULL);
Handle<String> name = args.at<String>(2);
Handle<JSObject> holder = args.at<JSObject>(3);
@@ -669,10 +668,9 @@
// locations of the arguments to this function maybe we don't have
// to explicitly create the structure but can just pass a pointer
// into the stack.
- v8::AccessorInfo info(
- v8::Utils::ToLocal(recv),
- v8::Utils::ToLocal(data),
- v8::Utils::ToLocal(holder));
+ v8::AccessorInfo info(v8::Utils::ToLocal(recv),
+ v8::Utils::ToLocal(data),
+ v8::Utils::ToLocal(holder));
v8::Handle<v8::Value> result;
{
// Leaving JavaScript.
@@ -680,30 +678,25 @@
result = fun(v8::Utils::ToLocal(name), info);
}
RETURN_IF_SCHEDULED_EXCEPTION();
- if (result.IsEmpty()) {
- return Heap::undefined_value();
- } else {
- return *v8::Utils::OpenHandle(*result);
- }
+ if (result.IsEmpty()) return Heap::undefined_value();
+ return *v8::Utils::OpenHandle(*result);
}
Object* StoreCallbackProperty(Arguments args) {
Handle<JSObject> recv = args.at<JSObject>(0);
AccessorInfo* callback = AccessorInfo::cast(args[1]);
- v8::AccessorSetter fun =
- FUNCTION_CAST<v8::AccessorSetter>(
- v8::ToCData<Address>(callback->setter()));
+ Address setter_address = v8::ToCData<Address>(callback->setter());
+ v8::AccessorSetter fun = FUNCTION_CAST<v8::AccessorSetter>(setter_address);
ASSERT(fun != NULL);
Handle<String> name = args.at<String>(2);
Handle<Object> value = args.at<Object>(3);
HandleScope scope;
Handle<Object> data(callback->data());
LOG(ApiNamedPropertyAccess("store", *recv, *name));
- v8::AccessorInfo info(
- v8::Utils::ToLocal(recv),
- v8::Utils::ToLocal(data),
- v8::Utils::ToLocal(recv));
+ v8::AccessorInfo info(v8::Utils::ToLocal(recv),
+ v8::Utils::ToLocal(data),
+ v8::Utils::ToLocal(recv));
{
// Leaving JavaScript.
VMState state(OTHER);
@@ -715,28 +708,17 @@
Object* LoadInterceptorProperty(Arguments args) {
- HandleScope scope;
- Handle<JSObject> recv = args.at<JSObject>(0);
- Handle<JSObject> holder = args.at<JSObject>(1);
- Handle<String> name = args.at<String>(2);
+ JSObject* recv = JSObject::cast(args[0]);
+ JSObject* holder = JSObject::cast(args[1]);
+ String* name = String::cast(args[2]);
ASSERT(holder->HasNamedInterceptor());
PropertyAttributes attr = NONE;
- Handle<Object> result = GetPropertyWithInterceptor(recv, holder, name, &attr);
+ Object* result = holder->GetPropertyWithInterceptor(recv, name, &attr);
- // GetPropertyWithInterceptor already converts a scheduled exception
- // to a pending one if any. Don't use RETURN_IF_SCHEDULED_EXCEPTION() here.
-
- // Make sure to propagate exceptions.
- if (result.is_null()) {
- // Failure::Exception is converted to a null handle in the
- // handle-based methods such as SetProperty. We therefore need
- // to convert null handles back to exceptions.
- ASSERT(Top::has_pending_exception());
- return Failure::Exception();
- }
+ if (result->IsFailure()) return result;
// If the property is present, return it.
- if (attr != ABSENT) return *result;
+ if (attr != ABSENT) return result;
// If the top frame is an internal frame, this is really a call
// IC. In this case, we simply return the undefined result which
@@ -744,43 +726,38 @@
// function.
StackFrameIterator it;
it.Advance(); // skip exit frame
- if (it.frame()->is_internal()) return *result;
+ if (it.frame()->is_internal()) return result;
// If the load is non-contextual, just return the undefined result.
// Note that both keyed and non-keyed loads may end up here, so we
// can't use either LoadIC or KeyedLoadIC constructors.
IC ic(IC::NO_EXTRA_FRAME);
ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub());
- if (!ic.is_contextual()) return *result;
+ if (!ic.is_contextual()) return result;
// Throw a reference error.
- Handle<Object> error =
- Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
- return Top::Throw(*error);
+ {
+ HandleScope scope;
+ // We cannot use the raw name pointer here since getting the
+ // property might cause a GC. However, we can get the name from
+ // the stack using the arguments object.
+ Handle<String> name_handle = args.at<String>(2);
+ Handle<Object> error =
+ Factory::NewReferenceError("not_defined",
+ HandleVector(&name_handle, 1));
+ return Top::Throw(*error);
+ }
}
Object* StoreInterceptorProperty(Arguments args) {
- HandleScope scope;
- Handle<JSObject> recv = args.at<JSObject>(0);
- Handle<String> name = args.at<String>(1);
- Handle<Object> value = args.at<Object>(2);
+ JSObject* recv = JSObject::cast(args[0]);
+ String* name = String::cast(args[1]);
+ Object* value = args[2];
ASSERT(recv->HasNamedInterceptor());
PropertyAttributes attr = NONE;
- Handle<Object> result = SetPropertyWithInterceptor(recv, name, value, attr);
-
- // SetPropertyWithInterceptor already converts a scheduled exception
- // to a pending one if any. Don't use RETURN_IF_SCHEDULED_EXCEPTION() here.
-
- // Make sure to propagate exceptions.
- if (result.is_null()) {
- // Failure::Exception is converted to a null handle in the
- // handle-based methods such as SetProperty. We therefore need
- // to convert null handles back to exceptions.
- ASSERT(Top::has_pending_exception());
- return Failure::Exception();
- }
- return *result;
+ Object* result = recv->SetPropertyWithInterceptor(name, value, attr);
+ return result;
}
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 5a3d210..3746bf5 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -1254,6 +1254,78 @@
}
+THREADED_TEST(IdentityHash) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ // Ensure that the test starts with an fresh heap to test whether the hash
+ // code is based on the address.
+ i::Heap::CollectAllGarbage();
+ Local<v8::Object> obj = v8::Object::New();
+ int hash = obj->GetIdentityHash();
+ int hash1 = obj->GetIdentityHash();
+ CHECK_EQ(hash, hash1);
+ int hash2 = v8::Object::New()->GetIdentityHash();
+ // Since the identity hash is essentially a random number two consecutive
+ // objects should not be assigned the same hash code. If the test below fails
+ // the random number generator should be evaluated.
+ CHECK_NE(hash, hash2);
+ i::Heap::CollectAllGarbage();
+ int hash3 = v8::Object::New()->GetIdentityHash();
+ // Make sure that the identity hash is not based on the initial address of
+ // the object alone. If the test below fails the random number generator
+ // should be evaluated.
+ CHECK_NE(hash, hash3);
+ int hash4 = obj->GetIdentityHash();
+ CHECK_EQ(hash, hash4);
+}
+
+
+THREADED_TEST(HiddenProperties) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ v8::Local<v8::Object> obj = v8::Object::New();
+ v8::Local<v8::String> key = v8_str("api-test::hidden-key");
+ v8::Local<v8::String> empty = v8_str("");
+ v8::Local<v8::String> prop_name = v8_str("prop_name");
+
+ i::Heap::CollectAllGarbage();
+
+ CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
+ CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
+ CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+
+ i::Heap::CollectAllGarbage();
+
+ // Make sure we do not find the hidden property.
+ CHECK(!obj->Has(empty));
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+ CHECK(obj->Get(empty)->IsUndefined());
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+ CHECK(obj->Set(empty, v8::Integer::New(2003)));
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+ CHECK_EQ(2003, obj->Get(empty)->Int32Value());
+
+ i::Heap::CollectAllGarbage();
+
+ // Add another property and delete it afterwards to force the object in
+ // slow case.
+ CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+ CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+ CHECK(obj->Delete(prop_name));
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+
+ i::Heap::CollectAllGarbage();
+
+ CHECK(obj->DeleteHiddenValue(key));
+ CHECK(obj->GetHiddenValue(key).IsEmpty());
+}
+
+
THREADED_TEST(External) {
v8::HandleScope scope;
int x = 3;
@@ -5799,3 +5871,37 @@
local_env->Exit();
}
+
+
+// Verify that we can clone an object
+TEST(ObjectClone) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ const char* sample =
+ "var rv = {};" \
+ "rv.alpha = 'hello';" \
+ "rv.beta = 123;" \
+ "rv;";
+
+ // Create an object, verify basics.
+ Local<Value> val = CompileRun(sample);
+ CHECK(val->IsObject());
+ Local<v8::Object> obj = Local<v8::Object>::Cast(val);
+ obj->Set(v8_str("gamma"), v8_str("cloneme"));
+
+ CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
+ CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
+ CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
+
+ // Clone it.
+ Local<v8::Object> clone = obj->Clone();
+ CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
+ CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
+ CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
+
+ // Set a property on the clone, verify each object.
+ clone->Set(v8_str("beta"), v8::Integer::New(456));
+ CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
+ CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
+}