// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "test/cctest/test-api.h" #include "include/v8-util.h" #include "src/api.h" #include "src/arguments.h" #include "src/base/platform/platform.h" #include "src/compilation-cache.h" #include "src/execution.h" #include "src/objects.h" #include "src/parsing/parser.h" #include "src/unicode-inl.h" #include "src/utils.h" #include "src/vm-state.h" using ::v8::Boolean; using ::v8::BooleanObject; using ::v8::Context; using ::v8::Extension; using ::v8::Function; using ::v8::FunctionTemplate; using ::v8::Handle; using ::v8::HandleScope; using ::v8::Local; using ::v8::Name; using ::v8::Message; using ::v8::MessageCallback; using ::v8::Object; using ::v8::ObjectTemplate; using ::v8::Persistent; using ::v8::Script; using ::v8::StackTrace; using ::v8::String; using ::v8::Symbol; using ::v8::TryCatch; using ::v8::Undefined; using ::v8::UniqueId; using ::v8::V8; using ::v8::Value; namespace { void Returns42(const v8::FunctionCallbackInfo& info) { info.GetReturnValue().Set(42); } void Return239Callback(Local name, const v8::PropertyCallbackInfo& info) { ApiTestFuzzer::Fuzz(); CheckReturnValue(info, FUNCTION_ADDR(Return239Callback)); info.GetReturnValue().Set(v8_str("bad value")); info.GetReturnValue().Set(v8_num(239)); } void EmptyInterceptorGetter(Local name, const v8::PropertyCallbackInfo& info) {} void EmptyInterceptorSetter(Local name, Local value, const v8::PropertyCallbackInfo& info) {} void SimpleAccessorGetter(Local name, const v8::PropertyCallbackInfo& info) { Handle self = Handle::Cast(info.This()); info.GetReturnValue().Set( self->Get(String::Concat(v8_str("accessor_"), name))); } void SimpleAccessorSetter(Local name, Local value, const v8::PropertyCallbackInfo& info) { Handle self = Handle::Cast(info.This()); self->Set(String::Concat(v8_str("accessor_"), name), value); } void SymbolAccessorGetter(Local name, const v8::PropertyCallbackInfo& info) { CHECK(name->IsSymbol()); Local sym = Local::Cast(name); if (sym->Name()->IsUndefined()) return; SimpleAccessorGetter(Local::Cast(sym->Name()), info); } void SymbolAccessorSetter(Local name, Local value, const v8::PropertyCallbackInfo& info) { CHECK(name->IsSymbol()); Local sym = Local::Cast(name); if (sym->Name()->IsUndefined()) return; SimpleAccessorSetter(Local::Cast(sym->Name()), value, info); } void StringInterceptorGetter( Local name, const v8::PropertyCallbackInfo& info) { // Intercept names that start with 'interceptor_'. String::Utf8Value utf8(name); char* name_str = *utf8; char prefix[] = "interceptor_"; int i; for (i = 0; name_str[i] && prefix[i]; ++i) { if (name_str[i] != prefix[i]) return; } Handle self = Handle::Cast(info.This()); info.GetReturnValue().Set( self->GetPrivate( info.GetIsolate()->GetCurrentContext(), v8::Private::ForApi(info.GetIsolate(), v8_str(name_str + i))) .ToLocalChecked()); } void StringInterceptorSetter(Local name, Local value, const v8::PropertyCallbackInfo& info) { // Intercept accesses that set certain integer values, for which the name does // not start with 'accessor_'. String::Utf8Value utf8(name); char* name_str = *utf8; char prefix[] = "accessor_"; int i; for (i = 0; name_str[i] && prefix[i]; ++i) { if (name_str[i] != prefix[i]) break; } if (!prefix[i]) return; if (value->IsInt32() && value->Int32Value() < 10000) { Handle self = Handle::Cast(info.This()); Handle context = info.GetIsolate()->GetCurrentContext(); Handle symbol = v8::Private::ForApi(info.GetIsolate(), name); self->SetPrivate(context, symbol, value).FromJust(); info.GetReturnValue().Set(value); } } void InterceptorGetter(Local generic_name, const v8::PropertyCallbackInfo& info) { if (generic_name->IsSymbol()) return; StringInterceptorGetter(Local::Cast(generic_name), info); } void InterceptorSetter(Local generic_name, Local value, const v8::PropertyCallbackInfo& info) { if (generic_name->IsSymbol()) return; StringInterceptorSetter(Local::Cast(generic_name), value, info); } void GenericInterceptorGetter(Local generic_name, const v8::PropertyCallbackInfo& info) { Local str; if (generic_name->IsSymbol()) { Local name = Local::Cast(generic_name)->Name(); if (name->IsUndefined()) return; str = String::Concat(v8_str("_sym_"), Local::Cast(name)); } else { Local name = Local::Cast(generic_name); String::Utf8Value utf8(name); char* name_str = *utf8; if (*name_str == '_') return; str = String::Concat(v8_str("_str_"), name); } Handle self = Handle::Cast(info.This()); info.GetReturnValue().Set(self->Get(str)); } void GenericInterceptorSetter(Local generic_name, Local value, const v8::PropertyCallbackInfo& info) { Local str; if (generic_name->IsSymbol()) { Local name = Local::Cast(generic_name)->Name(); if (name->IsUndefined()) return; str = String::Concat(v8_str("_sym_"), Local::Cast(name)); } else { Local name = Local::Cast(generic_name); String::Utf8Value utf8(name); char* name_str = *utf8; if (*name_str == '_') return; str = String::Concat(v8_str("_str_"), name); } Handle self = Handle::Cast(info.This()); self->Set(str, value); info.GetReturnValue().Set(value); } void AddAccessor(Handle templ, Handle name, v8::AccessorGetterCallback getter, v8::AccessorSetterCallback setter) { templ->PrototypeTemplate()->SetAccessor(name, getter, setter); } void AddInterceptor(Handle templ, v8::NamedPropertyGetterCallback getter, v8::NamedPropertySetterCallback setter) { templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter); } void AddAccessor(Handle templ, Handle name, v8::AccessorNameGetterCallback getter, v8::AccessorNameSetterCallback setter) { templ->PrototypeTemplate()->SetAccessor(name, getter, setter); } void AddInterceptor(Handle templ, v8::GenericNamedPropertyGetterCallback getter, v8::GenericNamedPropertySetterCallback setter) { templ->InstanceTemplate()->SetHandler( v8::NamedPropertyHandlerConfiguration(getter, setter)); } v8::Handle bottom; void CheckThisIndexedPropertyHandler( uint32_t index, const v8::PropertyCallbackInfo& info) { CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyHandler)); ApiTestFuzzer::Fuzz(); CHECK(info.This()->Equals(bottom)); } void CheckThisNamedPropertyHandler( Local name, const v8::PropertyCallbackInfo& info) { CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler)); ApiTestFuzzer::Fuzz(); CHECK(info.This()->Equals(bottom)); } void CheckThisIndexedPropertySetter( uint32_t index, Local value, const v8::PropertyCallbackInfo& info) { CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertySetter)); ApiTestFuzzer::Fuzz(); CHECK(info.This()->Equals(bottom)); } void CheckThisNamedPropertySetter( Local property, Local value, const v8::PropertyCallbackInfo& info) { CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter)); ApiTestFuzzer::Fuzz(); CHECK(info.This()->Equals(bottom)); } void CheckThisIndexedPropertyQuery( uint32_t index, const v8::PropertyCallbackInfo& info) { CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery)); ApiTestFuzzer::Fuzz(); CHECK(info.This()->Equals(bottom)); } void CheckThisNamedPropertyQuery( Local property, const v8::PropertyCallbackInfo& info) { CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery)); ApiTestFuzzer::Fuzz(); CHECK(info.This()->Equals(bottom)); } void CheckThisIndexedPropertyDeleter( uint32_t index, const v8::PropertyCallbackInfo& info) { CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter)); ApiTestFuzzer::Fuzz(); CHECK(info.This()->Equals(bottom)); } void CheckThisNamedPropertyDeleter( Local property, const v8::PropertyCallbackInfo& info) { CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter)); ApiTestFuzzer::Fuzz(); CHECK(info.This()->Equals(bottom)); } void CheckThisIndexedPropertyEnumerator( const v8::PropertyCallbackInfo& info) { CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator)); ApiTestFuzzer::Fuzz(); CHECK(info.This()->Equals(bottom)); } void CheckThisNamedPropertyEnumerator( const v8::PropertyCallbackInfo& info) { CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator)); ApiTestFuzzer::Fuzz(); CHECK(info.This()->Equals(bottom)); } int echo_named_call_count; void EchoNamedProperty(Local name, const v8::PropertyCallbackInfo& info) { ApiTestFuzzer::Fuzz(); CHECK(v8_str("data")->Equals(info.Data())); echo_named_call_count++; info.GetReturnValue().Set(name); } void InterceptorHasOwnPropertyGetter( Local name, const v8::PropertyCallbackInfo& info) { ApiTestFuzzer::Fuzz(); } void InterceptorHasOwnPropertyGetterGC( Local name, const v8::PropertyCallbackInfo& info) { ApiTestFuzzer::Fuzz(); CcTest::heap()->CollectAllGarbage(); } } // namespace THREADED_TEST(InterceptorHasOwnProperty) { LocalContext context; v8::Isolate* isolate = context->GetIsolate(); v8::HandleScope scope(isolate); Local fun_templ = v8::FunctionTemplate::New(isolate); Local instance_templ = fun_templ->InstanceTemplate(); instance_templ->SetHandler( v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetter)); Local function = fun_templ->GetFunction(); context->Global()->Set(v8_str("constructor"), function); v8::Handle value = CompileRun( "var o = new constructor();" "o.hasOwnProperty('ostehaps');"); CHECK_EQ(false, value->BooleanValue()); value = CompileRun( "o.ostehaps = 42;" "o.hasOwnProperty('ostehaps');"); CHECK_EQ(true, value->BooleanValue()); value = CompileRun( "var p = new constructor();" "p.hasOwnProperty('ostehaps');"); CHECK_EQ(false, value->BooleanValue()); } THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { LocalContext context; v8::Isolate* isolate = context->GetIsolate(); v8::HandleScope scope(isolate); Local fun_templ = v8::FunctionTemplate::New(isolate); Local instance_templ = fun_templ->InstanceTemplate(); instance_templ->SetHandler( v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetterGC)); Local function = fun_templ->GetFunction(); context->Global()->Set(v8_str("constructor"), function); // Let's first make some stuff so we can be sure to get a good GC. CompileRun( "function makestr(size) {" " switch (size) {" " case 1: return 'f';" " case 2: return 'fo';" " case 3: return 'foo';" " }" " return makestr(size >> 1) + makestr((size + 1) >> 1);" "}" "var x = makestr(12345);" "x = makestr(31415);" "x = makestr(23456);"); v8::Handle value = CompileRun( "var o = new constructor();" "o.__proto__ = new String(x);" "o.hasOwnProperty('ostehaps');"); CHECK_EQ(false, value->BooleanValue()); } static void CheckInterceptorLoadIC( v8::GenericNamedPropertyGetterCallback getter, const char* source, int expected) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); v8::Handle templ = ObjectTemplate::New(isolate); templ->SetHandler(v8::NamedPropertyHandlerConfiguration(getter, 0, 0, 0, 0, v8_str("data"))); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); v8::Handle value = CompileRun(source); CHECK_EQ(expected, value->Int32Value()); } static void InterceptorLoadICGetter( Local name, const v8::PropertyCallbackInfo& info) { ApiTestFuzzer::Fuzz(); v8::Isolate* isolate = CcTest::isolate(); CHECK_EQ(isolate, info.GetIsolate()); CHECK(v8_str("data")->Equals(info.Data())); CHECK(v8_str("x")->Equals(name)); info.GetReturnValue().Set(v8::Integer::New(isolate, 42)); } // This test should hit the load IC for the interceptor case. THREADED_TEST(InterceptorLoadIC) { CheckInterceptorLoadIC(InterceptorLoadICGetter, "var result = 0;" "for (var i = 0; i < 1000; i++) {" " result = o.x;" "}", 42); } // Below go several tests which verify that JITing for various // configurations of interceptor and explicit fields works fine // (those cases are special cased to get better performance). static void InterceptorLoadXICGetter( Local name, const v8::PropertyCallbackInfo& info) { ApiTestFuzzer::Fuzz(); info.GetReturnValue().Set( v8_str("x")->Equals(name) ? v8::Handle(v8::Integer::New(info.GetIsolate(), 42)) : v8::Handle()); } THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { CheckInterceptorLoadIC(InterceptorLoadXICGetter, "var result = 0;" "o.y = 239;" "for (var i = 0; i < 1000; i++) {" " result = o.y;" "}", 239); } THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { CheckInterceptorLoadIC(InterceptorLoadXICGetter, "var result = 0;" "o.__proto__ = { 'y': 239 };" "for (var i = 0; i < 1000; i++) {" " result = o.y + o.x;" "}", 239 + 42); } THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { CheckInterceptorLoadIC(InterceptorLoadXICGetter, "var result = 0;" "o.__proto__.y = 239;" "for (var i = 0; i < 1000; i++) {" " result = o.y + o.x;" "}", 239 + 42); } THREADED_TEST(InterceptorLoadICUndefined) { CheckInterceptorLoadIC(InterceptorLoadXICGetter, "var result = 0;" "for (var i = 0; i < 1000; i++) {" " result = (o.y == undefined) ? 239 : 42;" "}", 239); } THREADED_TEST(InterceptorLoadICWithOverride) { CheckInterceptorLoadIC(InterceptorLoadXICGetter, "fst = new Object(); fst.__proto__ = o;" "snd = new Object(); snd.__proto__ = fst;" "var result1 = 0;" "for (var i = 0; i < 1000; i++) {" " result1 = snd.x;" "}" "fst.x = 239;" "var result = 0;" "for (var i = 0; i < 1000; i++) {" " result = snd.x;" "}" "result + result1", 239 + 42); } // Test the case when we stored field into // a stub, but interceptor produced value on its own. THREADED_TEST(InterceptorLoadICFieldNotNeeded) { CheckInterceptorLoadIC( InterceptorLoadXICGetter, "proto = new Object();" "o.__proto__ = proto;" "proto.x = 239;" "for (var i = 0; i < 1000; i++) {" " o.x;" // Now it should be ICed and keep a reference to x defined on proto "}" "var result = 0;" "for (var i = 0; i < 1000; i++) {" " result += o.x;" "}" "result;", 42 * 1000); } // Test the case when we stored field into // a stub, but it got invalidated later on. THREADED_TEST(InterceptorLoadICInvalidatedField) { CheckInterceptorLoadIC( InterceptorLoadXICGetter, "proto1 = new Object();" "proto2 = new Object();" "o.__proto__ = proto1;" "proto1.__proto__ = proto2;" "proto2.y = 239;" "for (var i = 0; i < 1000; i++) {" " o.y;" // Now it should be ICed and keep a reference to y defined on proto2 "}" "proto1.y = 42;" "var result = 0;" "for (var i = 0; i < 1000; i++) {" " result += o.y;" "}" "result;", 42 * 1000); } static int interceptor_load_not_handled_calls = 0; static void InterceptorLoadNotHandled( Local name, const v8::PropertyCallbackInfo& info) { ++interceptor_load_not_handled_calls; } // Test how post-interceptor lookups are done in the non-cacheable // case: the interceptor should not be invoked during this lookup. THREADED_TEST(InterceptorLoadICPostInterceptor) { interceptor_load_not_handled_calls = 0; CheckInterceptorLoadIC(InterceptorLoadNotHandled, "receiver = new Object();" "receiver.__proto__ = o;" "proto = new Object();" "/* Make proto a slow-case object. */" "for (var i = 0; i < 1000; i++) {" " proto[\"xxxxxxxx\" + i] = [];" "}" "proto.x = 17;" "o.__proto__ = proto;" "var result = 0;" "for (var i = 0; i < 1000; i++) {" " result += receiver.x;" "}" "result;", 17 * 1000); CHECK_EQ(1000, interceptor_load_not_handled_calls); } // Test the case when we stored field into // a stub, but it got invalidated later on due to override on // global object which is between interceptor and fields' holders. THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { CheckInterceptorLoadIC( InterceptorLoadXICGetter, "o.__proto__ = this;" // set a global to be a proto of o. "this.__proto__.y = 239;" "for (var i = 0; i < 10; i++) {" " if (o.y != 239) throw 'oops: ' + o.y;" // Now it should be ICed and keep a reference to y defined on // field_holder. "}" "this.y = 42;" // Assign on a global. "var result = 0;" "for (var i = 0; i < 10; i++) {" " result += o.y;" "}" "result;", 42 * 10); } static void SetOnThis(Local name, Local value, const v8::PropertyCallbackInfo& info) { Local::Cast(info.This())->ForceSet(name, value); } THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); v8::Handle templ = ObjectTemplate::New(isolate); templ->SetHandler( v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); templ->SetAccessor(v8_str("y"), Return239Callback); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); // Check the case when receiver and interceptor's holder // are the same objects. v8::Handle value = CompileRun( "var result = 0;" "for (var i = 0; i < 7; i++) {" " result = o.y;" "}"); CHECK_EQ(239, value->Int32Value()); // Check the case when interceptor's holder is in proto chain // of receiver. value = CompileRun( "r = { __proto__: o };" "var result = 0;" "for (var i = 0; i < 7; i++) {" " result = r.y;" "}"); CHECK_EQ(239, value->Int32Value()); } THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); v8::Handle templ_o = ObjectTemplate::New(isolate); templ_o->SetHandler( v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); v8::Handle templ_p = ObjectTemplate::New(isolate); templ_p->SetAccessor(v8_str("y"), Return239Callback); LocalContext context; context->Global()->Set(v8_str("o"), templ_o->NewInstance()); context->Global()->Set(v8_str("p"), templ_p->NewInstance()); // Check the case when receiver and interceptor's holder // are the same objects. v8::Handle value = CompileRun( "o.__proto__ = p;" "var result = 0;" "for (var i = 0; i < 7; i++) {" " result = o.x + o.y;" "}"); CHECK_EQ(239 + 42, value->Int32Value()); // Check the case when interceptor's holder is in proto chain // of receiver. value = CompileRun( "r = { __proto__: o };" "var result = 0;" "for (var i = 0; i < 7; i++) {" " result = r.x + r.y;" "}"); CHECK_EQ(239 + 42, value->Int32Value()); } THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); v8::Handle templ = ObjectTemplate::New(isolate); templ->SetHandler( v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); templ->SetAccessor(v8_str("y"), Return239Callback); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); v8::Handle value = CompileRun( "fst = new Object(); fst.__proto__ = o;" "snd = new Object(); snd.__proto__ = fst;" "var result1 = 0;" "for (var i = 0; i < 7; i++) {" " result1 = snd.x;" "}" "fst.x = 239;" "var result = 0;" "for (var i = 0; i < 7; i++) {" " result = snd.x;" "}" "result + result1"); CHECK_EQ(239 + 42, value->Int32Value()); } // Test the case when we stored callback into // a stub, but interceptor produced value on its own. THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); v8::Handle templ_o = ObjectTemplate::New(isolate); templ_o->SetHandler( v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); v8::Handle templ_p = ObjectTemplate::New(isolate); templ_p->SetAccessor(v8_str("y"), Return239Callback); LocalContext context; context->Global()->Set(v8_str("o"), templ_o->NewInstance()); context->Global()->Set(v8_str("p"), templ_p->NewInstance()); v8::Handle value = CompileRun( "o.__proto__ = p;" "for (var i = 0; i < 7; i++) {" " o.x;" // Now it should be ICed and keep a reference to x defined on p "}" "var result = 0;" "for (var i = 0; i < 7; i++) {" " result += o.x;" "}" "result"); CHECK_EQ(42 * 7, value->Int32Value()); } // Test the case when we stored callback into // a stub, but it got invalidated later on. THREADED_TEST(InterceptorLoadICInvalidatedCallback) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); v8::Handle templ_o = ObjectTemplate::New(isolate); templ_o->SetHandler( v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); v8::Handle templ_p = ObjectTemplate::New(isolate); templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis); LocalContext context; context->Global()->Set(v8_str("o"), templ_o->NewInstance()); context->Global()->Set(v8_str("p"), templ_p->NewInstance()); v8::Handle value = CompileRun( "inbetween = new Object();" "o.__proto__ = inbetween;" "inbetween.__proto__ = p;" "for (var i = 0; i < 10; i++) {" " o.y;" // Now it should be ICed and keep a reference to y defined on p "}" "inbetween.y = 42;" "var result = 0;" "for (var i = 0; i < 10; i++) {" " result += o.y;" "}" "result"); CHECK_EQ(42 * 10, value->Int32Value()); } // Test the case when we stored callback into // a stub, but it got invalidated later on due to override on // global object which is between interceptor and callbacks' holders. THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); v8::Handle templ_o = ObjectTemplate::New(isolate); templ_o->SetHandler( v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); v8::Handle templ_p = ObjectTemplate::New(isolate); templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis); LocalContext context; context->Global()->Set(v8_str("o"), templ_o->NewInstance()); context->Global()->Set(v8_str("p"), templ_p->NewInstance()); v8::Handle value = CompileRun( "o.__proto__ = this;" "this.__proto__ = p;" "for (var i = 0; i < 10; i++) {" " if (o.y != 239) throw 'oops: ' + o.y;" // Now it should be ICed and keep a reference to y defined on p "}" "this.y = 42;" "var result = 0;" "for (var i = 0; i < 10; i++) {" " result += o.y;" "}" "result"); CHECK_EQ(42 * 10, value->Int32Value()); } static void InterceptorLoadICGetter0( Local name, const v8::PropertyCallbackInfo& info) { ApiTestFuzzer::Fuzz(); CHECK(v8_str("x")->Equals(name)); info.GetReturnValue().Set(v8::Integer::New(info.GetIsolate(), 0)); } THREADED_TEST(InterceptorReturningZero) { CheckInterceptorLoadIC(InterceptorLoadICGetter0, "o.x == undefined ? 1 : 0", 0); } static void InterceptorStoreICSetter( Local key, Local value, const v8::PropertyCallbackInfo& info) { CHECK(v8_str("x")->Equals(key)); CHECK_EQ(42, value->Int32Value()); info.GetReturnValue().Set(value); } // This test should hit the store IC for the interceptor case. THREADED_TEST(InterceptorStoreIC) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); v8::Handle templ = ObjectTemplate::New(isolate); templ->SetHandler(v8::NamedPropertyHandlerConfiguration( InterceptorLoadICGetter, InterceptorStoreICSetter, 0, 0, 0, v8_str("data"))); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); CompileRun( "for (var i = 0; i < 1000; i++) {" " o.x = 42;" "}"); } THREADED_TEST(InterceptorStoreICWithNoSetter) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); v8::Handle templ = ObjectTemplate::New(isolate); templ->SetHandler( v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); v8::Handle value = CompileRun( "for (var i = 0; i < 1000; i++) {" " o.y = 239;" "}" "42 + o.y"); CHECK_EQ(239 + 42, value->Int32Value()); } THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) { v8::HandleScope scope(CcTest::isolate()); Handle parent = FunctionTemplate::New(CcTest::isolate()); Handle child = FunctionTemplate::New(CcTest::isolate()); 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(LegacyInterceptorDoesNotSeeSymbols) { LocalContext env; v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); Handle parent = FunctionTemplate::New(isolate); Handle child = FunctionTemplate::New(isolate); v8::Local age = v8::Symbol::New(isolate, v8_str("age")); child->Inherit(parent); AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter); AddInterceptor(child, StringInterceptorGetter, StringInterceptorSetter); env->Global()->Set(v8_str("Child"), child->GetFunction()); env->Global()->Set(v8_str("age"), age); CompileRun( "var child = new Child;" "child[age] = 10;"); ExpectInt32("child[age]", 10); ExpectBoolean("child.hasOwnProperty('age')", false); ExpectBoolean("child.hasOwnProperty('accessor_age')", true); } THREADED_TEST(GenericInterceptorDoesSeeSymbols) { LocalContext env; v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); Handle parent = FunctionTemplate::New(isolate); Handle child = FunctionTemplate::New(isolate); v8::Local age = v8::Symbol::New(isolate, v8_str("age")); v8::Local anon = v8::Symbol::New(isolate); child->Inherit(parent); AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter); AddInterceptor(child, GenericInterceptorGetter, GenericInterceptorSetter); env->Global()->Set(v8_str("Child"), child->GetFunction()); env->Global()->Set(v8_str("age"), age); env->Global()->Set(v8_str("anon"), anon); CompileRun( "var child = new Child;" "child[age] = 10;"); ExpectInt32("child[age]", 10); ExpectInt32("child._sym_age", 10); // Check that it also sees strings. CompileRun("child.foo = 47"); ExpectInt32("child.foo", 47); ExpectInt32("child._str_foo", 47); // Check that the interceptor can punt (in this case, on anonymous symbols). CompileRun("child[anon] = 31337"); ExpectInt32("child[anon]", 31337); } THREADED_TEST(NamedPropertyHandlerGetter) { echo_named_call_count = 0; v8::HandleScope scope(CcTest::isolate()); v8::Handle templ = v8::FunctionTemplate::New(CcTest::isolate()); templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( EchoNamedProperty, 0, 0, 0, 0, v8_str("data"))); LocalContext env; env->Global()->Set(v8_str("obj"), templ->GetFunction()->NewInstance()); CHECK_EQ(echo_named_call_count, 0); v8_compile("obj.x")->Run(); CHECK_EQ(echo_named_call_count, 1); const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; v8::Handle str = CompileRun(code); String::Utf8Value value(str); CHECK_EQ(0, strcmp(*value, "oddlepoddle")); // Check default behavior CHECK_EQ(10, v8_compile("obj.flob = 10;")->Run()->Int32Value()); CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); } int echo_indexed_call_count = 0; static void EchoIndexedProperty( uint32_t index, const v8::PropertyCallbackInfo& info) { ApiTestFuzzer::Fuzz(); CHECK(v8_num(637)->Equals(info.Data())); echo_indexed_call_count++; info.GetReturnValue().Set(v8_num(index)); } THREADED_TEST(IndexedPropertyHandlerGetter) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope scope(isolate); v8::Handle templ = v8::FunctionTemplate::New(isolate); templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration( EchoIndexedProperty, 0, 0, 0, 0, v8_num(637))); LocalContext env; env->Global()->Set(v8_str("obj"), templ->GetFunction()->NewInstance()); Local