OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <stdlib.h> |
| 6 |
| 7 #include "test/cctest/test-api.h" |
| 8 |
| 9 #include "include/v8-util.h" |
| 10 #include "src/api.h" |
| 11 #include "src/arguments.h" |
| 12 #include "src/base/platform/platform.h" |
| 13 #include "src/compilation-cache.h" |
| 14 #include "src/execution.h" |
| 15 #include "src/objects.h" |
| 16 #include "src/parser.h" |
| 17 #include "src/smart-pointers.h" |
| 18 #include "src/snapshot.h" |
| 19 #include "src/unicode-inl.h" |
| 20 #include "src/utils.h" |
| 21 #include "src/vm-state.h" |
| 22 |
| 23 using ::v8::Boolean; |
| 24 using ::v8::BooleanObject; |
| 25 using ::v8::Context; |
| 26 using ::v8::Extension; |
| 27 using ::v8::Function; |
| 28 using ::v8::FunctionTemplate; |
| 29 using ::v8::Handle; |
| 30 using ::v8::HandleScope; |
| 31 using ::v8::Local; |
| 32 using ::v8::Name; |
| 33 using ::v8::Message; |
| 34 using ::v8::MessageCallback; |
| 35 using ::v8::Object; |
| 36 using ::v8::ObjectTemplate; |
| 37 using ::v8::Persistent; |
| 38 using ::v8::Script; |
| 39 using ::v8::StackTrace; |
| 40 using ::v8::String; |
| 41 using ::v8::Symbol; |
| 42 using ::v8::TryCatch; |
| 43 using ::v8::Undefined; |
| 44 using ::v8::UniqueId; |
| 45 using ::v8::V8; |
| 46 using ::v8::Value; |
| 47 |
| 48 |
| 49 namespace { |
| 50 |
| 51 void Returns42(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 52 info.GetReturnValue().Set(42); |
| 53 } |
| 54 |
| 55 void Return239Callback(Local<String> name, |
| 56 const v8::PropertyCallbackInfo<Value>& info) { |
| 57 ApiTestFuzzer::Fuzz(); |
| 58 CheckReturnValue(info, FUNCTION_ADDR(Return239Callback)); |
| 59 info.GetReturnValue().Set(v8_str("bad value")); |
| 60 info.GetReturnValue().Set(v8_num(239)); |
| 61 } |
| 62 |
| 63 |
| 64 void EmptyInterceptorGetter(Local<Name> name, |
| 65 const v8::PropertyCallbackInfo<v8::Value>& info) {} |
| 66 |
| 67 |
| 68 void EmptyInterceptorSetter(Local<Name> name, Local<Value> value, |
| 69 const v8::PropertyCallbackInfo<v8::Value>& info) {} |
| 70 |
| 71 |
| 72 void SimpleAccessorGetter(Local<String> name, |
| 73 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 74 Handle<Object> self = Handle<Object>::Cast(info.This()); |
| 75 info.GetReturnValue().Set( |
| 76 self->Get(String::Concat(v8_str("accessor_"), name))); |
| 77 } |
| 78 |
| 79 void SimpleAccessorSetter(Local<String> name, Local<Value> value, |
| 80 const v8::PropertyCallbackInfo<void>& info) { |
| 81 Handle<Object> self = Handle<Object>::Cast(info.This()); |
| 82 self->Set(String::Concat(v8_str("accessor_"), name), value); |
| 83 } |
| 84 |
| 85 |
| 86 void SymbolAccessorGetter(Local<Name> name, |
| 87 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 88 CHECK(name->IsSymbol()); |
| 89 Local<Symbol> sym = Local<Symbol>::Cast(name); |
| 90 if (sym->Name()->IsUndefined()) return; |
| 91 SimpleAccessorGetter(Local<String>::Cast(sym->Name()), info); |
| 92 } |
| 93 |
| 94 void SymbolAccessorSetter(Local<Name> name, Local<Value> value, |
| 95 const v8::PropertyCallbackInfo<void>& info) { |
| 96 CHECK(name->IsSymbol()); |
| 97 Local<Symbol> sym = Local<Symbol>::Cast(name); |
| 98 if (sym->Name()->IsUndefined()) return; |
| 99 SimpleAccessorSetter(Local<String>::Cast(sym->Name()), value, info); |
| 100 } |
| 101 |
| 102 void StringInterceptorGetter( |
| 103 Local<String> name, |
| 104 const v8::PropertyCallbackInfo<v8::Value>& |
| 105 info) { // Intercept names that start with 'interceptor_'. |
| 106 String::Utf8Value utf8(name); |
| 107 char* name_str = *utf8; |
| 108 char prefix[] = "interceptor_"; |
| 109 int i; |
| 110 for (i = 0; name_str[i] && prefix[i]; ++i) { |
| 111 if (name_str[i] != prefix[i]) return; |
| 112 } |
| 113 Handle<Object> self = Handle<Object>::Cast(info.This()); |
| 114 info.GetReturnValue().Set(self->GetHiddenValue(v8_str(name_str + i))); |
| 115 } |
| 116 |
| 117 |
| 118 void StringInterceptorSetter(Local<String> name, Local<Value> value, |
| 119 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 120 // Intercept accesses that set certain integer values, for which the name does |
| 121 // not start with 'accessor_'. |
| 122 String::Utf8Value utf8(name); |
| 123 char* name_str = *utf8; |
| 124 char prefix[] = "accessor_"; |
| 125 int i; |
| 126 for (i = 0; name_str[i] && prefix[i]; ++i) { |
| 127 if (name_str[i] != prefix[i]) break; |
| 128 } |
| 129 if (!prefix[i]) return; |
| 130 |
| 131 if (value->IsInt32() && value->Int32Value() < 10000) { |
| 132 Handle<Object> self = Handle<Object>::Cast(info.This()); |
| 133 self->SetHiddenValue(name, value); |
| 134 info.GetReturnValue().Set(value); |
| 135 } |
| 136 } |
| 137 |
| 138 void InterceptorGetter(Local<Name> generic_name, |
| 139 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 140 if (generic_name->IsSymbol()) return; |
| 141 StringInterceptorGetter(Local<String>::Cast(generic_name), info); |
| 142 } |
| 143 |
| 144 void InterceptorSetter(Local<Name> generic_name, Local<Value> value, |
| 145 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 146 if (generic_name->IsSymbol()) return; |
| 147 StringInterceptorSetter(Local<String>::Cast(generic_name), value, info); |
| 148 } |
| 149 |
| 150 void GenericInterceptorGetter(Local<Name> generic_name, |
| 151 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 152 Local<String> str; |
| 153 if (generic_name->IsSymbol()) { |
| 154 Local<Value> name = Local<Symbol>::Cast(generic_name)->Name(); |
| 155 if (name->IsUndefined()) return; |
| 156 str = String::Concat(v8_str("_sym_"), Local<String>::Cast(name)); |
| 157 } else { |
| 158 Local<String> name = Local<String>::Cast(generic_name); |
| 159 String::Utf8Value utf8(name); |
| 160 char* name_str = *utf8; |
| 161 if (*name_str == '_') return; |
| 162 str = String::Concat(v8_str("_str_"), name); |
| 163 } |
| 164 |
| 165 Handle<Object> self = Handle<Object>::Cast(info.This()); |
| 166 info.GetReturnValue().Set(self->Get(str)); |
| 167 } |
| 168 |
| 169 void GenericInterceptorSetter(Local<Name> generic_name, Local<Value> value, |
| 170 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 171 Local<String> str; |
| 172 if (generic_name->IsSymbol()) { |
| 173 Local<Value> name = Local<Symbol>::Cast(generic_name)->Name(); |
| 174 if (name->IsUndefined()) return; |
| 175 str = String::Concat(v8_str("_sym_"), Local<String>::Cast(name)); |
| 176 } else { |
| 177 Local<String> name = Local<String>::Cast(generic_name); |
| 178 String::Utf8Value utf8(name); |
| 179 char* name_str = *utf8; |
| 180 if (*name_str == '_') return; |
| 181 str = String::Concat(v8_str("_str_"), name); |
| 182 } |
| 183 |
| 184 Handle<Object> self = Handle<Object>::Cast(info.This()); |
| 185 self->Set(str, value); |
| 186 info.GetReturnValue().Set(value); |
| 187 } |
| 188 |
| 189 void AddAccessor(Handle<FunctionTemplate> templ, Handle<String> name, |
| 190 v8::AccessorGetterCallback getter, |
| 191 v8::AccessorSetterCallback setter) { |
| 192 templ->PrototypeTemplate()->SetAccessor(name, getter, setter); |
| 193 } |
| 194 |
| 195 void AddInterceptor(Handle<FunctionTemplate> templ, |
| 196 v8::NamedPropertyGetterCallback getter, |
| 197 v8::NamedPropertySetterCallback setter) { |
| 198 templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter); |
| 199 } |
| 200 |
| 201 |
| 202 void AddAccessor(Handle<FunctionTemplate> templ, Handle<Name> name, |
| 203 v8::AccessorNameGetterCallback getter, |
| 204 v8::AccessorNameSetterCallback setter) { |
| 205 templ->PrototypeTemplate()->SetAccessor(name, getter, setter); |
| 206 } |
| 207 |
| 208 void AddInterceptor(Handle<FunctionTemplate> templ, |
| 209 v8::GenericNamedPropertyGetterCallback getter, |
| 210 v8::GenericNamedPropertySetterCallback setter) { |
| 211 templ->InstanceTemplate()->SetHandler( |
| 212 v8::NamedPropertyHandlerConfiguration(getter, setter)); |
| 213 } |
| 214 |
| 215 |
| 216 v8::Handle<v8::Object> bottom; |
| 217 |
| 218 void CheckThisIndexedPropertyHandler( |
| 219 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 220 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyHandler)); |
| 221 ApiTestFuzzer::Fuzz(); |
| 222 CHECK(info.This()->Equals(bottom)); |
| 223 } |
| 224 |
| 225 void CheckThisNamedPropertyHandler( |
| 226 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 227 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler)); |
| 228 ApiTestFuzzer::Fuzz(); |
| 229 CHECK(info.This()->Equals(bottom)); |
| 230 } |
| 231 |
| 232 void CheckThisIndexedPropertySetter( |
| 233 uint32_t index, Local<Value> value, |
| 234 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 235 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertySetter)); |
| 236 ApiTestFuzzer::Fuzz(); |
| 237 CHECK(info.This()->Equals(bottom)); |
| 238 } |
| 239 |
| 240 |
| 241 void CheckThisNamedPropertySetter( |
| 242 Local<Name> property, Local<Value> value, |
| 243 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 244 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter)); |
| 245 ApiTestFuzzer::Fuzz(); |
| 246 CHECK(info.This()->Equals(bottom)); |
| 247 } |
| 248 |
| 249 void CheckThisIndexedPropertyQuery( |
| 250 uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| 251 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery)); |
| 252 ApiTestFuzzer::Fuzz(); |
| 253 CHECK(info.This()->Equals(bottom)); |
| 254 } |
| 255 |
| 256 |
| 257 void CheckThisNamedPropertyQuery( |
| 258 Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| 259 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery)); |
| 260 ApiTestFuzzer::Fuzz(); |
| 261 CHECK(info.This()->Equals(bottom)); |
| 262 } |
| 263 |
| 264 |
| 265 void CheckThisIndexedPropertyDeleter( |
| 266 uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
| 267 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter)); |
| 268 ApiTestFuzzer::Fuzz(); |
| 269 CHECK(info.This()->Equals(bottom)); |
| 270 } |
| 271 |
| 272 |
| 273 void CheckThisNamedPropertyDeleter( |
| 274 Local<Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
| 275 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter)); |
| 276 ApiTestFuzzer::Fuzz(); |
| 277 CHECK(info.This()->Equals(bottom)); |
| 278 } |
| 279 |
| 280 |
| 281 void CheckThisIndexedPropertyEnumerator( |
| 282 const v8::PropertyCallbackInfo<v8::Array>& info) { |
| 283 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator)); |
| 284 ApiTestFuzzer::Fuzz(); |
| 285 CHECK(info.This()->Equals(bottom)); |
| 286 } |
| 287 |
| 288 |
| 289 void CheckThisNamedPropertyEnumerator( |
| 290 const v8::PropertyCallbackInfo<v8::Array>& info) { |
| 291 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator)); |
| 292 ApiTestFuzzer::Fuzz(); |
| 293 CHECK(info.This()->Equals(bottom)); |
| 294 } |
| 295 |
| 296 |
| 297 int echo_named_call_count; |
| 298 |
| 299 |
| 300 void EchoNamedProperty(Local<Name> name, |
| 301 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 302 ApiTestFuzzer::Fuzz(); |
| 303 CHECK(v8_str("data")->Equals(info.Data())); |
| 304 echo_named_call_count++; |
| 305 info.GetReturnValue().Set(name); |
| 306 } |
| 307 |
| 308 void InterceptorHasOwnPropertyGetter( |
| 309 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 310 ApiTestFuzzer::Fuzz(); |
| 311 } |
| 312 |
| 313 void InterceptorHasOwnPropertyGetterGC( |
| 314 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 315 ApiTestFuzzer::Fuzz(); |
| 316 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); |
| 317 } |
| 318 |
| 319 } // namespace |
| 320 |
| 321 |
| 322 THREADED_TEST(InterceptorHasOwnProperty) { |
| 323 LocalContext context; |
| 324 v8::Isolate* isolate = context->GetIsolate(); |
| 325 v8::HandleScope scope(isolate); |
| 326 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); |
| 327 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); |
| 328 instance_templ->SetHandler( |
| 329 v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetter)); |
| 330 Local<Function> function = fun_templ->GetFunction(); |
| 331 context->Global()->Set(v8_str("constructor"), function); |
| 332 v8::Handle<Value> value = CompileRun( |
| 333 "var o = new constructor();" |
| 334 "o.hasOwnProperty('ostehaps');"); |
| 335 CHECK_EQ(false, value->BooleanValue()); |
| 336 value = CompileRun( |
| 337 "o.ostehaps = 42;" |
| 338 "o.hasOwnProperty('ostehaps');"); |
| 339 CHECK_EQ(true, value->BooleanValue()); |
| 340 value = CompileRun( |
| 341 "var p = new constructor();" |
| 342 "p.hasOwnProperty('ostehaps');"); |
| 343 CHECK_EQ(false, value->BooleanValue()); |
| 344 } |
| 345 |
| 346 |
| 347 THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { |
| 348 LocalContext context; |
| 349 v8::Isolate* isolate = context->GetIsolate(); |
| 350 v8::HandleScope scope(isolate); |
| 351 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); |
| 352 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); |
| 353 instance_templ->SetHandler( |
| 354 v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetterGC)); |
| 355 Local<Function> function = fun_templ->GetFunction(); |
| 356 context->Global()->Set(v8_str("constructor"), function); |
| 357 // Let's first make some stuff so we can be sure to get a good GC. |
| 358 CompileRun( |
| 359 "function makestr(size) {" |
| 360 " switch (size) {" |
| 361 " case 1: return 'f';" |
| 362 " case 2: return 'fo';" |
| 363 " case 3: return 'foo';" |
| 364 " }" |
| 365 " return makestr(size >> 1) + makestr((size + 1) >> 1);" |
| 366 "}" |
| 367 "var x = makestr(12345);" |
| 368 "x = makestr(31415);" |
| 369 "x = makestr(23456);"); |
| 370 v8::Handle<Value> value = CompileRun( |
| 371 "var o = new constructor();" |
| 372 "o.__proto__ = new String(x);" |
| 373 "o.hasOwnProperty('ostehaps');"); |
| 374 CHECK_EQ(false, value->BooleanValue()); |
| 375 } |
| 376 |
| 377 |
| 378 static void CheckInterceptorLoadIC( |
| 379 v8::GenericNamedPropertyGetterCallback getter, const char* source, |
| 380 int expected) { |
| 381 v8::Isolate* isolate = CcTest::isolate(); |
| 382 v8::HandleScope scope(isolate); |
| 383 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 384 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(getter, 0, 0, 0, 0, |
| 385 v8_str("data"))); |
| 386 LocalContext context; |
| 387 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 388 v8::Handle<Value> value = CompileRun(source); |
| 389 CHECK_EQ(expected, value->Int32Value()); |
| 390 } |
| 391 |
| 392 |
| 393 static void InterceptorLoadICGetter( |
| 394 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 395 ApiTestFuzzer::Fuzz(); |
| 396 v8::Isolate* isolate = CcTest::isolate(); |
| 397 CHECK_EQ(isolate, info.GetIsolate()); |
| 398 CHECK(v8_str("data")->Equals(info.Data())); |
| 399 CHECK(v8_str("x")->Equals(name)); |
| 400 info.GetReturnValue().Set(v8::Integer::New(isolate, 42)); |
| 401 } |
| 402 |
| 403 |
| 404 // This test should hit the load IC for the interceptor case. |
| 405 THREADED_TEST(InterceptorLoadIC) { |
| 406 CheckInterceptorLoadIC(InterceptorLoadICGetter, |
| 407 "var result = 0;" |
| 408 "for (var i = 0; i < 1000; i++) {" |
| 409 " result = o.x;" |
| 410 "}", |
| 411 42); |
| 412 } |
| 413 |
| 414 |
| 415 // Below go several tests which verify that JITing for various |
| 416 // configurations of interceptor and explicit fields works fine |
| 417 // (those cases are special cased to get better performance). |
| 418 |
| 419 static void InterceptorLoadXICGetter( |
| 420 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 421 ApiTestFuzzer::Fuzz(); |
| 422 info.GetReturnValue().Set( |
| 423 v8_str("x")->Equals(name) |
| 424 ? v8::Handle<v8::Value>(v8::Integer::New(info.GetIsolate(), 42)) |
| 425 : v8::Handle<v8::Value>()); |
| 426 } |
| 427 |
| 428 |
| 429 THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { |
| 430 CheckInterceptorLoadIC(InterceptorLoadXICGetter, |
| 431 "var result = 0;" |
| 432 "o.y = 239;" |
| 433 "for (var i = 0; i < 1000; i++) {" |
| 434 " result = o.y;" |
| 435 "}", |
| 436 239); |
| 437 } |
| 438 |
| 439 |
| 440 THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { |
| 441 CheckInterceptorLoadIC(InterceptorLoadXICGetter, |
| 442 "var result = 0;" |
| 443 "o.__proto__ = { 'y': 239 };" |
| 444 "for (var i = 0; i < 1000; i++) {" |
| 445 " result = o.y + o.x;" |
| 446 "}", |
| 447 239 + 42); |
| 448 } |
| 449 |
| 450 |
| 451 THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { |
| 452 CheckInterceptorLoadIC(InterceptorLoadXICGetter, |
| 453 "var result = 0;" |
| 454 "o.__proto__.y = 239;" |
| 455 "for (var i = 0; i < 1000; i++) {" |
| 456 " result = o.y + o.x;" |
| 457 "}", |
| 458 239 + 42); |
| 459 } |
| 460 |
| 461 |
| 462 THREADED_TEST(InterceptorLoadICUndefined) { |
| 463 CheckInterceptorLoadIC(InterceptorLoadXICGetter, |
| 464 "var result = 0;" |
| 465 "for (var i = 0; i < 1000; i++) {" |
| 466 " result = (o.y == undefined) ? 239 : 42;" |
| 467 "}", |
| 468 239); |
| 469 } |
| 470 |
| 471 |
| 472 THREADED_TEST(InterceptorLoadICWithOverride) { |
| 473 CheckInterceptorLoadIC(InterceptorLoadXICGetter, |
| 474 "fst = new Object(); fst.__proto__ = o;" |
| 475 "snd = new Object(); snd.__proto__ = fst;" |
| 476 "var result1 = 0;" |
| 477 "for (var i = 0; i < 1000; i++) {" |
| 478 " result1 = snd.x;" |
| 479 "}" |
| 480 "fst.x = 239;" |
| 481 "var result = 0;" |
| 482 "for (var i = 0; i < 1000; i++) {" |
| 483 " result = snd.x;" |
| 484 "}" |
| 485 "result + result1", |
| 486 239 + 42); |
| 487 } |
| 488 |
| 489 |
| 490 // Test the case when we stored field into |
| 491 // a stub, but interceptor produced value on its own. |
| 492 THREADED_TEST(InterceptorLoadICFieldNotNeeded) { |
| 493 CheckInterceptorLoadIC( |
| 494 InterceptorLoadXICGetter, |
| 495 "proto = new Object();" |
| 496 "o.__proto__ = proto;" |
| 497 "proto.x = 239;" |
| 498 "for (var i = 0; i < 1000; i++) {" |
| 499 " o.x;" |
| 500 // Now it should be ICed and keep a reference to x defined on proto |
| 501 "}" |
| 502 "var result = 0;" |
| 503 "for (var i = 0; i < 1000; i++) {" |
| 504 " result += o.x;" |
| 505 "}" |
| 506 "result;", |
| 507 42 * 1000); |
| 508 } |
| 509 |
| 510 |
| 511 // Test the case when we stored field into |
| 512 // a stub, but it got invalidated later on. |
| 513 THREADED_TEST(InterceptorLoadICInvalidatedField) { |
| 514 CheckInterceptorLoadIC( |
| 515 InterceptorLoadXICGetter, |
| 516 "proto1 = new Object();" |
| 517 "proto2 = new Object();" |
| 518 "o.__proto__ = proto1;" |
| 519 "proto1.__proto__ = proto2;" |
| 520 "proto2.y = 239;" |
| 521 "for (var i = 0; i < 1000; i++) {" |
| 522 " o.y;" |
| 523 // Now it should be ICed and keep a reference to y defined on proto2 |
| 524 "}" |
| 525 "proto1.y = 42;" |
| 526 "var result = 0;" |
| 527 "for (var i = 0; i < 1000; i++) {" |
| 528 " result += o.y;" |
| 529 "}" |
| 530 "result;", |
| 531 42 * 1000); |
| 532 } |
| 533 |
| 534 |
| 535 static int interceptor_load_not_handled_calls = 0; |
| 536 static void InterceptorLoadNotHandled( |
| 537 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 538 ++interceptor_load_not_handled_calls; |
| 539 } |
| 540 |
| 541 |
| 542 // Test how post-interceptor lookups are done in the non-cacheable |
| 543 // case: the interceptor should not be invoked during this lookup. |
| 544 THREADED_TEST(InterceptorLoadICPostInterceptor) { |
| 545 interceptor_load_not_handled_calls = 0; |
| 546 CheckInterceptorLoadIC(InterceptorLoadNotHandled, |
| 547 "receiver = new Object();" |
| 548 "receiver.__proto__ = o;" |
| 549 "proto = new Object();" |
| 550 "/* Make proto a slow-case object. */" |
| 551 "for (var i = 0; i < 1000; i++) {" |
| 552 " proto[\"xxxxxxxx\" + i] = [];" |
| 553 "}" |
| 554 "proto.x = 17;" |
| 555 "o.__proto__ = proto;" |
| 556 "var result = 0;" |
| 557 "for (var i = 0; i < 1000; i++) {" |
| 558 " result += receiver.x;" |
| 559 "}" |
| 560 "result;", |
| 561 17 * 1000); |
| 562 CHECK_EQ(1000, interceptor_load_not_handled_calls); |
| 563 } |
| 564 |
| 565 |
| 566 // Test the case when we stored field into |
| 567 // a stub, but it got invalidated later on due to override on |
| 568 // global object which is between interceptor and fields' holders. |
| 569 THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { |
| 570 CheckInterceptorLoadIC( |
| 571 InterceptorLoadXICGetter, |
| 572 "o.__proto__ = this;" // set a global to be a proto of o. |
| 573 "this.__proto__.y = 239;" |
| 574 "for (var i = 0; i < 10; i++) {" |
| 575 " if (o.y != 239) throw 'oops: ' + o.y;" |
| 576 // Now it should be ICed and keep a reference to y defined on |
| 577 // field_holder. |
| 578 "}" |
| 579 "this.y = 42;" // Assign on a global. |
| 580 "var result = 0;" |
| 581 "for (var i = 0; i < 10; i++) {" |
| 582 " result += o.y;" |
| 583 "}" |
| 584 "result;", |
| 585 42 * 10); |
| 586 } |
| 587 |
| 588 |
| 589 static void SetOnThis(Local<String> name, Local<Value> value, |
| 590 const v8::PropertyCallbackInfo<void>& info) { |
| 591 Local<Object>::Cast(info.This())->ForceSet(name, value); |
| 592 } |
| 593 |
| 594 |
| 595 THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { |
| 596 v8::Isolate* isolate = CcTest::isolate(); |
| 597 v8::HandleScope scope(isolate); |
| 598 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 599 templ->SetHandler( |
| 600 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); |
| 601 templ->SetAccessor(v8_str("y"), Return239Callback); |
| 602 LocalContext context; |
| 603 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 604 |
| 605 // Check the case when receiver and interceptor's holder |
| 606 // are the same objects. |
| 607 v8::Handle<Value> value = CompileRun( |
| 608 "var result = 0;" |
| 609 "for (var i = 0; i < 7; i++) {" |
| 610 " result = o.y;" |
| 611 "}"); |
| 612 CHECK_EQ(239, value->Int32Value()); |
| 613 |
| 614 // Check the case when interceptor's holder is in proto chain |
| 615 // of receiver. |
| 616 value = CompileRun( |
| 617 "r = { __proto__: o };" |
| 618 "var result = 0;" |
| 619 "for (var i = 0; i < 7; i++) {" |
| 620 " result = r.y;" |
| 621 "}"); |
| 622 CHECK_EQ(239, value->Int32Value()); |
| 623 } |
| 624 |
| 625 |
| 626 THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { |
| 627 v8::Isolate* isolate = CcTest::isolate(); |
| 628 v8::HandleScope scope(isolate); |
| 629 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); |
| 630 templ_o->SetHandler( |
| 631 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); |
| 632 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate); |
| 633 templ_p->SetAccessor(v8_str("y"), Return239Callback); |
| 634 |
| 635 LocalContext context; |
| 636 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); |
| 637 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); |
| 638 |
| 639 // Check the case when receiver and interceptor's holder |
| 640 // are the same objects. |
| 641 v8::Handle<Value> value = CompileRun( |
| 642 "o.__proto__ = p;" |
| 643 "var result = 0;" |
| 644 "for (var i = 0; i < 7; i++) {" |
| 645 " result = o.x + o.y;" |
| 646 "}"); |
| 647 CHECK_EQ(239 + 42, value->Int32Value()); |
| 648 |
| 649 // Check the case when interceptor's holder is in proto chain |
| 650 // of receiver. |
| 651 value = CompileRun( |
| 652 "r = { __proto__: o };" |
| 653 "var result = 0;" |
| 654 "for (var i = 0; i < 7; i++) {" |
| 655 " result = r.x + r.y;" |
| 656 "}"); |
| 657 CHECK_EQ(239 + 42, value->Int32Value()); |
| 658 } |
| 659 |
| 660 |
| 661 THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { |
| 662 v8::Isolate* isolate = CcTest::isolate(); |
| 663 v8::HandleScope scope(isolate); |
| 664 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 665 templ->SetHandler( |
| 666 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); |
| 667 templ->SetAccessor(v8_str("y"), Return239Callback); |
| 668 |
| 669 LocalContext context; |
| 670 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 671 |
| 672 v8::Handle<Value> value = CompileRun( |
| 673 "fst = new Object(); fst.__proto__ = o;" |
| 674 "snd = new Object(); snd.__proto__ = fst;" |
| 675 "var result1 = 0;" |
| 676 "for (var i = 0; i < 7; i++) {" |
| 677 " result1 = snd.x;" |
| 678 "}" |
| 679 "fst.x = 239;" |
| 680 "var result = 0;" |
| 681 "for (var i = 0; i < 7; i++) {" |
| 682 " result = snd.x;" |
| 683 "}" |
| 684 "result + result1"); |
| 685 CHECK_EQ(239 + 42, value->Int32Value()); |
| 686 } |
| 687 |
| 688 |
| 689 // Test the case when we stored callback into |
| 690 // a stub, but interceptor produced value on its own. |
| 691 THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { |
| 692 v8::Isolate* isolate = CcTest::isolate(); |
| 693 v8::HandleScope scope(isolate); |
| 694 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); |
| 695 templ_o->SetHandler( |
| 696 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); |
| 697 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate); |
| 698 templ_p->SetAccessor(v8_str("y"), Return239Callback); |
| 699 |
| 700 LocalContext context; |
| 701 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); |
| 702 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); |
| 703 |
| 704 v8::Handle<Value> value = CompileRun( |
| 705 "o.__proto__ = p;" |
| 706 "for (var i = 0; i < 7; i++) {" |
| 707 " o.x;" |
| 708 // Now it should be ICed and keep a reference to x defined on p |
| 709 "}" |
| 710 "var result = 0;" |
| 711 "for (var i = 0; i < 7; i++) {" |
| 712 " result += o.x;" |
| 713 "}" |
| 714 "result"); |
| 715 CHECK_EQ(42 * 7, value->Int32Value()); |
| 716 } |
| 717 |
| 718 |
| 719 // Test the case when we stored callback into |
| 720 // a stub, but it got invalidated later on. |
| 721 THREADED_TEST(InterceptorLoadICInvalidatedCallback) { |
| 722 v8::Isolate* isolate = CcTest::isolate(); |
| 723 v8::HandleScope scope(isolate); |
| 724 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); |
| 725 templ_o->SetHandler( |
| 726 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); |
| 727 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate); |
| 728 templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis); |
| 729 |
| 730 LocalContext context; |
| 731 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); |
| 732 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); |
| 733 |
| 734 v8::Handle<Value> value = CompileRun( |
| 735 "inbetween = new Object();" |
| 736 "o.__proto__ = inbetween;" |
| 737 "inbetween.__proto__ = p;" |
| 738 "for (var i = 0; i < 10; i++) {" |
| 739 " o.y;" |
| 740 // Now it should be ICed and keep a reference to y defined on p |
| 741 "}" |
| 742 "inbetween.y = 42;" |
| 743 "var result = 0;" |
| 744 "for (var i = 0; i < 10; i++) {" |
| 745 " result += o.y;" |
| 746 "}" |
| 747 "result"); |
| 748 CHECK_EQ(42 * 10, value->Int32Value()); |
| 749 } |
| 750 |
| 751 |
| 752 // Test the case when we stored callback into |
| 753 // a stub, but it got invalidated later on due to override on |
| 754 // global object which is between interceptor and callbacks' holders. |
| 755 THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { |
| 756 v8::Isolate* isolate = CcTest::isolate(); |
| 757 v8::HandleScope scope(isolate); |
| 758 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); |
| 759 templ_o->SetHandler( |
| 760 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); |
| 761 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate); |
| 762 templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis); |
| 763 |
| 764 LocalContext context; |
| 765 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); |
| 766 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); |
| 767 |
| 768 v8::Handle<Value> value = CompileRun( |
| 769 "o.__proto__ = this;" |
| 770 "this.__proto__ = p;" |
| 771 "for (var i = 0; i < 10; i++) {" |
| 772 " if (o.y != 239) throw 'oops: ' + o.y;" |
| 773 // Now it should be ICed and keep a reference to y defined on p |
| 774 "}" |
| 775 "this.y = 42;" |
| 776 "var result = 0;" |
| 777 "for (var i = 0; i < 10; i++) {" |
| 778 " result += o.y;" |
| 779 "}" |
| 780 "result"); |
| 781 CHECK_EQ(42 * 10, value->Int32Value()); |
| 782 } |
| 783 |
| 784 |
| 785 static void InterceptorLoadICGetter0( |
| 786 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 787 ApiTestFuzzer::Fuzz(); |
| 788 CHECK(v8_str("x")->Equals(name)); |
| 789 info.GetReturnValue().Set(v8::Integer::New(info.GetIsolate(), 0)); |
| 790 } |
| 791 |
| 792 |
| 793 THREADED_TEST(InterceptorReturningZero) { |
| 794 CheckInterceptorLoadIC(InterceptorLoadICGetter0, "o.x == undefined ? 1 : 0", |
| 795 0); |
| 796 } |
| 797 |
| 798 |
| 799 static void InterceptorStoreICSetter( |
| 800 Local<Name> key, Local<Value> value, |
| 801 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 802 CHECK(v8_str("x")->Equals(key)); |
| 803 CHECK_EQ(42, value->Int32Value()); |
| 804 info.GetReturnValue().Set(value); |
| 805 } |
| 806 |
| 807 |
| 808 // This test should hit the store IC for the interceptor case. |
| 809 THREADED_TEST(InterceptorStoreIC) { |
| 810 v8::Isolate* isolate = CcTest::isolate(); |
| 811 v8::HandleScope scope(isolate); |
| 812 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 813 templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 814 InterceptorLoadICGetter, InterceptorStoreICSetter, 0, 0, 0, |
| 815 v8_str("data"))); |
| 816 LocalContext context; |
| 817 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 818 CompileRun( |
| 819 "for (var i = 0; i < 1000; i++) {" |
| 820 " o.x = 42;" |
| 821 "}"); |
| 822 } |
| 823 |
| 824 |
| 825 THREADED_TEST(InterceptorStoreICWithNoSetter) { |
| 826 v8::Isolate* isolate = CcTest::isolate(); |
| 827 v8::HandleScope scope(isolate); |
| 828 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 829 templ->SetHandler( |
| 830 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); |
| 831 LocalContext context; |
| 832 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 833 v8::Handle<Value> value = CompileRun( |
| 834 "for (var i = 0; i < 1000; i++) {" |
| 835 " o.y = 239;" |
| 836 "}" |
| 837 "42 + o.y"); |
| 838 CHECK_EQ(239 + 42, value->Int32Value()); |
| 839 } |
| 840 |
| 841 |
| 842 THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) { |
| 843 v8::HandleScope scope(CcTest::isolate()); |
| 844 Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); |
| 845 Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); |
| 846 child->Inherit(parent); |
| 847 AddAccessor(parent, v8_str("age"), SimpleAccessorGetter, |
| 848 SimpleAccessorSetter); |
| 849 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); |
| 850 LocalContext env; |
| 851 env->Global()->Set(v8_str("Child"), child->GetFunction()); |
| 852 CompileRun( |
| 853 "var child = new Child;" |
| 854 "child.age = 10;"); |
| 855 ExpectBoolean("child.hasOwnProperty('age')", false); |
| 856 ExpectInt32("child.age", 10); |
| 857 ExpectInt32("child.accessor_age", 10); |
| 858 } |
| 859 |
| 860 |
| 861 THREADED_TEST(LegacyInterceptorDoesNotSeeSymbols) { |
| 862 LocalContext env; |
| 863 v8::Isolate* isolate = CcTest::isolate(); |
| 864 v8::HandleScope scope(isolate); |
| 865 Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate); |
| 866 Handle<FunctionTemplate> child = FunctionTemplate::New(isolate); |
| 867 v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age")); |
| 868 |
| 869 child->Inherit(parent); |
| 870 AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter); |
| 871 AddInterceptor(child, StringInterceptorGetter, StringInterceptorSetter); |
| 872 |
| 873 env->Global()->Set(v8_str("Child"), child->GetFunction()); |
| 874 env->Global()->Set(v8_str("age"), age); |
| 875 CompileRun( |
| 876 "var child = new Child;" |
| 877 "child[age] = 10;"); |
| 878 ExpectInt32("child[age]", 10); |
| 879 ExpectBoolean("child.hasOwnProperty('age')", false); |
| 880 ExpectBoolean("child.hasOwnProperty('accessor_age')", true); |
| 881 } |
| 882 |
| 883 |
| 884 THREADED_TEST(GenericInterceptorDoesSeeSymbols) { |
| 885 LocalContext env; |
| 886 v8::Isolate* isolate = CcTest::isolate(); |
| 887 v8::HandleScope scope(isolate); |
| 888 Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate); |
| 889 Handle<FunctionTemplate> child = FunctionTemplate::New(isolate); |
| 890 v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age")); |
| 891 v8::Local<v8::Symbol> anon = v8::Symbol::New(isolate); |
| 892 |
| 893 child->Inherit(parent); |
| 894 AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter); |
| 895 AddInterceptor(child, GenericInterceptorGetter, GenericInterceptorSetter); |
| 896 |
| 897 env->Global()->Set(v8_str("Child"), child->GetFunction()); |
| 898 env->Global()->Set(v8_str("age"), age); |
| 899 env->Global()->Set(v8_str("anon"), anon); |
| 900 CompileRun( |
| 901 "var child = new Child;" |
| 902 "child[age] = 10;"); |
| 903 ExpectInt32("child[age]", 10); |
| 904 ExpectInt32("child._sym_age", 10); |
| 905 |
| 906 // Check that it also sees strings. |
| 907 CompileRun("child.foo = 47"); |
| 908 ExpectInt32("child.foo", 47); |
| 909 ExpectInt32("child._str_foo", 47); |
| 910 |
| 911 // Check that the interceptor can punt (in this case, on anonymous symbols). |
| 912 CompileRun("child[anon] = 31337"); |
| 913 ExpectInt32("child[anon]", 31337); |
| 914 } |
| 915 |
| 916 |
| 917 THREADED_TEST(NamedPropertyHandlerGetter) { |
| 918 echo_named_call_count = 0; |
| 919 v8::HandleScope scope(CcTest::isolate()); |
| 920 v8::Handle<v8::FunctionTemplate> templ = |
| 921 v8::FunctionTemplate::New(CcTest::isolate()); |
| 922 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 923 EchoNamedProperty, 0, 0, 0, 0, v8_str("data"))); |
| 924 LocalContext env; |
| 925 env->Global()->Set(v8_str("obj"), templ->GetFunction()->NewInstance()); |
| 926 CHECK_EQ(echo_named_call_count, 0); |
| 927 v8_compile("obj.x")->Run(); |
| 928 CHECK_EQ(echo_named_call_count, 1); |
| 929 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; |
| 930 v8::Handle<Value> str = CompileRun(code); |
| 931 String::Utf8Value value(str); |
| 932 CHECK_EQ(0, strcmp(*value, "oddlepoddle")); |
| 933 // Check default behavior |
| 934 CHECK_EQ(10, v8_compile("obj.flob = 10;")->Run()->Int32Value()); |
| 935 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); |
| 936 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); |
| 937 } |
| 938 |
| 939 |
| 940 int echo_indexed_call_count = 0; |
| 941 |
| 942 |
| 943 static void EchoIndexedProperty( |
| 944 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 945 ApiTestFuzzer::Fuzz(); |
| 946 CHECK(v8_num(637)->Equals(info.Data())); |
| 947 echo_indexed_call_count++; |
| 948 info.GetReturnValue().Set(v8_num(index)); |
| 949 } |
| 950 |
| 951 |
| 952 THREADED_TEST(IndexedPropertyHandlerGetter) { |
| 953 v8::Isolate* isolate = CcTest::isolate(); |
| 954 v8::HandleScope scope(isolate); |
| 955 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 956 templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 957 EchoIndexedProperty, 0, 0, 0, 0, v8_num(637))); |
| 958 LocalContext env; |
| 959 env->Global()->Set(v8_str("obj"), templ->GetFunction()->NewInstance()); |
| 960 Local<Script> script = v8_compile("obj[900]"); |
| 961 CHECK_EQ(script->Run()->Int32Value(), 900); |
| 962 } |
| 963 |
| 964 |
| 965 THREADED_TEST(PropertyHandlerInPrototype) { |
| 966 LocalContext env; |
| 967 v8::Isolate* isolate = env->GetIsolate(); |
| 968 v8::HandleScope scope(isolate); |
| 969 |
| 970 // Set up a prototype chain with three interceptors. |
| 971 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 972 templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 973 CheckThisIndexedPropertyHandler, CheckThisIndexedPropertySetter, |
| 974 CheckThisIndexedPropertyQuery, CheckThisIndexedPropertyDeleter, |
| 975 CheckThisIndexedPropertyEnumerator)); |
| 976 |
| 977 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 978 CheckThisNamedPropertyHandler, CheckThisNamedPropertySetter, |
| 979 CheckThisNamedPropertyQuery, CheckThisNamedPropertyDeleter, |
| 980 CheckThisNamedPropertyEnumerator)); |
| 981 |
| 982 bottom = templ->GetFunction()->NewInstance(); |
| 983 Local<v8::Object> top = templ->GetFunction()->NewInstance(); |
| 984 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); |
| 985 |
| 986 bottom->SetPrototype(middle); |
| 987 middle->SetPrototype(top); |
| 988 env->Global()->Set(v8_str("obj"), bottom); |
| 989 |
| 990 // Indexed and named get. |
| 991 CompileRun("obj[0]"); |
| 992 CompileRun("obj.x"); |
| 993 |
| 994 // Indexed and named set. |
| 995 CompileRun("obj[1] = 42"); |
| 996 CompileRun("obj.y = 42"); |
| 997 |
| 998 // Indexed and named query. |
| 999 CompileRun("0 in obj"); |
| 1000 CompileRun("'x' in obj"); |
| 1001 |
| 1002 // Indexed and named deleter. |
| 1003 CompileRun("delete obj[0]"); |
| 1004 CompileRun("delete obj.x"); |
| 1005 |
| 1006 // Enumerators. |
| 1007 CompileRun("for (var p in obj) ;"); |
| 1008 } |
| 1009 |
| 1010 |
| 1011 static void PrePropertyHandlerGet( |
| 1012 Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1013 ApiTestFuzzer::Fuzz(); |
| 1014 if (v8_str("pre")->Equals(key)) { |
| 1015 info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre")); |
| 1016 } |
| 1017 } |
| 1018 |
| 1019 |
| 1020 static void PrePropertyHandlerQuery( |
| 1021 Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| 1022 if (v8_str("pre")->Equals(key)) { |
| 1023 info.GetReturnValue().Set(static_cast<int32_t>(v8::None)); |
| 1024 } |
| 1025 } |
| 1026 |
| 1027 |
| 1028 THREADED_TEST(PrePropertyHandler) { |
| 1029 v8::Isolate* isolate = CcTest::isolate(); |
| 1030 v8::HandleScope scope(isolate); |
| 1031 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); |
| 1032 desc->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 1033 PrePropertyHandlerGet, 0, PrePropertyHandlerQuery)); |
| 1034 LocalContext env(NULL, desc->InstanceTemplate()); |
| 1035 CompileRun("var pre = 'Object: pre'; var on = 'Object: on';"); |
| 1036 v8::Handle<Value> result_pre = CompileRun("pre"); |
| 1037 CHECK(v8_str("PrePropertyHandler: pre")->Equals(result_pre)); |
| 1038 v8::Handle<Value> result_on = CompileRun("on"); |
| 1039 CHECK(v8_str("Object: on")->Equals(result_on)); |
| 1040 v8::Handle<Value> result_post = CompileRun("post"); |
| 1041 CHECK(result_post.IsEmpty()); |
| 1042 } |
| 1043 |
| 1044 |
| 1045 THREADED_TEST(EmptyInterceptorBreakTransitions) { |
| 1046 v8::HandleScope scope(CcTest::isolate()); |
| 1047 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); |
| 1048 AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter); |
| 1049 LocalContext env; |
| 1050 env->Global()->Set(v8_str("Constructor"), templ->GetFunction()); |
| 1051 CompileRun( |
| 1052 "var o1 = new Constructor;" |
| 1053 "o1.a = 1;" // Ensure a and x share the descriptor array. |
| 1054 "Object.defineProperty(o1, 'x', {value: 10});"); |
| 1055 CompileRun( |
| 1056 "var o2 = new Constructor;" |
| 1057 "o2.a = 1;" |
| 1058 "Object.defineProperty(o2, 'x', {value: 10});"); |
| 1059 } |
| 1060 |
| 1061 |
| 1062 THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) { |
| 1063 v8::Isolate* isolate = CcTest::isolate(); |
| 1064 v8::HandleScope scope(isolate); |
| 1065 Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate); |
| 1066 Handle<FunctionTemplate> child = FunctionTemplate::New(isolate); |
| 1067 child->Inherit(parent); |
| 1068 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); |
| 1069 LocalContext env; |
| 1070 env->Global()->Set(v8_str("Child"), child->GetFunction()); |
| 1071 CompileRun( |
| 1072 "var child = new Child;" |
| 1073 "var parent = child.__proto__;" |
| 1074 "Object.defineProperty(parent, 'age', " |
| 1075 " {get: function(){ return this.accessor_age; }, " |
| 1076 " set: function(v){ this.accessor_age = v; }, " |
| 1077 " enumerable: true, configurable: true});" |
| 1078 "child.age = 10;"); |
| 1079 ExpectBoolean("child.hasOwnProperty('age')", false); |
| 1080 ExpectInt32("child.age", 10); |
| 1081 ExpectInt32("child.accessor_age", 10); |
| 1082 } |
| 1083 |
| 1084 |
| 1085 THREADED_TEST(EmptyInterceptorDoesNotShadowApiAccessors) { |
| 1086 v8::Isolate* isolate = CcTest::isolate(); |
| 1087 v8::HandleScope scope(isolate); |
| 1088 Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate); |
| 1089 auto returns_42 = FunctionTemplate::New(isolate, Returns42); |
| 1090 parent->PrototypeTemplate()->SetAccessorProperty(v8_str("age"), returns_42); |
| 1091 Handle<FunctionTemplate> child = FunctionTemplate::New(isolate); |
| 1092 child->Inherit(parent); |
| 1093 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); |
| 1094 LocalContext env; |
| 1095 env->Global()->Set(v8_str("Child"), child->GetFunction()); |
| 1096 CompileRun( |
| 1097 "var child = new Child;" |
| 1098 "var parent = child.__proto__;"); |
| 1099 ExpectBoolean("child.hasOwnProperty('age')", false); |
| 1100 ExpectInt32("child.age", 42); |
| 1101 // Check interceptor followup. |
| 1102 ExpectInt32( |
| 1103 "var result;" |
| 1104 "for (var i = 0; i < 4; ++i) {" |
| 1105 " result = child.age;" |
| 1106 "}" |
| 1107 "result", |
| 1108 42); |
| 1109 } |
| 1110 |
| 1111 |
| 1112 THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) { |
| 1113 v8::Isolate* isolate = CcTest::isolate(); |
| 1114 v8::HandleScope scope(isolate); |
| 1115 Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate); |
| 1116 Handle<FunctionTemplate> child = FunctionTemplate::New(isolate); |
| 1117 child->Inherit(parent); |
| 1118 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); |
| 1119 LocalContext env; |
| 1120 env->Global()->Set(v8_str("Child"), child->GetFunction()); |
| 1121 CompileRun( |
| 1122 "var child = new Child;" |
| 1123 "var parent = child.__proto__;" |
| 1124 "parent.name = 'Alice';"); |
| 1125 ExpectBoolean("child.hasOwnProperty('name')", false); |
| 1126 ExpectString("child.name", "Alice"); |
| 1127 CompileRun("child.name = 'Bob';"); |
| 1128 ExpectString("child.name", "Bob"); |
| 1129 ExpectBoolean("child.hasOwnProperty('name')", true); |
| 1130 ExpectString("parent.name", "Alice"); |
| 1131 } |
| 1132 |
| 1133 |
| 1134 THREADED_TEST(SwitchFromInterceptorToAccessor) { |
| 1135 v8::HandleScope scope(CcTest::isolate()); |
| 1136 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); |
| 1137 AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter); |
| 1138 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); |
| 1139 LocalContext env; |
| 1140 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); |
| 1141 CompileRun( |
| 1142 "var obj = new Obj;" |
| 1143 "function setAge(i){ obj.age = i; };" |
| 1144 "for(var i = 0; i <= 10000; i++) setAge(i);"); |
| 1145 // All i < 10000 go to the interceptor. |
| 1146 ExpectInt32("obj.interceptor_age", 9999); |
| 1147 // The last i goes to the accessor. |
| 1148 ExpectInt32("obj.accessor_age", 10000); |
| 1149 } |
| 1150 |
| 1151 |
| 1152 THREADED_TEST(SwitchFromAccessorToInterceptor) { |
| 1153 v8::HandleScope scope(CcTest::isolate()); |
| 1154 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); |
| 1155 AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter); |
| 1156 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); |
| 1157 LocalContext env; |
| 1158 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); |
| 1159 CompileRun( |
| 1160 "var obj = new Obj;" |
| 1161 "function setAge(i){ obj.age = i; };" |
| 1162 "for(var i = 20000; i >= 9999; i--) setAge(i);"); |
| 1163 // All i >= 10000 go to the accessor. |
| 1164 ExpectInt32("obj.accessor_age", 10000); |
| 1165 // The last i goes to the interceptor. |
| 1166 ExpectInt32("obj.interceptor_age", 9999); |
| 1167 } |
| 1168 |
| 1169 |
| 1170 THREADED_TEST(SwitchFromInterceptorToAccessorWithInheritance) { |
| 1171 v8::HandleScope scope(CcTest::isolate()); |
| 1172 Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); |
| 1173 Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); |
| 1174 child->Inherit(parent); |
| 1175 AddAccessor(parent, v8_str("age"), SimpleAccessorGetter, |
| 1176 SimpleAccessorSetter); |
| 1177 AddInterceptor(child, InterceptorGetter, InterceptorSetter); |
| 1178 LocalContext env; |
| 1179 env->Global()->Set(v8_str("Child"), child->GetFunction()); |
| 1180 CompileRun( |
| 1181 "var child = new Child;" |
| 1182 "function setAge(i){ child.age = i; };" |
| 1183 "for(var i = 0; i <= 10000; i++) setAge(i);"); |
| 1184 // All i < 10000 go to the interceptor. |
| 1185 ExpectInt32("child.interceptor_age", 9999); |
| 1186 // The last i goes to the accessor. |
| 1187 ExpectInt32("child.accessor_age", 10000); |
| 1188 } |
| 1189 |
| 1190 |
| 1191 THREADED_TEST(SwitchFromAccessorToInterceptorWithInheritance) { |
| 1192 v8::HandleScope scope(CcTest::isolate()); |
| 1193 Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); |
| 1194 Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); |
| 1195 child->Inherit(parent); |
| 1196 AddAccessor(parent, v8_str("age"), SimpleAccessorGetter, |
| 1197 SimpleAccessorSetter); |
| 1198 AddInterceptor(child, InterceptorGetter, InterceptorSetter); |
| 1199 LocalContext env; |
| 1200 env->Global()->Set(v8_str("Child"), child->GetFunction()); |
| 1201 CompileRun( |
| 1202 "var child = new Child;" |
| 1203 "function setAge(i){ child.age = i; };" |
| 1204 "for(var i = 20000; i >= 9999; i--) setAge(i);"); |
| 1205 // All i >= 10000 go to the accessor. |
| 1206 ExpectInt32("child.accessor_age", 10000); |
| 1207 // The last i goes to the interceptor. |
| 1208 ExpectInt32("child.interceptor_age", 9999); |
| 1209 } |
| 1210 |
| 1211 |
| 1212 THREADED_TEST(SwitchFromInterceptorToJSAccessor) { |
| 1213 v8::HandleScope scope(CcTest::isolate()); |
| 1214 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); |
| 1215 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); |
| 1216 LocalContext env; |
| 1217 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); |
| 1218 CompileRun( |
| 1219 "var obj = new Obj;" |
| 1220 "function setter(i) { this.accessor_age = i; };" |
| 1221 "function getter() { return this.accessor_age; };" |
| 1222 "function setAge(i) { obj.age = i; };" |
| 1223 "Object.defineProperty(obj, 'age', { get:getter, set:setter });" |
| 1224 "for(var i = 0; i <= 10000; i++) setAge(i);"); |
| 1225 // All i < 10000 go to the interceptor. |
| 1226 ExpectInt32("obj.interceptor_age", 9999); |
| 1227 // The last i goes to the JavaScript accessor. |
| 1228 ExpectInt32("obj.accessor_age", 10000); |
| 1229 // The installed JavaScript getter is still intact. |
| 1230 // This last part is a regression test for issue 1651 and relies on the fact |
| 1231 // that both interceptor and accessor are being installed on the same object. |
| 1232 ExpectInt32("obj.age", 10000); |
| 1233 ExpectBoolean("obj.hasOwnProperty('age')", true); |
| 1234 ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); |
| 1235 } |
| 1236 |
| 1237 |
| 1238 THREADED_TEST(SwitchFromJSAccessorToInterceptor) { |
| 1239 v8::HandleScope scope(CcTest::isolate()); |
| 1240 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); |
| 1241 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); |
| 1242 LocalContext env; |
| 1243 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); |
| 1244 CompileRun( |
| 1245 "var obj = new Obj;" |
| 1246 "function setter(i) { this.accessor_age = i; };" |
| 1247 "function getter() { return this.accessor_age; };" |
| 1248 "function setAge(i) { obj.age = i; };" |
| 1249 "Object.defineProperty(obj, 'age', { get:getter, set:setter });" |
| 1250 "for(var i = 20000; i >= 9999; i--) setAge(i);"); |
| 1251 // All i >= 10000 go to the accessor. |
| 1252 ExpectInt32("obj.accessor_age", 10000); |
| 1253 // The last i goes to the interceptor. |
| 1254 ExpectInt32("obj.interceptor_age", 9999); |
| 1255 // The installed JavaScript getter is still intact. |
| 1256 // This last part is a regression test for issue 1651 and relies on the fact |
| 1257 // that both interceptor and accessor are being installed on the same object. |
| 1258 ExpectInt32("obj.age", 10000); |
| 1259 ExpectBoolean("obj.hasOwnProperty('age')", true); |
| 1260 ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); |
| 1261 } |
| 1262 |
| 1263 |
| 1264 THREADED_TEST(SwitchFromInterceptorToProperty) { |
| 1265 v8::HandleScope scope(CcTest::isolate()); |
| 1266 Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); |
| 1267 Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); |
| 1268 child->Inherit(parent); |
| 1269 AddInterceptor(child, InterceptorGetter, InterceptorSetter); |
| 1270 LocalContext env; |
| 1271 env->Global()->Set(v8_str("Child"), child->GetFunction()); |
| 1272 CompileRun( |
| 1273 "var child = new Child;" |
| 1274 "function setAge(i){ child.age = i; };" |
| 1275 "for(var i = 0; i <= 10000; i++) setAge(i);"); |
| 1276 // All i < 10000 go to the interceptor. |
| 1277 ExpectInt32("child.interceptor_age", 9999); |
| 1278 // The last i goes to child's own property. |
| 1279 ExpectInt32("child.age", 10000); |
| 1280 } |
| 1281 |
| 1282 |
| 1283 THREADED_TEST(SwitchFromPropertyToInterceptor) { |
| 1284 v8::HandleScope scope(CcTest::isolate()); |
| 1285 Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); |
| 1286 Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); |
| 1287 child->Inherit(parent); |
| 1288 AddInterceptor(child, InterceptorGetter, InterceptorSetter); |
| 1289 LocalContext env; |
| 1290 env->Global()->Set(v8_str("Child"), child->GetFunction()); |
| 1291 CompileRun( |
| 1292 "var child = new Child;" |
| 1293 "function setAge(i){ child.age = i; };" |
| 1294 "for(var i = 20000; i >= 9999; i--) setAge(i);"); |
| 1295 // All i >= 10000 go to child's own property. |
| 1296 ExpectInt32("child.age", 10000); |
| 1297 // The last i goes to the interceptor. |
| 1298 ExpectInt32("child.interceptor_age", 9999); |
| 1299 } |
| 1300 |
| 1301 |
| 1302 static bool interceptor_for_hidden_properties_called; |
| 1303 static void InterceptorForHiddenProperties( |
| 1304 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1305 interceptor_for_hidden_properties_called = true; |
| 1306 } |
| 1307 |
| 1308 |
| 1309 THREADED_TEST(HiddenPropertiesWithInterceptors) { |
| 1310 LocalContext context; |
| 1311 v8::Isolate* isolate = context->GetIsolate(); |
| 1312 v8::HandleScope scope(isolate); |
| 1313 |
| 1314 interceptor_for_hidden_properties_called = false; |
| 1315 |
| 1316 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); |
| 1317 |
| 1318 // Associate an interceptor with an object and start setting hidden values. |
| 1319 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); |
| 1320 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); |
| 1321 instance_templ->SetHandler( |
| 1322 v8::NamedPropertyHandlerConfiguration(InterceptorForHiddenProperties)); |
| 1323 Local<v8::Function> function = fun_templ->GetFunction(); |
| 1324 Local<v8::Object> obj = function->NewInstance(); |
| 1325 CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 2302))); |
| 1326 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); |
| 1327 CHECK(!interceptor_for_hidden_properties_called); |
| 1328 } |
| 1329 |
| 1330 |
| 1331 static void XPropertyGetter(Local<Name> property, |
| 1332 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1333 ApiTestFuzzer::Fuzz(); |
| 1334 CHECK(info.Data()->IsUndefined()); |
| 1335 info.GetReturnValue().Set(property); |
| 1336 } |
| 1337 |
| 1338 |
| 1339 THREADED_TEST(NamedInterceptorPropertyRead) { |
| 1340 v8::Isolate* isolate = CcTest::isolate(); |
| 1341 v8::HandleScope scope(isolate); |
| 1342 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1343 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter)); |
| 1344 LocalContext context; |
| 1345 context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| 1346 Local<Script> script = v8_compile("obj.x"); |
| 1347 for (int i = 0; i < 10; i++) { |
| 1348 Local<Value> result = script->Run(); |
| 1349 CHECK(result->Equals(v8_str("x"))); |
| 1350 } |
| 1351 } |
| 1352 |
| 1353 |
| 1354 THREADED_TEST(NamedInterceptorDictionaryIC) { |
| 1355 v8::Isolate* isolate = CcTest::isolate(); |
| 1356 v8::HandleScope scope(isolate); |
| 1357 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1358 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter)); |
| 1359 LocalContext context; |
| 1360 // Create an object with a named interceptor. |
| 1361 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); |
| 1362 Local<Script> script = v8_compile("interceptor_obj.x"); |
| 1363 for (int i = 0; i < 10; i++) { |
| 1364 Local<Value> result = script->Run(); |
| 1365 CHECK(result->Equals(v8_str("x"))); |
| 1366 } |
| 1367 // Create a slow case object and a function accessing a property in |
| 1368 // that slow case object (with dictionary probing in generated |
| 1369 // code). Then force object with a named interceptor into slow-case, |
| 1370 // pass it to the function, and check that the interceptor is called |
| 1371 // instead of accessing the local property. |
| 1372 Local<Value> result = CompileRun( |
| 1373 "function get_x(o) { return o.x; };" |
| 1374 "var obj = { x : 42, y : 0 };" |
| 1375 "delete obj.y;" |
| 1376 "for (var i = 0; i < 10; i++) get_x(obj);" |
| 1377 "interceptor_obj.x = 42;" |
| 1378 "interceptor_obj.y = 10;" |
| 1379 "delete interceptor_obj.y;" |
| 1380 "get_x(interceptor_obj)"); |
| 1381 CHECK(result->Equals(v8_str("x"))); |
| 1382 } |
| 1383 |
| 1384 |
| 1385 THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) { |
| 1386 v8::Isolate* isolate = CcTest::isolate(); |
| 1387 v8::HandleScope scope(isolate); |
| 1388 v8::Local<Context> context1 = Context::New(isolate); |
| 1389 |
| 1390 context1->Enter(); |
| 1391 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1392 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter)); |
| 1393 // Create an object with a named interceptor. |
| 1394 v8::Local<v8::Object> object = templ->NewInstance(); |
| 1395 context1->Global()->Set(v8_str("interceptor_obj"), object); |
| 1396 |
| 1397 // Force the object into the slow case. |
| 1398 CompileRun( |
| 1399 "interceptor_obj.y = 0;" |
| 1400 "delete interceptor_obj.y;"); |
| 1401 context1->Exit(); |
| 1402 |
| 1403 { |
| 1404 // Introduce the object into a different context. |
| 1405 // Repeat named loads to exercise ICs. |
| 1406 LocalContext context2; |
| 1407 context2->Global()->Set(v8_str("interceptor_obj"), object); |
| 1408 Local<Value> result = CompileRun( |
| 1409 "function get_x(o) { return o.x; }" |
| 1410 "interceptor_obj.x = 42;" |
| 1411 "for (var i=0; i != 10; i++) {" |
| 1412 " get_x(interceptor_obj);" |
| 1413 "}" |
| 1414 "get_x(interceptor_obj)"); |
| 1415 // Check that the interceptor was actually invoked. |
| 1416 CHECK(result->Equals(v8_str("x"))); |
| 1417 } |
| 1418 |
| 1419 // Return to the original context and force some object to the slow case |
| 1420 // to cause the NormalizedMapCache to verify. |
| 1421 context1->Enter(); |
| 1422 CompileRun("var obj = { x : 0 }; delete obj.x;"); |
| 1423 context1->Exit(); |
| 1424 } |
| 1425 |
| 1426 |
| 1427 static void SetXOnPrototypeGetter( |
| 1428 Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1429 // Set x on the prototype object and do not handle the get request. |
| 1430 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); |
| 1431 proto.As<v8::Object>()->Set(v8_str("x"), |
| 1432 v8::Integer::New(info.GetIsolate(), 23)); |
| 1433 } |
| 1434 |
| 1435 |
| 1436 // This is a regression test for http://crbug.com/20104. Map |
| 1437 // transitions should not interfere with post interceptor lookup. |
| 1438 THREADED_TEST(NamedInterceptorMapTransitionRead) { |
| 1439 v8::Isolate* isolate = CcTest::isolate(); |
| 1440 v8::HandleScope scope(isolate); |
| 1441 Local<v8::FunctionTemplate> function_template = |
| 1442 v8::FunctionTemplate::New(isolate); |
| 1443 Local<v8::ObjectTemplate> instance_template = |
| 1444 function_template->InstanceTemplate(); |
| 1445 instance_template->SetHandler( |
| 1446 v8::NamedPropertyHandlerConfiguration(SetXOnPrototypeGetter)); |
| 1447 LocalContext context; |
| 1448 context->Global()->Set(v8_str("F"), function_template->GetFunction()); |
| 1449 // Create an instance of F and introduce a map transition for x. |
| 1450 CompileRun("var o = new F(); o.x = 23;"); |
| 1451 // Create an instance of F and invoke the getter. The result should be 23. |
| 1452 Local<Value> result = CompileRun("o = new F(); o.x"); |
| 1453 CHECK_EQ(result->Int32Value(), 23); |
| 1454 } |
| 1455 |
| 1456 |
| 1457 static void IndexedPropertyGetter( |
| 1458 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1459 ApiTestFuzzer::Fuzz(); |
| 1460 if (index == 37) { |
| 1461 info.GetReturnValue().Set(v8_num(625)); |
| 1462 } |
| 1463 } |
| 1464 |
| 1465 |
| 1466 static void IndexedPropertySetter( |
| 1467 uint32_t index, Local<Value> value, |
| 1468 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1469 ApiTestFuzzer::Fuzz(); |
| 1470 if (index == 39) { |
| 1471 info.GetReturnValue().Set(value); |
| 1472 } |
| 1473 } |
| 1474 |
| 1475 |
| 1476 THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { |
| 1477 v8::Isolate* isolate = CcTest::isolate(); |
| 1478 v8::HandleScope scope(isolate); |
| 1479 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1480 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 1481 IndexedPropertyGetter, IndexedPropertySetter)); |
| 1482 LocalContext context; |
| 1483 context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| 1484 Local<Script> getter_script = |
| 1485 v8_compile("obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"); |
| 1486 Local<Script> setter_script = v8_compile( |
| 1487 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" |
| 1488 "obj[17] = 23;" |
| 1489 "obj.foo;"); |
| 1490 Local<Script> interceptor_setter_script = v8_compile( |
| 1491 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" |
| 1492 "obj[39] = 47;" |
| 1493 "obj.foo;"); // This setter should not run, due to the interceptor. |
| 1494 Local<Script> interceptor_getter_script = v8_compile("obj[37];"); |
| 1495 Local<Value> result = getter_script->Run(); |
| 1496 CHECK(v8_num(5)->Equals(result)); |
| 1497 result = setter_script->Run(); |
| 1498 CHECK(v8_num(23)->Equals(result)); |
| 1499 result = interceptor_setter_script->Run(); |
| 1500 CHECK(v8_num(23)->Equals(result)); |
| 1501 result = interceptor_getter_script->Run(); |
| 1502 CHECK(v8_num(625)->Equals(result)); |
| 1503 } |
| 1504 |
| 1505 |
| 1506 static void UnboxedDoubleIndexedPropertyGetter( |
| 1507 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1508 ApiTestFuzzer::Fuzz(); |
| 1509 if (index < 25) { |
| 1510 info.GetReturnValue().Set(v8_num(index)); |
| 1511 } |
| 1512 } |
| 1513 |
| 1514 |
| 1515 static void UnboxedDoubleIndexedPropertySetter( |
| 1516 uint32_t index, Local<Value> value, |
| 1517 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1518 ApiTestFuzzer::Fuzz(); |
| 1519 if (index < 25) { |
| 1520 info.GetReturnValue().Set(v8_num(index)); |
| 1521 } |
| 1522 } |
| 1523 |
| 1524 |
| 1525 void UnboxedDoubleIndexedPropertyEnumerator( |
| 1526 const v8::PropertyCallbackInfo<v8::Array>& info) { |
| 1527 // Force the list of returned keys to be stored in a FastDoubleArray. |
| 1528 Local<Script> indexed_property_names_script = v8_compile( |
| 1529 "keys = new Array(); keys[125000] = 1;" |
| 1530 "for(i = 0; i < 80000; i++) { keys[i] = i; };" |
| 1531 "keys.length = 25; keys;"); |
| 1532 Local<Value> result = indexed_property_names_script->Run(); |
| 1533 info.GetReturnValue().Set(Local<v8::Array>::Cast(result)); |
| 1534 } |
| 1535 |
| 1536 |
| 1537 // Make sure that the the interceptor code in the runtime properly handles |
| 1538 // merging property name lists for double-array-backed arrays. |
| 1539 THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) { |
| 1540 v8::Isolate* isolate = CcTest::isolate(); |
| 1541 v8::HandleScope scope(isolate); |
| 1542 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1543 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 1544 UnboxedDoubleIndexedPropertyGetter, UnboxedDoubleIndexedPropertySetter, 0, |
| 1545 0, UnboxedDoubleIndexedPropertyEnumerator)); |
| 1546 LocalContext context; |
| 1547 context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| 1548 // When obj is created, force it to be Stored in a FastDoubleArray. |
| 1549 Local<Script> create_unboxed_double_script = v8_compile( |
| 1550 "obj[125000] = 1; for(i = 0; i < 80000; i+=2) { obj[i] = i; } " |
| 1551 "key_count = 0; " |
| 1552 "for (x in obj) {key_count++;};" |
| 1553 "obj;"); |
| 1554 Local<Value> result = create_unboxed_double_script->Run(); |
| 1555 CHECK(result->ToObject(isolate)->HasRealIndexedProperty(2000)); |
| 1556 Local<Script> key_count_check = v8_compile("key_count;"); |
| 1557 result = key_count_check->Run(); |
| 1558 CHECK(v8_num(40013)->Equals(result)); |
| 1559 } |
| 1560 |
| 1561 |
| 1562 void SloppyArgsIndexedPropertyEnumerator( |
| 1563 const v8::PropertyCallbackInfo<v8::Array>& info) { |
| 1564 // Force the list of returned keys to be stored in a Arguments object. |
| 1565 Local<Script> indexed_property_names_script = v8_compile( |
| 1566 "function f(w,x) {" |
| 1567 " return arguments;" |
| 1568 "}" |
| 1569 "keys = f(0, 1, 2, 3);" |
| 1570 "keys;"); |
| 1571 Local<Object> result = |
| 1572 Local<Object>::Cast(indexed_property_names_script->Run()); |
| 1573 // Have to populate the handle manually, as it's not Cast-able. |
| 1574 i::Handle<i::JSObject> o = v8::Utils::OpenHandle<Object, i::JSObject>(result); |
| 1575 i::Handle<i::JSArray> array(reinterpret_cast<i::JSArray*>(*o)); |
| 1576 info.GetReturnValue().Set(v8::Utils::ToLocal(array)); |
| 1577 } |
| 1578 |
| 1579 |
| 1580 static void SloppyIndexedPropertyGetter( |
| 1581 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1582 ApiTestFuzzer::Fuzz(); |
| 1583 if (index < 4) { |
| 1584 info.GetReturnValue().Set(v8_num(index)); |
| 1585 } |
| 1586 } |
| 1587 |
| 1588 |
| 1589 // Make sure that the the interceptor code in the runtime properly handles |
| 1590 // merging property name lists for non-string arguments arrays. |
| 1591 THREADED_TEST(IndexedInterceptorSloppyArgsWithIndexedAccessor) { |
| 1592 v8::Isolate* isolate = CcTest::isolate(); |
| 1593 v8::HandleScope scope(isolate); |
| 1594 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1595 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 1596 SloppyIndexedPropertyGetter, 0, 0, 0, |
| 1597 SloppyArgsIndexedPropertyEnumerator)); |
| 1598 LocalContext context; |
| 1599 context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| 1600 Local<Script> create_args_script = v8_compile( |
| 1601 "var key_count = 0;" |
| 1602 "for (x in obj) {key_count++;} key_count;"); |
| 1603 Local<Value> result = create_args_script->Run(); |
| 1604 CHECK(v8_num(4)->Equals(result)); |
| 1605 } |
| 1606 |
| 1607 |
| 1608 static void IdentityIndexedPropertyGetter( |
| 1609 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1610 info.GetReturnValue().Set(index); |
| 1611 } |
| 1612 |
| 1613 |
| 1614 THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) { |
| 1615 v8::Isolate* isolate = CcTest::isolate(); |
| 1616 v8::HandleScope scope(isolate); |
| 1617 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1618 templ->SetHandler( |
| 1619 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); |
| 1620 |
| 1621 LocalContext context; |
| 1622 context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| 1623 |
| 1624 // Check fast object case. |
| 1625 const char* fast_case_code = |
| 1626 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()"; |
| 1627 ExpectString(fast_case_code, "0"); |
| 1628 |
| 1629 // Check slow case. |
| 1630 const char* slow_case_code = |
| 1631 "obj.x = 1; delete obj.x;" |
| 1632 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()"; |
| 1633 ExpectString(slow_case_code, "1"); |
| 1634 } |
| 1635 |
| 1636 |
| 1637 THREADED_TEST(IndexedInterceptorWithNoSetter) { |
| 1638 v8::Isolate* isolate = CcTest::isolate(); |
| 1639 v8::HandleScope scope(isolate); |
| 1640 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1641 templ->SetHandler( |
| 1642 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); |
| 1643 |
| 1644 LocalContext context; |
| 1645 context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| 1646 |
| 1647 const char* code = |
| 1648 "try {" |
| 1649 " obj[0] = 239;" |
| 1650 " for (var i = 0; i < 100; i++) {" |
| 1651 " var v = obj[0];" |
| 1652 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" |
| 1653 " }" |
| 1654 " 'PASSED'" |
| 1655 "} catch(e) {" |
| 1656 " e" |
| 1657 "}"; |
| 1658 ExpectString(code, "PASSED"); |
| 1659 } |
| 1660 |
| 1661 |
| 1662 THREADED_TEST(IndexedInterceptorWithAccessorCheck) { |
| 1663 v8::Isolate* isolate = CcTest::isolate(); |
| 1664 v8::HandleScope scope(isolate); |
| 1665 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1666 templ->SetHandler( |
| 1667 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); |
| 1668 |
| 1669 LocalContext context; |
| 1670 Local<v8::Object> obj = templ->NewInstance(); |
| 1671 obj->TurnOnAccessCheck(); |
| 1672 context->Global()->Set(v8_str("obj"), obj); |
| 1673 |
| 1674 const char* code = |
| 1675 "var result = 'PASSED';" |
| 1676 "for (var i = 0; i < 100; i++) {" |
| 1677 " try {" |
| 1678 " var v = obj[0];" |
| 1679 " result = 'Wrong value ' + v + ' at iteration ' + i;" |
| 1680 " break;" |
| 1681 " } catch (e) {" |
| 1682 " /* pass */" |
| 1683 " }" |
| 1684 "}" |
| 1685 "result"; |
| 1686 ExpectString(code, "PASSED"); |
| 1687 } |
| 1688 |
| 1689 |
| 1690 THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { |
| 1691 i::FLAG_allow_natives_syntax = true; |
| 1692 v8::Isolate* isolate = CcTest::isolate(); |
| 1693 v8::HandleScope scope(isolate); |
| 1694 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1695 templ->SetHandler( |
| 1696 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); |
| 1697 |
| 1698 LocalContext context; |
| 1699 Local<v8::Object> obj = templ->NewInstance(); |
| 1700 context->Global()->Set(v8_str("obj"), obj); |
| 1701 |
| 1702 const char* code = |
| 1703 "var result = 'PASSED';" |
| 1704 "for (var i = 0; i < 100; i++) {" |
| 1705 " var expected = i;" |
| 1706 " if (i == 5) {" |
| 1707 " %EnableAccessChecks(obj);" |
| 1708 " }" |
| 1709 " try {" |
| 1710 " var v = obj[i];" |
| 1711 " if (i == 5) {" |
| 1712 " result = 'Should not have reached this!';" |
| 1713 " break;" |
| 1714 " } else if (v != expected) {" |
| 1715 " result = 'Wrong value ' + v + ' at iteration ' + i;" |
| 1716 " break;" |
| 1717 " }" |
| 1718 " } catch (e) {" |
| 1719 " if (i != 5) {" |
| 1720 " result = e;" |
| 1721 " }" |
| 1722 " }" |
| 1723 " if (i == 5) %DisableAccessChecks(obj);" |
| 1724 "}" |
| 1725 "result"; |
| 1726 ExpectString(code, "PASSED"); |
| 1727 } |
| 1728 |
| 1729 |
| 1730 THREADED_TEST(IndexedInterceptorWithDifferentIndices) { |
| 1731 v8::Isolate* isolate = CcTest::isolate(); |
| 1732 v8::HandleScope scope(isolate); |
| 1733 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1734 templ->SetHandler( |
| 1735 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); |
| 1736 |
| 1737 LocalContext context; |
| 1738 Local<v8::Object> obj = templ->NewInstance(); |
| 1739 context->Global()->Set(v8_str("obj"), obj); |
| 1740 |
| 1741 const char* code = |
| 1742 "try {" |
| 1743 " for (var i = 0; i < 100; i++) {" |
| 1744 " var v = obj[i];" |
| 1745 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" |
| 1746 " }" |
| 1747 " 'PASSED'" |
| 1748 "} catch(e) {" |
| 1749 " e" |
| 1750 "}"; |
| 1751 ExpectString(code, "PASSED"); |
| 1752 } |
| 1753 |
| 1754 |
| 1755 THREADED_TEST(IndexedInterceptorWithNegativeIndices) { |
| 1756 v8::Isolate* isolate = CcTest::isolate(); |
| 1757 v8::HandleScope scope(isolate); |
| 1758 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1759 templ->SetHandler( |
| 1760 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); |
| 1761 |
| 1762 LocalContext context; |
| 1763 Local<v8::Object> obj = templ->NewInstance(); |
| 1764 context->Global()->Set(v8_str("obj"), obj); |
| 1765 |
| 1766 const char* code = |
| 1767 "try {" |
| 1768 " for (var i = 0; i < 100; i++) {" |
| 1769 " var expected = i;" |
| 1770 " var key = i;" |
| 1771 " if (i == 25) {" |
| 1772 " key = -1;" |
| 1773 " expected = undefined;" |
| 1774 " }" |
| 1775 " if (i == 50) {" |
| 1776 " /* probe minimal Smi number on 32-bit platforms */" |
| 1777 " key = -(1 << 30);" |
| 1778 " expected = undefined;" |
| 1779 " }" |
| 1780 " if (i == 75) {" |
| 1781 " /* probe minimal Smi number on 64-bit platforms */" |
| 1782 " key = 1 << 31;" |
| 1783 " expected = undefined;" |
| 1784 " }" |
| 1785 " var v = obj[key];" |
| 1786 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" |
| 1787 " }" |
| 1788 " 'PASSED'" |
| 1789 "} catch(e) {" |
| 1790 " e" |
| 1791 "}"; |
| 1792 ExpectString(code, "PASSED"); |
| 1793 } |
| 1794 |
| 1795 |
| 1796 THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { |
| 1797 v8::Isolate* isolate = CcTest::isolate(); |
| 1798 v8::HandleScope scope(isolate); |
| 1799 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1800 templ->SetHandler( |
| 1801 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); |
| 1802 |
| 1803 LocalContext context; |
| 1804 Local<v8::Object> obj = templ->NewInstance(); |
| 1805 context->Global()->Set(v8_str("obj"), obj); |
| 1806 |
| 1807 const char* code = |
| 1808 "try {" |
| 1809 " for (var i = 0; i < 100; i++) {" |
| 1810 " var expected = i;" |
| 1811 " var key = i;" |
| 1812 " if (i == 50) {" |
| 1813 " key = 'foobar';" |
| 1814 " expected = undefined;" |
| 1815 " }" |
| 1816 " var v = obj[key];" |
| 1817 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" |
| 1818 " }" |
| 1819 " 'PASSED'" |
| 1820 "} catch(e) {" |
| 1821 " e" |
| 1822 "}"; |
| 1823 ExpectString(code, "PASSED"); |
| 1824 } |
| 1825 |
| 1826 |
| 1827 THREADED_TEST(IndexedInterceptorGoingMegamorphic) { |
| 1828 v8::Isolate* isolate = CcTest::isolate(); |
| 1829 v8::HandleScope scope(isolate); |
| 1830 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1831 templ->SetHandler( |
| 1832 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); |
| 1833 |
| 1834 LocalContext context; |
| 1835 Local<v8::Object> obj = templ->NewInstance(); |
| 1836 context->Global()->Set(v8_str("obj"), obj); |
| 1837 |
| 1838 const char* code = |
| 1839 "var original = obj;" |
| 1840 "try {" |
| 1841 " for (var i = 0; i < 100; i++) {" |
| 1842 " var expected = i;" |
| 1843 " if (i == 50) {" |
| 1844 " obj = {50: 'foobar'};" |
| 1845 " expected = 'foobar';" |
| 1846 " }" |
| 1847 " var v = obj[i];" |
| 1848 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" |
| 1849 " if (i == 50) obj = original;" |
| 1850 " }" |
| 1851 " 'PASSED'" |
| 1852 "} catch(e) {" |
| 1853 " e" |
| 1854 "}"; |
| 1855 ExpectString(code, "PASSED"); |
| 1856 } |
| 1857 |
| 1858 |
| 1859 THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { |
| 1860 v8::Isolate* isolate = CcTest::isolate(); |
| 1861 v8::HandleScope scope(isolate); |
| 1862 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1863 templ->SetHandler( |
| 1864 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); |
| 1865 |
| 1866 LocalContext context; |
| 1867 Local<v8::Object> obj = templ->NewInstance(); |
| 1868 context->Global()->Set(v8_str("obj"), obj); |
| 1869 |
| 1870 const char* code = |
| 1871 "var original = obj;" |
| 1872 "try {" |
| 1873 " for (var i = 0; i < 100; i++) {" |
| 1874 " var expected = i;" |
| 1875 " if (i == 5) {" |
| 1876 " obj = 239;" |
| 1877 " expected = undefined;" |
| 1878 " }" |
| 1879 " var v = obj[i];" |
| 1880 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" |
| 1881 " if (i == 5) obj = original;" |
| 1882 " }" |
| 1883 " 'PASSED'" |
| 1884 "} catch(e) {" |
| 1885 " e" |
| 1886 "}"; |
| 1887 ExpectString(code, "PASSED"); |
| 1888 } |
| 1889 |
| 1890 |
| 1891 THREADED_TEST(IndexedInterceptorOnProto) { |
| 1892 v8::Isolate* isolate = CcTest::isolate(); |
| 1893 v8::HandleScope scope(isolate); |
| 1894 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 1895 templ->SetHandler( |
| 1896 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter)); |
| 1897 |
| 1898 LocalContext context; |
| 1899 Local<v8::Object> obj = templ->NewInstance(); |
| 1900 context->Global()->Set(v8_str("obj"), obj); |
| 1901 |
| 1902 const char* code = |
| 1903 "var o = {__proto__: obj};" |
| 1904 "try {" |
| 1905 " for (var i = 0; i < 100; i++) {" |
| 1906 " var v = o[i];" |
| 1907 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" |
| 1908 " }" |
| 1909 " 'PASSED'" |
| 1910 "} catch(e) {" |
| 1911 " e" |
| 1912 "}"; |
| 1913 ExpectString(code, "PASSED"); |
| 1914 } |
| 1915 |
| 1916 |
| 1917 static void NoBlockGetterX(Local<Name> name, |
| 1918 const v8::PropertyCallbackInfo<v8::Value>&) {} |
| 1919 |
| 1920 |
| 1921 static void NoBlockGetterI(uint32_t index, |
| 1922 const v8::PropertyCallbackInfo<v8::Value>&) {} |
| 1923 |
| 1924 |
| 1925 static void PDeleter(Local<Name> name, |
| 1926 const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
| 1927 if (!name->Equals(v8_str("foo"))) { |
| 1928 return; // not intercepted |
| 1929 } |
| 1930 |
| 1931 info.GetReturnValue().Set(false); // intercepted, don't delete the property |
| 1932 } |
| 1933 |
| 1934 |
| 1935 static void IDeleter(uint32_t index, |
| 1936 const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
| 1937 if (index != 2) { |
| 1938 return; // not intercepted |
| 1939 } |
| 1940 |
| 1941 info.GetReturnValue().Set(false); // intercepted, don't delete the property |
| 1942 } |
| 1943 |
| 1944 |
| 1945 THREADED_TEST(Deleter) { |
| 1946 v8::Isolate* isolate = CcTest::isolate(); |
| 1947 v8::HandleScope scope(isolate); |
| 1948 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| 1949 obj->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX, NULL, |
| 1950 NULL, PDeleter, NULL)); |
| 1951 obj->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 1952 NoBlockGetterI, NULL, NULL, IDeleter, NULL)); |
| 1953 LocalContext context; |
| 1954 context->Global()->Set(v8_str("k"), obj->NewInstance()); |
| 1955 CompileRun( |
| 1956 "k.foo = 'foo';" |
| 1957 "k.bar = 'bar';" |
| 1958 "k[2] = 2;" |
| 1959 "k[4] = 4;"); |
| 1960 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); |
| 1961 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); |
| 1962 |
| 1963 CHECK(v8_compile("k.foo")->Run()->Equals(v8_str("foo"))); |
| 1964 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); |
| 1965 |
| 1966 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); |
| 1967 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); |
| 1968 |
| 1969 CHECK(v8_compile("k[2]")->Run()->Equals(v8_num(2))); |
| 1970 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); |
| 1971 } |
| 1972 |
| 1973 |
| 1974 static void GetK(Local<Name> name, |
| 1975 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1976 ApiTestFuzzer::Fuzz(); |
| 1977 if (name->Equals(v8_str("foo")) || name->Equals(v8_str("bar")) || |
| 1978 name->Equals(v8_str("baz"))) { |
| 1979 info.GetReturnValue().SetUndefined(); |
| 1980 } |
| 1981 } |
| 1982 |
| 1983 |
| 1984 static void IndexedGetK(uint32_t index, |
| 1985 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1986 ApiTestFuzzer::Fuzz(); |
| 1987 if (index == 0 || index == 1) info.GetReturnValue().SetUndefined(); |
| 1988 } |
| 1989 |
| 1990 |
| 1991 static void NamedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) { |
| 1992 ApiTestFuzzer::Fuzz(); |
| 1993 v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 3); |
| 1994 result->Set(v8::Integer::New(info.GetIsolate(), 0), v8_str("foo")); |
| 1995 result->Set(v8::Integer::New(info.GetIsolate(), 1), v8_str("bar")); |
| 1996 result->Set(v8::Integer::New(info.GetIsolate(), 2), v8_str("baz")); |
| 1997 info.GetReturnValue().Set(result); |
| 1998 } |
| 1999 |
| 2000 |
| 2001 static void IndexedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) { |
| 2002 ApiTestFuzzer::Fuzz(); |
| 2003 v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 2); |
| 2004 result->Set(v8::Integer::New(info.GetIsolate(), 0), v8_str("0")); |
| 2005 result->Set(v8::Integer::New(info.GetIsolate(), 1), v8_str("1")); |
| 2006 info.GetReturnValue().Set(result); |
| 2007 } |
| 2008 |
| 2009 |
| 2010 THREADED_TEST(Enumerators) { |
| 2011 v8::Isolate* isolate = CcTest::isolate(); |
| 2012 v8::HandleScope scope(isolate); |
| 2013 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| 2014 obj->SetHandler( |
| 2015 v8::NamedPropertyHandlerConfiguration(GetK, NULL, NULL, NULL, NamedEnum)); |
| 2016 obj->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 2017 IndexedGetK, NULL, NULL, NULL, IndexedEnum)); |
| 2018 LocalContext context; |
| 2019 context->Global()->Set(v8_str("k"), obj->NewInstance()); |
| 2020 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( |
| 2021 "k[10] = 0;" |
| 2022 "k.a = 0;" |
| 2023 "k[5] = 0;" |
| 2024 "k.b = 0;" |
| 2025 "k[4294967295] = 0;" |
| 2026 "k.c = 0;" |
| 2027 "k[4294967296] = 0;" |
| 2028 "k.d = 0;" |
| 2029 "k[140000] = 0;" |
| 2030 "k.e = 0;" |
| 2031 "k[30000000000] = 0;" |
| 2032 "k.f = 0;" |
| 2033 "var result = [];" |
| 2034 "for (var prop in k) {" |
| 2035 " result.push(prop);" |
| 2036 "}" |
| 2037 "result")); |
| 2038 // Check that we get all the property names returned including the |
| 2039 // ones from the enumerators in the right order: indexed properties |
| 2040 // in numerical order, indexed interceptor properties, named |
| 2041 // properties in insertion order, named interceptor properties. |
| 2042 // This order is not mandated by the spec, so this test is just |
| 2043 // documenting our behavior. |
| 2044 CHECK_EQ(17u, result->Length()); |
| 2045 // Indexed properties in numerical order. |
| 2046 CHECK(v8_str("5")->Equals(result->Get(v8::Integer::New(isolate, 0)))); |
| 2047 CHECK(v8_str("10")->Equals(result->Get(v8::Integer::New(isolate, 1)))); |
| 2048 CHECK(v8_str("140000")->Equals(result->Get(v8::Integer::New(isolate, 2)))); |
| 2049 CHECK( |
| 2050 v8_str("4294967295")->Equals(result->Get(v8::Integer::New(isolate, 3)))); |
| 2051 // Indexed interceptor properties in the order they are returned |
| 2052 // from the enumerator interceptor. |
| 2053 CHECK(v8_str("0")->Equals(result->Get(v8::Integer::New(isolate, 4)))); |
| 2054 CHECK(v8_str("1")->Equals(result->Get(v8::Integer::New(isolate, 5)))); |
| 2055 // Named properties in insertion order. |
| 2056 CHECK(v8_str("a")->Equals(result->Get(v8::Integer::New(isolate, 6)))); |
| 2057 CHECK(v8_str("b")->Equals(result->Get(v8::Integer::New(isolate, 7)))); |
| 2058 CHECK(v8_str("c")->Equals(result->Get(v8::Integer::New(isolate, 8)))); |
| 2059 CHECK( |
| 2060 v8_str("4294967296")->Equals(result->Get(v8::Integer::New(isolate, 9)))); |
| 2061 CHECK(v8_str("d")->Equals(result->Get(v8::Integer::New(isolate, 10)))); |
| 2062 CHECK(v8_str("e")->Equals(result->Get(v8::Integer::New(isolate, 11)))); |
| 2063 CHECK(v8_str("30000000000") |
| 2064 ->Equals(result->Get(v8::Integer::New(isolate, 12)))); |
| 2065 CHECK(v8_str("f")->Equals(result->Get(v8::Integer::New(isolate, 13)))); |
| 2066 // Named interceptor properties. |
| 2067 CHECK(v8_str("foo")->Equals(result->Get(v8::Integer::New(isolate, 14)))); |
| 2068 CHECK(v8_str("bar")->Equals(result->Get(v8::Integer::New(isolate, 15)))); |
| 2069 CHECK(v8_str("baz")->Equals(result->Get(v8::Integer::New(isolate, 16)))); |
| 2070 } |
| 2071 |
| 2072 |
| 2073 v8::Handle<Value> call_ic_function; |
| 2074 v8::Handle<Value> call_ic_function2; |
| 2075 v8::Handle<Value> call_ic_function3; |
| 2076 |
| 2077 static void InterceptorCallICGetter( |
| 2078 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2079 ApiTestFuzzer::Fuzz(); |
| 2080 CHECK(v8_str("x")->Equals(name)); |
| 2081 info.GetReturnValue().Set(call_ic_function); |
| 2082 } |
| 2083 |
| 2084 |
| 2085 // This test should hit the call IC for the interceptor case. |
| 2086 THREADED_TEST(InterceptorCallIC) { |
| 2087 v8::Isolate* isolate = CcTest::isolate(); |
| 2088 v8::HandleScope scope(isolate); |
| 2089 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2090 templ->SetHandler( |
| 2091 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter)); |
| 2092 LocalContext context; |
| 2093 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2094 call_ic_function = v8_compile("function f(x) { return x + 1; }; f")->Run(); |
| 2095 v8::Handle<Value> value = CompileRun( |
| 2096 "var result = 0;" |
| 2097 "for (var i = 0; i < 1000; i++) {" |
| 2098 " result = o.x(41);" |
| 2099 "}"); |
| 2100 CHECK_EQ(42, value->Int32Value()); |
| 2101 } |
| 2102 |
| 2103 |
| 2104 // This test checks that if interceptor doesn't provide |
| 2105 // a value, we can fetch regular value. |
| 2106 THREADED_TEST(InterceptorCallICSeesOthers) { |
| 2107 v8::Isolate* isolate = CcTest::isolate(); |
| 2108 v8::HandleScope scope(isolate); |
| 2109 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2110 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2111 LocalContext context; |
| 2112 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2113 v8::Handle<Value> value = CompileRun( |
| 2114 "o.x = function f(x) { return x + 1; };" |
| 2115 "var result = 0;" |
| 2116 "for (var i = 0; i < 7; i++) {" |
| 2117 " result = o.x(41);" |
| 2118 "}"); |
| 2119 CHECK_EQ(42, value->Int32Value()); |
| 2120 } |
| 2121 |
| 2122 |
| 2123 static v8::Handle<Value> call_ic_function4; |
| 2124 static void InterceptorCallICGetter4( |
| 2125 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2126 ApiTestFuzzer::Fuzz(); |
| 2127 CHECK(v8_str("x")->Equals(name)); |
| 2128 info.GetReturnValue().Set(call_ic_function4); |
| 2129 } |
| 2130 |
| 2131 |
| 2132 // This test checks that if interceptor provides a function, |
| 2133 // even if we cached shadowed variant, interceptor's function |
| 2134 // is invoked |
| 2135 THREADED_TEST(InterceptorCallICCacheableNotNeeded) { |
| 2136 v8::Isolate* isolate = CcTest::isolate(); |
| 2137 v8::HandleScope scope(isolate); |
| 2138 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2139 templ->SetHandler( |
| 2140 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter4)); |
| 2141 LocalContext context; |
| 2142 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2143 call_ic_function4 = v8_compile("function f(x) { return x - 1; }; f")->Run(); |
| 2144 v8::Handle<Value> value = CompileRun( |
| 2145 "Object.getPrototypeOf(o).x = function(x) { return x + 1; };" |
| 2146 "var result = 0;" |
| 2147 "for (var i = 0; i < 1000; i++) {" |
| 2148 " result = o.x(42);" |
| 2149 "}"); |
| 2150 CHECK_EQ(41, value->Int32Value()); |
| 2151 } |
| 2152 |
| 2153 |
| 2154 // Test the case when we stored cacheable lookup into |
| 2155 // a stub, but it got invalidated later on |
| 2156 THREADED_TEST(InterceptorCallICInvalidatedCacheable) { |
| 2157 v8::Isolate* isolate = CcTest::isolate(); |
| 2158 v8::HandleScope scope(isolate); |
| 2159 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2160 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2161 LocalContext context; |
| 2162 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2163 v8::Handle<Value> value = CompileRun( |
| 2164 "proto1 = new Object();" |
| 2165 "proto2 = new Object();" |
| 2166 "o.__proto__ = proto1;" |
| 2167 "proto1.__proto__ = proto2;" |
| 2168 "proto2.y = function(x) { return x + 1; };" |
| 2169 // Invoke it many times to compile a stub |
| 2170 "for (var i = 0; i < 7; i++) {" |
| 2171 " o.y(42);" |
| 2172 "}" |
| 2173 "proto1.y = function(x) { return x - 1; };" |
| 2174 "var result = 0;" |
| 2175 "for (var i = 0; i < 7; i++) {" |
| 2176 " result += o.y(42);" |
| 2177 "}"); |
| 2178 CHECK_EQ(41 * 7, value->Int32Value()); |
| 2179 } |
| 2180 |
| 2181 |
| 2182 // This test checks that if interceptor doesn't provide a function, |
| 2183 // cached constant function is used |
| 2184 THREADED_TEST(InterceptorCallICConstantFunctionUsed) { |
| 2185 v8::Isolate* isolate = CcTest::isolate(); |
| 2186 v8::HandleScope scope(isolate); |
| 2187 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2188 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2189 LocalContext context; |
| 2190 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2191 v8::Handle<Value> value = CompileRun( |
| 2192 "function inc(x) { return x + 1; };" |
| 2193 "inc(1);" |
| 2194 "o.x = inc;" |
| 2195 "var result = 0;" |
| 2196 "for (var i = 0; i < 1000; i++) {" |
| 2197 " result = o.x(42);" |
| 2198 "}"); |
| 2199 CHECK_EQ(43, value->Int32Value()); |
| 2200 } |
| 2201 |
| 2202 |
| 2203 static v8::Handle<Value> call_ic_function5; |
| 2204 static void InterceptorCallICGetter5( |
| 2205 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2206 ApiTestFuzzer::Fuzz(); |
| 2207 if (v8_str("x")->Equals(name)) info.GetReturnValue().Set(call_ic_function5); |
| 2208 } |
| 2209 |
| 2210 |
| 2211 // This test checks that if interceptor provides a function, |
| 2212 // even if we cached constant function, interceptor's function |
| 2213 // is invoked |
| 2214 THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { |
| 2215 v8::Isolate* isolate = CcTest::isolate(); |
| 2216 v8::HandleScope scope(isolate); |
| 2217 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2218 templ->SetHandler( |
| 2219 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter5)); |
| 2220 LocalContext context; |
| 2221 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2222 call_ic_function5 = v8_compile("function f(x) { return x - 1; }; f")->Run(); |
| 2223 v8::Handle<Value> value = CompileRun( |
| 2224 "function inc(x) { return x + 1; };" |
| 2225 "inc(1);" |
| 2226 "o.x = inc;" |
| 2227 "var result = 0;" |
| 2228 "for (var i = 0; i < 1000; i++) {" |
| 2229 " result = o.x(42);" |
| 2230 "}"); |
| 2231 CHECK_EQ(41, value->Int32Value()); |
| 2232 } |
| 2233 |
| 2234 |
| 2235 static v8::Handle<Value> call_ic_function6; |
| 2236 static void InterceptorCallICGetter6( |
| 2237 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2238 ApiTestFuzzer::Fuzz(); |
| 2239 if (v8_str("x")->Equals(name)) info.GetReturnValue().Set(call_ic_function6); |
| 2240 } |
| 2241 |
| 2242 |
| 2243 // Same test as above, except the code is wrapped in a function |
| 2244 // to test the optimized compiler. |
| 2245 THREADED_TEST(InterceptorCallICConstantFunctionNotNeededWrapped) { |
| 2246 i::FLAG_allow_natives_syntax = true; |
| 2247 v8::Isolate* isolate = CcTest::isolate(); |
| 2248 v8::HandleScope scope(isolate); |
| 2249 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2250 templ->SetHandler( |
| 2251 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter6)); |
| 2252 LocalContext context; |
| 2253 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2254 call_ic_function6 = v8_compile("function f(x) { return x - 1; }; f")->Run(); |
| 2255 v8::Handle<Value> value = CompileRun( |
| 2256 "function inc(x) { return x + 1; };" |
| 2257 "inc(1);" |
| 2258 "o.x = inc;" |
| 2259 "function test() {" |
| 2260 " var result = 0;" |
| 2261 " for (var i = 0; i < 1000; i++) {" |
| 2262 " result = o.x(42);" |
| 2263 " }" |
| 2264 " return result;" |
| 2265 "};" |
| 2266 "test();" |
| 2267 "test();" |
| 2268 "test();" |
| 2269 "%OptimizeFunctionOnNextCall(test);" |
| 2270 "test()"); |
| 2271 CHECK_EQ(41, value->Int32Value()); |
| 2272 } |
| 2273 |
| 2274 |
| 2275 // Test the case when we stored constant function into |
| 2276 // a stub, but it got invalidated later on |
| 2277 THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { |
| 2278 v8::Isolate* isolate = CcTest::isolate(); |
| 2279 v8::HandleScope scope(isolate); |
| 2280 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2281 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2282 LocalContext context; |
| 2283 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2284 v8::Handle<Value> value = CompileRun( |
| 2285 "function inc(x) { return x + 1; };" |
| 2286 "inc(1);" |
| 2287 "proto1 = new Object();" |
| 2288 "proto2 = new Object();" |
| 2289 "o.__proto__ = proto1;" |
| 2290 "proto1.__proto__ = proto2;" |
| 2291 "proto2.y = inc;" |
| 2292 // Invoke it many times to compile a stub |
| 2293 "for (var i = 0; i < 7; i++) {" |
| 2294 " o.y(42);" |
| 2295 "}" |
| 2296 "proto1.y = function(x) { return x - 1; };" |
| 2297 "var result = 0;" |
| 2298 "for (var i = 0; i < 7; i++) {" |
| 2299 " result += o.y(42);" |
| 2300 "}"); |
| 2301 CHECK_EQ(41 * 7, value->Int32Value()); |
| 2302 } |
| 2303 |
| 2304 |
| 2305 // Test the case when we stored constant function into |
| 2306 // a stub, but it got invalidated later on due to override on |
| 2307 // global object which is between interceptor and constant function' holders. |
| 2308 THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { |
| 2309 v8::Isolate* isolate = CcTest::isolate(); |
| 2310 v8::HandleScope scope(isolate); |
| 2311 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2312 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2313 LocalContext context; |
| 2314 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2315 v8::Handle<Value> value = CompileRun( |
| 2316 "function inc(x) { return x + 1; };" |
| 2317 "inc(1);" |
| 2318 "o.__proto__ = this;" |
| 2319 "this.__proto__.y = inc;" |
| 2320 // Invoke it many times to compile a stub |
| 2321 "for (var i = 0; i < 7; i++) {" |
| 2322 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" |
| 2323 "}" |
| 2324 "this.y = function(x) { return x - 1; };" |
| 2325 "var result = 0;" |
| 2326 "for (var i = 0; i < 7; i++) {" |
| 2327 " result += o.y(42);" |
| 2328 "}"); |
| 2329 CHECK_EQ(41 * 7, value->Int32Value()); |
| 2330 } |
| 2331 |
| 2332 |
| 2333 // Test the case when actual function to call sits on global object. |
| 2334 THREADED_TEST(InterceptorCallICCachedFromGlobal) { |
| 2335 v8::Isolate* isolate = CcTest::isolate(); |
| 2336 v8::HandleScope scope(isolate); |
| 2337 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); |
| 2338 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2339 |
| 2340 LocalContext context; |
| 2341 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); |
| 2342 |
| 2343 v8::Handle<Value> value = CompileRun( |
| 2344 "try {" |
| 2345 " o.__proto__ = this;" |
| 2346 " for (var i = 0; i < 10; i++) {" |
| 2347 " var v = o.parseFloat('239');" |
| 2348 " if (v != 239) throw v;" |
| 2349 // Now it should be ICed and keep a reference to parseFloat. |
| 2350 " }" |
| 2351 " var result = 0;" |
| 2352 " for (var i = 0; i < 10; i++) {" |
| 2353 " result += o.parseFloat('239');" |
| 2354 " }" |
| 2355 " result" |
| 2356 "} catch(e) {" |
| 2357 " e" |
| 2358 "};"); |
| 2359 CHECK_EQ(239 * 10, value->Int32Value()); |
| 2360 } |
| 2361 |
| 2362 |
| 2363 v8::Handle<Value> keyed_call_ic_function; |
| 2364 |
| 2365 static void InterceptorKeyedCallICGetter( |
| 2366 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2367 ApiTestFuzzer::Fuzz(); |
| 2368 if (v8_str("x")->Equals(name)) { |
| 2369 info.GetReturnValue().Set(keyed_call_ic_function); |
| 2370 } |
| 2371 } |
| 2372 |
| 2373 |
| 2374 // Test the case when we stored cacheable lookup into |
| 2375 // a stub, but the function name changed (to another cacheable function). |
| 2376 THREADED_TEST(InterceptorKeyedCallICKeyChange1) { |
| 2377 v8::Isolate* isolate = CcTest::isolate(); |
| 2378 v8::HandleScope scope(isolate); |
| 2379 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2380 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2381 LocalContext context; |
| 2382 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2383 CompileRun( |
| 2384 "proto = new Object();" |
| 2385 "proto.y = function(x) { return x + 1; };" |
| 2386 "proto.z = function(x) { return x - 1; };" |
| 2387 "o.__proto__ = proto;" |
| 2388 "var result = 0;" |
| 2389 "var method = 'y';" |
| 2390 "for (var i = 0; i < 10; i++) {" |
| 2391 " if (i == 5) { method = 'z'; };" |
| 2392 " result += o[method](41);" |
| 2393 "}"); |
| 2394 CHECK_EQ(42 * 5 + 40 * 5, |
| 2395 context->Global()->Get(v8_str("result"))->Int32Value()); |
| 2396 } |
| 2397 |
| 2398 |
| 2399 // Test the case when we stored cacheable lookup into |
| 2400 // a stub, but the function name changed (and the new function is present |
| 2401 // both before and after the interceptor in the prototype chain). |
| 2402 THREADED_TEST(InterceptorKeyedCallICKeyChange2) { |
| 2403 v8::Isolate* isolate = CcTest::isolate(); |
| 2404 v8::HandleScope scope(isolate); |
| 2405 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2406 templ->SetHandler( |
| 2407 v8::NamedPropertyHandlerConfiguration(InterceptorKeyedCallICGetter)); |
| 2408 LocalContext context; |
| 2409 context->Global()->Set(v8_str("proto1"), templ->NewInstance()); |
| 2410 keyed_call_ic_function = |
| 2411 v8_compile("function f(x) { return x - 1; }; f")->Run(); |
| 2412 CompileRun( |
| 2413 "o = new Object();" |
| 2414 "proto2 = new Object();" |
| 2415 "o.y = function(x) { return x + 1; };" |
| 2416 "proto2.y = function(x) { return x + 2; };" |
| 2417 "o.__proto__ = proto1;" |
| 2418 "proto1.__proto__ = proto2;" |
| 2419 "var result = 0;" |
| 2420 "var method = 'x';" |
| 2421 "for (var i = 0; i < 10; i++) {" |
| 2422 " if (i == 5) { method = 'y'; };" |
| 2423 " result += o[method](41);" |
| 2424 "}"); |
| 2425 CHECK_EQ(42 * 5 + 40 * 5, |
| 2426 context->Global()->Get(v8_str("result"))->Int32Value()); |
| 2427 } |
| 2428 |
| 2429 |
| 2430 // Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit |
| 2431 // on the global object. |
| 2432 THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { |
| 2433 v8::Isolate* isolate = CcTest::isolate(); |
| 2434 v8::HandleScope scope(isolate); |
| 2435 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2436 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2437 LocalContext context; |
| 2438 context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| 2439 CompileRun( |
| 2440 "function inc(x) { return x + 1; };" |
| 2441 "inc(1);" |
| 2442 "function dec(x) { return x - 1; };" |
| 2443 "dec(1);" |
| 2444 "o.__proto__ = this;" |
| 2445 "this.__proto__.x = inc;" |
| 2446 "this.__proto__.y = dec;" |
| 2447 "var result = 0;" |
| 2448 "var method = 'x';" |
| 2449 "for (var i = 0; i < 10; i++) {" |
| 2450 " if (i == 5) { method = 'y'; };" |
| 2451 " result += o[method](41);" |
| 2452 "}"); |
| 2453 CHECK_EQ(42 * 5 + 40 * 5, |
| 2454 context->Global()->Get(v8_str("result"))->Int32Value()); |
| 2455 } |
| 2456 |
| 2457 |
| 2458 // Test the case when actual function to call sits on global object. |
| 2459 THREADED_TEST(InterceptorKeyedCallICFromGlobal) { |
| 2460 v8::Isolate* isolate = CcTest::isolate(); |
| 2461 v8::HandleScope scope(isolate); |
| 2462 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); |
| 2463 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2464 LocalContext context; |
| 2465 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); |
| 2466 |
| 2467 CompileRun( |
| 2468 "function len(x) { return x.length; };" |
| 2469 "o.__proto__ = this;" |
| 2470 "var m = 'parseFloat';" |
| 2471 "var result = 0;" |
| 2472 "for (var i = 0; i < 10; i++) {" |
| 2473 " if (i == 5) {" |
| 2474 " m = 'len';" |
| 2475 " saved_result = result;" |
| 2476 " };" |
| 2477 " result = o[m]('239');" |
| 2478 "}"); |
| 2479 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value()); |
| 2480 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value()); |
| 2481 } |
| 2482 |
| 2483 |
| 2484 // Test the map transition before the interceptor. |
| 2485 THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { |
| 2486 v8::Isolate* isolate = CcTest::isolate(); |
| 2487 v8::HandleScope scope(isolate); |
| 2488 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); |
| 2489 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2490 LocalContext context; |
| 2491 context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); |
| 2492 |
| 2493 CompileRun( |
| 2494 "var o = new Object();" |
| 2495 "o.__proto__ = proto;" |
| 2496 "o.method = function(x) { return x + 1; };" |
| 2497 "var m = 'method';" |
| 2498 "var result = 0;" |
| 2499 "for (var i = 0; i < 10; i++) {" |
| 2500 " if (i == 5) { o.method = function(x) { return x - 1; }; };" |
| 2501 " result += o[m](41);" |
| 2502 "}"); |
| 2503 CHECK_EQ(42 * 5 + 40 * 5, |
| 2504 context->Global()->Get(v8_str("result"))->Int32Value()); |
| 2505 } |
| 2506 |
| 2507 |
| 2508 // Test the map transition after the interceptor. |
| 2509 THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { |
| 2510 v8::Isolate* isolate = CcTest::isolate(); |
| 2511 v8::HandleScope scope(isolate); |
| 2512 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate); |
| 2513 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX)); |
| 2514 LocalContext context; |
| 2515 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); |
| 2516 |
| 2517 CompileRun( |
| 2518 "var proto = new Object();" |
| 2519 "o.__proto__ = proto;" |
| 2520 "proto.method = function(x) { return x + 1; };" |
| 2521 "var m = 'method';" |
| 2522 "var result = 0;" |
| 2523 "for (var i = 0; i < 10; i++) {" |
| 2524 " if (i == 5) { proto.method = function(x) { return x - 1; }; };" |
| 2525 " result += o[m](41);" |
| 2526 "}"); |
| 2527 CHECK_EQ(42 * 5 + 40 * 5, |
| 2528 context->Global()->Get(v8_str("result"))->Int32Value()); |
| 2529 } |
| 2530 |
| 2531 |
| 2532 static int interceptor_call_count = 0; |
| 2533 |
| 2534 static void InterceptorICRefErrorGetter( |
| 2535 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2536 ApiTestFuzzer::Fuzz(); |
| 2537 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { |
| 2538 info.GetReturnValue().Set(call_ic_function2); |
| 2539 } |
| 2540 } |
| 2541 |
| 2542 |
| 2543 // This test should hit load and call ICs for the interceptor case. |
| 2544 // Once in a while, the interceptor will reply that a property was not |
| 2545 // found in which case we should get a reference error. |
| 2546 THREADED_TEST(InterceptorICReferenceErrors) { |
| 2547 v8::Isolate* isolate = CcTest::isolate(); |
| 2548 v8::HandleScope scope(isolate); |
| 2549 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2550 templ->SetHandler( |
| 2551 v8::NamedPropertyHandlerConfiguration(InterceptorICRefErrorGetter)); |
| 2552 LocalContext context(0, templ, v8::Handle<Value>()); |
| 2553 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); |
| 2554 v8::Handle<Value> value = CompileRun( |
| 2555 "function f() {" |
| 2556 " for (var i = 0; i < 1000; i++) {" |
| 2557 " try { x; } catch(e) { return true; }" |
| 2558 " }" |
| 2559 " return false;" |
| 2560 "};" |
| 2561 "f();"); |
| 2562 CHECK_EQ(true, value->BooleanValue()); |
| 2563 interceptor_call_count = 0; |
| 2564 value = CompileRun( |
| 2565 "function g() {" |
| 2566 " for (var i = 0; i < 1000; i++) {" |
| 2567 " try { x(42); } catch(e) { return true; }" |
| 2568 " }" |
| 2569 " return false;" |
| 2570 "};" |
| 2571 "g();"); |
| 2572 CHECK_EQ(true, value->BooleanValue()); |
| 2573 } |
| 2574 |
| 2575 |
| 2576 static int interceptor_ic_exception_get_count = 0; |
| 2577 |
| 2578 static void InterceptorICExceptionGetter( |
| 2579 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2580 ApiTestFuzzer::Fuzz(); |
| 2581 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { |
| 2582 info.GetReturnValue().Set(call_ic_function3); |
| 2583 } |
| 2584 if (interceptor_ic_exception_get_count == 20) { |
| 2585 info.GetIsolate()->ThrowException(v8_num(42)); |
| 2586 return; |
| 2587 } |
| 2588 } |
| 2589 |
| 2590 |
| 2591 // Test interceptor load/call IC where the interceptor throws an |
| 2592 // exception once in a while. |
| 2593 THREADED_TEST(InterceptorICGetterExceptions) { |
| 2594 interceptor_ic_exception_get_count = 0; |
| 2595 v8::Isolate* isolate = CcTest::isolate(); |
| 2596 v8::HandleScope scope(isolate); |
| 2597 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2598 templ->SetHandler( |
| 2599 v8::NamedPropertyHandlerConfiguration(InterceptorICExceptionGetter)); |
| 2600 LocalContext context(0, templ, v8::Handle<Value>()); |
| 2601 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); |
| 2602 v8::Handle<Value> value = CompileRun( |
| 2603 "function f() {" |
| 2604 " for (var i = 0; i < 100; i++) {" |
| 2605 " try { x; } catch(e) { return true; }" |
| 2606 " }" |
| 2607 " return false;" |
| 2608 "};" |
| 2609 "f();"); |
| 2610 CHECK_EQ(true, value->BooleanValue()); |
| 2611 interceptor_ic_exception_get_count = 0; |
| 2612 value = CompileRun( |
| 2613 "function f() {" |
| 2614 " for (var i = 0; i < 100; i++) {" |
| 2615 " try { x(42); } catch(e) { return true; }" |
| 2616 " }" |
| 2617 " return false;" |
| 2618 "};" |
| 2619 "f();"); |
| 2620 CHECK_EQ(true, value->BooleanValue()); |
| 2621 } |
| 2622 |
| 2623 |
| 2624 static int interceptor_ic_exception_set_count = 0; |
| 2625 |
| 2626 static void InterceptorICExceptionSetter( |
| 2627 Local<Name> key, Local<Value> value, |
| 2628 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2629 ApiTestFuzzer::Fuzz(); |
| 2630 if (++interceptor_ic_exception_set_count > 20) { |
| 2631 info.GetIsolate()->ThrowException(v8_num(42)); |
| 2632 } |
| 2633 } |
| 2634 |
| 2635 |
| 2636 // Test interceptor store IC where the interceptor throws an exception |
| 2637 // once in a while. |
| 2638 THREADED_TEST(InterceptorICSetterExceptions) { |
| 2639 interceptor_ic_exception_set_count = 0; |
| 2640 v8::Isolate* isolate = CcTest::isolate(); |
| 2641 v8::HandleScope scope(isolate); |
| 2642 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2643 templ->SetHandler( |
| 2644 v8::NamedPropertyHandlerConfiguration(0, InterceptorICExceptionSetter)); |
| 2645 LocalContext context(0, templ, v8::Handle<Value>()); |
| 2646 v8::Handle<Value> value = CompileRun( |
| 2647 "function f() {" |
| 2648 " for (var i = 0; i < 100; i++) {" |
| 2649 " try { x = 42; } catch(e) { return true; }" |
| 2650 " }" |
| 2651 " return false;" |
| 2652 "};" |
| 2653 "f();"); |
| 2654 CHECK_EQ(true, value->BooleanValue()); |
| 2655 } |
| 2656 |
| 2657 |
| 2658 // Test that we ignore null interceptors. |
| 2659 THREADED_TEST(NullNamedInterceptor) { |
| 2660 v8::Isolate* isolate = CcTest::isolate(); |
| 2661 v8::HandleScope scope(isolate); |
| 2662 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2663 templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 2664 static_cast<v8::GenericNamedPropertyGetterCallback>(0))); |
| 2665 LocalContext context; |
| 2666 templ->Set(CcTest::isolate(), "x", v8_num(42)); |
| 2667 v8::Handle<v8::Object> obj = templ->NewInstance(); |
| 2668 context->Global()->Set(v8_str("obj"), obj); |
| 2669 v8::Handle<Value> value = CompileRun("obj.x"); |
| 2670 CHECK(value->IsInt32()); |
| 2671 CHECK_EQ(42, value->Int32Value()); |
| 2672 } |
| 2673 |
| 2674 |
| 2675 // Test that we ignore null interceptors. |
| 2676 THREADED_TEST(NullIndexedInterceptor) { |
| 2677 v8::Isolate* isolate = CcTest::isolate(); |
| 2678 v8::HandleScope scope(isolate); |
| 2679 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 2680 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 2681 static_cast<v8::IndexedPropertyGetterCallback>(0))); |
| 2682 LocalContext context; |
| 2683 templ->Set(CcTest::isolate(), "42", v8_num(42)); |
| 2684 v8::Handle<v8::Object> obj = templ->NewInstance(); |
| 2685 context->Global()->Set(v8_str("obj"), obj); |
| 2686 v8::Handle<Value> value = CompileRun("obj[42]"); |
| 2687 CHECK(value->IsInt32()); |
| 2688 CHECK_EQ(42, value->Int32Value()); |
| 2689 } |
| 2690 |
| 2691 |
| 2692 THREADED_TEST(NamedPropertyHandlerGetterAttributes) { |
| 2693 v8::Isolate* isolate = CcTest::isolate(); |
| 2694 v8::HandleScope scope(isolate); |
| 2695 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 2696 templ->InstanceTemplate()->SetHandler( |
| 2697 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter)); |
| 2698 LocalContext env; |
| 2699 env->Global()->Set(v8_str("obj"), templ->GetFunction()->NewInstance()); |
| 2700 ExpectTrue("obj.x === 42"); |
| 2701 ExpectTrue("!obj.propertyIsEnumerable('x')"); |
| 2702 } |
| 2703 |
| 2704 |
| 2705 THREADED_TEST(Regress256330) { |
| 2706 i::FLAG_allow_natives_syntax = true; |
| 2707 LocalContext context; |
| 2708 v8::HandleScope scope(context->GetIsolate()); |
| 2709 Handle<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); |
| 2710 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); |
| 2711 context->Global()->Set(v8_str("Bug"), templ->GetFunction()); |
| 2712 CompileRun( |
| 2713 "\"use strict\"; var o = new Bug;" |
| 2714 "function f(o) { o.x = 10; };" |
| 2715 "f(o); f(o); f(o);" |
| 2716 "%OptimizeFunctionOnNextCall(f);" |
| 2717 "f(o);"); |
| 2718 ExpectBoolean("%GetOptimizationStatus(f) != 2", true); |
| 2719 } |
| 2720 |
| 2721 |
| 2722 THREADED_TEST(CrankshaftInterceptorSetter) { |
| 2723 i::FLAG_allow_natives_syntax = true; |
| 2724 v8::HandleScope scope(CcTest::isolate()); |
| 2725 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); |
| 2726 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); |
| 2727 LocalContext env; |
| 2728 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); |
| 2729 CompileRun( |
| 2730 "var obj = new Obj;" |
| 2731 // Initialize fields to avoid transitions later. |
| 2732 "obj.age = 0;" |
| 2733 "obj.accessor_age = 42;" |
| 2734 "function setter(i) { this.accessor_age = i; };" |
| 2735 "function getter() { return this.accessor_age; };" |
| 2736 "function setAge(i) { obj.age = i; };" |
| 2737 "Object.defineProperty(obj, 'age', { get:getter, set:setter });" |
| 2738 "setAge(1);" |
| 2739 "setAge(2);" |
| 2740 "setAge(3);" |
| 2741 "%OptimizeFunctionOnNextCall(setAge);" |
| 2742 "setAge(4);"); |
| 2743 // All stores went through the interceptor. |
| 2744 ExpectInt32("obj.interceptor_age", 4); |
| 2745 ExpectInt32("obj.accessor_age", 42); |
| 2746 } |
| 2747 |
| 2748 |
| 2749 THREADED_TEST(CrankshaftInterceptorGetter) { |
| 2750 i::FLAG_allow_natives_syntax = true; |
| 2751 v8::HandleScope scope(CcTest::isolate()); |
| 2752 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); |
| 2753 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); |
| 2754 LocalContext env; |
| 2755 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); |
| 2756 CompileRun( |
| 2757 "var obj = new Obj;" |
| 2758 // Initialize fields to avoid transitions later. |
| 2759 "obj.age = 1;" |
| 2760 "obj.accessor_age = 42;" |
| 2761 "function getter() { return this.accessor_age; };" |
| 2762 "function getAge() { return obj.interceptor_age; };" |
| 2763 "Object.defineProperty(obj, 'interceptor_age', { get:getter });" |
| 2764 "getAge();" |
| 2765 "getAge();" |
| 2766 "getAge();" |
| 2767 "%OptimizeFunctionOnNextCall(getAge);"); |
| 2768 // Access through interceptor. |
| 2769 ExpectInt32("getAge()", 1); |
| 2770 } |
| 2771 |
| 2772 |
| 2773 THREADED_TEST(CrankshaftInterceptorFieldRead) { |
| 2774 i::FLAG_allow_natives_syntax = true; |
| 2775 v8::HandleScope scope(CcTest::isolate()); |
| 2776 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); |
| 2777 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); |
| 2778 LocalContext env; |
| 2779 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); |
| 2780 CompileRun( |
| 2781 "var obj = new Obj;" |
| 2782 "obj.__proto__.interceptor_age = 42;" |
| 2783 "obj.age = 100;" |
| 2784 "function getAge() { return obj.interceptor_age; };"); |
| 2785 ExpectInt32("getAge();", 100); |
| 2786 ExpectInt32("getAge();", 100); |
| 2787 ExpectInt32("getAge();", 100); |
| 2788 CompileRun("%OptimizeFunctionOnNextCall(getAge);"); |
| 2789 // Access through interceptor. |
| 2790 ExpectInt32("getAge();", 100); |
| 2791 } |
| 2792 |
| 2793 |
| 2794 THREADED_TEST(CrankshaftInterceptorFieldWrite) { |
| 2795 i::FLAG_allow_natives_syntax = true; |
| 2796 v8::HandleScope scope(CcTest::isolate()); |
| 2797 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); |
| 2798 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); |
| 2799 LocalContext env; |
| 2800 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); |
| 2801 CompileRun( |
| 2802 "var obj = new Obj;" |
| 2803 "obj.age = 100000;" |
| 2804 "function setAge(i) { obj.age = i };" |
| 2805 "setAge(100);" |
| 2806 "setAge(101);" |
| 2807 "setAge(102);" |
| 2808 "%OptimizeFunctionOnNextCall(setAge);" |
| 2809 "setAge(103);"); |
| 2810 ExpectInt32("obj.age", 100000); |
| 2811 ExpectInt32("obj.interceptor_age", 103); |
| 2812 } |
| 2813 |
| 2814 |
| 2815 THREADED_TEST(Regress149912) { |
| 2816 LocalContext context; |
| 2817 v8::HandleScope scope(context->GetIsolate()); |
| 2818 Handle<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); |
| 2819 AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter); |
| 2820 context->Global()->Set(v8_str("Bug"), templ->GetFunction()); |
| 2821 CompileRun("Number.prototype.__proto__ = new Bug; var x = 0; x.foo();"); |
| 2822 } |
| 2823 |
| 2824 |
| 2825 THREADED_TEST(Regress125988) { |
| 2826 v8::HandleScope scope(CcTest::isolate()); |
| 2827 Handle<FunctionTemplate> intercept = FunctionTemplate::New(CcTest::isolate()); |
| 2828 AddInterceptor(intercept, EmptyInterceptorGetter, EmptyInterceptorSetter); |
| 2829 LocalContext env; |
| 2830 env->Global()->Set(v8_str("Intercept"), intercept->GetFunction()); |
| 2831 CompileRun( |
| 2832 "var a = new Object();" |
| 2833 "var b = new Intercept();" |
| 2834 "var c = new Object();" |
| 2835 "c.__proto__ = b;" |
| 2836 "b.__proto__ = a;" |
| 2837 "a.x = 23;" |
| 2838 "for (var i = 0; i < 3; i++) c.x;"); |
| 2839 ExpectBoolean("c.hasOwnProperty('x')", false); |
| 2840 ExpectInt32("c.x", 23); |
| 2841 CompileRun( |
| 2842 "a.y = 42;" |
| 2843 "for (var i = 0; i < 3; i++) c.x;"); |
| 2844 ExpectBoolean("c.hasOwnProperty('x')", false); |
| 2845 ExpectInt32("c.x", 23); |
| 2846 ExpectBoolean("c.hasOwnProperty('y')", false); |
| 2847 ExpectInt32("c.y", 42); |
| 2848 } |
| 2849 |
| 2850 |
| 2851 static void IndexedPropertyEnumerator( |
| 2852 const v8::PropertyCallbackInfo<v8::Array>& info) { |
| 2853 v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 1); |
| 2854 result->Set(0, v8::Integer::New(info.GetIsolate(), 7)); |
| 2855 info.GetReturnValue().Set(result); |
| 2856 } |
| 2857 |
| 2858 |
| 2859 static void NamedPropertyEnumerator( |
| 2860 const v8::PropertyCallbackInfo<v8::Array>& info) { |
| 2861 v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 2); |
| 2862 result->Set(0, v8_str("x")); |
| 2863 result->Set(1, v8::Symbol::GetIterator(info.GetIsolate())); |
| 2864 info.GetReturnValue().Set(result); |
| 2865 } |
| 2866 |
| 2867 |
| 2868 THREADED_TEST(GetOwnPropertyNamesWithInterceptor) { |
| 2869 v8::Isolate* isolate = CcTest::isolate(); |
| 2870 v8::HandleScope handle_scope(isolate); |
| 2871 v8::Handle<v8::ObjectTemplate> obj_template = |
| 2872 v8::ObjectTemplate::New(isolate); |
| 2873 |
| 2874 obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7)); |
| 2875 obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42)); |
| 2876 obj_template->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 2877 NULL, NULL, NULL, NULL, IndexedPropertyEnumerator)); |
| 2878 obj_template->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 2879 NULL, NULL, NULL, NULL, NamedPropertyEnumerator)); |
| 2880 |
| 2881 LocalContext context; |
| 2882 v8::Handle<v8::Object> global = context->Global(); |
| 2883 global->Set(v8_str("object"), obj_template->NewInstance()); |
| 2884 |
| 2885 v8::Handle<v8::Value> result = |
| 2886 CompileRun("Object.getOwnPropertyNames(object)"); |
| 2887 CHECK(result->IsArray()); |
| 2888 v8::Handle<v8::Array> result_array = v8::Handle<v8::Array>::Cast(result); |
| 2889 CHECK_EQ(2u, result_array->Length()); |
| 2890 CHECK(result_array->Get(0)->IsString()); |
| 2891 CHECK(result_array->Get(1)->IsString()); |
| 2892 CHECK(v8_str("7")->Equals(result_array->Get(0))); |
| 2893 CHECK(v8_str("x")->Equals(result_array->Get(1))); |
| 2894 |
| 2895 result = CompileRun("var ret = []; for (var k in object) ret.push(k); ret"); |
| 2896 CHECK(result->IsArray()); |
| 2897 result_array = v8::Handle<v8::Array>::Cast(result); |
| 2898 CHECK_EQ(2u, result_array->Length()); |
| 2899 CHECK(result_array->Get(0)->IsString()); |
| 2900 CHECK(result_array->Get(1)->IsString()); |
| 2901 CHECK(v8_str("7")->Equals(result_array->Get(0))); |
| 2902 CHECK(v8_str("x")->Equals(result_array->Get(1))); |
| 2903 |
| 2904 result = CompileRun("Object.getOwnPropertySymbols(object)"); |
| 2905 CHECK(result->IsArray()); |
| 2906 result_array = v8::Handle<v8::Array>::Cast(result); |
| 2907 CHECK_EQ(1u, result_array->Length()); |
| 2908 CHECK(result_array->Get(0)->Equals(v8::Symbol::GetIterator(isolate))); |
| 2909 } |
OLD | NEW |