Index: test/cctest/test-api.cc |
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc |
index 23ceee2411a3ecc97ec3c89c903861802a1ce0cd..17fd226ee05a01c90870a50e14254b124439020e 100644 |
--- a/test/cctest/test-api.cc |
+++ b/test/cctest/test-api.cc |
@@ -81,6 +81,11 @@ static void ExpectString(const char* code, const char* expected) { |
CHECK_EQ(expected, *ascii); |
} |
+static void ExpectInt32(const char* code, int expected) { |
+ Local<Value> result = CompileRun(code); |
+ CHECK(result->IsInt32()); |
+ CHECK_EQ(expected, result->Int32Value()); |
+} |
static void ExpectBoolean(const char* code, bool expected) { |
Local<Value> result = CompileRun(code); |
@@ -1297,6 +1302,197 @@ static v8::Handle<Value> EchoNamedProperty(Local<String> name, |
return name; |
} |
+// Helper functions for Interceptor/Accessor interaction tests |
+ |
+Handle<Value> SimpleAccessorGetter(Local<String> name, |
+ const AccessorInfo& info) { |
+ Handle<Object> self = info.This(); |
+ return self->Get(String::Concat(v8_str("accessor_"), name)); |
+} |
+ |
+void SimpleAccessorSetter(Local<String> name, Local<Value> value, |
+ const AccessorInfo& info) { |
+ Handle<Object> self = info.This(); |
+ self->Set(String::Concat(v8_str("accessor_"), name), value); |
+} |
+ |
+Handle<Value> EmptyInterceptorGetter(Local<String> name, |
+ const AccessorInfo& info) { |
+ return Handle<Value>(); |
+} |
+ |
+Handle<Value> EmptyInterceptorSetter(Local<String> name, |
+ Local<Value> value, |
+ const AccessorInfo& info) { |
+ return Handle<Value>(); |
+} |
+ |
+Handle<Value> InterceptorGetter(Local<String> name, |
+ const AccessorInfo& info) { |
+ // Intercept names that start with 'interceptor_'. |
+ String::AsciiValue ascii(name); |
+ char* name_str = *ascii; |
+ char prefix[] = "interceptor_"; |
+ int i; |
+ for (i = 0; name_str[i] && prefix[i]; ++i) { |
+ if (name_str[i] != prefix[i]) return Handle<Value>(); |
+ } |
+ Handle<Object> self = info.This(); |
+ return self->GetHiddenValue(v8_str(name_str + i)); |
+} |
+ |
+Handle<Value> InterceptorSetter(Local<String> name, |
+ Local<Value> value, |
+ const AccessorInfo& info) { |
+ // Intercept accesses that set certain integer values. |
+ if (value->IsInt32() && value->Int32Value() < 10000) { |
+ Handle<Object> self = info.This(); |
+ self->SetHiddenValue(name, value); |
+ return value; |
+ } |
+ return Handle<Value>(); |
+} |
+ |
+void AddAccessor(Handle<FunctionTemplate> templ, |
+ Handle<String> name, |
+ v8::AccessorGetter getter, |
+ v8::AccessorSetter setter) { |
+ templ->PrototypeTemplate()->SetAccessor(name, getter, setter); |
+} |
+ |
+void AddInterceptor(Handle<FunctionTemplate> templ, |
+ v8::NamedPropertyGetter getter, |
+ v8::NamedPropertySetter setter) { |
+ templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter); |
+} |
+ |
+THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) { |
+ v8::HandleScope scope; |
+ Handle<FunctionTemplate> parent = FunctionTemplate::New(); |
+ Handle<FunctionTemplate> child = FunctionTemplate::New(); |
+ child->Inherit(parent); |
+ AddAccessor(parent, v8_str("age"), |
+ SimpleAccessorGetter, SimpleAccessorSetter); |
+ AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); |
+ LocalContext env; |
+ env->Global()->Set(v8_str("Child"), child->GetFunction()); |
+ CompileRun("var child = new Child;" |
+ "child.age = 10;"); |
+ ExpectBoolean("child.hasOwnProperty('age')", false); |
+ ExpectInt32("child.age", 10); |
+ ExpectInt32("child.accessor_age", 10); |
+} |
+ |
+THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) { |
+ v8::HandleScope scope; |
+ Handle<FunctionTemplate> parent = FunctionTemplate::New(); |
+ Handle<FunctionTemplate> child = FunctionTemplate::New(); |
+ child->Inherit(parent); |
+ AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); |
+ LocalContext env; |
+ env->Global()->Set(v8_str("Child"), child->GetFunction()); |
+ CompileRun("var child = new Child;" |
+ "var parent = child.__proto__;" |
+ "Object.defineProperty(parent, 'age', " |
+ " {get: function(){ return this.accessor_age; }, " |
+ " set: function(v){ this.accessor_age = v; }, " |
+ " enumerable: true, configurable: true});" |
+ "child.age = 10;"); |
+ ExpectBoolean("child.hasOwnProperty('age')", false); |
+ ExpectInt32("child.age", 10); |
+ ExpectInt32("child.accessor_age", 10); |
+} |
+ |
+THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) { |
+ v8::HandleScope scope; |
+ Handle<FunctionTemplate> parent = FunctionTemplate::New(); |
+ Handle<FunctionTemplate> child = FunctionTemplate::New(); |
+ child->Inherit(parent); |
+ AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); |
+ LocalContext env; |
+ env->Global()->Set(v8_str("Child"), child->GetFunction()); |
+ CompileRun("var child = new Child;" |
+ "var parent = child.__proto__;" |
+ "parent.name = 'Alice';"); |
+ ExpectBoolean("child.hasOwnProperty('name')", false); |
+ ExpectString("child.name", "Alice"); |
+ CompileRun("child.name = 'Bob';"); |
+ ExpectString("child.name", "Bob"); |
+ ExpectBoolean("child.hasOwnProperty('name')", true); |
+ ExpectString("parent.name", "Alice"); |
+} |
+ |
+THREADED_TEST(SwitchFromInterceptorToAccessor) { |
+ v8::HandleScope scope; |
+ Handle<FunctionTemplate> parent = FunctionTemplate::New(); |
+ Handle<FunctionTemplate> child = FunctionTemplate::New(); |
+ child->Inherit(parent); |
+ AddAccessor(parent, v8_str("age"), |
+ SimpleAccessorGetter, SimpleAccessorSetter); |
+ AddInterceptor(child, InterceptorGetter, InterceptorSetter); |
+ LocalContext env; |
+ env->Global()->Set(v8_str("Child"), child->GetFunction()); |
+ CompileRun("var child = new Child;" |
+ "function setAge(i){ child.age = i; };" |
+ "for(var i = 0; i <= 10000; i++) setAge(i);"); |
+ // All i < 10000 go to the interceptor. |
+ ExpectInt32("child.interceptor_age", 9999); |
+ // The last i goes to the accessor. |
+ ExpectInt32("child.accessor_age", 10000); |
+} |
+ |
+THREADED_TEST(SwitchFromAccessorToInterceptor) { |
+ v8::HandleScope scope; |
+ Handle<FunctionTemplate> parent = FunctionTemplate::New(); |
+ Handle<FunctionTemplate> child = FunctionTemplate::New(); |
+ child->Inherit(parent); |
+ AddAccessor(parent, v8_str("age"), |
+ SimpleAccessorGetter, SimpleAccessorSetter); |
+ AddInterceptor(child, InterceptorGetter, InterceptorSetter); |
+ LocalContext env; |
+ env->Global()->Set(v8_str("Child"), child->GetFunction()); |
+ CompileRun("var child = new Child;" |
+ "function setAge(i){ child.age = i; };" |
+ "for(var i = 20000; i >= 9999; i--) setAge(i);"); |
+ // All i >= 10000 go to the accessor. |
+ ExpectInt32("child.accessor_age", 10000); |
+ // The last i goes to the interceptor. |
+ ExpectInt32("child.interceptor_age", 9999); |
+} |
+ |
+THREADED_TEST(SwitchFromInterceptorToProperty) { |
+ v8::HandleScope scope; |
+ Handle<FunctionTemplate> parent = FunctionTemplate::New(); |
+ Handle<FunctionTemplate> child = FunctionTemplate::New(); |
+ child->Inherit(parent); |
+ AddInterceptor(child, InterceptorGetter, InterceptorSetter); |
+ LocalContext env; |
+ env->Global()->Set(v8_str("Child"), child->GetFunction()); |
+ CompileRun("var child = new Child;" |
+ "function setAge(i){ child.age = i; };" |
+ "for(var i = 0; i <= 10000; i++) setAge(i);"); |
+ // All i < 10000 go to the interceptor. |
+ ExpectInt32("child.interceptor_age", 9999); |
+ // The last i goes to child's own property. |
+ ExpectInt32("child.age", 10000); |
+} |
+ |
+THREADED_TEST(SwitchFromPropertyToInterceptor) { |
+ v8::HandleScope scope; |
+ Handle<FunctionTemplate> parent = FunctionTemplate::New(); |
+ Handle<FunctionTemplate> child = FunctionTemplate::New(); |
+ child->Inherit(parent); |
+ AddInterceptor(child, InterceptorGetter, InterceptorSetter); |
+ LocalContext env; |
+ env->Global()->Set(v8_str("Child"), child->GetFunction()); |
+ CompileRun("var child = new Child;" |
+ "function setAge(i){ child.age = i; };" |
+ "for(var i = 20000; i >= 9999; i--) setAge(i);"); |
+ // All i >= 10000 go to child's own property. |
+ ExpectInt32("child.age", 10000); |
+ // The last i goes to the interceptor. |
+ ExpectInt32("child.interceptor_age", 9999); |
+} |
THREADED_TEST(NamedPropertyHandlerGetter) { |
echo_named_call_count = 0; |