|  | // 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 <stdlib.h> | 
|  |  | 
|  | #include "test/cctest/test-api.h" | 
|  |  | 
|  | #include "include/v8-util.h" | 
|  | #include "src/api/api-inl.h" | 
|  | #include "src/base/platform/platform.h" | 
|  | #include "src/codegen/compilation-cache.h" | 
|  | #include "src/execution/arguments.h" | 
|  | #include "src/execution/execution.h" | 
|  | #include "src/objects/objects-inl.h" | 
|  | #include "src/objects/objects.h" | 
|  | #include "src/runtime/runtime.h" | 
|  | #include "src/strings/unicode-inl.h" | 
|  | #include "src/utils/utils.h" | 
|  |  | 
|  | using ::v8::Boolean; | 
|  | using ::v8::BooleanObject; | 
|  | using ::v8::Context; | 
|  | using ::v8::Extension; | 
|  | using ::v8::Function; | 
|  | using ::v8::FunctionTemplate; | 
|  | 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::V8; | 
|  | using ::v8::Value; | 
|  |  | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void Returns42(const v8::FunctionCallbackInfo<v8::Value>& info) { | 
|  | info.GetReturnValue().Set(42); | 
|  | } | 
|  |  | 
|  | void Return239Callback(Local<String> name, | 
|  | const v8::PropertyCallbackInfo<Value>& 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> name, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) {} | 
|  |  | 
|  |  | 
|  | void EmptyInterceptorSetter(Local<Name> name, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) {} | 
|  |  | 
|  | void EmptyInterceptorQuery(Local<Name> name, | 
|  | const v8::PropertyCallbackInfo<v8::Integer>& info) {} | 
|  |  | 
|  | void EmptyInterceptorDeleter( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Boolean>& info) {} | 
|  |  | 
|  | void EmptyInterceptorEnumerator( | 
|  | const v8::PropertyCallbackInfo<v8::Array>& info) {} | 
|  |  | 
|  | void SimpleAccessorGetter(Local<String> name, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | Local<Object> self = Local<Object>::Cast(info.This()); | 
|  | info.GetReturnValue().Set( | 
|  | self->Get(info.GetIsolate()->GetCurrentContext(), | 
|  | String::Concat(info.GetIsolate(), v8_str("accessor_"), name)) | 
|  | .ToLocalChecked()); | 
|  | } | 
|  |  | 
|  | void SimpleAccessorSetter(Local<String> name, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<void>& info) { | 
|  | Local<Object> self = Local<Object>::Cast(info.This()); | 
|  | self->Set(info.GetIsolate()->GetCurrentContext(), | 
|  | String::Concat(info.GetIsolate(), v8_str("accessor_"), name), value) | 
|  | .FromJust(); | 
|  | } | 
|  |  | 
|  |  | 
|  | void SymbolAccessorGetter(Local<Name> name, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CHECK(name->IsSymbol()); | 
|  | Local<Symbol> sym = Local<Symbol>::Cast(name); | 
|  | if (sym->Name()->IsUndefined()) return; | 
|  | SimpleAccessorGetter(Local<String>::Cast(sym->Name()), info); | 
|  | } | 
|  |  | 
|  | void SymbolAccessorSetter(Local<Name> name, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<void>& info) { | 
|  | CHECK(name->IsSymbol()); | 
|  | Local<Symbol> sym = Local<Symbol>::Cast(name); | 
|  | if (sym->Name()->IsUndefined()) return; | 
|  | SimpleAccessorSetter(Local<String>::Cast(sym->Name()), value, info); | 
|  | } | 
|  |  | 
|  | void InterceptorGetter(Local<Name> generic_name, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | if (generic_name->IsSymbol()) return; | 
|  | Local<String> name = Local<String>::Cast(generic_name); | 
|  | String::Utf8Value utf8(info.GetIsolate(), 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; | 
|  | } | 
|  | Local<Object> self = Local<Object>::Cast(info.This()); | 
|  | info.GetReturnValue().Set( | 
|  | self->GetPrivate( | 
|  | info.GetIsolate()->GetCurrentContext(), | 
|  | v8::Private::ForApi(info.GetIsolate(), v8_str(name_str + i))) | 
|  | .ToLocalChecked()); | 
|  | } | 
|  |  | 
|  | void InterceptorSetter(Local<Name> generic_name, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | if (generic_name->IsSymbol()) return; | 
|  | Local<String> name = Local<String>::Cast(generic_name); | 
|  | // Intercept accesses that set certain integer values, for which the name does | 
|  | // not start with 'accessor_'. | 
|  | String::Utf8Value utf8(info.GetIsolate(), 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; | 
|  |  | 
|  | Local<Context> context = info.GetIsolate()->GetCurrentContext(); | 
|  | if (value->IsInt32() && value->Int32Value(context).FromJust() < 10000) { | 
|  | Local<Object> self = Local<Object>::Cast(info.This()); | 
|  | Local<v8::Private> symbol = v8::Private::ForApi(info.GetIsolate(), name); | 
|  | self->SetPrivate(context, symbol, value).FromJust(); | 
|  | info.GetReturnValue().Set(value); | 
|  | } | 
|  | } | 
|  |  | 
|  | void GenericInterceptorGetter(Local<Name> generic_name, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | Local<String> str; | 
|  | if (generic_name->IsSymbol()) { | 
|  | Local<Value> name = Local<Symbol>::Cast(generic_name)->Name(); | 
|  | if (name->IsUndefined()) return; | 
|  | str = String::Concat(info.GetIsolate(), v8_str("_sym_"), | 
|  | Local<String>::Cast(name)); | 
|  | } else { | 
|  | Local<String> name = Local<String>::Cast(generic_name); | 
|  | String::Utf8Value utf8(info.GetIsolate(), name); | 
|  | char* name_str = *utf8; | 
|  | if (*name_str == '_') return; | 
|  | str = String::Concat(info.GetIsolate(), v8_str("_str_"), name); | 
|  | } | 
|  |  | 
|  | Local<Object> self = Local<Object>::Cast(info.This()); | 
|  | info.GetReturnValue().Set( | 
|  | self->Get(info.GetIsolate()->GetCurrentContext(), str).ToLocalChecked()); | 
|  | } | 
|  |  | 
|  | void GenericInterceptorSetter(Local<Name> generic_name, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | Local<String> str; | 
|  | if (generic_name->IsSymbol()) { | 
|  | Local<Value> name = Local<Symbol>::Cast(generic_name)->Name(); | 
|  | if (name->IsUndefined()) return; | 
|  | str = String::Concat(info.GetIsolate(), v8_str("_sym_"), | 
|  | Local<String>::Cast(name)); | 
|  | } else { | 
|  | Local<String> name = Local<String>::Cast(generic_name); | 
|  | String::Utf8Value utf8(info.GetIsolate(), name); | 
|  | char* name_str = *utf8; | 
|  | if (*name_str == '_') return; | 
|  | str = String::Concat(info.GetIsolate(), v8_str("_str_"), name); | 
|  | } | 
|  |  | 
|  | Local<Object> self = Local<Object>::Cast(info.This()); | 
|  | self->Set(info.GetIsolate()->GetCurrentContext(), str, value).FromJust(); | 
|  | info.GetReturnValue().Set(value); | 
|  | } | 
|  |  | 
|  | void AddAccessor(Local<FunctionTemplate> templ, Local<String> name, | 
|  | v8::AccessorGetterCallback getter, | 
|  | v8::AccessorSetterCallback setter) { | 
|  | templ->PrototypeTemplate()->SetAccessor(name, getter, setter); | 
|  | } | 
|  |  | 
|  | void AddAccessor(Local<FunctionTemplate> templ, Local<Name> name, | 
|  | v8::AccessorNameGetterCallback getter, | 
|  | v8::AccessorNameSetterCallback setter) { | 
|  | templ->PrototypeTemplate()->SetAccessor(name, getter, setter); | 
|  | } | 
|  |  | 
|  | void AddStringOnlyInterceptor(Local<FunctionTemplate> templ, | 
|  | v8::GenericNamedPropertyGetterCallback getter, | 
|  | v8::GenericNamedPropertySetterCallback setter) { | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | getter, setter, nullptr, nullptr, nullptr, Local<v8::Value>(), | 
|  | v8::PropertyHandlerFlags::kOnlyInterceptStrings)); | 
|  | } | 
|  |  | 
|  | void AddInterceptor(Local<FunctionTemplate> templ, | 
|  | v8::GenericNamedPropertyGetterCallback getter, | 
|  | v8::GenericNamedPropertySetterCallback setter) { | 
|  | templ->InstanceTemplate()->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(getter, setter)); | 
|  | } | 
|  |  | 
|  |  | 
|  | v8::Local<v8::Object> bottom; | 
|  |  | 
|  | void CheckThisIndexedPropertyHandler( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyHandler)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | void CheckThisNamedPropertyHandler( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | void CheckThisIndexedPropertyDefiner( | 
|  | uint32_t index, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDefiner)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | void CheckThisNamedPropertyDefiner( | 
|  | Local<Name> property, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDefiner)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | void CheckThisIndexedPropertySetter( | 
|  | uint32_t index, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertySetter)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | void CheckThisIndexedPropertyDescriptor( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDescriptor)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | void CheckThisNamedPropertyDescriptor( | 
|  | Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDescriptor)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | void CheckThisNamedPropertySetter( | 
|  | Local<Name> property, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | void CheckThisIndexedPropertyQuery( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | void CheckThisNamedPropertyQuery( | 
|  | Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | void CheckThisIndexedPropertyDeleter( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | void CheckThisNamedPropertyDeleter( | 
|  | Local<Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | void CheckThisIndexedPropertyEnumerator( | 
|  | const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | void CheckThisNamedPropertyEnumerator( | 
|  | const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator)); | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.This() | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), bottom) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | int echo_named_call_count; | 
|  |  | 
|  |  | 
|  | void EchoNamedProperty(Local<Name> name, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(v8_str("data") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), info.Data()) | 
|  | .FromJust()); | 
|  | echo_named_call_count++; | 
|  | info.GetReturnValue().Set(name); | 
|  | } | 
|  |  | 
|  | void InterceptorHasOwnPropertyGetter( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | } | 
|  |  | 
|  | void InterceptorHasOwnPropertyGetterGC( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CcTest::CollectAllGarbage(); | 
|  | } | 
|  |  | 
|  | int query_counter_int = 0; | 
|  |  | 
|  | void QueryCallback(Local<Name> property, | 
|  | const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | query_counter_int++; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Examples that show when the query callback is triggered. | 
|  | THREADED_TEST(QueryInterceptor) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); | 
|  | templ->InstanceTemplate()->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(nullptr, nullptr, QueryCallback)); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CHECK_EQ(0, query_counter_int); | 
|  | v8::Local<Value> result = | 
|  | v8_compile("Object.getOwnPropertyDescriptor(obj, 'x');") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked(); | 
|  | CHECK_EQ(1, query_counter_int); | 
|  | CHECK_EQ(v8::PropertyAttribute::None, | 
|  | static_cast<v8::PropertyAttribute>( | 
|  | result->Int32Value(env.local()).FromJust())); | 
|  |  | 
|  | v8_compile("Object.defineProperty(obj, 'not_enum', {value: 17});") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked(); | 
|  | CHECK_EQ(2, query_counter_int); | 
|  |  | 
|  | v8_compile( | 
|  | "Object.defineProperty(obj, 'enum', {value: 17, enumerable: true, " | 
|  | "writable: true});") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked(); | 
|  | CHECK_EQ(3, query_counter_int); | 
|  |  | 
|  | CHECK(v8_compile("obj.propertyIsEnumerable('enum');") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->BooleanValue(isolate)); | 
|  | CHECK_EQ(4, query_counter_int); | 
|  |  | 
|  | CHECK(!v8_compile("obj.propertyIsEnumerable('not_enum');") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->BooleanValue(isolate)); | 
|  | CHECK_EQ(5, query_counter_int); | 
|  |  | 
|  | CHECK(v8_compile("obj.hasOwnProperty('enum');") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->BooleanValue(isolate)); | 
|  | CHECK_EQ(5, query_counter_int); | 
|  |  | 
|  | CHECK(v8_compile("obj.hasOwnProperty('not_enum');") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->BooleanValue(isolate)); | 
|  | CHECK_EQ(5, query_counter_int); | 
|  |  | 
|  | CHECK(!v8_compile("obj.hasOwnProperty('x');") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->BooleanValue(isolate)); | 
|  | CHECK_EQ(6, query_counter_int); | 
|  |  | 
|  | CHECK(!v8_compile("obj.propertyIsEnumerable('undef');") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->BooleanValue(isolate)); | 
|  | CHECK_EQ(7, query_counter_int); | 
|  |  | 
|  | v8_compile("Object.defineProperty(obj, 'enum', {value: 42});") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked(); | 
|  | CHECK_EQ(8, query_counter_int); | 
|  |  | 
|  | v8_compile("Object.isFrozen('obj.x');")->Run(env.local()).ToLocalChecked(); | 
|  | CHECK_EQ(8, query_counter_int); | 
|  |  | 
|  | v8_compile("'x' in obj;")->Run(env.local()).ToLocalChecked(); | 
|  | CHECK_EQ(9, query_counter_int); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool get_was_called = false; | 
|  | bool set_was_called = false; | 
|  |  | 
|  | int set_was_called_counter = 0; | 
|  |  | 
|  | void GetterCallback(Local<Name> property, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | get_was_called = true; | 
|  | } | 
|  |  | 
|  | void SetterCallback(Local<Name> property, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | set_was_called = true; | 
|  | set_was_called_counter++; | 
|  | } | 
|  |  | 
|  | void InterceptingSetterCallback( | 
|  | Local<Name> property, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | info.GetReturnValue().Set(value); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Check that get callback is called in defineProperty with accessor descriptor. | 
|  | THREADED_TEST(DefinerCallbackAccessorInterceptor) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(GetterCallback, SetterCallback)); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | get_was_called = false; | 
|  | set_was_called = false; | 
|  |  | 
|  | v8_compile("Object.defineProperty(obj, 'x', {set: function() {return 17;}});") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked(); | 
|  | CHECK(get_was_called); | 
|  | CHECK(!set_was_called); | 
|  | } | 
|  |  | 
|  | // Check that set callback is called for function declarations. | 
|  | THREADED_TEST(SetterCallbackFunctionDeclarationInterceptor) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  |  | 
|  | v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate(); | 
|  | object_template->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(nullptr, SetterCallback)); | 
|  | v8::Local<v8::Context> ctx = | 
|  | v8::Context::New(CcTest::isolate(), nullptr, object_template); | 
|  |  | 
|  | set_was_called_counter = 0; | 
|  |  | 
|  | // Declare function. | 
|  | v8::Local<v8::String> code = v8_str("function x() {return 42;}; x();"); | 
|  | CHECK_EQ(42, v8::Script::Compile(ctx, code) | 
|  | .ToLocalChecked() | 
|  | ->Run(ctx) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(ctx) | 
|  | .FromJust()); | 
|  | CHECK_EQ(1, set_was_called_counter); | 
|  |  | 
|  | // Redeclare function. | 
|  | code = v8_str("function x() {return 43;}; x();"); | 
|  | CHECK_EQ(43, v8::Script::Compile(ctx, code) | 
|  | .ToLocalChecked() | 
|  | ->Run(ctx) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(ctx) | 
|  | .FromJust()); | 
|  | CHECK_EQ(2, set_was_called_counter); | 
|  |  | 
|  | // Redefine function. | 
|  | code = v8_str("x = function() {return 44;}; x();"); | 
|  | CHECK_EQ(44, v8::Script::Compile(ctx, code) | 
|  | .ToLocalChecked() | 
|  | ->Run(ctx) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(ctx) | 
|  | .FromJust()); | 
|  | CHECK_EQ(3, set_was_called_counter); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | int descriptor_was_called; | 
|  |  | 
|  | void PropertyDescriptorCallback( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | // Intercept the callback by setting a different descriptor. | 
|  | descriptor_was_called++; | 
|  | const char* code = | 
|  | "var desc = {value: 5};" | 
|  | "desc;"; | 
|  | Local<Value> descriptor = v8_compile(code) | 
|  | ->Run(info.GetIsolate()->GetCurrentContext()) | 
|  | .ToLocalChecked(); | 
|  | info.GetReturnValue().Set(descriptor); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | // Check that the descriptor callback is called on the global object. | 
|  | THREADED_TEST(DescriptorCallbackOnGlobalObject) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  |  | 
|  | v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate(); | 
|  | object_template->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, PropertyDescriptorCallback, nullptr, nullptr, nullptr)); | 
|  | v8::Local<v8::Context> ctx = | 
|  | v8::Context::New(CcTest::isolate(), nullptr, object_template); | 
|  |  | 
|  | descriptor_was_called = 0; | 
|  |  | 
|  | // Declare function. | 
|  | v8::Local<v8::String> code = v8_str( | 
|  | "var x = 42; var desc = Object.getOwnPropertyDescriptor(this, 'x'); " | 
|  | "desc.value;"); | 
|  | CHECK_EQ(5, v8::Script::Compile(ctx, code) | 
|  | .ToLocalChecked() | 
|  | ->Run(ctx) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(ctx) | 
|  | .FromJust()); | 
|  | CHECK_EQ(1, descriptor_was_called); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | void QueryCallbackSetDontDelete( | 
|  | Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | info.GetReturnValue().Set(v8::PropertyAttribute::DontDelete); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Regression for a Node.js test that fails in debug mode. | 
|  | THREADED_TEST(InterceptorFunctionRedeclareWithQueryCallback) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  |  | 
|  | v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate(); | 
|  | object_template->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, QueryCallbackSetDontDelete)); | 
|  | v8::Local<v8::Context> ctx = | 
|  | v8::Context::New(CcTest::isolate(), nullptr, object_template); | 
|  |  | 
|  | // Declare and redeclare function. | 
|  | v8::Local<v8::String> code = v8_str( | 
|  | "function x() {return 42;};" | 
|  | "function x() {return 43;};"); | 
|  | v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).ToLocalChecked(); | 
|  | } | 
|  |  | 
|  | // Regression test for chromium bug 656648. | 
|  | // Do not crash on non-masking, intercepting setter callbacks. | 
|  | THREADED_TEST(NonMaskingInterceptor) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  |  | 
|  | v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate(); | 
|  | object_template->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, InterceptingSetterCallback, nullptr, nullptr, nullptr, | 
|  | Local<Value>(), v8::PropertyHandlerFlags::kNonMasking)); | 
|  | v8::Local<v8::Context> ctx = | 
|  | v8::Context::New(CcTest::isolate(), nullptr, object_template); | 
|  |  | 
|  | v8::Local<v8::String> code = v8_str("function x() {return 43;};"); | 
|  | v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).ToLocalChecked(); | 
|  | } | 
|  |  | 
|  | // Check that function re-declarations throw if they are read-only. | 
|  | THREADED_TEST(SetterCallbackFunctionDeclarationInterceptorThrow) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  |  | 
|  | v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate(); | 
|  | object_template->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(nullptr, SetterCallback)); | 
|  | v8::Local<v8::Context> ctx = | 
|  | v8::Context::New(CcTest::isolate(), nullptr, object_template); | 
|  |  | 
|  | set_was_called = false; | 
|  |  | 
|  | v8::Local<v8::String> code = v8_str( | 
|  | "function x() {return 42;};" | 
|  | "Object.defineProperty(this, 'x', {" | 
|  | "configurable: false, " | 
|  | "writable: false});" | 
|  | "x();"); | 
|  | CHECK_EQ(42, v8::Script::Compile(ctx, code) | 
|  | .ToLocalChecked() | 
|  | ->Run(ctx) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(ctx) | 
|  | .FromJust()); | 
|  |  | 
|  | CHECK(set_was_called); | 
|  |  | 
|  | v8::TryCatch try_catch(CcTest::isolate()); | 
|  | set_was_called = false; | 
|  |  | 
|  | // Redeclare function that is read-only. | 
|  | code = v8_str("function x() {return 43;};"); | 
|  | CHECK(v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).IsEmpty()); | 
|  | CHECK(try_catch.HasCaught()); | 
|  |  | 
|  | CHECK(!set_was_called); | 
|  | } | 
|  |  | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool get_was_called_in_order = false; | 
|  | bool define_was_called_in_order = false; | 
|  |  | 
|  | void GetterCallbackOrder(Local<Name> property, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | get_was_called_in_order = true; | 
|  | CHECK(!define_was_called_in_order); | 
|  | info.GetReturnValue().Set(property); | 
|  | } | 
|  |  | 
|  | void DefinerCallbackOrder(Local<Name> property, | 
|  | const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | // Get called before DefineProperty because we query the descriptor first. | 
|  | CHECK(get_was_called_in_order); | 
|  | define_was_called_in_order = true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Check that getter callback is called before definer callback. | 
|  | THREADED_TEST(DefinerCallbackGetAndDefine) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | GetterCallbackOrder, SetterCallback, nullptr, nullptr, nullptr, | 
|  | DefinerCallbackOrder)); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | CHECK(!get_was_called_in_order); | 
|  | CHECK(!define_was_called_in_order); | 
|  |  | 
|  | v8_compile("Object.defineProperty(obj, 'x', {set: function() {return 17;}});") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked(); | 
|  | CHECK(get_was_called_in_order); | 
|  | CHECK(define_was_called_in_order); | 
|  | } | 
|  |  | 
|  | namespace {  //  namespace for InObjectLiteralDefinitionWithInterceptor | 
|  |  | 
|  | // Workaround for no-snapshot builds: only intercept once Context::New() is | 
|  | // done, otherwise we'll intercept | 
|  | // bootstrapping like defining array on the global object. | 
|  | bool context_is_done = false; | 
|  | bool getter_callback_was_called = false; | 
|  |  | 
|  | void ReturnUndefinedGetterCallback( | 
|  | Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | if (context_is_done) { | 
|  | getter_callback_was_called = true; | 
|  | info.GetReturnValue().SetUndefined(); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Check that an interceptor is not invoked during ES6 style definitions inside | 
|  | // an object literal. | 
|  | THREADED_TEST(InObjectLiteralDefinitionWithInterceptor) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  |  | 
|  | // Set up a context in which all global object definitions are intercepted. | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate(); | 
|  | object_template->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(ReturnUndefinedGetterCallback)); | 
|  | v8::Local<v8::Context> ctx = | 
|  | v8::Context::New(CcTest::isolate(), nullptr, object_template); | 
|  |  | 
|  | context_is_done = true; | 
|  |  | 
|  | // The interceptor returns undefined for any global object, | 
|  | // so setting a property on an object should throw. | 
|  | v8::Local<v8::String> code = v8_str("var o = {}; o.x = 5"); | 
|  | { | 
|  | getter_callback_was_called = false; | 
|  | v8::TryCatch try_catch(CcTest::isolate()); | 
|  | CHECK(v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).IsEmpty()); | 
|  | CHECK(try_catch.HasCaught()); | 
|  | CHECK(getter_callback_was_called); | 
|  | } | 
|  |  | 
|  | // Defining a property in the object literal should not throw | 
|  | // because the interceptor is not invoked. | 
|  | { | 
|  | getter_callback_was_called = false; | 
|  | v8::TryCatch try_catch(CcTest::isolate()); | 
|  | code = v8_str("var l = {x: 5};"); | 
|  | CHECK(v8::Script::Compile(ctx, code) | 
|  | .ToLocalChecked() | 
|  | ->Run(ctx) | 
|  | .ToLocalChecked() | 
|  | ->IsUndefined()); | 
|  | CHECK(!try_catch.HasCaught()); | 
|  | CHECK(!getter_callback_was_called); | 
|  | } | 
|  | } | 
|  |  | 
|  | THREADED_TEST(InterceptorHasOwnProperty) { | 
|  | LocalContext context; | 
|  | v8::Isolate* isolate = context->GetIsolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); | 
|  | Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); | 
|  | instance_templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetter)); | 
|  | Local<Function> function = | 
|  | fun_templ->GetFunction(context.local()).ToLocalChecked(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("constructor"), function) | 
|  | .FromJust(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "var o = new constructor();" | 
|  | "o.hasOwnProperty('ostehaps');"); | 
|  | CHECK(!value->BooleanValue(isolate)); | 
|  | value = CompileRun( | 
|  | "o.ostehaps = 42;" | 
|  | "o.hasOwnProperty('ostehaps');"); | 
|  | CHECK(value->BooleanValue(isolate)); | 
|  | value = CompileRun( | 
|  | "var p = new constructor();" | 
|  | "p.hasOwnProperty('ostehaps');"); | 
|  | CHECK(!value->BooleanValue(isolate)); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { | 
|  | LocalContext context; | 
|  | v8::Isolate* isolate = context->GetIsolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); | 
|  | Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); | 
|  | instance_templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetterGC)); | 
|  | Local<Function> function = | 
|  | fun_templ->GetFunction(context.local()).ToLocalChecked(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("constructor"), function) | 
|  | .FromJust(); | 
|  | // 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::Local<Value> value = CompileRun( | 
|  | "var o = new constructor();" | 
|  | "o.__proto__ = new String(x);" | 
|  | "o.hasOwnProperty('ostehaps');"); | 
|  | CHECK(!value->BooleanValue(isolate)); | 
|  | } | 
|  |  | 
|  | static void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter, | 
|  | v8::GenericNamedPropertyQueryCallback query, | 
|  | const char* source, int expected) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | getter, nullptr, query, nullptr, nullptr, v8_str("data"))); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | v8::Local<Value> value = CompileRun(source); | 
|  | CHECK_EQ(expected, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  | static void CheckInterceptorLoadIC( | 
|  | v8::GenericNamedPropertyGetterCallback getter, const char* source, | 
|  | int expected) { | 
|  | CheckInterceptorIC(getter, nullptr, source, expected); | 
|  | } | 
|  |  | 
|  | static void InterceptorLoadICGetter( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | CHECK_EQ(isolate, info.GetIsolate()); | 
|  | v8::Local<v8::Context> context = isolate->GetCurrentContext(); | 
|  | CHECK(v8_str("data")->Equals(context, info.Data()).FromJust()); | 
|  | CHECK(v8_str("x")->Equals(context, name).FromJust()); | 
|  | 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> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | info.GetReturnValue().Set( | 
|  | v8_str("x") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), name) | 
|  | .FromJust() | 
|  | ? v8::Local<v8::Value>(v8::Integer::New(info.GetIsolate(), 42)) | 
|  | : v8::Local<v8::Value>()); | 
|  | } | 
|  |  | 
|  |  | 
|  | 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> name, const v8::PropertyCallbackInfo<v8::Value>& 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<String> name, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<void>& info) { | 
|  | Local<Object>::Cast(info.This()) | 
|  | ->CreateDataProperty(info.GetIsolate()->GetCurrentContext(), name, value) | 
|  | .FromJust(); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); | 
|  | templ->SetAccessor(v8_str("y"), Return239Callback); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | // Check the case when receiver and interceptor's holder | 
|  | // are the same objects. | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 7; i++) {" | 
|  | "  result = o.y;" | 
|  | "}"); | 
|  | CHECK_EQ(239, value->Int32Value(context.local()).FromJust()); | 
|  |  | 
|  | // 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(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); | 
|  | templ_o->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); | 
|  | v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate); | 
|  | templ_p->SetAccessor(v8_str("y"), Return239Callback); | 
|  |  | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ_o->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("p"), | 
|  | templ_p->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | // Check the case when receiver and interceptor's holder | 
|  | // are the same objects. | 
|  | v8::Local<Value> 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(context.local()).FromJust()); | 
|  |  | 
|  | // 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(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); | 
|  | templ->SetAccessor(v8_str("y"), Return239Callback); | 
|  |  | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | v8::Local<Value> 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(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // 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::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); | 
|  | templ_o->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); | 
|  | v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate); | 
|  | templ_p->SetAccessor(v8_str("y"), Return239Callback); | 
|  |  | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ_o->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("p"), | 
|  | templ_p->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | v8::Local<Value> 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(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // 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::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); | 
|  | templ_o->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); | 
|  | v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate); | 
|  | templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis); | 
|  |  | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ_o->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("p"), | 
|  | templ_p->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | v8::Local<Value> 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(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // 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::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); | 
|  | templ_o->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); | 
|  | v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate); | 
|  | templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis); | 
|  |  | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ_o->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("p"), | 
|  | templ_p->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | v8::Local<Value> 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(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  | // Test load of a non-existing global when a global object has an interceptor. | 
|  | THREADED_TEST(InterceptorLoadGlobalICGlobalWithInterceptor) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ_global = v8::ObjectTemplate::New(isolate); | 
|  | templ_global->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | EmptyInterceptorGetter, EmptyInterceptorSetter)); | 
|  |  | 
|  | LocalContext context(nullptr, templ_global); | 
|  | i::Handle<i::JSReceiver> global_proxy = | 
|  | v8::Utils::OpenHandle<Object, i::JSReceiver>(context->Global()); | 
|  | CHECK(global_proxy->IsJSGlobalProxy()); | 
|  | i::Handle<i::JSGlobalObject> global( | 
|  | i::JSGlobalObject::cast(global_proxy->map().prototype()), | 
|  | global_proxy->GetIsolate()); | 
|  | CHECK(global->map().has_named_interceptor()); | 
|  |  | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "var f = function() { " | 
|  | "  try {" | 
|  | "    x1;" | 
|  | "  } catch(e) {" | 
|  | "  }" | 
|  | "  return typeof x1 === 'undefined';" | 
|  | "};" | 
|  | "for (var i = 0; i < 10; i++) {" | 
|  | "  f();" | 
|  | "};" | 
|  | "f();"); | 
|  | CHECK(value->BooleanValue(isolate)); | 
|  |  | 
|  | value = CompileRun( | 
|  | "var f = function() { " | 
|  | "  try {" | 
|  | "    x2;" | 
|  | "    return false;" | 
|  | "  } catch(e) {" | 
|  | "    return true;" | 
|  | "  }" | 
|  | "};" | 
|  | "for (var i = 0; i < 10; i++) {" | 
|  | "  f();" | 
|  | "};" | 
|  | "f();"); | 
|  | CHECK(value->BooleanValue(isolate)); | 
|  |  | 
|  | value = CompileRun( | 
|  | "var f = function() { " | 
|  | "  try {" | 
|  | "    typeof(x3);" | 
|  | "    return true;" | 
|  | "  } catch(e) {" | 
|  | "    return false;" | 
|  | "  }" | 
|  | "};" | 
|  | "for (var i = 0; i < 10; i++) {" | 
|  | "  f();" | 
|  | "};" | 
|  | "f();"); | 
|  | CHECK(value->BooleanValue(isolate)); | 
|  | } | 
|  |  | 
|  | // Test load of a non-existing global through prototype chain when a global | 
|  | // object has an interceptor. | 
|  | THREADED_TEST(InterceptorLoadICGlobalWithInterceptor) { | 
|  | i::FLAG_allow_natives_syntax = true; | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ_global = v8::ObjectTemplate::New(isolate); | 
|  | templ_global->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | GenericInterceptorGetter, GenericInterceptorSetter)); | 
|  |  | 
|  | LocalContext context(nullptr, templ_global); | 
|  | i::Handle<i::JSReceiver> global_proxy = | 
|  | v8::Utils::OpenHandle<Object, i::JSReceiver>(context->Global()); | 
|  | CHECK(global_proxy->IsJSGlobalProxy()); | 
|  | i::Handle<i::JSGlobalObject> global( | 
|  | i::JSGlobalObject::cast(global_proxy->map().prototype()), | 
|  | global_proxy->GetIsolate()); | 
|  | CHECK(global->map().has_named_interceptor()); | 
|  |  | 
|  | ExpectInt32( | 
|  | "(function() {" | 
|  | "  var f = function(obj) { " | 
|  | "    return obj.foo;" | 
|  | "  };" | 
|  | "  var obj = { __proto__: this, _str_foo: 42 };" | 
|  | "  for (var i = 0; i < 1500; i++) obj['p' + i] = 0;" | 
|  | "  /* Ensure that |obj| is in dictionary mode. */" | 
|  | "  if (%HasFastProperties(obj)) return -1;" | 
|  | "  for (var i = 0; i < 3; i++) {" | 
|  | "    f(obj);" | 
|  | "  };" | 
|  | "  return f(obj);" | 
|  | "})();", | 
|  | 42); | 
|  | } | 
|  |  | 
|  | static void InterceptorLoadICGetter0( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(v8_str("x") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), name) | 
|  | .FromJust()); | 
|  | info.GetReturnValue().Set(v8::Integer::New(info.GetIsolate(), 0)); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(InterceptorReturningZero) { | 
|  | CheckInterceptorLoadIC(InterceptorLoadICGetter0, "o.x == undefined ? 1 : 0", | 
|  | 0); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | template <typename TKey, v8::internal::PropertyAttributes attribute> | 
|  | void HasICQuery(TKey name, const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | CHECK_EQ(isolate, info.GetIsolate()); | 
|  | info.GetReturnValue().Set(v8::Integer::New(isolate, attribute)); | 
|  | } | 
|  |  | 
|  | template <typename TKey> | 
|  | void HasICQueryToggle(TKey name, | 
|  | const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | static bool toggle = false; | 
|  | toggle = !toggle; | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | CHECK_EQ(isolate, info.GetIsolate()); | 
|  | info.GetReturnValue().Set(v8::Integer::New( | 
|  | isolate, toggle ? v8::internal::ABSENT : v8::internal::NONE)); | 
|  | } | 
|  |  | 
|  | int named_query_counter = 0; | 
|  | void NamedQueryCallback(Local<Name> name, | 
|  | const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | named_query_counter++; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | THREADED_TEST(InterceptorHasIC) { | 
|  | named_query_counter = 0; | 
|  | CheckInterceptorIC(nullptr, NamedQueryCallback, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  'x' in o;" | 
|  | "}", | 
|  | 0); | 
|  | CHECK_EQ(1000, named_query_counter); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(InterceptorHasICQueryAbsent) { | 
|  | CheckInterceptorIC(nullptr, HasICQuery<Local<Name>, v8::internal::ABSENT>, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  if ('x' in o) ++result;" | 
|  | "}", | 
|  | 0); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(InterceptorHasICQueryNone) { | 
|  | CheckInterceptorIC(nullptr, HasICQuery<Local<Name>, v8::internal::NONE>, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  if ('x' in o) ++result;" | 
|  | "}", | 
|  | 1000); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(InterceptorHasICGetter) { | 
|  | CheckInterceptorIC(InterceptorLoadICGetter, nullptr, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  if ('x' in o) ++result;" | 
|  | "}", | 
|  | 1000); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(InterceptorHasICQueryGetter) { | 
|  | CheckInterceptorIC(InterceptorLoadICGetter, | 
|  | HasICQuery<Local<Name>, v8::internal::ABSENT>, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  if ('x' in o) ++result;" | 
|  | "}", | 
|  | 0); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(InterceptorHasICQueryToggle) { | 
|  | CheckInterceptorIC(InterceptorLoadICGetter, HasICQueryToggle<Local<Name>>, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  if ('x' in o) ++result;" | 
|  | "}", | 
|  | 500); | 
|  | } | 
|  |  | 
|  | static void InterceptorStoreICSetter( | 
|  | Local<Name> key, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); | 
|  | CHECK(v8_str("x")->Equals(context, key).FromJust()); | 
|  | CHECK_EQ(42, value->Int32Value(context).FromJust()); | 
|  | 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::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | InterceptorLoadICGetter, InterceptorStoreICSetter, nullptr, nullptr, | 
|  | nullptr, v8_str("data"))); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  o.x = 42;" | 
|  | "}"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(InterceptorStoreICWithNoSetter) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  o.y = 239;" | 
|  | "}" | 
|  | "42 + o.y"); | 
|  | CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); | 
|  | Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); | 
|  | child->Inherit(parent); | 
|  | AddAccessor(parent, v8_str("age"), SimpleAccessorGetter, | 
|  | SimpleAccessorSetter); | 
|  | AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Child"), | 
|  | child->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | 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); | 
|  | Local<FunctionTemplate> parent = FunctionTemplate::New(isolate); | 
|  | Local<FunctionTemplate> child = FunctionTemplate::New(isolate); | 
|  | v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age")); | 
|  |  | 
|  | child->Inherit(parent); | 
|  | AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter); | 
|  | AddStringOnlyInterceptor(child, InterceptorGetter, InterceptorSetter); | 
|  |  | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Child"), | 
|  | child->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | env->Global()->Set(env.local(), v8_str("age"), age).FromJust(); | 
|  | 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); | 
|  | Local<FunctionTemplate> parent = FunctionTemplate::New(isolate); | 
|  | Local<FunctionTemplate> child = FunctionTemplate::New(isolate); | 
|  | v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age")); | 
|  | v8::Local<v8::Symbol> anon = v8::Symbol::New(isolate); | 
|  |  | 
|  | child->Inherit(parent); | 
|  | AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter); | 
|  | AddInterceptor(child, GenericInterceptorGetter, GenericInterceptorSetter); | 
|  |  | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Child"), | 
|  | child->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | env->Global()->Set(env.local(), v8_str("age"), age).FromJust(); | 
|  | env->Global()->Set(env.local(), v8_str("anon"), anon).FromJust(); | 
|  | 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::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | EchoNamedProperty, nullptr, nullptr, nullptr, nullptr, v8_str("data"))); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CHECK_EQ(0, echo_named_call_count); | 
|  | v8_compile("obj.x")->Run(env.local()).ToLocalChecked(); | 
|  | CHECK_EQ(1, echo_named_call_count); | 
|  | const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; | 
|  | v8::Local<Value> str = CompileRun(code); | 
|  | String::Utf8Value value(isolate, str); | 
|  | CHECK_EQ(0, strcmp(*value, "oddlepoddle")); | 
|  | // Check default behavior | 
|  | CHECK_EQ(10, v8_compile("obj.flob = 10;") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | CHECK(v8_compile("'myProperty' in obj") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->BooleanValue(isolate)); | 
|  | CHECK(v8_compile("delete obj.myProperty") | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->BooleanValue(isolate)); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | void NotInterceptingPropertyDefineCallback( | 
|  | Local<Name> name, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | // Do not intercept by not calling info.GetReturnValue().Set(). | 
|  | } | 
|  |  | 
|  | void InterceptingPropertyDefineCallback( | 
|  | Local<Name> name, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | // Intercept the callback by setting a non-empty handle | 
|  | info.GetReturnValue().Set(name); | 
|  | } | 
|  |  | 
|  | void CheckDescriptorInDefineCallback( | 
|  | Local<Name> name, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CHECK(!desc.has_writable()); | 
|  | CHECK(!desc.has_value()); | 
|  | CHECK(!desc.has_enumerable()); | 
|  | CHECK(desc.has_configurable()); | 
|  | CHECK(!desc.configurable()); | 
|  | CHECK(desc.has_get()); | 
|  | CHECK(desc.get()->IsFunction()); | 
|  | CHECK(desc.has_set()); | 
|  | CHECK(desc.set()->IsUndefined()); | 
|  | // intercept the callback by setting a non-empty handle | 
|  | info.GetReturnValue().Set(name); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | THREADED_TEST(PropertyDefinerCallback) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  |  | 
|  | {  // Intercept defineProperty() | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | NotInterceptingPropertyDefineCallback)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj.x = 17; " | 
|  | "Object.defineProperty(obj, 'x', {value: 42});" | 
|  | "obj.x;"; | 
|  | CHECK_EQ(42, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | {  // Intercept defineProperty() for correct accessor descriptor | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | CheckDescriptorInDefineCallback)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj.x = 17; " | 
|  | "Object.defineProperty(obj, 'x', {" | 
|  | "get: function(){ return 42; }, " | 
|  | "set: undefined," | 
|  | "configurable: 0" | 
|  | "});" | 
|  | "obj.x;"; | 
|  | CHECK_EQ(17, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | {  // Do not intercept defineProperty() | 
|  | v8::Local<v8::FunctionTemplate> templ2 = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ2->InstanceTemplate()->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | InterceptingPropertyDefineCallback)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ2->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | const char* code = | 
|  | "obj.x = 17; " | 
|  | "Object.defineProperty(obj, 'x', {value: 42});" | 
|  | "obj.x;"; | 
|  | CHECK_EQ(17, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | void NotInterceptingPropertyDefineCallbackIndexed( | 
|  | uint32_t index, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | // Do not intercept by not calling info.GetReturnValue().Set() | 
|  | } | 
|  |  | 
|  | void InterceptingPropertyDefineCallbackIndexed( | 
|  | uint32_t index, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | // intercept the callback by setting a non-empty handle | 
|  | info.GetReturnValue().Set(index); | 
|  | } | 
|  |  | 
|  | void CheckDescriptorInDefineCallbackIndexed( | 
|  | uint32_t index, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CHECK(!desc.has_writable()); | 
|  | CHECK(!desc.has_value()); | 
|  | CHECK(desc.has_enumerable()); | 
|  | CHECK(desc.enumerable()); | 
|  | CHECK(!desc.has_configurable()); | 
|  | CHECK(desc.has_get()); | 
|  | CHECK(desc.get()->IsFunction()); | 
|  | CHECK(desc.has_set()); | 
|  | CHECK(desc.set()->IsUndefined()); | 
|  | // intercept the callback by setting a non-empty handle | 
|  | info.GetReturnValue().Set(index); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | THREADED_TEST(PropertyDefinerCallbackIndexed) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  |  | 
|  | {  // Intercept defineProperty() | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | NotInterceptingPropertyDefineCallbackIndexed)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj[2] = 17; " | 
|  | "Object.defineProperty(obj, 2, {value: 42});" | 
|  | "obj[2];"; | 
|  | CHECK_EQ(42, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | {  // Intercept defineProperty() for correct accessor descriptor | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | CheckDescriptorInDefineCallbackIndexed)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj[2] = 17; " | 
|  | "Object.defineProperty(obj, 2, {" | 
|  | "get: function(){ return 42; }, " | 
|  | "set: undefined," | 
|  | "enumerable: true" | 
|  | "});" | 
|  | "obj[2];"; | 
|  | CHECK_EQ(17, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | {  // Do not intercept defineProperty() | 
|  | v8::Local<v8::FunctionTemplate> templ2 = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ2->InstanceTemplate()->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | InterceptingPropertyDefineCallbackIndexed)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ2->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | const char* code = | 
|  | "obj[2] = 17; " | 
|  | "Object.defineProperty(obj, 2, {value: 42});" | 
|  | "obj[2];"; | 
|  | CHECK_EQ(17, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test that freeze() is intercepted. | 
|  | THREADED_TEST(PropertyDefinerCallbackForFreeze) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | InterceptingPropertyDefineCallback)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj.x = 17; " | 
|  | "Object.freeze(obj.x); " | 
|  | "Object.isFrozen(obj.x);"; | 
|  |  | 
|  | CHECK(v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->BooleanValue(isolate)); | 
|  | } | 
|  |  | 
|  | // Check that the descriptor passed to the callback is enumerable. | 
|  | namespace { | 
|  | void CheckEnumerablePropertyDefineCallback( | 
|  | Local<Name> name, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CHECK(desc.has_value()); | 
|  | CHECK_EQ(42, desc.value() | 
|  | ->Int32Value(info.GetIsolate()->GetCurrentContext()) | 
|  | .FromJust()); | 
|  | CHECK(desc.has_enumerable()); | 
|  | CHECK(desc.enumerable()); | 
|  | CHECK(!desc.has_writable()); | 
|  |  | 
|  | // intercept the callback by setting a non-empty handle | 
|  | info.GetReturnValue().Set(name); | 
|  | } | 
|  | }  // namespace | 
|  | THREADED_TEST(PropertyDefinerCallbackEnumerable) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | CheckEnumerablePropertyDefineCallback)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj.x = 17; " | 
|  | "Object.defineProperty(obj, 'x', {value: 42, enumerable: true});" | 
|  | "obj.x;"; | 
|  | CHECK_EQ(17, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | // Check that the descriptor passed to the callback is configurable. | 
|  | namespace { | 
|  | void CheckConfigurablePropertyDefineCallback( | 
|  | Local<Name> name, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CHECK(desc.has_value()); | 
|  | CHECK_EQ(42, desc.value() | 
|  | ->Int32Value(info.GetIsolate()->GetCurrentContext()) | 
|  | .FromJust()); | 
|  | CHECK(desc.has_configurable()); | 
|  | CHECK(desc.configurable()); | 
|  |  | 
|  | // intercept the callback by setting a non-empty handle | 
|  | info.GetReturnValue().Set(name); | 
|  | } | 
|  | }  // namespace | 
|  | THREADED_TEST(PropertyDefinerCallbackConfigurable) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | CheckConfigurablePropertyDefineCallback)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj.x = 17; " | 
|  | "Object.defineProperty(obj, 'x', {value: 42, configurable: true});" | 
|  | "obj.x;"; | 
|  | CHECK_EQ(17, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | // Check that the descriptor passed to the callback is writable. | 
|  | namespace { | 
|  | void CheckWritablePropertyDefineCallback( | 
|  | Local<Name> name, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CHECK(desc.has_writable()); | 
|  | CHECK(desc.writable()); | 
|  |  | 
|  | // intercept the callback by setting a non-empty handle | 
|  | info.GetReturnValue().Set(name); | 
|  | } | 
|  | }  // namespace | 
|  | THREADED_TEST(PropertyDefinerCallbackWritable) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | CheckWritablePropertyDefineCallback)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj.x = 17; " | 
|  | "Object.defineProperty(obj, 'x', {value: 42, writable: true});" | 
|  | "obj.x;"; | 
|  | CHECK_EQ(17, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | // Check that the descriptor passed to the callback has a getter. | 
|  | namespace { | 
|  | void CheckGetterPropertyDefineCallback( | 
|  | Local<Name> name, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CHECK(desc.has_get()); | 
|  | CHECK(!desc.has_set()); | 
|  | // intercept the callback by setting a non-empty handle | 
|  | info.GetReturnValue().Set(name); | 
|  | } | 
|  | }  // namespace | 
|  | THREADED_TEST(PropertyDefinerCallbackWithGetter) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | CheckGetterPropertyDefineCallback)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj.x = 17;" | 
|  | "Object.defineProperty(obj, 'x', {get: function() {return 42;}});" | 
|  | "obj.x;"; | 
|  | CHECK_EQ(17, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | // Check that the descriptor passed to the callback has a setter. | 
|  | namespace { | 
|  | void CheckSetterPropertyDefineCallback( | 
|  | Local<Name> name, const v8::PropertyDescriptor& desc, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CHECK(desc.has_set()); | 
|  | CHECK(!desc.has_get()); | 
|  | // intercept the callback by setting a non-empty handle | 
|  | info.GetReturnValue().Set(name); | 
|  | } | 
|  | }  // namespace | 
|  | THREADED_TEST(PropertyDefinerCallbackWithSetter) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, nullptr, | 
|  | CheckSetterPropertyDefineCallback)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "Object.defineProperty(obj, 'x', {set: function() {return 42;}});" | 
|  | "obj.x = 17;"; | 
|  | CHECK_EQ(17, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | void EmptyPropertyDescriptorCallback( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | // Do not intercept by not calling info.GetReturnValue().Set(). | 
|  | } | 
|  |  | 
|  | void InterceptingPropertyDescriptorCallback( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | // Intercept the callback by setting a different descriptor. | 
|  | const char* code = | 
|  | "var desc = {value: 42};" | 
|  | "desc;"; | 
|  | Local<Value> descriptor = v8_compile(code) | 
|  | ->Run(info.GetIsolate()->GetCurrentContext()) | 
|  | .ToLocalChecked(); | 
|  | info.GetReturnValue().Set(descriptor); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | THREADED_TEST(PropertyDescriptorCallback) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | LocalContext env; | 
|  |  | 
|  | {  // Normal behavior of getOwnPropertyDescriptor() with empty callback. | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, EmptyPropertyDescriptorCallback, nullptr, nullptr, | 
|  | nullptr)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj.x = 17; " | 
|  | "var desc = Object.getOwnPropertyDescriptor(obj, 'x');" | 
|  | "desc.value;"; | 
|  | CHECK_EQ(17, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  | {  // Intercept getOwnPropertyDescriptor(). | 
|  | v8::Local<v8::FunctionTemplate> templ = | 
|  | v8::FunctionTemplate::New(CcTest::isolate()); | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, InterceptingPropertyDescriptorCallback, nullptr, | 
|  | nullptr, nullptr)); | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | const char* code = | 
|  | "obj.x = 17; " | 
|  | "var desc = Object.getOwnPropertyDescriptor(obj, 'x');" | 
|  | "desc.value;"; | 
|  | CHECK_EQ(42, v8_compile(code) | 
|  | ->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | int echo_indexed_call_count = 0; | 
|  | }  // namespace | 
|  |  | 
|  | static void EchoIndexedProperty( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(v8_num(637) | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), info.Data()) | 
|  | .FromJust()); | 
|  | echo_indexed_call_count++; | 
|  | info.GetReturnValue().Set(v8_num(index)); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedPropertyHandlerGetter) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); | 
|  | templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | EchoIndexedProperty, nullptr, nullptr, nullptr, nullptr, v8_num(637))); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | Local<Script> script = v8_compile("obj[900]"); | 
|  | CHECK_EQ(900, script->Run(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(env.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(PropertyHandlerInPrototype) { | 
|  | LocalContext env; | 
|  | v8::Isolate* isolate = env->GetIsolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  |  | 
|  | v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); | 
|  | templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | CheckThisIndexedPropertyHandler, CheckThisIndexedPropertySetter, | 
|  | CheckThisIndexedPropertyQuery, CheckThisIndexedPropertyDeleter, | 
|  | CheckThisIndexedPropertyEnumerator)); | 
|  |  | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | CheckThisNamedPropertyHandler, CheckThisNamedPropertySetter, | 
|  | CheckThisNamedPropertyQuery, CheckThisNamedPropertyDeleter, | 
|  | CheckThisNamedPropertyEnumerator)); | 
|  |  | 
|  | bottom = templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked(); | 
|  | Local<v8::Object> top = templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked(); | 
|  | Local<v8::Object> middle = templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked(); | 
|  |  | 
|  | bottom->SetPrototype(env.local(), middle).FromJust(); | 
|  | middle->SetPrototype(env.local(), top).FromJust(); | 
|  | env->Global()->Set(env.local(), v8_str("obj"), bottom).FromJust(); | 
|  |  | 
|  | // Indexed and named get. | 
|  | CompileRun("obj[0]"); | 
|  | CompileRun("obj.x"); | 
|  |  | 
|  | // Indexed and named set. | 
|  | CompileRun("obj[1] = 42"); | 
|  | CompileRun("obj.y = 42"); | 
|  |  | 
|  | // Indexed and named query. | 
|  | CompileRun("0 in obj"); | 
|  | CompileRun("'x' in obj"); | 
|  |  | 
|  | // Indexed and named deleter. | 
|  | CompileRun("delete obj[0]"); | 
|  | CompileRun("delete obj.x"); | 
|  |  | 
|  | // Enumerators. | 
|  | CompileRun("for (var p in obj) ;"); | 
|  | } | 
|  |  | 
|  | TEST(PropertyHandlerInPrototypeWithDefine) { | 
|  | LocalContext env; | 
|  | v8::Isolate* isolate = env->GetIsolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  |  | 
|  | v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); | 
|  | templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | CheckThisIndexedPropertyHandler, CheckThisIndexedPropertySetter, | 
|  | CheckThisIndexedPropertyDescriptor, CheckThisIndexedPropertyDeleter, | 
|  | CheckThisIndexedPropertyEnumerator, CheckThisIndexedPropertyDefiner)); | 
|  |  | 
|  | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | CheckThisNamedPropertyHandler, CheckThisNamedPropertySetter, | 
|  | CheckThisNamedPropertyDescriptor, CheckThisNamedPropertyDeleter, | 
|  | CheckThisNamedPropertyEnumerator, CheckThisNamedPropertyDefiner)); | 
|  |  | 
|  | bottom = templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked(); | 
|  | Local<v8::Object> top = templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked(); | 
|  | Local<v8::Object> middle = templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked(); | 
|  |  | 
|  | bottom->SetPrototype(env.local(), middle).FromJust(); | 
|  | middle->SetPrototype(env.local(), top).FromJust(); | 
|  | env->Global()->Set(env.local(), v8_str("obj"), bottom).FromJust(); | 
|  |  | 
|  | // Indexed and named get. | 
|  | CompileRun("obj[0]"); | 
|  | CompileRun("obj.x"); | 
|  |  | 
|  | // Indexed and named set. | 
|  | CompileRun("obj[1] = 42"); | 
|  | CompileRun("obj.y = 42"); | 
|  |  | 
|  | // Indexed and named deleter. | 
|  | CompileRun("delete obj[0]"); | 
|  | CompileRun("delete obj.x"); | 
|  |  | 
|  | // Enumerators. | 
|  | CompileRun("for (var p in obj) ;"); | 
|  |  | 
|  | // Indexed and named definer. | 
|  | CompileRun("Object.defineProperty(obj, 2, {});"); | 
|  | CompileRun("Object.defineProperty(obj, 'z', {});"); | 
|  |  | 
|  | // Indexed and named propertyDescriptor. | 
|  | CompileRun("Object.getOwnPropertyDescriptor(obj, 2);"); | 
|  | CompileRun("Object.getOwnPropertyDescriptor(obj, 'z');"); | 
|  | } | 
|  |  | 
|  |  | 
|  | bool is_bootstrapping = false; | 
|  | static void PrePropertyHandlerGet( | 
|  | Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (!is_bootstrapping && | 
|  | v8_str("pre") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), key) | 
|  | .FromJust()) { | 
|  | info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre")); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static void PrePropertyHandlerQuery( | 
|  | Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | if (!is_bootstrapping && | 
|  | v8_str("pre") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), key) | 
|  | .FromJust()) { | 
|  | info.GetReturnValue().Set(static_cast<int32_t>(v8::None)); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(PrePropertyHandler) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); | 
|  | desc->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | PrePropertyHandlerGet, nullptr, PrePropertyHandlerQuery)); | 
|  | is_bootstrapping = true; | 
|  | LocalContext env(nullptr, desc->InstanceTemplate()); | 
|  | is_bootstrapping = false; | 
|  | CompileRun("var pre = 'Object: pre'; var on = 'Object: on';"); | 
|  | v8::Local<Value> result_pre = CompileRun("pre"); | 
|  | CHECK(v8_str("PrePropertyHandler: pre") | 
|  | ->Equals(env.local(), result_pre) | 
|  | .FromJust()); | 
|  | v8::Local<Value> result_on = CompileRun("on"); | 
|  | CHECK(v8_str("Object: on")->Equals(env.local(), result_on).FromJust()); | 
|  | v8::Local<Value> result_post = CompileRun("post"); | 
|  | CHECK(result_post.IsEmpty()); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(EmptyInterceptorBreakTransitions) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); | 
|  | AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Constructor"), | 
|  | templ->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var o1 = new Constructor;" | 
|  | "o1.a = 1;"  // Ensure a and x share the descriptor array. | 
|  | "Object.defineProperty(o1, 'x', {value: 10});"); | 
|  | CompileRun( | 
|  | "var o2 = new Constructor;" | 
|  | "o2.a = 1;" | 
|  | "Object.defineProperty(o2, 'x', {value: 10});"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<FunctionTemplate> parent = FunctionTemplate::New(isolate); | 
|  | Local<FunctionTemplate> child = FunctionTemplate::New(isolate); | 
|  | child->Inherit(parent); | 
|  | AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Child"), | 
|  | child->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | 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(EmptyInterceptorDoesNotShadowApiAccessors) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<FunctionTemplate> parent = FunctionTemplate::New(isolate); | 
|  | auto returns_42 = FunctionTemplate::New(isolate, Returns42); | 
|  | parent->PrototypeTemplate()->SetAccessorProperty(v8_str("age"), returns_42); | 
|  | Local<FunctionTemplate> child = FunctionTemplate::New(isolate); | 
|  | child->Inherit(parent); | 
|  | AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Child"), | 
|  | child->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var child = new Child;" | 
|  | "var parent = child.__proto__;"); | 
|  | ExpectBoolean("child.hasOwnProperty('age')", false); | 
|  | ExpectInt32("child.age", 42); | 
|  | // Check interceptor followup. | 
|  | ExpectInt32( | 
|  | "var result;" | 
|  | "for (var i = 0; i < 4; ++i) {" | 
|  | "  result = child.age;" | 
|  | "}" | 
|  | "result", | 
|  | 42); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<FunctionTemplate> parent = FunctionTemplate::New(isolate); | 
|  | Local<FunctionTemplate> child = FunctionTemplate::New(isolate); | 
|  | child->Inherit(parent); | 
|  | AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Child"), | 
|  | child->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | 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(CcTest::isolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); | 
|  | AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter); | 
|  | AddInterceptor(templ, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Obj"), | 
|  | templ->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var obj = new Obj;" | 
|  | "function setAge(i){ obj.age = i; };" | 
|  | "for(var i = 0; i <= 10000; i++) setAge(i);"); | 
|  | // All i < 10000 go to the interceptor. | 
|  | ExpectInt32("obj.interceptor_age", 9999); | 
|  | // The last i goes to the accessor. | 
|  | ExpectInt32("obj.accessor_age", 10000); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(SwitchFromAccessorToInterceptor) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); | 
|  | AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter); | 
|  | AddInterceptor(templ, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Obj"), | 
|  | templ->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var obj = new Obj;" | 
|  | "function setAge(i){ obj.age = i; };" | 
|  | "for(var i = 20000; i >= 9999; i--) setAge(i);"); | 
|  | // All i >= 10000 go to the accessor. | 
|  | ExpectInt32("obj.accessor_age", 10000); | 
|  | // The last i goes to the interceptor. | 
|  | ExpectInt32("obj.interceptor_age", 9999); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(SwitchFromInterceptorToAccessorWithInheritance) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); | 
|  | Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); | 
|  | child->Inherit(parent); | 
|  | AddAccessor(parent, v8_str("age"), SimpleAccessorGetter, | 
|  | SimpleAccessorSetter); | 
|  | AddInterceptor(child, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Child"), | 
|  | child->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | 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(SwitchFromAccessorToInterceptorWithInheritance) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); | 
|  | Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); | 
|  | child->Inherit(parent); | 
|  | AddAccessor(parent, v8_str("age"), SimpleAccessorGetter, | 
|  | SimpleAccessorSetter); | 
|  | AddInterceptor(child, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Child"), | 
|  | child->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | 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(SwitchFromInterceptorToJSAccessor) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); | 
|  | AddInterceptor(templ, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Obj"), | 
|  | templ->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var obj = new Obj;" | 
|  | "function setter(i) { this.accessor_age = i; };" | 
|  | "function getter() { return this.accessor_age; };" | 
|  | "function setAge(i) { obj.age = i; };" | 
|  | "Object.defineProperty(obj, 'age', { get:getter, set:setter });" | 
|  | "for(var i = 0; i <= 10000; i++) setAge(i);"); | 
|  | // All i < 10000 go to the interceptor. | 
|  | ExpectInt32("obj.interceptor_age", 9999); | 
|  | // The last i goes to the JavaScript accessor. | 
|  | ExpectInt32("obj.accessor_age", 10000); | 
|  | // The installed JavaScript getter is still intact. | 
|  | // This last part is a regression test for issue 1651 and relies on the fact | 
|  | // that both interceptor and accessor are being installed on the same object. | 
|  | ExpectInt32("obj.age", 10000); | 
|  | ExpectBoolean("obj.hasOwnProperty('age')", true); | 
|  | ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(SwitchFromJSAccessorToInterceptor) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); | 
|  | AddInterceptor(templ, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Obj"), | 
|  | templ->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var obj = new Obj;" | 
|  | "function setter(i) { this.accessor_age = i; };" | 
|  | "function getter() { return this.accessor_age; };" | 
|  | "function setAge(i) { obj.age = i; };" | 
|  | "Object.defineProperty(obj, 'age', { get:getter, set:setter });" | 
|  | "for(var i = 20000; i >= 9999; i--) setAge(i);"); | 
|  | // All i >= 10000 go to the accessor. | 
|  | ExpectInt32("obj.accessor_age", 10000); | 
|  | // The last i goes to the interceptor. | 
|  | ExpectInt32("obj.interceptor_age", 9999); | 
|  | // The installed JavaScript getter is still intact. | 
|  | // This last part is a regression test for issue 1651 and relies on the fact | 
|  | // that both interceptor and accessor are being installed on the same object. | 
|  | ExpectInt32("obj.age", 10000); | 
|  | ExpectBoolean("obj.hasOwnProperty('age')", true); | 
|  | ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(SwitchFromInterceptorToProperty) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); | 
|  | Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); | 
|  | child->Inherit(parent); | 
|  | AddInterceptor(child, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Child"), | 
|  | child->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | 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(CcTest::isolate()); | 
|  | Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); | 
|  | Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); | 
|  | child->Inherit(parent); | 
|  | AddInterceptor(child, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Child"), | 
|  | child->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | 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); | 
|  | } | 
|  |  | 
|  |  | 
|  | static bool interceptor_for_hidden_properties_called; | 
|  | static void InterceptorForHiddenProperties( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | interceptor_for_hidden_properties_called = true; | 
|  | } | 
|  |  | 
|  | THREADED_TEST(NoSideEffectPropertyHandler) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | LocalContext context; | 
|  |  | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | EmptyInterceptorGetter, EmptyInterceptorSetter, EmptyInterceptorQuery, | 
|  | EmptyInterceptorDeleter, EmptyInterceptorEnumerator)); | 
|  | v8::Local<v8::Object> object = | 
|  | templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj"), object).FromJust(); | 
|  |  | 
|  | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj.x"), true).IsEmpty()); | 
|  | CHECK( | 
|  | v8::debug::EvaluateGlobal(isolate, v8_str("obj.x = 1"), true).IsEmpty()); | 
|  | CHECK( | 
|  | v8::debug::EvaluateGlobal(isolate, v8_str("'x' in obj"), true).IsEmpty()); | 
|  | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("delete obj.x"), true) | 
|  | .IsEmpty()); | 
|  | // Wrap the variable declaration since declaring globals is a side effect. | 
|  | CHECK(v8::debug::EvaluateGlobal( | 
|  | isolate, v8_str("(function() { for (var p in obj) ; })()"), true) | 
|  | .IsEmpty()); | 
|  |  | 
|  | // Side-effect-free version. | 
|  | Local<ObjectTemplate> templ2 = ObjectTemplate::New(isolate); | 
|  | templ2->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | EmptyInterceptorGetter, EmptyInterceptorSetter, EmptyInterceptorQuery, | 
|  | EmptyInterceptorDeleter, EmptyInterceptorEnumerator, | 
|  | v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kHasNoSideEffect)); | 
|  | v8::Local<v8::Object> object2 = | 
|  | templ2->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj2"), object2).FromJust(); | 
|  |  | 
|  | v8::debug::EvaluateGlobal(isolate, v8_str("obj2.x"), true).ToLocalChecked(); | 
|  | CHECK( | 
|  | v8::debug::EvaluateGlobal(isolate, v8_str("obj2.x = 1"), true).IsEmpty()); | 
|  | v8::debug::EvaluateGlobal(isolate, v8_str("'x' in obj2"), true) | 
|  | .ToLocalChecked(); | 
|  | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("delete obj2.x"), true) | 
|  | .IsEmpty()); | 
|  | v8::debug::EvaluateGlobal( | 
|  | isolate, v8_str("(function() { for (var p in obj2) ; })()"), true) | 
|  | .ToLocalChecked(); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(HiddenPropertiesWithInterceptors) { | 
|  | LocalContext context; | 
|  | v8::Isolate* isolate = context->GetIsolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  |  | 
|  | interceptor_for_hidden_properties_called = false; | 
|  |  | 
|  | v8::Local<v8::Private> key = | 
|  | v8::Private::New(isolate, v8_str("api-test::hidden-key")); | 
|  |  | 
|  | // Associate an interceptor with an object and start setting hidden values. | 
|  | Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); | 
|  | Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); | 
|  | instance_templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorForHiddenProperties)); | 
|  | Local<v8::Function> function = | 
|  | fun_templ->GetFunction(context.local()).ToLocalChecked(); | 
|  | Local<v8::Object> obj = | 
|  | function->NewInstance(context.local()).ToLocalChecked(); | 
|  | CHECK(obj->SetPrivate(context.local(), key, v8::Integer::New(isolate, 2302)) | 
|  | .FromJust()); | 
|  | CHECK_EQ(2302, obj->GetPrivate(context.local(), key) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(context.local()) | 
|  | .FromJust()); | 
|  | CHECK(!interceptor_for_hidden_properties_called); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void XPropertyGetter(Local<Name> property, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(info.Data()->IsUndefined()); | 
|  | info.GetReturnValue().Set(property); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(NamedInterceptorPropertyRead) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | Local<Script> script = v8_compile("obj.x"); | 
|  | for (int i = 0; i < 10; i++) { | 
|  | Local<Value> result = script->Run(context.local()).ToLocalChecked(); | 
|  | CHECK(result->Equals(context.local(), v8_str("x")).FromJust()); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(NamedInterceptorDictionaryIC) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter)); | 
|  | LocalContext context; | 
|  | // Create an object with a named interceptor. | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("interceptor_obj"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | Local<Script> script = v8_compile("interceptor_obj.x"); | 
|  | for (int i = 0; i < 10; i++) { | 
|  | Local<Value> result = script->Run(context.local()).ToLocalChecked(); | 
|  | CHECK(result->Equals(context.local(), v8_str("x")).FromJust()); | 
|  | } | 
|  | // Create a slow case object and a function accessing a property in | 
|  | // that slow case object (with dictionary probing in generated | 
|  | // code). Then force object with a named interceptor into slow-case, | 
|  | // pass it to the function, and check that the interceptor is called | 
|  | // instead of accessing the local property. | 
|  | Local<Value> result = CompileRun( | 
|  | "function get_x(o) { return o.x; };" | 
|  | "var obj = { x : 42, y : 0 };" | 
|  | "delete obj.y;" | 
|  | "for (var i = 0; i < 10; i++) get_x(obj);" | 
|  | "interceptor_obj.x = 42;" | 
|  | "interceptor_obj.y = 10;" | 
|  | "delete interceptor_obj.y;" | 
|  | "get_x(interceptor_obj)"); | 
|  | CHECK(result->Equals(context.local(), v8_str("x")).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<Context> context1 = Context::New(isolate); | 
|  |  | 
|  | context1->Enter(); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter)); | 
|  | // Create an object with a named interceptor. | 
|  | v8::Local<v8::Object> object = templ->NewInstance(context1).ToLocalChecked(); | 
|  | context1->Global() | 
|  | ->Set(context1, v8_str("interceptor_obj"), object) | 
|  | .FromJust(); | 
|  |  | 
|  | // Force the object into the slow case. | 
|  | CompileRun( | 
|  | "interceptor_obj.y = 0;" | 
|  | "delete interceptor_obj.y;"); | 
|  | context1->Exit(); | 
|  |  | 
|  | { | 
|  | // Introduce the object into a different context. | 
|  | // Repeat named loads to exercise ICs. | 
|  | LocalContext context2; | 
|  | context2->Global() | 
|  | ->Set(context2.local(), v8_str("interceptor_obj"), object) | 
|  | .FromJust(); | 
|  | Local<Value> result = CompileRun( | 
|  | "function get_x(o) { return o.x; }" | 
|  | "interceptor_obj.x = 42;" | 
|  | "for (var i=0; i != 10; i++) {" | 
|  | "  get_x(interceptor_obj);" | 
|  | "}" | 
|  | "get_x(interceptor_obj)"); | 
|  | // Check that the interceptor was actually invoked. | 
|  | CHECK(result->Equals(context2.local(), v8_str("x")).FromJust()); | 
|  | } | 
|  |  | 
|  | // Return to the original context and force some object to the slow case | 
|  | // to cause the NormalizedMapCache to verify. | 
|  | context1->Enter(); | 
|  | CompileRun("var obj = { x : 0 }; delete obj.x;"); | 
|  | context1->Exit(); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void SetXOnPrototypeGetter( | 
|  | Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | // Set x on the prototype object and do not handle the get request. | 
|  | v8::Local<v8::Value> proto = info.Holder()->GetPrototype(); | 
|  | proto.As<v8::Object>() | 
|  | ->Set(info.GetIsolate()->GetCurrentContext(), v8_str("x"), | 
|  | v8::Integer::New(info.GetIsolate(), 23)) | 
|  | .FromJust(); | 
|  | } | 
|  |  | 
|  |  | 
|  | // This is a regression test for http://crbug.com/20104. Map | 
|  | // transitions should not interfere with post interceptor lookup. | 
|  | THREADED_TEST(NamedInterceptorMapTransitionRead) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<v8::FunctionTemplate> function_template = | 
|  | v8::FunctionTemplate::New(isolate); | 
|  | Local<v8::ObjectTemplate> instance_template = | 
|  | function_template->InstanceTemplate(); | 
|  | instance_template->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(SetXOnPrototypeGetter)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("F"), | 
|  | function_template->GetFunction(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | // Create an instance of F and introduce a map transition for x. | 
|  | CompileRun("var o = new F(); o.x = 23;"); | 
|  | // Create an instance of F and invoke the getter. The result should be 23. | 
|  | Local<Value> result = CompileRun("o = new F(); o.x"); | 
|  | CHECK_EQ(23, result->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void IndexedPropertyGetter( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (index == 37) { | 
|  | info.GetReturnValue().Set(v8_num(625)); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static void IndexedPropertySetter( | 
|  | uint32_t index, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (index == 39) { | 
|  | info.GetReturnValue().Set(value); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | IndexedPropertyGetter, IndexedPropertySetter)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | Local<Script> getter_script = | 
|  | v8_compile("obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"); | 
|  | Local<Script> setter_script = v8_compile( | 
|  | "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" | 
|  | "obj[17] = 23;" | 
|  | "obj.foo;"); | 
|  | Local<Script> interceptor_setter_script = v8_compile( | 
|  | "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" | 
|  | "obj[39] = 47;" | 
|  | "obj.foo;");  // This setter should not run, due to the interceptor. | 
|  | Local<Script> interceptor_getter_script = v8_compile("obj[37];"); | 
|  | Local<Value> result = getter_script->Run(context.local()).ToLocalChecked(); | 
|  | CHECK(v8_num(5)->Equals(context.local(), result).FromJust()); | 
|  | result = setter_script->Run(context.local()).ToLocalChecked(); | 
|  | CHECK(v8_num(23)->Equals(context.local(), result).FromJust()); | 
|  | result = interceptor_setter_script->Run(context.local()).ToLocalChecked(); | 
|  | CHECK(v8_num(23)->Equals(context.local(), result).FromJust()); | 
|  | result = interceptor_getter_script->Run(context.local()).ToLocalChecked(); | 
|  | CHECK(v8_num(625)->Equals(context.local(), result).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void UnboxedDoubleIndexedPropertyGetter( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (index < 25) { | 
|  | info.GetReturnValue().Set(v8_num(index)); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static void UnboxedDoubleIndexedPropertySetter( | 
|  | uint32_t index, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (index < 25) { | 
|  | info.GetReturnValue().Set(v8_num(index)); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | void UnboxedDoubleIndexedPropertyEnumerator( | 
|  | const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | // Force the list of returned keys to be stored in a FastDoubleArray. | 
|  | Local<Script> indexed_property_names_script = v8_compile( | 
|  | "keys = new Array(); keys[125000] = 1;" | 
|  | "for(i = 0; i < 80000; i++) { keys[i] = i; };" | 
|  | "keys.length = 25; keys;"); | 
|  | Local<Value> result = | 
|  | indexed_property_names_script->Run(info.GetIsolate()->GetCurrentContext()) | 
|  | .ToLocalChecked(); | 
|  | info.GetReturnValue().Set(Local<v8::Array>::Cast(result)); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Make sure that the the interceptor code in the runtime properly handles | 
|  | // merging property name lists for double-array-backed arrays. | 
|  | THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | UnboxedDoubleIndexedPropertyGetter, UnboxedDoubleIndexedPropertySetter, | 
|  | nullptr, nullptr, UnboxedDoubleIndexedPropertyEnumerator)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | // When obj is created, force it to be Stored in a FastDoubleArray. | 
|  | Local<Script> create_unboxed_double_script = v8_compile( | 
|  | "obj[125000] = 1; for(i = 0; i < 80000; i+=2) { obj[i] = i; } " | 
|  | "key_count = 0; " | 
|  | "for (x in obj) {key_count++;};" | 
|  | "obj;"); | 
|  | Local<Value> result = | 
|  | create_unboxed_double_script->Run(context.local()).ToLocalChecked(); | 
|  | CHECK(result->ToObject(context.local()) | 
|  | .ToLocalChecked() | 
|  | ->HasRealIndexedProperty(context.local(), 2000) | 
|  | .FromJust()); | 
|  | Local<Script> key_count_check = v8_compile("key_count;"); | 
|  | result = key_count_check->Run(context.local()).ToLocalChecked(); | 
|  | CHECK(v8_num(40013)->Equals(context.local(), result).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | void SloppyArgsIndexedPropertyEnumerator( | 
|  | const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | // Force the list of returned keys to be stored in a Arguments object. | 
|  | Local<Script> indexed_property_names_script = v8_compile( | 
|  | "function f(w,x) {" | 
|  | " return arguments;" | 
|  | "}" | 
|  | "keys = f(0, 1, 2, 3);" | 
|  | "keys;"); | 
|  | Local<Object> result = Local<Object>::Cast( | 
|  | indexed_property_names_script->Run(info.GetIsolate()->GetCurrentContext()) | 
|  | .ToLocalChecked()); | 
|  | // Have to populate the handle manually, as it's not Cast-able. | 
|  | i::Handle<i::JSReceiver> o = | 
|  | v8::Utils::OpenHandle<Object, i::JSReceiver>(result); | 
|  | i::Handle<i::JSArray> array(i::JSArray::unchecked_cast(*o), o->GetIsolate()); | 
|  | info.GetReturnValue().Set(v8::Utils::ToLocal(array)); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void SloppyIndexedPropertyGetter( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (index < 4) { | 
|  | info.GetReturnValue().Set(v8_num(index)); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // Make sure that the the interceptor code in the runtime properly handles | 
|  | // merging property name lists for non-string arguments arrays. | 
|  | THREADED_TEST(IndexedInterceptorSloppyArgsWithIndexedAccessor) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | SloppyIndexedPropertyGetter, nullptr, nullptr, nullptr, | 
|  | SloppyArgsIndexedPropertyEnumerator)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | Local<Script> create_args_script = v8_compile( | 
|  | "var key_count = 0;" | 
|  | "for (x in obj) {key_count++;} key_count;"); | 
|  | Local<Value> result = | 
|  | create_args_script->Run(context.local()).ToLocalChecked(); | 
|  | CHECK(v8_num(4)->Equals(context.local(), result).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void IdentityIndexedPropertyGetter( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | info.GetReturnValue().Set(index); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); | 
|  |  | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | // Check fast object case. | 
|  | const char* fast_case_code = | 
|  | "Object.getOwnPropertyDescriptor(obj, 0).value.toString()"; | 
|  | ExpectString(fast_case_code, "0"); | 
|  |  | 
|  | // Check slow case. | 
|  | const char* slow_case_code = | 
|  | "obj.x = 1; delete obj.x;" | 
|  | "Object.getOwnPropertyDescriptor(obj, 1).value.toString()"; | 
|  | ExpectString(slow_case_code, "1"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorWithNoSetter) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); | 
|  |  | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | const char* code = | 
|  | "try {" | 
|  | "  obj[0] = 239;" | 
|  | "  for (var i = 0; i < 100; i++) {" | 
|  | "    var v = obj[0];" | 
|  | "    if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" | 
|  | "  }" | 
|  | "  'PASSED'" | 
|  | "} catch(e) {" | 
|  | "  e" | 
|  | "}"; | 
|  | ExpectString(code, "PASSED"); | 
|  | } | 
|  |  | 
|  | static bool AccessAlwaysBlocked(Local<v8::Context> accessing_context, | 
|  | Local<v8::Object> accessed_object, | 
|  | Local<v8::Value> data) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorWithAccessorCheck) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); | 
|  |  | 
|  | templ->SetAccessCheckCallback(AccessAlwaysBlocked); | 
|  |  | 
|  | LocalContext context; | 
|  | Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust(); | 
|  |  | 
|  | const char* code = | 
|  | "var result = 'PASSED';" | 
|  | "for (var i = 0; i < 100; i++) {" | 
|  | "  try {" | 
|  | "    var v = obj[0];" | 
|  | "    result = 'Wrong value ' + v + ' at iteration ' + i;" | 
|  | "    break;" | 
|  | "  } catch (e) {" | 
|  | "    /* pass */" | 
|  | "  }" | 
|  | "}" | 
|  | "result"; | 
|  | ExpectString(code, "PASSED"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorWithDifferentIndices) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); | 
|  |  | 
|  | LocalContext context; | 
|  | Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust(); | 
|  |  | 
|  | const char* code = | 
|  | "try {" | 
|  | "  for (var i = 0; i < 100; i++) {" | 
|  | "    var v = obj[i];" | 
|  | "    if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" | 
|  | "  }" | 
|  | "  'PASSED'" | 
|  | "} catch(e) {" | 
|  | "  e" | 
|  | "}"; | 
|  | ExpectString(code, "PASSED"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorWithNegativeIndices) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); | 
|  |  | 
|  | LocalContext context; | 
|  | Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust(); | 
|  |  | 
|  | const char* code = | 
|  | "try {" | 
|  | "  for (var i = 0; i < 100; i++) {" | 
|  | "    var expected = i;" | 
|  | "    var key = i;" | 
|  | "    if (i == 25) {" | 
|  | "       key = -1;" | 
|  | "       expected = undefined;" | 
|  | "    }" | 
|  | "    if (i == 50) {" | 
|  | "       /* probe minimal Smi number on 32-bit platforms */" | 
|  | "       key = -(1 << 30);" | 
|  | "       expected = undefined;" | 
|  | "    }" | 
|  | "    if (i == 75) {" | 
|  | "       /* probe minimal Smi number on 64-bit platforms */" | 
|  | "       key = 1 << 31;" | 
|  | "       expected = undefined;" | 
|  | "    }" | 
|  | "    var v = obj[key];" | 
|  | "    if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" | 
|  | "  }" | 
|  | "  'PASSED'" | 
|  | "} catch(e) {" | 
|  | "  e" | 
|  | "}"; | 
|  | ExpectString(code, "PASSED"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); | 
|  |  | 
|  | LocalContext context; | 
|  | Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust(); | 
|  |  | 
|  | const char* code = | 
|  | "try {" | 
|  | "  for (var i = 0; i < 100; i++) {" | 
|  | "    var expected = i;" | 
|  | "    var key = i;" | 
|  | "    if (i == 50) {" | 
|  | "       key = 'foobar';" | 
|  | "       expected = undefined;" | 
|  | "    }" | 
|  | "    var v = obj[key];" | 
|  | "    if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" | 
|  | "  }" | 
|  | "  'PASSED'" | 
|  | "} catch(e) {" | 
|  | "  e" | 
|  | "}"; | 
|  | ExpectString(code, "PASSED"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorGoingMegamorphic) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); | 
|  |  | 
|  | LocalContext context; | 
|  | Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust(); | 
|  |  | 
|  | const char* code = | 
|  | "var original = obj;" | 
|  | "try {" | 
|  | "  for (var i = 0; i < 100; i++) {" | 
|  | "    var expected = i;" | 
|  | "    if (i == 50) {" | 
|  | "       obj = {50: 'foobar'};" | 
|  | "       expected = 'foobar';" | 
|  | "    }" | 
|  | "    var v = obj[i];" | 
|  | "    if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" | 
|  | "    if (i == 50) obj = original;" | 
|  | "  }" | 
|  | "  'PASSED'" | 
|  | "} catch(e) {" | 
|  | "  e" | 
|  | "}"; | 
|  | ExpectString(code, "PASSED"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); | 
|  |  | 
|  | LocalContext context; | 
|  | Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust(); | 
|  |  | 
|  | const char* code = | 
|  | "var original = obj;" | 
|  | "try {" | 
|  | "  for (var i = 0; i < 100; i++) {" | 
|  | "    var expected = i;" | 
|  | "    if (i == 5) {" | 
|  | "       obj = 239;" | 
|  | "       expected = undefined;" | 
|  | "    }" | 
|  | "    var v = obj[i];" | 
|  | "    if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" | 
|  | "    if (i == 5) obj = original;" | 
|  | "  }" | 
|  | "  'PASSED'" | 
|  | "} catch(e) {" | 
|  | "  e" | 
|  | "}"; | 
|  | ExpectString(code, "PASSED"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorOnProto) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); | 
|  |  | 
|  | LocalContext context; | 
|  | Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust(); | 
|  |  | 
|  | const char* code = | 
|  | "var o = {__proto__: obj};" | 
|  | "try {" | 
|  | "  for (var i = 0; i < 100; i++) {" | 
|  | "    var v = o[i];" | 
|  | "    if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" | 
|  | "  }" | 
|  | "  'PASSED'" | 
|  | "} catch(e) {" | 
|  | "  e" | 
|  | "}"; | 
|  | ExpectString(code, "PASSED"); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void CheckIndexedInterceptorHasIC(v8::IndexedPropertyGetterCallback getter, | 
|  | v8::IndexedPropertyQueryCallback query, | 
|  | const char* source, int expected) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | getter, nullptr, query, nullptr, nullptr, v8_str("data"))); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | v8::Local<Value> value = CompileRun(source); | 
|  | CHECK_EQ(expected, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  | int indexed_query_counter = 0; | 
|  | void IndexedQueryCallback(uint32_t index, | 
|  | const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | indexed_query_counter++; | 
|  | } | 
|  |  | 
|  | void IndexHasICQueryAbsent(uint32_t index, | 
|  | const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | CHECK_EQ(isolate, info.GetIsolate()); | 
|  | info.GetReturnValue().Set(v8::Integer::New(isolate, v8::internal::ABSENT)); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorHasIC) { | 
|  | indexed_query_counter = 0; | 
|  | CheckIndexedInterceptorHasIC(nullptr, IndexedQueryCallback, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  i in o;" | 
|  | "}", | 
|  | 0); | 
|  | CHECK_EQ(1000, indexed_query_counter); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorHasICQueryAbsent) { | 
|  | CheckIndexedInterceptorHasIC(nullptr, | 
|  | // HasICQuery<uint32_t, v8::internal::ABSENT>, | 
|  | IndexHasICQueryAbsent, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  if (i in o) ++result;" | 
|  | "}", | 
|  | 0); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorHasICQueryNone) { | 
|  | CheckIndexedInterceptorHasIC(nullptr, | 
|  | HasICQuery<uint32_t, v8::internal::NONE>, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  if (i in o) ++result;" | 
|  | "}", | 
|  | 1000); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorHasICGetter) { | 
|  | CheckIndexedInterceptorHasIC(IdentityIndexedPropertyGetter, nullptr, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  if (i in o) ++result;" | 
|  | "}", | 
|  | 1000); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorHasICQueryGetter) { | 
|  | CheckIndexedInterceptorHasIC(IdentityIndexedPropertyGetter, | 
|  | HasICQuery<uint32_t, v8::internal::ABSENT>, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  if (i in o) ++result;" | 
|  | "}", | 
|  | 0); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(IndexedInterceptorHasICQueryToggle) { | 
|  | CheckIndexedInterceptorHasIC(IdentityIndexedPropertyGetter, | 
|  | HasICQueryToggle<uint32_t>, | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  if (i in o) ++result;" | 
|  | "}", | 
|  | 500); | 
|  | } | 
|  |  | 
|  | static void NoBlockGetterX(Local<Name> name, | 
|  | const v8::PropertyCallbackInfo<v8::Value>&) {} | 
|  |  | 
|  |  | 
|  | static void NoBlockGetterI(uint32_t index, | 
|  | const v8::PropertyCallbackInfo<v8::Value>&) {} | 
|  |  | 
|  |  | 
|  | static void PDeleter(Local<Name> name, | 
|  | const v8::PropertyCallbackInfo<v8::Boolean>& info) { | 
|  | if (!name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("foo")) | 
|  | .FromJust()) { | 
|  | return;  // not intercepted | 
|  | } | 
|  |  | 
|  | info.GetReturnValue().Set(false);  // intercepted, don't delete the property | 
|  | } | 
|  |  | 
|  |  | 
|  | static void IDeleter(uint32_t index, | 
|  | const v8::PropertyCallbackInfo<v8::Boolean>& info) { | 
|  | if (index != 2) { | 
|  | return;  // not intercepted | 
|  | } | 
|  |  | 
|  | info.GetReturnValue().Set(false);  // intercepted, don't delete the property | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(Deleter) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); | 
|  | obj->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | NoBlockGetterX, nullptr, nullptr, PDeleter, nullptr)); | 
|  | obj->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | NoBlockGetterI, nullptr, nullptr, IDeleter, nullptr)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("k"), | 
|  | obj->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "k.foo = 'foo';" | 
|  | "k.bar = 'bar';" | 
|  | "k[2] = 2;" | 
|  | "k[4] = 4;"); | 
|  | CHECK(v8_compile("delete k.foo") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked() | 
|  | ->IsFalse()); | 
|  | CHECK(v8_compile("delete k.bar") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked() | 
|  | ->IsTrue()); | 
|  |  | 
|  | CHECK(v8_compile("k.foo") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked() | 
|  | ->Equals(context.local(), v8_str("foo")) | 
|  | .FromJust()); | 
|  | CHECK(v8_compile("k.bar") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked() | 
|  | ->IsUndefined()); | 
|  |  | 
|  | CHECK(v8_compile("delete k[2]") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked() | 
|  | ->IsFalse()); | 
|  | CHECK(v8_compile("delete k[4]") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked() | 
|  | ->IsTrue()); | 
|  |  | 
|  | CHECK(v8_compile("k[2]") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked() | 
|  | ->Equals(context.local(), v8_num(2)) | 
|  | .FromJust()); | 
|  | CHECK( | 
|  | v8_compile("k[4]")->Run(context.local()).ToLocalChecked()->IsUndefined()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void GetK(Local<Name> name, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); | 
|  | if (name->Equals(context, v8_str("foo")).FromJust() || | 
|  | name->Equals(context, v8_str("bar")).FromJust() || | 
|  | name->Equals(context, v8_str("baz")).FromJust()) { | 
|  | info.GetReturnValue().SetUndefined(); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static void IndexedGetK(uint32_t index, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (index == 0 || index == 1) info.GetReturnValue().SetUndefined(); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void NamedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 3); | 
|  | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); | 
|  | CHECK( | 
|  | result | 
|  | ->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_str("foo")) | 
|  | .FromJust()); | 
|  | CHECK( | 
|  | result | 
|  | ->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_str("bar")) | 
|  | .FromJust()); | 
|  | CHECK( | 
|  | result | 
|  | ->Set(context, v8::Integer::New(info.GetIsolate(), 2), v8_str("baz")) | 
|  | .FromJust()); | 
|  | info.GetReturnValue().Set(result); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void IndexedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 2); | 
|  | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); | 
|  | CHECK( | 
|  | result->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_str("0")) | 
|  | .FromJust()); | 
|  | CHECK( | 
|  | result->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_str("1")) | 
|  | .FromJust()); | 
|  | info.GetReturnValue().Set(result); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(Enumerators) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); | 
|  | obj->SetHandler(v8::NamedPropertyHandlerConfiguration(GetK, nullptr, nullptr, | 
|  | nullptr, NamedEnum)); | 
|  | obj->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | IndexedGetK, nullptr, nullptr, nullptr, IndexedEnum)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("k"), | 
|  | obj->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | v8::Local<v8::Array> result = | 
|  | v8::Local<v8::Array>::Cast(CompileRun("k[10] = 0;" | 
|  | "k.a = 0;" | 
|  | "k[5] = 0;" | 
|  | "k.b = 0;" | 
|  | "k[4294967294] = 0;" | 
|  | "k.c = 0;" | 
|  | "k[4294967295] = 0;" | 
|  | "k.d = 0;" | 
|  | "k[140000] = 0;" | 
|  | "k.e = 0;" | 
|  | "k[30000000000] = 0;" | 
|  | "k.f = 0;" | 
|  | "var result = [];" | 
|  | "for (var prop in k) {" | 
|  | "  result.push(prop);" | 
|  | "}" | 
|  | "result")); | 
|  | // Check that we get all the property names returned including the | 
|  | // ones from the enumerators in the right order: indexed properties | 
|  | // in numerical order, indexed interceptor properties, named | 
|  | // properties in insertion order, named interceptor properties. | 
|  | // This order is not mandated by the spec, so this test is just | 
|  | // documenting our behavior. | 
|  | CHECK_EQ(17u, result->Length()); | 
|  | // Indexed properties. | 
|  | CHECK(v8_str("5") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 0)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("10") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 1)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("140000") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 2)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("4294967294") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 3)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | // Indexed Interceptor properties | 
|  | CHECK(v8_str("0") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 4)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("1") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 5)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | // Named properties in insertion order. | 
|  | CHECK(v8_str("a") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 6)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("b") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 7)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("c") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 8)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("4294967295") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 9)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("d") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 10)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("e") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 11)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("30000000000") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 12)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("f") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 13)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | // Named interceptor properties. | 
|  | CHECK(v8_str("foo") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 14)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("bar") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 15)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("baz") | 
|  | ->Equals(context.local(), | 
|  | result->Get(context.local(), v8::Integer::New(isolate, 16)) | 
|  | .ToLocalChecked()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | v8::Local<Value> call_ic_function; | 
|  | v8::Local<Value> call_ic_function2; | 
|  | v8::Local<Value> call_ic_function3; | 
|  |  | 
|  | static void InterceptorCallICGetter( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(v8_str("x") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), name) | 
|  | .FromJust()); | 
|  | info.GetReturnValue().Set(call_ic_function); | 
|  | } | 
|  |  | 
|  |  | 
|  | // This test should hit the call IC for the interceptor case. | 
|  | THREADED_TEST(InterceptorCallIC) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | call_ic_function = v8_compile("function f(x) { return x + 1; }; f") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  result = o.x(41);" | 
|  | "}"); | 
|  | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // This test checks that if interceptor doesn't provide | 
|  | // a value, we can fetch regular value. | 
|  | THREADED_TEST(InterceptorCallICSeesOthers) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "o.x = function f(x) { return x + 1; };" | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 7; i++) {" | 
|  | "  result = o.x(41);" | 
|  | "}"); | 
|  | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static v8::Local<Value> call_ic_function4; | 
|  | static void InterceptorCallICGetter4( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CHECK(v8_str("x") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), name) | 
|  | .FromJust()); | 
|  | info.GetReturnValue().Set(call_ic_function4); | 
|  | } | 
|  |  | 
|  |  | 
|  | // This test checks that if interceptor provides a function, | 
|  | // even if we cached shadowed variant, interceptor's function | 
|  | // is invoked | 
|  | THREADED_TEST(InterceptorCallICCacheableNotNeeded) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter4)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | call_ic_function4 = v8_compile("function f(x) { return x - 1; }; f") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "Object.getPrototypeOf(o).x = function(x) { return x + 1; };" | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  result = o.x(42);" | 
|  | "}"); | 
|  | CHECK_EQ(41, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test the case when we stored cacheable lookup into | 
|  | // a stub, but it got invalidated later on | 
|  | THREADED_TEST(InterceptorCallICInvalidatedCacheable) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "proto1 = new Object();" | 
|  | "proto2 = new Object();" | 
|  | "o.__proto__ = proto1;" | 
|  | "proto1.__proto__ = proto2;" | 
|  | "proto2.y = function(x) { return x + 1; };" | 
|  | // Invoke it many times to compile a stub | 
|  | "for (var i = 0; i < 7; i++) {" | 
|  | "  o.y(42);" | 
|  | "}" | 
|  | "proto1.y = function(x) { return x - 1; };" | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 7; i++) {" | 
|  | "  result += o.y(42);" | 
|  | "}"); | 
|  | CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // This test checks that if interceptor doesn't provide a function, | 
|  | // cached constant function is used | 
|  | THREADED_TEST(InterceptorCallICConstantFunctionUsed) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "function inc(x) { return x + 1; };" | 
|  | "inc(1);" | 
|  | "o.x = inc;" | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  result = o.x(42);" | 
|  | "}"); | 
|  | CHECK_EQ(43, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static v8::Local<Value> call_ic_function5; | 
|  | static void InterceptorCallICGetter5( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (v8_str("x") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), name) | 
|  | .FromJust()) | 
|  | info.GetReturnValue().Set(call_ic_function5); | 
|  | } | 
|  |  | 
|  |  | 
|  | // This test checks that if interceptor provides a function, | 
|  | // even if we cached constant function, interceptor's function | 
|  | // is invoked | 
|  | THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter5)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | call_ic_function5 = v8_compile("function f(x) { return x - 1; }; f") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "function inc(x) { return x + 1; };" | 
|  | "inc(1);" | 
|  | "o.x = inc;" | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 1000; i++) {" | 
|  | "  result = o.x(42);" | 
|  | "}"); | 
|  | CHECK_EQ(41, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static v8::Local<Value> call_ic_function6; | 
|  | static void InterceptorCallICGetter6( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (v8_str("x") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), name) | 
|  | .FromJust()) | 
|  | info.GetReturnValue().Set(call_ic_function6); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Same test as above, except the code is wrapped in a function | 
|  | // to test the optimized compiler. | 
|  | THREADED_TEST(InterceptorCallICConstantFunctionNotNeededWrapped) { | 
|  | i::FLAG_allow_natives_syntax = true; | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter6)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | call_ic_function6 = v8_compile("function f(x) { return x - 1; }; f") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "function inc(x) { return x + 1; };" | 
|  | "inc(1);" | 
|  | "o.x = inc;" | 
|  | "function test() {" | 
|  | "  var result = 0;" | 
|  | "  for (var i = 0; i < 1000; i++) {" | 
|  | "    result = o.x(42);" | 
|  | "  }" | 
|  | "  return result;" | 
|  | "};" | 
|  | "%PrepareFunctionForOptimization(test);" | 
|  | "test();" | 
|  | "test();" | 
|  | "test();" | 
|  | "%OptimizeFunctionOnNextCall(test);" | 
|  | "test()"); | 
|  | CHECK_EQ(41, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test the case when we stored constant function into | 
|  | // a stub, but it got invalidated later on | 
|  | THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "function inc(x) { return x + 1; };" | 
|  | "inc(1);" | 
|  | "proto1 = new Object();" | 
|  | "proto2 = new Object();" | 
|  | "o.__proto__ = proto1;" | 
|  | "proto1.__proto__ = proto2;" | 
|  | "proto2.y = inc;" | 
|  | // Invoke it many times to compile a stub | 
|  | "for (var i = 0; i < 7; i++) {" | 
|  | "  o.y(42);" | 
|  | "}" | 
|  | "proto1.y = function(x) { return x - 1; };" | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 7; i++) {" | 
|  | "  result += o.y(42);" | 
|  | "}"); | 
|  | CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test the case when we stored constant function into | 
|  | // a stub, but it got invalidated later on due to override on | 
|  | // global object which is between interceptor and constant function' holders. | 
|  | THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "function inc(x) { return x + 1; };" | 
|  | "inc(1);" | 
|  | "o.__proto__ = this;" | 
|  | "this.__proto__.y = inc;" | 
|  | // Invoke it many times to compile a stub | 
|  | "for (var i = 0; i < 7; i++) {" | 
|  | "  if (o.y(42) != 43) throw 'oops: ' + o.y(42);" | 
|  | "}" | 
|  | "this.y = function(x) { return x - 1; };" | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 7; i++) {" | 
|  | "  result += o.y(42);" | 
|  | "}"); | 
|  | CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test the case when actual function to call sits on global object. | 
|  | THREADED_TEST(InterceptorCallICCachedFromGlobal) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); | 
|  | templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  |  | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ_o->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "try {" | 
|  | "  o.__proto__ = this;" | 
|  | "  for (var i = 0; i < 10; i++) {" | 
|  | "    var v = o.parseFloat('239');" | 
|  | "    if (v != 239) throw v;" | 
|  | // Now it should be ICed and keep a reference to parseFloat. | 
|  | "  }" | 
|  | "  var result = 0;" | 
|  | "  for (var i = 0; i < 10; i++) {" | 
|  | "    result += o.parseFloat('239');" | 
|  | "  }" | 
|  | "  result" | 
|  | "} catch(e) {" | 
|  | "  e" | 
|  | "};"); | 
|  | CHECK_EQ(239 * 10, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | v8::Local<Value> keyed_call_ic_function; | 
|  |  | 
|  | static void InterceptorKeyedCallICGetter( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (v8_str("x") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), name) | 
|  | .FromJust()) { | 
|  | info.GetReturnValue().Set(keyed_call_ic_function); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test the case when we stored cacheable lookup into | 
|  | // a stub, but the function name changed (to another cacheable function). | 
|  | THREADED_TEST(InterceptorKeyedCallICKeyChange1) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "proto = new Object();" | 
|  | "proto.y = function(x) { return x + 1; };" | 
|  | "proto.z = function(x) { return x - 1; };" | 
|  | "o.__proto__ = proto;" | 
|  | "var result = 0;" | 
|  | "var method = 'y';" | 
|  | "for (var i = 0; i < 10; i++) {" | 
|  | "  if (i == 5) { method = 'z'; };" | 
|  | "  result += o[method](41);" | 
|  | "}"); | 
|  | CHECK_EQ(42 * 5 + 40 * 5, context->Global() | 
|  | ->Get(context.local(), v8_str("result")) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(context.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test the case when we stored cacheable lookup into | 
|  | // a stub, but the function name changed (and the new function is present | 
|  | // both before and after the interceptor in the prototype chain). | 
|  | THREADED_TEST(InterceptorKeyedCallICKeyChange2) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorKeyedCallICGetter)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("proto1"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | keyed_call_ic_function = v8_compile("function f(x) { return x - 1; }; f") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked(); | 
|  | CompileRun( | 
|  | "o = new Object();" | 
|  | "proto2 = new Object();" | 
|  | "o.y = function(x) { return x + 1; };" | 
|  | "proto2.y = function(x) { return x + 2; };" | 
|  | "o.__proto__ = proto1;" | 
|  | "proto1.__proto__ = proto2;" | 
|  | "var result = 0;" | 
|  | "var method = 'x';" | 
|  | "for (var i = 0; i < 10; i++) {" | 
|  | "  if (i == 5) { method = 'y'; };" | 
|  | "  result += o[method](41);" | 
|  | "}"); | 
|  | CHECK_EQ(42 * 5 + 40 * 5, context->Global() | 
|  | ->Get(context.local(), v8_str("result")) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(context.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit | 
|  | // on the global object. | 
|  | THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "function inc(x) { return x + 1; };" | 
|  | "inc(1);" | 
|  | "function dec(x) { return x - 1; };" | 
|  | "dec(1);" | 
|  | "o.__proto__ = this;" | 
|  | "this.__proto__.x = inc;" | 
|  | "this.__proto__.y = dec;" | 
|  | "var result = 0;" | 
|  | "var method = 'x';" | 
|  | "for (var i = 0; i < 10; i++) {" | 
|  | "  if (i == 5) { method = 'y'; };" | 
|  | "  result += o[method](41);" | 
|  | "}"); | 
|  | CHECK_EQ(42 * 5 + 40 * 5, context->Global() | 
|  | ->Get(context.local(), v8_str("result")) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(context.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test the case when actual function to call sits on global object. | 
|  | THREADED_TEST(InterceptorKeyedCallICFromGlobal) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); | 
|  | templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ_o->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | CompileRun( | 
|  | "function len(x) { return x.length; };" | 
|  | "o.__proto__ = this;" | 
|  | "var m = 'parseFloat';" | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 10; i++) {" | 
|  | "  if (i == 5) {" | 
|  | "    m = 'len';" | 
|  | "    saved_result = result;" | 
|  | "  };" | 
|  | "  result = o[m]('239');" | 
|  | "}"); | 
|  | CHECK_EQ(3, context->Global() | 
|  | ->Get(context.local(), v8_str("result")) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(context.local()) | 
|  | .FromJust()); | 
|  | CHECK_EQ(239, context->Global() | 
|  | ->Get(context.local(), v8_str("saved_result")) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(context.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test the map transition before the interceptor. | 
|  | THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); | 
|  | templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("proto"), | 
|  | templ_o->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | CompileRun( | 
|  | "var o = new Object();" | 
|  | "o.__proto__ = proto;" | 
|  | "o.method = function(x) { return x + 1; };" | 
|  | "var m = 'method';" | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 10; i++) {" | 
|  | "  if (i == 5) { o.method = function(x) { return x - 1; }; };" | 
|  | "  result += o[m](41);" | 
|  | "}"); | 
|  | CHECK_EQ(42 * 5 + 40 * 5, context->Global() | 
|  | ->Get(context.local(), v8_str("result")) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(context.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test the map transition after the interceptor. | 
|  | THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); | 
|  | templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("o"), | 
|  | templ_o->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | CompileRun( | 
|  | "var proto = new Object();" | 
|  | "o.__proto__ = proto;" | 
|  | "proto.method = function(x) { return x + 1; };" | 
|  | "var m = 'method';" | 
|  | "var result = 0;" | 
|  | "for (var i = 0; i < 10; i++) {" | 
|  | "  if (i == 5) { proto.method = function(x) { return x - 1; }; };" | 
|  | "  result += o[m](41);" | 
|  | "}"); | 
|  | CHECK_EQ(42 * 5 + 40 * 5, context->Global() | 
|  | ->Get(context.local(), v8_str("result")) | 
|  | .ToLocalChecked() | 
|  | ->Int32Value(context.local()) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static int interceptor_call_count = 0; | 
|  |  | 
|  | static void InterceptorICRefErrorGetter( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (!is_bootstrapping && | 
|  | v8_str("x") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), name) | 
|  | .FromJust() && | 
|  | interceptor_call_count++ < 20) { | 
|  | info.GetReturnValue().Set(call_ic_function2); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // This test should hit load and call ICs for the interceptor case. | 
|  | // Once in a while, the interceptor will reply that a property was not | 
|  | // found in which case we should get a reference error. | 
|  | THREADED_TEST(InterceptorICReferenceErrors) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorICRefErrorGetter)); | 
|  | is_bootstrapping = true; | 
|  | LocalContext context(nullptr, templ, v8::Local<Value>()); | 
|  | is_bootstrapping = false; | 
|  | call_ic_function2 = v8_compile("function h(x) { return x; }; h") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "function f() {" | 
|  | "  for (var i = 0; i < 1000; i++) {" | 
|  | "    try { x; } catch(e) { return true; }" | 
|  | "  }" | 
|  | "  return false;" | 
|  | "};" | 
|  | "f();"); | 
|  | CHECK(value->BooleanValue(isolate)); | 
|  | interceptor_call_count = 0; | 
|  | value = CompileRun( | 
|  | "function g() {" | 
|  | "  for (var i = 0; i < 1000; i++) {" | 
|  | "    try { x(42); } catch(e) { return true; }" | 
|  | "  }" | 
|  | "  return false;" | 
|  | "};" | 
|  | "g();"); | 
|  | CHECK(value->BooleanValue(isolate)); | 
|  | } | 
|  |  | 
|  |  | 
|  | static int interceptor_ic_exception_get_count = 0; | 
|  |  | 
|  | static void InterceptorICExceptionGetter( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (is_bootstrapping) return; | 
|  | if (v8_str("x") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), name) | 
|  | .FromJust() && | 
|  | ++interceptor_ic_exception_get_count < 20) { | 
|  | info.GetReturnValue().Set(call_ic_function3); | 
|  | } | 
|  | if (interceptor_ic_exception_get_count == 20) { | 
|  | info.GetIsolate()->ThrowException(v8_num(42)); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test interceptor load/call IC where the interceptor throws an | 
|  | // exception once in a while. | 
|  | THREADED_TEST(InterceptorICGetterExceptions) { | 
|  | interceptor_ic_exception_get_count = 0; | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorICExceptionGetter)); | 
|  | is_bootstrapping = true; | 
|  | LocalContext context(nullptr, templ, v8::Local<Value>()); | 
|  | is_bootstrapping = false; | 
|  | call_ic_function3 = v8_compile("function h(x) { return x; }; h") | 
|  | ->Run(context.local()) | 
|  | .ToLocalChecked(); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "function f() {" | 
|  | "  for (var i = 0; i < 100; i++) {" | 
|  | "    try { x; } catch(e) { return true; }" | 
|  | "  }" | 
|  | "  return false;" | 
|  | "};" | 
|  | "f();"); | 
|  | CHECK(value->BooleanValue(isolate)); | 
|  | interceptor_ic_exception_get_count = 0; | 
|  | value = CompileRun( | 
|  | "function f() {" | 
|  | "  for (var i = 0; i < 100; i++) {" | 
|  | "    try { x(42); } catch(e) { return true; }" | 
|  | "  }" | 
|  | "  return false;" | 
|  | "};" | 
|  | "f();"); | 
|  | CHECK(value->BooleanValue(isolate)); | 
|  | } | 
|  |  | 
|  |  | 
|  | static int interceptor_ic_exception_set_count = 0; | 
|  |  | 
|  | static void InterceptorICExceptionSetter( | 
|  | Local<Name> key, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | if (++interceptor_ic_exception_set_count > 20) { | 
|  | info.GetIsolate()->ThrowException(v8_num(42)); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test interceptor store IC where the interceptor throws an exception | 
|  | // once in a while. | 
|  | THREADED_TEST(InterceptorICSetterExceptions) { | 
|  | interceptor_ic_exception_set_count = 0; | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, InterceptorICExceptionSetter)); | 
|  | LocalContext context(nullptr, templ, v8::Local<Value>()); | 
|  | v8::Local<Value> value = CompileRun( | 
|  | "function f() {" | 
|  | "  for (var i = 0; i < 100; i++) {" | 
|  | "    try { x = 42; } catch(e) { return true; }" | 
|  | "  }" | 
|  | "  return false;" | 
|  | "};" | 
|  | "f();"); | 
|  | CHECK(value->BooleanValue(isolate)); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test that we ignore null interceptors. | 
|  | THREADED_TEST(NullNamedInterceptor) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | static_cast<v8::GenericNamedPropertyGetterCallback>(nullptr))); | 
|  | LocalContext context; | 
|  | templ->Set(CcTest::isolate(), "x", v8_num(42)); | 
|  | v8::Local<v8::Object> obj = | 
|  | templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust(); | 
|  | v8::Local<Value> value = CompileRun("obj.x"); | 
|  | CHECK(value->IsInt32()); | 
|  | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Test that we ignore null interceptors. | 
|  | THREADED_TEST(NullIndexedInterceptor) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); | 
|  | templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | static_cast<v8::IndexedPropertyGetterCallback>(nullptr))); | 
|  | LocalContext context; | 
|  | templ->Set(CcTest::isolate(), "42", v8_num(42)); | 
|  | v8::Local<v8::Object> obj = | 
|  | templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust(); | 
|  | v8::Local<Value> value = CompileRun("obj[42]"); | 
|  | CHECK(value->IsInt32()); | 
|  | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(NamedPropertyHandlerGetterAttributes) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); | 
|  | templ->InstanceTemplate()->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) | 
|  | .ToLocalChecked() | 
|  | ->NewInstance(env.local()) | 
|  | .ToLocalChecked()) | 
|  | .FromJust(); | 
|  | ExpectTrue("obj.x === 42"); | 
|  | ExpectTrue("!obj.propertyIsEnumerable('x')"); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(Regress256330) { | 
|  | if (!i::FLAG_opt) return; | 
|  | i::FLAG_allow_natives_syntax = true; | 
|  | LocalContext context; | 
|  | v8::HandleScope scope(context->GetIsolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); | 
|  | AddInterceptor(templ, InterceptorGetter, InterceptorSetter); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("Bug"), | 
|  | templ->GetFunction(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "\"use strict\"; var o = new Bug;" | 
|  | "function f(o) { o.x = 10; };" | 
|  | "%PrepareFunctionForOptimization(f);" | 
|  | "f(o); f(o); f(o);" | 
|  | "%OptimizeFunctionOnNextCall(f);" | 
|  | "f(o);"); | 
|  | int status = v8_run_int32value(v8_compile("%GetOptimizationStatus(f)")); | 
|  | int mask = static_cast<int>(i::OptimizationStatus::kIsFunction) | | 
|  | static_cast<int>(i::OptimizationStatus::kOptimized); | 
|  | CHECK_EQ(mask, status & mask); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(OptimizedInterceptorSetter) { | 
|  | i::FLAG_allow_natives_syntax = true; | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); | 
|  | AddInterceptor(templ, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Obj"), | 
|  | templ->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var obj = new Obj;" | 
|  | // Initialize fields to avoid transitions later. | 
|  | "obj.age = 0;" | 
|  | "obj.accessor_age = 42;" | 
|  | "function setter(i) { this.accessor_age = i; };" | 
|  | "function getter() { return this.accessor_age; };" | 
|  | "function setAge(i) { obj.age = i; };" | 
|  | "Object.defineProperty(obj, 'age', { get:getter, set:setter });" | 
|  | "%PrepareFunctionForOptimization(setAge);" | 
|  | "setAge(1);" | 
|  | "setAge(2);" | 
|  | "setAge(3);" | 
|  | "%OptimizeFunctionOnNextCall(setAge);" | 
|  | "setAge(4);"); | 
|  | // All stores went through the interceptor. | 
|  | ExpectInt32("obj.interceptor_age", 4); | 
|  | ExpectInt32("obj.accessor_age", 42); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(OptimizedInterceptorGetter) { | 
|  | i::FLAG_allow_natives_syntax = true; | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); | 
|  | AddInterceptor(templ, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Obj"), | 
|  | templ->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var obj = new Obj;" | 
|  | // Initialize fields to avoid transitions later. | 
|  | "obj.age = 1;" | 
|  | "obj.accessor_age = 42;" | 
|  | "function getter() { return this.accessor_age; };" | 
|  | "function getAge() { return obj.interceptor_age; };" | 
|  | "Object.defineProperty(obj, 'interceptor_age', { get:getter });" | 
|  | "%PrepareFunctionForOptimization(getAge);" | 
|  | "getAge();" | 
|  | "getAge();" | 
|  | "getAge();" | 
|  | "%OptimizeFunctionOnNextCall(getAge);"); | 
|  | // Access through interceptor. | 
|  | ExpectInt32("getAge()", 1); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(OptimizedInterceptorFieldRead) { | 
|  | i::FLAG_allow_natives_syntax = true; | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); | 
|  | AddInterceptor(templ, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Obj"), | 
|  | templ->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var obj = new Obj;" | 
|  | "obj.__proto__.interceptor_age = 42;" | 
|  | "obj.age = 100;" | 
|  | "function getAge() { return obj.interceptor_age; };" | 
|  | "%PrepareFunctionForOptimization(getAge);"); | 
|  | ExpectInt32("getAge();", 100); | 
|  | ExpectInt32("getAge();", 100); | 
|  | ExpectInt32("getAge();", 100); | 
|  | CompileRun("%OptimizeFunctionOnNextCall(getAge);"); | 
|  | // Access through interceptor. | 
|  | ExpectInt32("getAge();", 100); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(OptimizedInterceptorFieldWrite) { | 
|  | i::FLAG_allow_natives_syntax = true; | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); | 
|  | AddInterceptor(templ, InterceptorGetter, InterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Obj"), | 
|  | templ->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var obj = new Obj;" | 
|  | "obj.age = 100000;" | 
|  | "function setAge(i) { obj.age = i };" | 
|  | "%PrepareFunctionForOptimization(setAge);" | 
|  | "setAge(100);" | 
|  | "setAge(101);" | 
|  | "setAge(102);" | 
|  | "%OptimizeFunctionOnNextCall(setAge);" | 
|  | "setAge(103);"); | 
|  | ExpectInt32("obj.age", 100000); | 
|  | ExpectInt32("obj.interceptor_age", 103); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(Regress149912) { | 
|  | LocalContext context; | 
|  | v8::HandleScope scope(context->GetIsolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); | 
|  | AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("Bug"), | 
|  | templ->GetFunction(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun("Number.prototype.__proto__ = new Bug; var x = 0; x.foo();"); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(Regress625155) { | 
|  | LocalContext context; | 
|  | v8::HandleScope scope(context->GetIsolate()); | 
|  | Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); | 
|  | AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("Bug"), | 
|  | templ->GetFunction(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "Number.prototype.__proto__ = new Bug;" | 
|  | "var x;" | 
|  | "x = 0xDEAD;" | 
|  | "x.boom = 0;" | 
|  | "x = 's';" | 
|  | "x.boom = 0;" | 
|  | "x = 1.5;" | 
|  | "x.boom = 0;"); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(Regress125988) { | 
|  | v8::HandleScope scope(CcTest::isolate()); | 
|  | Local<FunctionTemplate> intercept = FunctionTemplate::New(CcTest::isolate()); | 
|  | AddInterceptor(intercept, EmptyInterceptorGetter, EmptyInterceptorSetter); | 
|  | LocalContext env; | 
|  | env->Global() | 
|  | ->Set(env.local(), v8_str("Intercept"), | 
|  | intercept->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "var a = new Object();" | 
|  | "var b = new Intercept();" | 
|  | "var c = new Object();" | 
|  | "c.__proto__ = b;" | 
|  | "b.__proto__ = a;" | 
|  | "a.x = 23;" | 
|  | "for (var i = 0; i < 3; i++) c.x;"); | 
|  | ExpectBoolean("c.hasOwnProperty('x')", false); | 
|  | ExpectInt32("c.x", 23); | 
|  | CompileRun( | 
|  | "a.y = 42;" | 
|  | "for (var i = 0; i < 3; i++) c.x;"); | 
|  | ExpectBoolean("c.hasOwnProperty('x')", false); | 
|  | ExpectInt32("c.x", 23); | 
|  | ExpectBoolean("c.hasOwnProperty('y')", false); | 
|  | ExpectInt32("c.y", 42); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void IndexedPropertyEnumerator( | 
|  | const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 1); | 
|  | result->Set(info.GetIsolate()->GetCurrentContext(), 0, | 
|  | v8::Integer::New(info.GetIsolate(), 7)) | 
|  | .FromJust(); | 
|  | info.GetReturnValue().Set(result); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void NamedPropertyEnumerator( | 
|  | const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 2); | 
|  | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); | 
|  | result->Set(context, 0, v8_str("x")).FromJust(); | 
|  | result->Set(context, 1, v8::Symbol::GetIterator(info.GetIsolate())) | 
|  | .FromJust(); | 
|  | info.GetReturnValue().Set(result); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(GetOwnPropertyNamesWithInterceptor) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope handle_scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate); | 
|  |  | 
|  | obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7)); | 
|  | obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42)); | 
|  | obj_template->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, IndexedPropertyEnumerator)); | 
|  | obj_template->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, NamedPropertyEnumerator)); | 
|  |  | 
|  | LocalContext context; | 
|  | v8::Local<v8::Object> global = context->Global(); | 
|  | global->Set(context.local(), v8_str("object"), | 
|  | obj_template->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | v8::Local<v8::Value> result = | 
|  | CompileRun("Object.getOwnPropertyNames(object)"); | 
|  | CHECK(result->IsArray()); | 
|  | v8::Local<v8::Array> result_array = v8::Local<v8::Array>::Cast(result); | 
|  | CHECK_EQ(2u, result_array->Length()); | 
|  | CHECK(result_array->Get(context.local(), 0).ToLocalChecked()->IsString()); | 
|  | CHECK(result_array->Get(context.local(), 1).ToLocalChecked()->IsString()); | 
|  | CHECK(v8_str("7") | 
|  | ->Equals(context.local(), | 
|  | result_array->Get(context.local(), 0).ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("x") | 
|  | ->Equals(context.local(), | 
|  | result_array->Get(context.local(), 1).ToLocalChecked()) | 
|  | .FromJust()); | 
|  |  | 
|  | result = CompileRun("var ret = []; for (var k in object) ret.push(k); ret"); | 
|  | CHECK(result->IsArray()); | 
|  | result_array = v8::Local<v8::Array>::Cast(result); | 
|  | CHECK_EQ(2u, result_array->Length()); | 
|  | CHECK(result_array->Get(context.local(), 0).ToLocalChecked()->IsString()); | 
|  | CHECK(result_array->Get(context.local(), 1).ToLocalChecked()->IsString()); | 
|  | CHECK(v8_str("7") | 
|  | ->Equals(context.local(), | 
|  | result_array->Get(context.local(), 0).ToLocalChecked()) | 
|  | .FromJust()); | 
|  | CHECK(v8_str("x") | 
|  | ->Equals(context.local(), | 
|  | result_array->Get(context.local(), 1).ToLocalChecked()) | 
|  | .FromJust()); | 
|  |  | 
|  | result = CompileRun("Object.getOwnPropertySymbols(object)"); | 
|  | CHECK(result->IsArray()); | 
|  | result_array = v8::Local<v8::Array>::Cast(result); | 
|  | CHECK_EQ(1u, result_array->Length()); | 
|  | CHECK(result_array->Get(context.local(), 0) | 
|  | .ToLocalChecked() | 
|  | ->Equals(context.local(), v8::Symbol::GetIterator(isolate)) | 
|  | .FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void IndexedPropertyEnumeratorException( | 
|  | const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | info.GetIsolate()->ThrowException(v8_num(42)); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(GetOwnPropertyNamesWithIndexedInterceptorExceptions_regress4026) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope handle_scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate); | 
|  |  | 
|  | obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7)); | 
|  | obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42)); | 
|  | // First just try a failing indexed interceptor. | 
|  | obj_template->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, IndexedPropertyEnumeratorException)); | 
|  |  | 
|  | LocalContext context; | 
|  | v8::Local<v8::Object> global = context->Global(); | 
|  | global->Set(context.local(), v8_str("object"), | 
|  | obj_template->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | v8::Local<v8::Value> result = CompileRun( | 
|  | "var result  = []; " | 
|  | "try { " | 
|  | "  for (var k in object) result .push(k);" | 
|  | "} catch (e) {" | 
|  | "  result  = e" | 
|  | "}" | 
|  | "result "); | 
|  | CHECK(!result->IsArray()); | 
|  | CHECK(v8_num(42)->Equals(context.local(), result).FromJust()); | 
|  |  | 
|  | result = CompileRun( | 
|  | "var result = [];" | 
|  | "try { " | 
|  | "  result = Object.keys(object);" | 
|  | "} catch (e) {" | 
|  | "  result = e;" | 
|  | "}" | 
|  | "result"); | 
|  | CHECK(!result->IsArray()); | 
|  | CHECK(v8_num(42)->Equals(context.local(), result).FromJust()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void NamedPropertyEnumeratorException( | 
|  | const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | info.GetIsolate()->ThrowException(v8_num(43)); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(GetOwnPropertyNamesWithNamedInterceptorExceptions_regress4026) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope handle_scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate); | 
|  |  | 
|  | obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7)); | 
|  | obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42)); | 
|  | // First just try a failing indexed interceptor. | 
|  | obj_template->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | nullptr, nullptr, nullptr, nullptr, NamedPropertyEnumeratorException)); | 
|  |  | 
|  | LocalContext context; | 
|  | v8::Local<v8::Object> global = context->Global(); | 
|  | global->Set(context.local(), v8_str("object"), | 
|  | obj_template->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | v8::Local<v8::Value> result = CompileRun( | 
|  | "var result = []; " | 
|  | "try { " | 
|  | "  for (var k in object) result.push(k);" | 
|  | "} catch (e) {" | 
|  | "  result = e" | 
|  | "}" | 
|  | "result"); | 
|  | CHECK(!result->IsArray()); | 
|  | CHECK(v8_num(43)->Equals(context.local(), result).FromJust()); | 
|  |  | 
|  | result = CompileRun( | 
|  | "var result = [];" | 
|  | "try { " | 
|  | "  result = Object.keys(object);" | 
|  | "} catch (e) {" | 
|  | "  result = e;" | 
|  | "}" | 
|  | "result"); | 
|  | CHECK(!result->IsArray()); | 
|  | CHECK(v8_num(43)->Equals(context.local(), result).FromJust()); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | template <typename T> | 
|  | Local<Object> BuildWrappedObject(v8::Isolate* isolate, T* data) { | 
|  | auto templ = v8::ObjectTemplate::New(isolate); | 
|  | templ->SetInternalFieldCount(1); | 
|  | auto instance = | 
|  | templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); | 
|  | instance->SetAlignedPointerInInternalField(0, data); | 
|  | return instance; | 
|  | } | 
|  |  | 
|  |  | 
|  | template <typename T> | 
|  | T* GetWrappedObject(Local<Value> data) { | 
|  | return reinterpret_cast<T*>( | 
|  | Object::Cast(*data)->GetAlignedPointerFromInternalField(0)); | 
|  | } | 
|  |  | 
|  |  | 
|  | struct AccessCheckData { | 
|  | int count; | 
|  | bool result; | 
|  | }; | 
|  |  | 
|  | AccessCheckData* g_access_check_data = nullptr; | 
|  |  | 
|  | bool SimpleAccessChecker(Local<v8::Context> accessing_context, | 
|  | Local<v8::Object> access_object, | 
|  | Local<v8::Value> data) { | 
|  | g_access_check_data->count++; | 
|  | return g_access_check_data->result; | 
|  | } | 
|  |  | 
|  |  | 
|  | struct ShouldInterceptData { | 
|  | int value; | 
|  | bool should_intercept; | 
|  | }; | 
|  |  | 
|  |  | 
|  | void ShouldNamedInterceptor(Local<Name> name, | 
|  | const v8::PropertyCallbackInfo<Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CheckReturnValue(info, FUNCTION_ADDR(ShouldNamedInterceptor)); | 
|  | auto data = GetWrappedObject<ShouldInterceptData>(info.Data()); | 
|  | if (!data->should_intercept) return; | 
|  | info.GetReturnValue().Set(v8_num(data->value)); | 
|  | } | 
|  |  | 
|  |  | 
|  | void ShouldIndexedInterceptor(uint32_t, | 
|  | const v8::PropertyCallbackInfo<Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | CheckReturnValue(info, FUNCTION_ADDR(ShouldIndexedInterceptor)); | 
|  | auto data = GetWrappedObject<ShouldInterceptData>(info.Data()); | 
|  | if (!data->should_intercept) return; | 
|  | info.GetReturnValue().Set(v8_num(data->value)); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  |  | 
|  | TEST(NamedAllCanReadInterceptor) { | 
|  | auto isolate = CcTest::isolate(); | 
|  | v8::HandleScope handle_scope(isolate); | 
|  | LocalContext context; | 
|  |  | 
|  | AccessCheckData access_check_data; | 
|  | access_check_data.result = true; | 
|  | access_check_data.count = 0; | 
|  |  | 
|  | g_access_check_data = &access_check_data; | 
|  |  | 
|  | ShouldInterceptData intercept_data_0; | 
|  | intercept_data_0.value = 239; | 
|  | intercept_data_0.should_intercept = true; | 
|  |  | 
|  | ShouldInterceptData intercept_data_1; | 
|  | intercept_data_1.value = 165; | 
|  | intercept_data_1.should_intercept = false; | 
|  |  | 
|  | auto intercepted_0 = v8::ObjectTemplate::New(isolate); | 
|  | { | 
|  | v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor); | 
|  | conf.flags = v8::PropertyHandlerFlags::kAllCanRead; | 
|  | conf.data = | 
|  | BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_0); | 
|  | intercepted_0->SetHandler(conf); | 
|  | } | 
|  |  | 
|  | auto intercepted_1 = v8::ObjectTemplate::New(isolate); | 
|  | { | 
|  | v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor); | 
|  | conf.flags = v8::PropertyHandlerFlags::kAllCanRead; | 
|  | conf.data = | 
|  | BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_1); | 
|  | intercepted_1->SetHandler(conf); | 
|  | } | 
|  |  | 
|  | auto checked = v8::ObjectTemplate::New(isolate); | 
|  | checked->SetAccessCheckCallback(SimpleAccessChecker); | 
|  |  | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("intercepted_0"), | 
|  | intercepted_0->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("intercepted_1"), | 
|  | intercepted_1->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | auto checked_instance = | 
|  | checked->NewInstance(context.local()).ToLocalChecked(); | 
|  | checked_instance->Set(context.local(), v8_str("whatever"), v8_num(17)) | 
|  | .FromJust(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("checked"), checked_instance) | 
|  | .FromJust(); | 
|  | CompileRun( | 
|  | "checked.__proto__ = intercepted_1;" | 
|  | "intercepted_1.__proto__ = intercepted_0;"); | 
|  |  | 
|  | CHECK_EQ(3, access_check_data.count); | 
|  |  | 
|  | ExpectInt32("checked.whatever", 17); | 
|  | CHECK(!CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')") | 
|  | ->IsUndefined()); | 
|  | CHECK_EQ(5, access_check_data.count); | 
|  |  | 
|  | access_check_data.result = false; | 
|  | ExpectInt32("checked.whatever", intercept_data_0.value); | 
|  | { | 
|  | v8::TryCatch try_catch(isolate); | 
|  | CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')"); | 
|  | CHECK(try_catch.HasCaught()); | 
|  | } | 
|  | CHECK_EQ(8, access_check_data.count); | 
|  |  | 
|  | intercept_data_1.should_intercept = true; | 
|  | ExpectInt32("checked.whatever", intercept_data_1.value); | 
|  | { | 
|  | v8::TryCatch try_catch(isolate); | 
|  | CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')"); | 
|  | CHECK(try_catch.HasCaught()); | 
|  | } | 
|  | CHECK_EQ(11, access_check_data.count); | 
|  | g_access_check_data = nullptr; | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST(IndexedAllCanReadInterceptor) { | 
|  | auto isolate = CcTest::isolate(); | 
|  | v8::HandleScope handle_scope(isolate); | 
|  | LocalContext context; | 
|  |  | 
|  | AccessCheckData access_check_data; | 
|  | access_check_data.result = true; | 
|  | access_check_data.count = 0; | 
|  |  | 
|  | g_access_check_data = &access_check_data; | 
|  |  | 
|  | ShouldInterceptData intercept_data_0; | 
|  | intercept_data_0.value = 239; | 
|  | intercept_data_0.should_intercept = true; | 
|  |  | 
|  | ShouldInterceptData intercept_data_1; | 
|  | intercept_data_1.value = 165; | 
|  | intercept_data_1.should_intercept = false; | 
|  |  | 
|  | auto intercepted_0 = v8::ObjectTemplate::New(isolate); | 
|  | { | 
|  | v8::IndexedPropertyHandlerConfiguration conf(ShouldIndexedInterceptor); | 
|  | conf.flags = v8::PropertyHandlerFlags::kAllCanRead; | 
|  | conf.data = | 
|  | BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_0); | 
|  | intercepted_0->SetHandler(conf); | 
|  | } | 
|  |  | 
|  | auto intercepted_1 = v8::ObjectTemplate::New(isolate); | 
|  | { | 
|  | v8::IndexedPropertyHandlerConfiguration conf(ShouldIndexedInterceptor); | 
|  | conf.flags = v8::PropertyHandlerFlags::kAllCanRead; | 
|  | conf.data = | 
|  | BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_1); | 
|  | intercepted_1->SetHandler(conf); | 
|  | } | 
|  |  | 
|  | auto checked = v8::ObjectTemplate::New(isolate); | 
|  | checked->SetAccessCheckCallback(SimpleAccessChecker); | 
|  |  | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("intercepted_0"), | 
|  | intercepted_0->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("intercepted_1"), | 
|  | intercepted_1->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | auto checked_instance = | 
|  | checked->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("checked"), checked_instance) | 
|  | .FromJust(); | 
|  | checked_instance->Set(context.local(), 15, v8_num(17)).FromJust(); | 
|  | CompileRun( | 
|  | "checked.__proto__ = intercepted_1;" | 
|  | "intercepted_1.__proto__ = intercepted_0;"); | 
|  |  | 
|  | CHECK_EQ(3, access_check_data.count); | 
|  |  | 
|  | access_check_data.result = true; | 
|  | ExpectInt32("checked[15]", 17); | 
|  | CHECK(!CompileRun("Object.getOwnPropertyDescriptor(checked, '15')") | 
|  | ->IsUndefined()); | 
|  | CHECK_EQ(5, access_check_data.count); | 
|  |  | 
|  | access_check_data.result = false; | 
|  | ExpectInt32("checked[15]", intercept_data_0.value); | 
|  | { | 
|  | v8::TryCatch try_catch(isolate); | 
|  | CompileRun("Object.getOwnPropertyDescriptor(checked, '15')"); | 
|  | CHECK(try_catch.HasCaught()); | 
|  | } | 
|  | CHECK_EQ(8, access_check_data.count); | 
|  |  | 
|  | intercept_data_1.should_intercept = true; | 
|  | ExpectInt32("checked[15]", intercept_data_1.value); | 
|  | { | 
|  | v8::TryCatch try_catch(isolate); | 
|  | CompileRun("Object.getOwnPropertyDescriptor(checked, '15')"); | 
|  | CHECK(try_catch.HasCaught()); | 
|  | } | 
|  | CHECK_EQ(11, access_check_data.count); | 
|  |  | 
|  | g_access_check_data = nullptr; | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(NonMaskingInterceptorOwnProperty) { | 
|  | auto isolate = CcTest::isolate(); | 
|  | v8::HandleScope handle_scope(isolate); | 
|  | LocalContext context; | 
|  |  | 
|  | ShouldInterceptData intercept_data; | 
|  | intercept_data.value = 239; | 
|  | intercept_data.should_intercept = true; | 
|  |  | 
|  | auto interceptor_templ = v8::ObjectTemplate::New(isolate); | 
|  | v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor); | 
|  | conf.flags = v8::PropertyHandlerFlags::kNonMasking; | 
|  | conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data); | 
|  | interceptor_templ->SetHandler(conf); | 
|  |  | 
|  | auto interceptor = | 
|  | interceptor_templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), interceptor) | 
|  | .FromJust(); | 
|  |  | 
|  | ExpectInt32("obj.whatever", 239); | 
|  |  | 
|  | CompileRun("obj.whatever = 4;"); | 
|  |  | 
|  | // obj.whatever exists, thus it is not affected by the non-masking | 
|  | // interceptor. | 
|  | ExpectInt32("obj.whatever", 4); | 
|  |  | 
|  | CompileRun("delete obj.whatever;"); | 
|  | ExpectInt32("obj.whatever", 239); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(NonMaskingInterceptorPrototypeProperty) { | 
|  | auto isolate = CcTest::isolate(); | 
|  | v8::HandleScope handle_scope(isolate); | 
|  | LocalContext context; | 
|  |  | 
|  | ShouldInterceptData intercept_data; | 
|  | intercept_data.value = 239; | 
|  | intercept_data.should_intercept = true; | 
|  |  | 
|  | auto interceptor_templ = v8::ObjectTemplate::New(isolate); | 
|  | v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor); | 
|  | conf.flags = v8::PropertyHandlerFlags::kNonMasking; | 
|  | conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data); | 
|  | interceptor_templ->SetHandler(conf); | 
|  |  | 
|  | auto interceptor = | 
|  | interceptor_templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), interceptor) | 
|  | .FromJust(); | 
|  |  | 
|  | ExpectInt32("obj.whatever", 239); | 
|  |  | 
|  | CompileRun("obj.__proto__ = {'whatever': 4};"); | 
|  | ExpectInt32("obj.whatever", 4); | 
|  |  | 
|  | CompileRun("delete obj.__proto__.whatever;"); | 
|  | ExpectInt32("obj.whatever", 239); | 
|  | } | 
|  |  | 
|  |  | 
|  | THREADED_TEST(NonMaskingInterceptorPrototypePropertyIC) { | 
|  | auto isolate = CcTest::isolate(); | 
|  | v8::HandleScope handle_scope(isolate); | 
|  | LocalContext context; | 
|  |  | 
|  | ShouldInterceptData intercept_data; | 
|  | intercept_data.value = 239; | 
|  | intercept_data.should_intercept = true; | 
|  |  | 
|  | auto interceptor_templ = v8::ObjectTemplate::New(isolate); | 
|  | v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor); | 
|  | conf.flags = v8::PropertyHandlerFlags::kNonMasking; | 
|  | conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data); | 
|  | interceptor_templ->SetHandler(conf); | 
|  |  | 
|  | auto interceptor = | 
|  | interceptor_templ->NewInstance(context.local()).ToLocalChecked(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), interceptor) | 
|  | .FromJust(); | 
|  |  | 
|  | CompileRun( | 
|  | "outer = {};" | 
|  | "outer.__proto__ = obj;" | 
|  | "function f(obj) {" | 
|  | "  var x;" | 
|  | "  for (var i = 0; i < 4; i++) {" | 
|  | "    x = obj.whatever;" | 
|  | "  }" | 
|  | "  return x;" | 
|  | "}"); | 
|  |  | 
|  | // Receiver == holder. | 
|  | CompileRun("obj.__proto__ = null;"); | 
|  | ExpectInt32("f(obj)", 239); | 
|  | ExpectInt32("f(outer)", 239); | 
|  |  | 
|  | // Receiver != holder. | 
|  | CompileRun("Object.setPrototypeOf(obj, {});"); | 
|  | ExpectInt32("f(obj)", 239); | 
|  | ExpectInt32("f(outer)", 239); | 
|  |  | 
|  | // Masked value on prototype. | 
|  | CompileRun("obj.__proto__.whatever = 4;"); | 
|  | CompileRun("obj.__proto__.__proto__ = { 'whatever' : 5 };"); | 
|  | ExpectInt32("f(obj)", 4); | 
|  | ExpectInt32("f(outer)", 4); | 
|  |  | 
|  | // Masked value on prototype prototype. | 
|  | CompileRun("delete obj.__proto__.whatever;"); | 
|  | ExpectInt32("f(obj)", 5); | 
|  | ExpectInt32("f(outer)", 5); | 
|  |  | 
|  | // Reset. | 
|  | CompileRun("delete obj.__proto__.__proto__.whatever;"); | 
|  | ExpectInt32("f(obj)", 239); | 
|  | ExpectInt32("f(outer)", 239); | 
|  |  | 
|  | // Masked value on self. | 
|  | CompileRun("obj.whatever = 4;"); | 
|  | ExpectInt32("f(obj)", 4); | 
|  | ExpectInt32("f(outer)", 4); | 
|  |  | 
|  | // Reset. | 
|  | CompileRun("delete obj.whatever;"); | 
|  | ExpectInt32("f(obj)", 239); | 
|  | ExpectInt32("f(outer)", 239); | 
|  |  | 
|  | CompileRun("outer.whatever = 4;"); | 
|  | ExpectInt32("f(obj)", 239); | 
|  | ExpectInt32("f(outer)", 4); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void ConcatNamedPropertyGetter( | 
|  | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | info.GetReturnValue().Set( | 
|  | // Return the property name concatenated with itself. | 
|  | String::Concat(info.GetIsolate(), name.As<String>(), name.As<String>())); | 
|  | } | 
|  |  | 
|  | void ConcatIndexedPropertyGetter( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | info.GetReturnValue().Set( | 
|  | // Return the double value of the index. | 
|  | v8_num(index + index)); | 
|  | } | 
|  |  | 
|  | void EnumCallbackWithNames(const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 4); | 
|  | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); | 
|  | CHECK( | 
|  | result | 
|  | ->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_str("foo")) | 
|  | .FromJust()); | 
|  | CHECK( | 
|  | result | 
|  | ->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_str("bar")) | 
|  | .FromJust()); | 
|  | CHECK( | 
|  | result | 
|  | ->Set(context, v8::Integer::New(info.GetIsolate(), 2), v8_str("baz")) | 
|  | .FromJust()); | 
|  | CHECK( | 
|  | result->Set(context, v8::Integer::New(info.GetIsolate(), 3), v8_str("10")) | 
|  | .FromJust()); | 
|  |  | 
|  | //  Create a holey array. | 
|  | CHECK(result->Delete(context, v8::Integer::New(info.GetIsolate(), 1)) | 
|  | .FromJust()); | 
|  | info.GetReturnValue().Set(result); | 
|  | } | 
|  |  | 
|  | void EnumCallbackWithIndices(const v8::PropertyCallbackInfo<v8::Array>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 4); | 
|  | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); | 
|  |  | 
|  | CHECK(result->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_num(10)) | 
|  | .FromJust()); | 
|  | CHECK(result->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_num(11)) | 
|  | .FromJust()); | 
|  | CHECK(result->Set(context, v8::Integer::New(info.GetIsolate(), 2), v8_num(12)) | 
|  | .FromJust()); | 
|  | CHECK(result->Set(context, v8::Integer::New(info.GetIsolate(), 3), v8_num(14)) | 
|  | .FromJust()); | 
|  |  | 
|  | //  Create a holey array. | 
|  | CHECK(result->Delete(context, v8::Integer::New(info.GetIsolate(), 1)) | 
|  | .FromJust()); | 
|  | info.GetReturnValue().Set(result); | 
|  | } | 
|  |  | 
|  | void RestrictiveNamedQuery(Local<Name> property, | 
|  | const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | // Only "foo" is enumerable. | 
|  | if (v8_str("foo") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), property) | 
|  | .FromJust()) { | 
|  | info.GetReturnValue().Set(v8::PropertyAttribute::None); | 
|  | return; | 
|  | } | 
|  | info.GetReturnValue().Set(v8::PropertyAttribute::DontEnum); | 
|  | } | 
|  |  | 
|  | void RestrictiveIndexedQuery( | 
|  | uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | // Only index 2 and 12 are enumerable. | 
|  | if (index == 2 || index == 12) { | 
|  | info.GetReturnValue().Set(v8::PropertyAttribute::None); | 
|  | return; | 
|  | } | 
|  | info.GetReturnValue().Set(v8::PropertyAttribute::DontEnum); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | // Regression test for V8 bug 6627. | 
|  | // Object.keys() must return enumerable keys only. | 
|  | THREADED_TEST(EnumeratorsAndUnenumerableNamedProperties) { | 
|  | // The enumerator interceptor returns a list | 
|  | // of items which are filtered according to the | 
|  | // properties defined in the query interceptor. | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); | 
|  | obj->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | ConcatNamedPropertyGetter, nullptr, RestrictiveNamedQuery, nullptr, | 
|  | EnumCallbackWithNames)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | obj->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | ExpectInt32("Object.getOwnPropertyNames(obj).length", 3); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[0]", "foo"); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[1]", "baz"); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[2]", "10"); | 
|  |  | 
|  | ExpectTrue("Object.getOwnPropertyDescriptor(obj, 'foo').enumerable"); | 
|  | ExpectFalse("Object.getOwnPropertyDescriptor(obj, 'baz').enumerable"); | 
|  |  | 
|  | ExpectInt32("Object.entries(obj).length", 1); | 
|  | ExpectString("Object.entries(obj)[0][0]", "foo"); | 
|  | ExpectString("Object.entries(obj)[0][1]", "foofoo"); | 
|  |  | 
|  | ExpectInt32("Object.keys(obj).length", 1); | 
|  | ExpectString("Object.keys(obj)[0]", "foo"); | 
|  |  | 
|  | ExpectInt32("Object.values(obj).length", 1); | 
|  | ExpectString("Object.values(obj)[0]", "foofoo"); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | void QueryInterceptorForFoo(Local<Name> property, | 
|  | const v8::PropertyCallbackInfo<v8::Integer>& info) { | 
|  | // Don't intercept anything except "foo." | 
|  | if (!v8_str("foo") | 
|  | ->Equals(info.GetIsolate()->GetCurrentContext(), property) | 
|  | .FromJust()) { | 
|  | return; | 
|  | } | 
|  | // "foo" is enumerable. | 
|  | info.GetReturnValue().Set(v8::PropertyAttribute::None); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | // Test that calls to the query interceptor are independent of each | 
|  | // other. | 
|  | THREADED_TEST(EnumeratorsAndUnenumerableNamedPropertiesWithoutSet) { | 
|  | // The enumerator interceptor returns a list | 
|  | // of items which are filtered according to the | 
|  | // properties defined in the query interceptor. | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); | 
|  | obj->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | ConcatNamedPropertyGetter, nullptr, QueryInterceptorForFoo, nullptr, | 
|  | EnumCallbackWithNames)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | obj->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | ExpectInt32("Object.getOwnPropertyNames(obj).length", 3); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[0]", "foo"); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[1]", "baz"); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[2]", "10"); | 
|  |  | 
|  | ExpectTrue("Object.getOwnPropertyDescriptor(obj, 'foo').enumerable"); | 
|  | ExpectInt32("Object.keys(obj).length", 1); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(EnumeratorsAndUnenumerableIndexedPropertiesArgumentsElements) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); | 
|  | obj->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | ConcatIndexedPropertyGetter, nullptr, RestrictiveIndexedQuery, nullptr, | 
|  | SloppyArgsIndexedPropertyEnumerator)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | obj->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | ExpectInt32("Object.getOwnPropertyNames(obj).length", 4); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[0]", "0"); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[1]", "1"); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[2]", "2"); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[3]", "3"); | 
|  |  | 
|  | ExpectTrue("Object.getOwnPropertyDescriptor(obj, '2').enumerable"); | 
|  |  | 
|  | ExpectInt32("Object.entries(obj).length", 1); | 
|  | ExpectString("Object.entries(obj)[0][0]", "2"); | 
|  | ExpectInt32("Object.entries(obj)[0][1]", 4); | 
|  |  | 
|  | ExpectInt32("Object.keys(obj).length", 1); | 
|  | ExpectString("Object.keys(obj)[0]", "2"); | 
|  |  | 
|  | ExpectInt32("Object.values(obj).length", 1); | 
|  | ExpectInt32("Object.values(obj)[0]", 4); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(EnumeratorsAndUnenumerableIndexedProperties) { | 
|  | // The enumerator interceptor returns a list | 
|  | // of items which are filtered according to the | 
|  | // properties defined in the query interceptor. | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); | 
|  | obj->SetHandler(v8::IndexedPropertyHandlerConfiguration( | 
|  | ConcatIndexedPropertyGetter, nullptr, RestrictiveIndexedQuery, nullptr, | 
|  | EnumCallbackWithIndices)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | obj->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | ExpectInt32("Object.getOwnPropertyNames(obj).length", 3); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[0]", "10"); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[1]", "12"); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[2]", "14"); | 
|  |  | 
|  | ExpectFalse("Object.getOwnPropertyDescriptor(obj, '10').enumerable"); | 
|  | ExpectTrue("Object.getOwnPropertyDescriptor(obj, '12').enumerable"); | 
|  |  | 
|  | ExpectInt32("Object.entries(obj).length", 1); | 
|  | ExpectString("Object.entries(obj)[0][0]", "12"); | 
|  | ExpectInt32("Object.entries(obj)[0][1]", 24); | 
|  |  | 
|  | ExpectInt32("Object.keys(obj).length", 1); | 
|  | ExpectString("Object.keys(obj)[0]", "12"); | 
|  |  | 
|  | ExpectInt32("Object.values(obj).length", 1); | 
|  | ExpectInt32("Object.values(obj)[0]", 24); | 
|  | } | 
|  |  | 
|  | THREADED_TEST(EnumeratorsAndForIn) { | 
|  | v8::Isolate* isolate = CcTest::isolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); | 
|  | obj->SetHandler(v8::NamedPropertyHandlerConfiguration( | 
|  | ConcatNamedPropertyGetter, nullptr, RestrictiveNamedQuery, nullptr, | 
|  | NamedEnum)); | 
|  | LocalContext context; | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("obj"), | 
|  | obj->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | ExpectInt32("Object.getOwnPropertyNames(obj).length", 3); | 
|  | ExpectString("Object.getOwnPropertyNames(obj)[0]", "foo"); | 
|  |  | 
|  | ExpectTrue("Object.getOwnPropertyDescriptor(obj, 'foo').enumerable"); | 
|  |  | 
|  | CompileRun( | 
|  | "let concat = '';" | 
|  | "for(var prop in obj) {" | 
|  | "  concat += `key:${prop}:value:${obj[prop]}`;" | 
|  | "}"); | 
|  |  | 
|  | // Check that for...in only iterates over enumerable properties. | 
|  | ExpectString("concat", "key:foo:value:foofoo"); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void DatabaseGetter(Local<Name> name, | 
|  | const v8::PropertyCallbackInfo<Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | auto context = info.GetIsolate()->GetCurrentContext(); | 
|  | Local<v8::Object> db = info.Holder() | 
|  | ->GetRealNamedProperty(context, v8_str("db")) | 
|  | .ToLocalChecked() | 
|  | .As<v8::Object>(); | 
|  | if (!db->Has(context, name).FromJust()) return; | 
|  | info.GetReturnValue().Set(db->Get(context, name).ToLocalChecked()); | 
|  | } | 
|  |  | 
|  |  | 
|  | void DatabaseSetter(Local<Name> name, Local<Value> value, | 
|  | const v8::PropertyCallbackInfo<Value>& info) { | 
|  | ApiTestFuzzer::Fuzz(); | 
|  | auto context = info.GetIsolate()->GetCurrentContext(); | 
|  | if (name->Equals(context, v8_str("db")).FromJust()) return; | 
|  | Local<v8::Object> db = info.Holder() | 
|  | ->GetRealNamedProperty(context, v8_str("db")) | 
|  | .ToLocalChecked() | 
|  | .As<v8::Object>(); | 
|  | db->Set(context, name, value).FromJust(); | 
|  | info.GetReturnValue().Set(value); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  |  | 
|  | THREADED_TEST(NonMaskingInterceptorGlobalEvalRegression) { | 
|  | auto isolate = CcTest::isolate(); | 
|  | v8::HandleScope handle_scope(isolate); | 
|  | LocalContext context; | 
|  |  | 
|  | auto interceptor_templ = v8::ObjectTemplate::New(isolate); | 
|  | v8::NamedPropertyHandlerConfiguration conf(DatabaseGetter, DatabaseSetter); | 
|  | conf.flags = v8::PropertyHandlerFlags::kNonMasking; | 
|  | interceptor_templ->SetHandler(conf); | 
|  |  | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("intercepted_1"), | 
|  | interceptor_templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  | context->Global() | 
|  | ->Set(context.local(), v8_str("intercepted_2"), | 
|  | interceptor_templ->NewInstance(context.local()).ToLocalChecked()) | 
|  | .FromJust(); | 
|  |  | 
|  | // Init dbs. | 
|  | CompileRun( | 
|  | "intercepted_1.db = {};" | 
|  | "intercepted_2.db = {};"); | 
|  |  | 
|  | ExpectInt32( | 
|  | "var obj = intercepted_1;" | 
|  | "obj.x = 4;" | 
|  | "eval('obj.x');" | 
|  | "eval('obj.x');" | 
|  | "eval('obj.x');" | 
|  | "obj = intercepted_2;" | 
|  | "obj.x = 9;" | 
|  | "eval('obj.x');", | 
|  | 9); | 
|  | } | 
|  |  | 
|  | static void CheckReceiver(Local<Name> name, | 
|  | const v8::PropertyCallbackInfo<v8::Value>& info) { | 
|  | CHECK(info.This()->IsObject()); | 
|  | } | 
|  |  | 
|  | TEST(Regress609134Interceptor) { | 
|  | LocalContext env; | 
|  | v8::Isolate* isolate = env->GetIsolate(); | 
|  | v8::HandleScope scope(isolate); | 
|  | auto fun_templ = v8::FunctionTemplate::New(isolate); | 
|  | fun_templ->InstanceTemplate()->SetHandler( | 
|  | v8::NamedPropertyHandlerConfiguration(CheckReceiver)); | 
|  |  | 
|  | CHECK(env->Global() | 
|  | ->Set(env.local(), v8_str("Fun"), | 
|  | fun_templ->GetFunction(env.local()).ToLocalChecked()) | 
|  | .FromJust()); | 
|  |  | 
|  | CompileRun( | 
|  | "var f = new Fun();" | 
|  | "Number.prototype.__proto__ = f;" | 
|  | "var a = 42;" | 
|  | "for (var i = 0; i<3; i++) { a.foo; }"); | 
|  | } |