blob: 9f5eb2195471e6fcb4ada6ae4f7511789e8a7787 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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/parsing/parser.h"
17#include "src/unicode-inl.h"
18#include "src/utils.h"
19#include "src/vm-state.h"
20
21using ::v8::Boolean;
22using ::v8::BooleanObject;
23using ::v8::Context;
24using ::v8::Extension;
25using ::v8::Function;
26using ::v8::FunctionTemplate;
27using ::v8::HandleScope;
28using ::v8::Local;
29using ::v8::Name;
30using ::v8::Message;
31using ::v8::MessageCallback;
32using ::v8::Object;
33using ::v8::ObjectTemplate;
34using ::v8::Persistent;
35using ::v8::Script;
36using ::v8::StackTrace;
37using ::v8::String;
38using ::v8::Symbol;
39using ::v8::TryCatch;
40using ::v8::Undefined;
41using ::v8::UniqueId;
42using ::v8::V8;
43using ::v8::Value;
44
45
46namespace {
47
48void Returns42(const v8::FunctionCallbackInfo<v8::Value>& info) {
49 info.GetReturnValue().Set(42);
50}
51
52void Return239Callback(Local<String> name,
53 const v8::PropertyCallbackInfo<Value>& info) {
54 ApiTestFuzzer::Fuzz();
55 CheckReturnValue(info, FUNCTION_ADDR(Return239Callback));
56 info.GetReturnValue().Set(v8_str("bad value"));
57 info.GetReturnValue().Set(v8_num(239));
58}
59
60
61void EmptyInterceptorGetter(Local<Name> name,
62 const v8::PropertyCallbackInfo<v8::Value>& info) {}
63
64
65void EmptyInterceptorSetter(Local<Name> name, Local<Value> value,
66 const v8::PropertyCallbackInfo<v8::Value>& info) {}
67
68
69void SimpleAccessorGetter(Local<String> name,
70 const v8::PropertyCallbackInfo<v8::Value>& info) {
71 Local<Object> self = Local<Object>::Cast(info.This());
72 info.GetReturnValue().Set(self->Get(info.GetIsolate()->GetCurrentContext(),
73 String::Concat(v8_str("accessor_"), name))
74 .ToLocalChecked());
75}
76
77void SimpleAccessorSetter(Local<String> name, Local<Value> value,
78 const v8::PropertyCallbackInfo<void>& info) {
79 Local<Object> self = Local<Object>::Cast(info.This());
80 self->Set(info.GetIsolate()->GetCurrentContext(),
81 String::Concat(v8_str("accessor_"), name), value)
82 .FromJust();
83}
84
85
86void 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
94void 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
102void 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 Local<Object> self = Local<Object>::Cast(info.This());
114 info.GetReturnValue().Set(
115 self->GetPrivate(
116 info.GetIsolate()->GetCurrentContext(),
117 v8::Private::ForApi(info.GetIsolate(), v8_str(name_str + i)))
118 .ToLocalChecked());
119}
120
121
122void StringInterceptorSetter(Local<String> name, Local<Value> value,
123 const v8::PropertyCallbackInfo<v8::Value>& info) {
124 // Intercept accesses that set certain integer values, for which the name does
125 // not start with 'accessor_'.
126 String::Utf8Value utf8(name);
127 char* name_str = *utf8;
128 char prefix[] = "accessor_";
129 int i;
130 for (i = 0; name_str[i] && prefix[i]; ++i) {
131 if (name_str[i] != prefix[i]) break;
132 }
133 if (!prefix[i]) return;
134
135 Local<Context> context = info.GetIsolate()->GetCurrentContext();
136 if (value->IsInt32() && value->Int32Value(context).FromJust() < 10000) {
137 Local<Object> self = Local<Object>::Cast(info.This());
138 Local<v8::Private> symbol = v8::Private::ForApi(info.GetIsolate(), name);
139 self->SetPrivate(context, symbol, value).FromJust();
140 info.GetReturnValue().Set(value);
141 }
142}
143
144void InterceptorGetter(Local<Name> generic_name,
145 const v8::PropertyCallbackInfo<v8::Value>& info) {
146 if (generic_name->IsSymbol()) return;
147 StringInterceptorGetter(Local<String>::Cast(generic_name), info);
148}
149
150void InterceptorSetter(Local<Name> generic_name, Local<Value> value,
151 const v8::PropertyCallbackInfo<v8::Value>& info) {
152 if (generic_name->IsSymbol()) return;
153 StringInterceptorSetter(Local<String>::Cast(generic_name), value, info);
154}
155
156void GenericInterceptorGetter(Local<Name> generic_name,
157 const v8::PropertyCallbackInfo<v8::Value>& info) {
158 Local<String> str;
159 if (generic_name->IsSymbol()) {
160 Local<Value> name = Local<Symbol>::Cast(generic_name)->Name();
161 if (name->IsUndefined()) return;
162 str = String::Concat(v8_str("_sym_"), Local<String>::Cast(name));
163 } else {
164 Local<String> name = Local<String>::Cast(generic_name);
165 String::Utf8Value utf8(name);
166 char* name_str = *utf8;
167 if (*name_str == '_') return;
168 str = String::Concat(v8_str("_str_"), name);
169 }
170
171 Local<Object> self = Local<Object>::Cast(info.This());
172 info.GetReturnValue().Set(
173 self->Get(info.GetIsolate()->GetCurrentContext(), str).ToLocalChecked());
174}
175
176void GenericInterceptorSetter(Local<Name> generic_name, Local<Value> value,
177 const v8::PropertyCallbackInfo<v8::Value>& info) {
178 Local<String> str;
179 if (generic_name->IsSymbol()) {
180 Local<Value> name = Local<Symbol>::Cast(generic_name)->Name();
181 if (name->IsUndefined()) return;
182 str = String::Concat(v8_str("_sym_"), Local<String>::Cast(name));
183 } else {
184 Local<String> name = Local<String>::Cast(generic_name);
185 String::Utf8Value utf8(name);
186 char* name_str = *utf8;
187 if (*name_str == '_') return;
188 str = String::Concat(v8_str("_str_"), name);
189 }
190
191 Local<Object> self = Local<Object>::Cast(info.This());
192 self->Set(info.GetIsolate()->GetCurrentContext(), str, value).FromJust();
193 info.GetReturnValue().Set(value);
194}
195
196void AddAccessor(Local<FunctionTemplate> templ, Local<String> name,
197 v8::AccessorGetterCallback getter,
198 v8::AccessorSetterCallback setter) {
199 templ->PrototypeTemplate()->SetAccessor(name, getter, setter);
200}
201
202void AddInterceptor(Local<FunctionTemplate> templ,
203 v8::NamedPropertyGetterCallback getter,
204 v8::NamedPropertySetterCallback setter) {
205 templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter);
206}
207
208
209void AddAccessor(Local<FunctionTemplate> templ, Local<Name> name,
210 v8::AccessorNameGetterCallback getter,
211 v8::AccessorNameSetterCallback setter) {
212 templ->PrototypeTemplate()->SetAccessor(name, getter, setter);
213}
214
215void AddInterceptor(Local<FunctionTemplate> templ,
216 v8::GenericNamedPropertyGetterCallback getter,
217 v8::GenericNamedPropertySetterCallback setter) {
218 templ->InstanceTemplate()->SetHandler(
219 v8::NamedPropertyHandlerConfiguration(getter, setter));
220}
221
222
223v8::Local<v8::Object> bottom;
224
225void CheckThisIndexedPropertyHandler(
226 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
227 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyHandler));
228 ApiTestFuzzer::Fuzz();
229 CHECK(info.This()
230 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
231 .FromJust());
232}
233
234void CheckThisNamedPropertyHandler(
235 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
236 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler));
237 ApiTestFuzzer::Fuzz();
238 CHECK(info.This()
239 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
240 .FromJust());
241}
242
243void CheckThisIndexedPropertySetter(
244 uint32_t index, Local<Value> value,
245 const v8::PropertyCallbackInfo<v8::Value>& info) {
246 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertySetter));
247 ApiTestFuzzer::Fuzz();
248 CHECK(info.This()
249 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
250 .FromJust());
251}
252
253
254void CheckThisNamedPropertySetter(
255 Local<Name> property, Local<Value> value,
256 const v8::PropertyCallbackInfo<v8::Value>& info) {
257 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter));
258 ApiTestFuzzer::Fuzz();
259 CHECK(info.This()
260 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
261 .FromJust());
262}
263
264void CheckThisIndexedPropertyQuery(
265 uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) {
266 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery));
267 ApiTestFuzzer::Fuzz();
268 CHECK(info.This()
269 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
270 .FromJust());
271}
272
273
274void CheckThisNamedPropertyQuery(
275 Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) {
276 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery));
277 ApiTestFuzzer::Fuzz();
278 CHECK(info.This()
279 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
280 .FromJust());
281}
282
283
284void CheckThisIndexedPropertyDeleter(
285 uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
286 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter));
287 ApiTestFuzzer::Fuzz();
288 CHECK(info.This()
289 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
290 .FromJust());
291}
292
293
294void CheckThisNamedPropertyDeleter(
295 Local<Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
296 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter));
297 ApiTestFuzzer::Fuzz();
298 CHECK(info.This()
299 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
300 .FromJust());
301}
302
303
304void CheckThisIndexedPropertyEnumerator(
305 const v8::PropertyCallbackInfo<v8::Array>& info) {
306 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator));
307 ApiTestFuzzer::Fuzz();
308 CHECK(info.This()
309 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
310 .FromJust());
311}
312
313
314void CheckThisNamedPropertyEnumerator(
315 const v8::PropertyCallbackInfo<v8::Array>& info) {
316 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator));
317 ApiTestFuzzer::Fuzz();
318 CHECK(info.This()
319 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
320 .FromJust());
321}
322
323
324int echo_named_call_count;
325
326
327void EchoNamedProperty(Local<Name> name,
328 const v8::PropertyCallbackInfo<v8::Value>& info) {
329 ApiTestFuzzer::Fuzz();
330 CHECK(v8_str("data")
331 ->Equals(info.GetIsolate()->GetCurrentContext(), info.Data())
332 .FromJust());
333 echo_named_call_count++;
334 info.GetReturnValue().Set(name);
335}
336
337void InterceptorHasOwnPropertyGetter(
338 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
339 ApiTestFuzzer::Fuzz();
340}
341
342void InterceptorHasOwnPropertyGetterGC(
343 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
344 ApiTestFuzzer::Fuzz();
345 CcTest::heap()->CollectAllGarbage();
346}
347
348} // namespace
349
350
351THREADED_TEST(InterceptorHasOwnProperty) {
352 LocalContext context;
353 v8::Isolate* isolate = context->GetIsolate();
354 v8::HandleScope scope(isolate);
355 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
356 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
357 instance_templ->SetHandler(
358 v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetter));
359 Local<Function> function =
360 fun_templ->GetFunction(context.local()).ToLocalChecked();
361 context->Global()
362 ->Set(context.local(), v8_str("constructor"), function)
363 .FromJust();
364 v8::Local<Value> value = CompileRun(
365 "var o = new constructor();"
366 "o.hasOwnProperty('ostehaps');");
367 CHECK_EQ(false, value->BooleanValue(context.local()).FromJust());
368 value = CompileRun(
369 "o.ostehaps = 42;"
370 "o.hasOwnProperty('ostehaps');");
371 CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
372 value = CompileRun(
373 "var p = new constructor();"
374 "p.hasOwnProperty('ostehaps');");
375 CHECK_EQ(false, value->BooleanValue(context.local()).FromJust());
376}
377
378
379THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
380 LocalContext context;
381 v8::Isolate* isolate = context->GetIsolate();
382 v8::HandleScope scope(isolate);
383 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
384 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
385 instance_templ->SetHandler(
386 v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetterGC));
387 Local<Function> function =
388 fun_templ->GetFunction(context.local()).ToLocalChecked();
389 context->Global()
390 ->Set(context.local(), v8_str("constructor"), function)
391 .FromJust();
392 // Let's first make some stuff so we can be sure to get a good GC.
393 CompileRun(
394 "function makestr(size) {"
395 " switch (size) {"
396 " case 1: return 'f';"
397 " case 2: return 'fo';"
398 " case 3: return 'foo';"
399 " }"
400 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
401 "}"
402 "var x = makestr(12345);"
403 "x = makestr(31415);"
404 "x = makestr(23456);");
405 v8::Local<Value> value = CompileRun(
406 "var o = new constructor();"
407 "o.__proto__ = new String(x);"
408 "o.hasOwnProperty('ostehaps');");
409 CHECK_EQ(false, value->BooleanValue(context.local()).FromJust());
410}
411
412
413static void CheckInterceptorLoadIC(
414 v8::GenericNamedPropertyGetterCallback getter, const char* source,
415 int expected) {
416 v8::Isolate* isolate = CcTest::isolate();
417 v8::HandleScope scope(isolate);
418 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
419 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(getter, 0, 0, 0, 0,
420 v8_str("data")));
421 LocalContext context;
422 context->Global()
423 ->Set(context.local(), v8_str("o"),
424 templ->NewInstance(context.local()).ToLocalChecked())
425 .FromJust();
426 v8::Local<Value> value = CompileRun(source);
427 CHECK_EQ(expected, value->Int32Value(context.local()).FromJust());
428}
429
430
431static void InterceptorLoadICGetter(
432 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
433 ApiTestFuzzer::Fuzz();
434 v8::Isolate* isolate = CcTest::isolate();
435 CHECK_EQ(isolate, info.GetIsolate());
436 v8::Local<v8::Context> context = isolate->GetCurrentContext();
437 CHECK(v8_str("data")->Equals(context, info.Data()).FromJust());
438 CHECK(v8_str("x")->Equals(context, name).FromJust());
439 info.GetReturnValue().Set(v8::Integer::New(isolate, 42));
440}
441
442
443// This test should hit the load IC for the interceptor case.
444THREADED_TEST(InterceptorLoadIC) {
445 CheckInterceptorLoadIC(InterceptorLoadICGetter,
446 "var result = 0;"
447 "for (var i = 0; i < 1000; i++) {"
448 " result = o.x;"
449 "}",
450 42);
451}
452
453
454// Below go several tests which verify that JITing for various
455// configurations of interceptor and explicit fields works fine
456// (those cases are special cased to get better performance).
457
458static void InterceptorLoadXICGetter(
459 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
460 ApiTestFuzzer::Fuzz();
461 info.GetReturnValue().Set(
462 v8_str("x")
463 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
464 .FromJust()
465 ? v8::Local<v8::Value>(v8::Integer::New(info.GetIsolate(), 42))
466 : v8::Local<v8::Value>());
467}
468
469
470THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
471 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
472 "var result = 0;"
473 "o.y = 239;"
474 "for (var i = 0; i < 1000; i++) {"
475 " result = o.y;"
476 "}",
477 239);
478}
479
480
481THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
482 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
483 "var result = 0;"
484 "o.__proto__ = { 'y': 239 };"
485 "for (var i = 0; i < 1000; i++) {"
486 " result = o.y + o.x;"
487 "}",
488 239 + 42);
489}
490
491
492THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
493 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
494 "var result = 0;"
495 "o.__proto__.y = 239;"
496 "for (var i = 0; i < 1000; i++) {"
497 " result = o.y + o.x;"
498 "}",
499 239 + 42);
500}
501
502
503THREADED_TEST(InterceptorLoadICUndefined) {
504 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
505 "var result = 0;"
506 "for (var i = 0; i < 1000; i++) {"
507 " result = (o.y == undefined) ? 239 : 42;"
508 "}",
509 239);
510}
511
512
513THREADED_TEST(InterceptorLoadICWithOverride) {
514 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
515 "fst = new Object(); fst.__proto__ = o;"
516 "snd = new Object(); snd.__proto__ = fst;"
517 "var result1 = 0;"
518 "for (var i = 0; i < 1000; i++) {"
519 " result1 = snd.x;"
520 "}"
521 "fst.x = 239;"
522 "var result = 0;"
523 "for (var i = 0; i < 1000; i++) {"
524 " result = snd.x;"
525 "}"
526 "result + result1",
527 239 + 42);
528}
529
530
531// Test the case when we stored field into
532// a stub, but interceptor produced value on its own.
533THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
534 CheckInterceptorLoadIC(
535 InterceptorLoadXICGetter,
536 "proto = new Object();"
537 "o.__proto__ = proto;"
538 "proto.x = 239;"
539 "for (var i = 0; i < 1000; i++) {"
540 " o.x;"
541 // Now it should be ICed and keep a reference to x defined on proto
542 "}"
543 "var result = 0;"
544 "for (var i = 0; i < 1000; i++) {"
545 " result += o.x;"
546 "}"
547 "result;",
548 42 * 1000);
549}
550
551
552// Test the case when we stored field into
553// a stub, but it got invalidated later on.
554THREADED_TEST(InterceptorLoadICInvalidatedField) {
555 CheckInterceptorLoadIC(
556 InterceptorLoadXICGetter,
557 "proto1 = new Object();"
558 "proto2 = new Object();"
559 "o.__proto__ = proto1;"
560 "proto1.__proto__ = proto2;"
561 "proto2.y = 239;"
562 "for (var i = 0; i < 1000; i++) {"
563 " o.y;"
564 // Now it should be ICed and keep a reference to y defined on proto2
565 "}"
566 "proto1.y = 42;"
567 "var result = 0;"
568 "for (var i = 0; i < 1000; i++) {"
569 " result += o.y;"
570 "}"
571 "result;",
572 42 * 1000);
573}
574
575
576static int interceptor_load_not_handled_calls = 0;
577static void InterceptorLoadNotHandled(
578 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
579 ++interceptor_load_not_handled_calls;
580}
581
582
583// Test how post-interceptor lookups are done in the non-cacheable
584// case: the interceptor should not be invoked during this lookup.
585THREADED_TEST(InterceptorLoadICPostInterceptor) {
586 interceptor_load_not_handled_calls = 0;
587 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
588 "receiver = new Object();"
589 "receiver.__proto__ = o;"
590 "proto = new Object();"
591 "/* Make proto a slow-case object. */"
592 "for (var i = 0; i < 1000; i++) {"
593 " proto[\"xxxxxxxx\" + i] = [];"
594 "}"
595 "proto.x = 17;"
596 "o.__proto__ = proto;"
597 "var result = 0;"
598 "for (var i = 0; i < 1000; i++) {"
599 " result += receiver.x;"
600 "}"
601 "result;",
602 17 * 1000);
603 CHECK_EQ(1000, interceptor_load_not_handled_calls);
604}
605
606
607// Test the case when we stored field into
608// a stub, but it got invalidated later on due to override on
609// global object which is between interceptor and fields' holders.
610THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
611 CheckInterceptorLoadIC(
612 InterceptorLoadXICGetter,
613 "o.__proto__ = this;" // set a global to be a proto of o.
614 "this.__proto__.y = 239;"
615 "for (var i = 0; i < 10; i++) {"
616 " if (o.y != 239) throw 'oops: ' + o.y;"
617 // Now it should be ICed and keep a reference to y defined on
618 // field_holder.
619 "}"
620 "this.y = 42;" // Assign on a global.
621 "var result = 0;"
622 "for (var i = 0; i < 10; i++) {"
623 " result += o.y;"
624 "}"
625 "result;",
626 42 * 10);
627}
628
629
630static void SetOnThis(Local<String> name, Local<Value> value,
631 const v8::PropertyCallbackInfo<void>& info) {
632 Local<Object>::Cast(info.This())
633 ->CreateDataProperty(info.GetIsolate()->GetCurrentContext(), name, value)
634 .FromJust();
635}
636
637
638THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
639 v8::Isolate* isolate = CcTest::isolate();
640 v8::HandleScope scope(isolate);
641 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
642 templ->SetHandler(
643 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
644 templ->SetAccessor(v8_str("y"), Return239Callback);
645 LocalContext context;
646 context->Global()
647 ->Set(context.local(), v8_str("o"),
648 templ->NewInstance(context.local()).ToLocalChecked())
649 .FromJust();
650
651 // Check the case when receiver and interceptor's holder
652 // are the same objects.
653 v8::Local<Value> value = CompileRun(
654 "var result = 0;"
655 "for (var i = 0; i < 7; i++) {"
656 " result = o.y;"
657 "}");
658 CHECK_EQ(239, value->Int32Value(context.local()).FromJust());
659
660 // Check the case when interceptor's holder is in proto chain
661 // of receiver.
662 value = CompileRun(
663 "r = { __proto__: o };"
664 "var result = 0;"
665 "for (var i = 0; i < 7; i++) {"
666 " result = r.y;"
667 "}");
668 CHECK_EQ(239, value->Int32Value(context.local()).FromJust());
669}
670
671
672THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
673 v8::Isolate* isolate = CcTest::isolate();
674 v8::HandleScope scope(isolate);
675 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
676 templ_o->SetHandler(
677 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
678 v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
679 templ_p->SetAccessor(v8_str("y"), Return239Callback);
680
681 LocalContext context;
682 context->Global()
683 ->Set(context.local(), v8_str("o"),
684 templ_o->NewInstance(context.local()).ToLocalChecked())
685 .FromJust();
686 context->Global()
687 ->Set(context.local(), v8_str("p"),
688 templ_p->NewInstance(context.local()).ToLocalChecked())
689 .FromJust();
690
691 // Check the case when receiver and interceptor's holder
692 // are the same objects.
693 v8::Local<Value> value = CompileRun(
694 "o.__proto__ = p;"
695 "var result = 0;"
696 "for (var i = 0; i < 7; i++) {"
697 " result = o.x + o.y;"
698 "}");
699 CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
700
701 // Check the case when interceptor's holder is in proto chain
702 // of receiver.
703 value = CompileRun(
704 "r = { __proto__: o };"
705 "var result = 0;"
706 "for (var i = 0; i < 7; i++) {"
707 " result = r.x + r.y;"
708 "}");
709 CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
710}
711
712
713THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
714 v8::Isolate* isolate = CcTest::isolate();
715 v8::HandleScope scope(isolate);
716 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
717 templ->SetHandler(
718 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
719 templ->SetAccessor(v8_str("y"), Return239Callback);
720
721 LocalContext context;
722 context->Global()
723 ->Set(context.local(), v8_str("o"),
724 templ->NewInstance(context.local()).ToLocalChecked())
725 .FromJust();
726
727 v8::Local<Value> value = CompileRun(
728 "fst = new Object(); fst.__proto__ = o;"
729 "snd = new Object(); snd.__proto__ = fst;"
730 "var result1 = 0;"
731 "for (var i = 0; i < 7; i++) {"
732 " result1 = snd.x;"
733 "}"
734 "fst.x = 239;"
735 "var result = 0;"
736 "for (var i = 0; i < 7; i++) {"
737 " result = snd.x;"
738 "}"
739 "result + result1");
740 CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
741}
742
743
744// Test the case when we stored callback into
745// a stub, but interceptor produced value on its own.
746THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
747 v8::Isolate* isolate = CcTest::isolate();
748 v8::HandleScope scope(isolate);
749 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
750 templ_o->SetHandler(
751 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
752 v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
753 templ_p->SetAccessor(v8_str("y"), Return239Callback);
754
755 LocalContext context;
756 context->Global()
757 ->Set(context.local(), v8_str("o"),
758 templ_o->NewInstance(context.local()).ToLocalChecked())
759 .FromJust();
760 context->Global()
761 ->Set(context.local(), v8_str("p"),
762 templ_p->NewInstance(context.local()).ToLocalChecked())
763 .FromJust();
764
765 v8::Local<Value> value = CompileRun(
766 "o.__proto__ = p;"
767 "for (var i = 0; i < 7; i++) {"
768 " o.x;"
769 // Now it should be ICed and keep a reference to x defined on p
770 "}"
771 "var result = 0;"
772 "for (var i = 0; i < 7; i++) {"
773 " result += o.x;"
774 "}"
775 "result");
776 CHECK_EQ(42 * 7, value->Int32Value(context.local()).FromJust());
777}
778
779
780// Test the case when we stored callback into
781// a stub, but it got invalidated later on.
782THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
783 v8::Isolate* isolate = CcTest::isolate();
784 v8::HandleScope scope(isolate);
785 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
786 templ_o->SetHandler(
787 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
788 v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
789 templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis);
790
791 LocalContext context;
792 context->Global()
793 ->Set(context.local(), v8_str("o"),
794 templ_o->NewInstance(context.local()).ToLocalChecked())
795 .FromJust();
796 context->Global()
797 ->Set(context.local(), v8_str("p"),
798 templ_p->NewInstance(context.local()).ToLocalChecked())
799 .FromJust();
800
801 v8::Local<Value> value = CompileRun(
802 "inbetween = new Object();"
803 "o.__proto__ = inbetween;"
804 "inbetween.__proto__ = p;"
805 "for (var i = 0; i < 10; i++) {"
806 " o.y;"
807 // Now it should be ICed and keep a reference to y defined on p
808 "}"
809 "inbetween.y = 42;"
810 "var result = 0;"
811 "for (var i = 0; i < 10; i++) {"
812 " result += o.y;"
813 "}"
814 "result");
815 CHECK_EQ(42 * 10, value->Int32Value(context.local()).FromJust());
816}
817
818
819// Test the case when we stored callback into
820// a stub, but it got invalidated later on due to override on
821// global object which is between interceptor and callbacks' holders.
822THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
823 v8::Isolate* isolate = CcTest::isolate();
824 v8::HandleScope scope(isolate);
825 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
826 templ_o->SetHandler(
827 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
828 v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
829 templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis);
830
831 LocalContext context;
832 context->Global()
833 ->Set(context.local(), v8_str("o"),
834 templ_o->NewInstance(context.local()).ToLocalChecked())
835 .FromJust();
836 context->Global()
837 ->Set(context.local(), v8_str("p"),
838 templ_p->NewInstance(context.local()).ToLocalChecked())
839 .FromJust();
840
841 v8::Local<Value> value = CompileRun(
842 "o.__proto__ = this;"
843 "this.__proto__ = p;"
844 "for (var i = 0; i < 10; i++) {"
845 " if (o.y != 239) throw 'oops: ' + o.y;"
846 // Now it should be ICed and keep a reference to y defined on p
847 "}"
848 "this.y = 42;"
849 "var result = 0;"
850 "for (var i = 0; i < 10; i++) {"
851 " result += o.y;"
852 "}"
853 "result");
854 CHECK_EQ(42 * 10, value->Int32Value(context.local()).FromJust());
855}
856
857
858static void InterceptorLoadICGetter0(
859 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
860 ApiTestFuzzer::Fuzz();
861 CHECK(v8_str("x")
862 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
863 .FromJust());
864 info.GetReturnValue().Set(v8::Integer::New(info.GetIsolate(), 0));
865}
866
867
868THREADED_TEST(InterceptorReturningZero) {
869 CheckInterceptorLoadIC(InterceptorLoadICGetter0, "o.x == undefined ? 1 : 0",
870 0);
871}
872
873
874static void InterceptorStoreICSetter(
875 Local<Name> key, Local<Value> value,
876 const v8::PropertyCallbackInfo<v8::Value>& info) {
877 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
878 CHECK(v8_str("x")->Equals(context, key).FromJust());
879 CHECK_EQ(42, value->Int32Value(context).FromJust());
880 info.GetReturnValue().Set(value);
881}
882
883
884// This test should hit the store IC for the interceptor case.
885THREADED_TEST(InterceptorStoreIC) {
886 v8::Isolate* isolate = CcTest::isolate();
887 v8::HandleScope scope(isolate);
888 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
889 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
890 InterceptorLoadICGetter, InterceptorStoreICSetter, 0, 0, 0,
891 v8_str("data")));
892 LocalContext context;
893 context->Global()
894 ->Set(context.local(), v8_str("o"),
895 templ->NewInstance(context.local()).ToLocalChecked())
896 .FromJust();
897 CompileRun(
898 "for (var i = 0; i < 1000; i++) {"
899 " o.x = 42;"
900 "}");
901}
902
903
904THREADED_TEST(InterceptorStoreICWithNoSetter) {
905 v8::Isolate* isolate = CcTest::isolate();
906 v8::HandleScope scope(isolate);
907 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
908 templ->SetHandler(
909 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
910 LocalContext context;
911 context->Global()
912 ->Set(context.local(), v8_str("o"),
913 templ->NewInstance(context.local()).ToLocalChecked())
914 .FromJust();
915 v8::Local<Value> value = CompileRun(
916 "for (var i = 0; i < 1000; i++) {"
917 " o.y = 239;"
918 "}"
919 "42 + o.y");
920 CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
921}
922
923
924THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) {
925 v8::HandleScope scope(CcTest::isolate());
926 Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
927 Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
928 child->Inherit(parent);
929 AddAccessor(parent, v8_str("age"), SimpleAccessorGetter,
930 SimpleAccessorSetter);
931 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
932 LocalContext env;
933 env->Global()
934 ->Set(env.local(), v8_str("Child"),
935 child->GetFunction(env.local()).ToLocalChecked())
936 .FromJust();
937 CompileRun(
938 "var child = new Child;"
939 "child.age = 10;");
940 ExpectBoolean("child.hasOwnProperty('age')", false);
941 ExpectInt32("child.age", 10);
942 ExpectInt32("child.accessor_age", 10);
943}
944
945
946THREADED_TEST(LegacyInterceptorDoesNotSeeSymbols) {
947 LocalContext env;
948 v8::Isolate* isolate = CcTest::isolate();
949 v8::HandleScope scope(isolate);
950 Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
951 Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
952 v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age"));
953
954 child->Inherit(parent);
955 AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter);
956 AddInterceptor(child, StringInterceptorGetter, StringInterceptorSetter);
957
958 env->Global()
959 ->Set(env.local(), v8_str("Child"),
960 child->GetFunction(env.local()).ToLocalChecked())
961 .FromJust();
962 env->Global()->Set(env.local(), v8_str("age"), age).FromJust();
963 CompileRun(
964 "var child = new Child;"
965 "child[age] = 10;");
966 ExpectInt32("child[age]", 10);
967 ExpectBoolean("child.hasOwnProperty('age')", false);
968 ExpectBoolean("child.hasOwnProperty('accessor_age')", true);
969}
970
971
972THREADED_TEST(GenericInterceptorDoesSeeSymbols) {
973 LocalContext env;
974 v8::Isolate* isolate = CcTest::isolate();
975 v8::HandleScope scope(isolate);
976 Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
977 Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
978 v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age"));
979 v8::Local<v8::Symbol> anon = v8::Symbol::New(isolate);
980
981 child->Inherit(parent);
982 AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter);
983 AddInterceptor(child, GenericInterceptorGetter, GenericInterceptorSetter);
984
985 env->Global()
986 ->Set(env.local(), v8_str("Child"),
987 child->GetFunction(env.local()).ToLocalChecked())
988 .FromJust();
989 env->Global()->Set(env.local(), v8_str("age"), age).FromJust();
990 env->Global()->Set(env.local(), v8_str("anon"), anon).FromJust();
991 CompileRun(
992 "var child = new Child;"
993 "child[age] = 10;");
994 ExpectInt32("child[age]", 10);
995 ExpectInt32("child._sym_age", 10);
996
997 // Check that it also sees strings.
998 CompileRun("child.foo = 47");
999 ExpectInt32("child.foo", 47);
1000 ExpectInt32("child._str_foo", 47);
1001
1002 // Check that the interceptor can punt (in this case, on anonymous symbols).
1003 CompileRun("child[anon] = 31337");
1004 ExpectInt32("child[anon]", 31337);
1005}
1006
1007
1008THREADED_TEST(NamedPropertyHandlerGetter) {
1009 echo_named_call_count = 0;
1010 v8::HandleScope scope(CcTest::isolate());
1011 v8::Local<v8::FunctionTemplate> templ =
1012 v8::FunctionTemplate::New(CcTest::isolate());
1013 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
1014 EchoNamedProperty, 0, 0, 0, 0, v8_str("data")));
1015 LocalContext env;
1016 env->Global()
1017 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
1018 .ToLocalChecked()
1019 ->NewInstance(env.local())
1020 .ToLocalChecked())
1021 .FromJust();
1022 CHECK_EQ(echo_named_call_count, 0);
1023 v8_compile("obj.x")->Run(env.local()).ToLocalChecked();
1024 CHECK_EQ(echo_named_call_count, 1);
1025 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1026 v8::Local<Value> str = CompileRun(code);
1027 String::Utf8Value value(str);
1028 CHECK_EQ(0, strcmp(*value, "oddlepoddle"));
1029 // Check default behavior
1030 CHECK_EQ(10, v8_compile("obj.flob = 10;")
1031 ->Run(env.local())
1032 .ToLocalChecked()
1033 ->Int32Value(env.local())
1034 .FromJust());
1035 CHECK(v8_compile("'myProperty' in obj")
1036 ->Run(env.local())
1037 .ToLocalChecked()
1038 ->BooleanValue(env.local())
1039 .FromJust());
1040 CHECK(v8_compile("delete obj.myProperty")
1041 ->Run(env.local())
1042 .ToLocalChecked()
1043 ->BooleanValue(env.local())
1044 .FromJust());
1045}
1046
1047
1048int echo_indexed_call_count = 0;
1049
1050
1051static void EchoIndexedProperty(
1052 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
1053 ApiTestFuzzer::Fuzz();
1054 CHECK(v8_num(637)
1055 ->Equals(info.GetIsolate()->GetCurrentContext(), info.Data())
1056 .FromJust());
1057 echo_indexed_call_count++;
1058 info.GetReturnValue().Set(v8_num(index));
1059}
1060
1061
1062THREADED_TEST(IndexedPropertyHandlerGetter) {
1063 v8::Isolate* isolate = CcTest::isolate();
1064 v8::HandleScope scope(isolate);
1065 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
1066 templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
1067 EchoIndexedProperty, 0, 0, 0, 0, v8_num(637)));
1068 LocalContext env;
1069 env->Global()
1070 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
1071 .ToLocalChecked()
1072 ->NewInstance(env.local())
1073 .ToLocalChecked())
1074 .FromJust();
1075 Local<Script> script = v8_compile("obj[900]");
1076 CHECK_EQ(script->Run(env.local())
1077 .ToLocalChecked()
1078 ->Int32Value(env.local())
1079 .FromJust(),
1080 900);
1081}
1082
1083
1084THREADED_TEST(PropertyHandlerInPrototype) {
1085 LocalContext env;
1086 v8::Isolate* isolate = env->GetIsolate();
1087 v8::HandleScope scope(isolate);
1088
1089 // Set up a prototype chain with three interceptors.
1090 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
1091 templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
1092 CheckThisIndexedPropertyHandler, CheckThisIndexedPropertySetter,
1093 CheckThisIndexedPropertyQuery, CheckThisIndexedPropertyDeleter,
1094 CheckThisIndexedPropertyEnumerator));
1095
1096 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
1097 CheckThisNamedPropertyHandler, CheckThisNamedPropertySetter,
1098 CheckThisNamedPropertyQuery, CheckThisNamedPropertyDeleter,
1099 CheckThisNamedPropertyEnumerator));
1100
1101 bottom = templ->GetFunction(env.local())
1102 .ToLocalChecked()
1103 ->NewInstance(env.local())
1104 .ToLocalChecked();
1105 Local<v8::Object> top = templ->GetFunction(env.local())
1106 .ToLocalChecked()
1107 ->NewInstance(env.local())
1108 .ToLocalChecked();
1109 Local<v8::Object> middle = templ->GetFunction(env.local())
1110 .ToLocalChecked()
1111 ->NewInstance(env.local())
1112 .ToLocalChecked();
1113
1114 bottom->SetPrototype(env.local(), middle).FromJust();
1115 middle->SetPrototype(env.local(), top).FromJust();
1116 env->Global()->Set(env.local(), v8_str("obj"), bottom).FromJust();
1117
1118 // Indexed and named get.
1119 CompileRun("obj[0]");
1120 CompileRun("obj.x");
1121
1122 // Indexed and named set.
1123 CompileRun("obj[1] = 42");
1124 CompileRun("obj.y = 42");
1125
1126 // Indexed and named query.
1127 CompileRun("0 in obj");
1128 CompileRun("'x' in obj");
1129
1130 // Indexed and named deleter.
1131 CompileRun("delete obj[0]");
1132 CompileRun("delete obj.x");
1133
1134 // Enumerators.
1135 CompileRun("for (var p in obj) ;");
1136}
1137
1138
1139bool is_bootstrapping = false;
1140static void PrePropertyHandlerGet(
1141 Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
1142 ApiTestFuzzer::Fuzz();
1143 if (!is_bootstrapping &&
1144 v8_str("pre")
1145 ->Equals(info.GetIsolate()->GetCurrentContext(), key)
1146 .FromJust()) {
1147 info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre"));
1148 }
1149}
1150
1151
1152static void PrePropertyHandlerQuery(
1153 Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) {
1154 if (!is_bootstrapping &&
1155 v8_str("pre")
1156 ->Equals(info.GetIsolate()->GetCurrentContext(), key)
1157 .FromJust()) {
1158 info.GetReturnValue().Set(static_cast<int32_t>(v8::None));
1159 }
1160}
1161
1162
1163THREADED_TEST(PrePropertyHandler) {
1164 v8::Isolate* isolate = CcTest::isolate();
1165 v8::HandleScope scope(isolate);
1166 v8::Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate);
1167 desc->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
1168 PrePropertyHandlerGet, 0, PrePropertyHandlerQuery));
1169 is_bootstrapping = true;
1170 LocalContext env(NULL, desc->InstanceTemplate());
1171 is_bootstrapping = false;
1172 CompileRun("var pre = 'Object: pre'; var on = 'Object: on';");
1173 v8::Local<Value> result_pre = CompileRun("pre");
1174 CHECK(v8_str("PrePropertyHandler: pre")
1175 ->Equals(env.local(), result_pre)
1176 .FromJust());
1177 v8::Local<Value> result_on = CompileRun("on");
1178 CHECK(v8_str("Object: on")->Equals(env.local(), result_on).FromJust());
1179 v8::Local<Value> result_post = CompileRun("post");
1180 CHECK(result_post.IsEmpty());
1181}
1182
1183
1184THREADED_TEST(EmptyInterceptorBreakTransitions) {
1185 v8::HandleScope scope(CcTest::isolate());
1186 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
1187 AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
1188 LocalContext env;
1189 env->Global()
1190 ->Set(env.local(), v8_str("Constructor"),
1191 templ->GetFunction(env.local()).ToLocalChecked())
1192 .FromJust();
1193 CompileRun(
1194 "var o1 = new Constructor;"
1195 "o1.a = 1;" // Ensure a and x share the descriptor array.
1196 "Object.defineProperty(o1, 'x', {value: 10});");
1197 CompileRun(
1198 "var o2 = new Constructor;"
1199 "o2.a = 1;"
1200 "Object.defineProperty(o2, 'x', {value: 10});");
1201}
1202
1203
1204THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) {
1205 v8::Isolate* isolate = CcTest::isolate();
1206 v8::HandleScope scope(isolate);
1207 Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
1208 Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
1209 child->Inherit(parent);
1210 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
1211 LocalContext env;
1212 env->Global()
1213 ->Set(env.local(), v8_str("Child"),
1214 child->GetFunction(env.local()).ToLocalChecked())
1215 .FromJust();
1216 CompileRun(
1217 "var child = new Child;"
1218 "var parent = child.__proto__;"
1219 "Object.defineProperty(parent, 'age', "
1220 " {get: function(){ return this.accessor_age; }, "
1221 " set: function(v){ this.accessor_age = v; }, "
1222 " enumerable: true, configurable: true});"
1223 "child.age = 10;");
1224 ExpectBoolean("child.hasOwnProperty('age')", false);
1225 ExpectInt32("child.age", 10);
1226 ExpectInt32("child.accessor_age", 10);
1227}
1228
1229
1230THREADED_TEST(EmptyInterceptorDoesNotShadowApiAccessors) {
1231 v8::Isolate* isolate = CcTest::isolate();
1232 v8::HandleScope scope(isolate);
1233 Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
1234 auto returns_42 = FunctionTemplate::New(isolate, Returns42);
1235 parent->PrototypeTemplate()->SetAccessorProperty(v8_str("age"), returns_42);
1236 Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
1237 child->Inherit(parent);
1238 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
1239 LocalContext env;
1240 env->Global()
1241 ->Set(env.local(), v8_str("Child"),
1242 child->GetFunction(env.local()).ToLocalChecked())
1243 .FromJust();
1244 CompileRun(
1245 "var child = new Child;"
1246 "var parent = child.__proto__;");
1247 ExpectBoolean("child.hasOwnProperty('age')", false);
1248 ExpectInt32("child.age", 42);
1249 // Check interceptor followup.
1250 ExpectInt32(
1251 "var result;"
1252 "for (var i = 0; i < 4; ++i) {"
1253 " result = child.age;"
1254 "}"
1255 "result",
1256 42);
1257}
1258
1259
1260THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) {
1261 v8::Isolate* isolate = CcTest::isolate();
1262 v8::HandleScope scope(isolate);
1263 Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
1264 Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
1265 child->Inherit(parent);
1266 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
1267 LocalContext env;
1268 env->Global()
1269 ->Set(env.local(), v8_str("Child"),
1270 child->GetFunction(env.local()).ToLocalChecked())
1271 .FromJust();
1272 CompileRun(
1273 "var child = new Child;"
1274 "var parent = child.__proto__;"
1275 "parent.name = 'Alice';");
1276 ExpectBoolean("child.hasOwnProperty('name')", false);
1277 ExpectString("child.name", "Alice");
1278 CompileRun("child.name = 'Bob';");
1279 ExpectString("child.name", "Bob");
1280 ExpectBoolean("child.hasOwnProperty('name')", true);
1281 ExpectString("parent.name", "Alice");
1282}
1283
1284
1285THREADED_TEST(SwitchFromInterceptorToAccessor) {
1286 v8::HandleScope scope(CcTest::isolate());
1287 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
1288 AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter);
1289 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
1290 LocalContext env;
1291 env->Global()
1292 ->Set(env.local(), v8_str("Obj"),
1293 templ->GetFunction(env.local()).ToLocalChecked())
1294 .FromJust();
1295 CompileRun(
1296 "var obj = new Obj;"
1297 "function setAge(i){ obj.age = i; };"
1298 "for(var i = 0; i <= 10000; i++) setAge(i);");
1299 // All i < 10000 go to the interceptor.
1300 ExpectInt32("obj.interceptor_age", 9999);
1301 // The last i goes to the accessor.
1302 ExpectInt32("obj.accessor_age", 10000);
1303}
1304
1305
1306THREADED_TEST(SwitchFromAccessorToInterceptor) {
1307 v8::HandleScope scope(CcTest::isolate());
1308 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
1309 AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter);
1310 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
1311 LocalContext env;
1312 env->Global()
1313 ->Set(env.local(), v8_str("Obj"),
1314 templ->GetFunction(env.local()).ToLocalChecked())
1315 .FromJust();
1316 CompileRun(
1317 "var obj = new Obj;"
1318 "function setAge(i){ obj.age = i; };"
1319 "for(var i = 20000; i >= 9999; i--) setAge(i);");
1320 // All i >= 10000 go to the accessor.
1321 ExpectInt32("obj.accessor_age", 10000);
1322 // The last i goes to the interceptor.
1323 ExpectInt32("obj.interceptor_age", 9999);
1324}
1325
1326
1327THREADED_TEST(SwitchFromInterceptorToAccessorWithInheritance) {
1328 v8::HandleScope scope(CcTest::isolate());
1329 Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
1330 Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
1331 child->Inherit(parent);
1332 AddAccessor(parent, v8_str("age"), SimpleAccessorGetter,
1333 SimpleAccessorSetter);
1334 AddInterceptor(child, InterceptorGetter, InterceptorSetter);
1335 LocalContext env;
1336 env->Global()
1337 ->Set(env.local(), v8_str("Child"),
1338 child->GetFunction(env.local()).ToLocalChecked())
1339 .FromJust();
1340 CompileRun(
1341 "var child = new Child;"
1342 "function setAge(i){ child.age = i; };"
1343 "for(var i = 0; i <= 10000; i++) setAge(i);");
1344 // All i < 10000 go to the interceptor.
1345 ExpectInt32("child.interceptor_age", 9999);
1346 // The last i goes to the accessor.
1347 ExpectInt32("child.accessor_age", 10000);
1348}
1349
1350
1351THREADED_TEST(SwitchFromAccessorToInterceptorWithInheritance) {
1352 v8::HandleScope scope(CcTest::isolate());
1353 Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
1354 Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
1355 child->Inherit(parent);
1356 AddAccessor(parent, v8_str("age"), SimpleAccessorGetter,
1357 SimpleAccessorSetter);
1358 AddInterceptor(child, InterceptorGetter, InterceptorSetter);
1359 LocalContext env;
1360 env->Global()
1361 ->Set(env.local(), v8_str("Child"),
1362 child->GetFunction(env.local()).ToLocalChecked())
1363 .FromJust();
1364 CompileRun(
1365 "var child = new Child;"
1366 "function setAge(i){ child.age = i; };"
1367 "for(var i = 20000; i >= 9999; i--) setAge(i);");
1368 // All i >= 10000 go to the accessor.
1369 ExpectInt32("child.accessor_age", 10000);
1370 // The last i goes to the interceptor.
1371 ExpectInt32("child.interceptor_age", 9999);
1372}
1373
1374
1375THREADED_TEST(SwitchFromInterceptorToJSAccessor) {
1376 v8::HandleScope scope(CcTest::isolate());
1377 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
1378 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
1379 LocalContext env;
1380 env->Global()
1381 ->Set(env.local(), v8_str("Obj"),
1382 templ->GetFunction(env.local()).ToLocalChecked())
1383 .FromJust();
1384 CompileRun(
1385 "var obj = new Obj;"
1386 "function setter(i) { this.accessor_age = i; };"
1387 "function getter() { return this.accessor_age; };"
1388 "function setAge(i) { obj.age = i; };"
1389 "Object.defineProperty(obj, 'age', { get:getter, set:setter });"
1390 "for(var i = 0; i <= 10000; i++) setAge(i);");
1391 // All i < 10000 go to the interceptor.
1392 ExpectInt32("obj.interceptor_age", 9999);
1393 // The last i goes to the JavaScript accessor.
1394 ExpectInt32("obj.accessor_age", 10000);
1395 // The installed JavaScript getter is still intact.
1396 // This last part is a regression test for issue 1651 and relies on the fact
1397 // that both interceptor and accessor are being installed on the same object.
1398 ExpectInt32("obj.age", 10000);
1399 ExpectBoolean("obj.hasOwnProperty('age')", true);
1400 ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value");
1401}
1402
1403
1404THREADED_TEST(SwitchFromJSAccessorToInterceptor) {
1405 v8::HandleScope scope(CcTest::isolate());
1406 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
1407 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
1408 LocalContext env;
1409 env->Global()
1410 ->Set(env.local(), v8_str("Obj"),
1411 templ->GetFunction(env.local()).ToLocalChecked())
1412 .FromJust();
1413 CompileRun(
1414 "var obj = new Obj;"
1415 "function setter(i) { this.accessor_age = i; };"
1416 "function getter() { return this.accessor_age; };"
1417 "function setAge(i) { obj.age = i; };"
1418 "Object.defineProperty(obj, 'age', { get:getter, set:setter });"
1419 "for(var i = 20000; i >= 9999; i--) setAge(i);");
1420 // All i >= 10000 go to the accessor.
1421 ExpectInt32("obj.accessor_age", 10000);
1422 // The last i goes to the interceptor.
1423 ExpectInt32("obj.interceptor_age", 9999);
1424 // The installed JavaScript getter is still intact.
1425 // This last part is a regression test for issue 1651 and relies on the fact
1426 // that both interceptor and accessor are being installed on the same object.
1427 ExpectInt32("obj.age", 10000);
1428 ExpectBoolean("obj.hasOwnProperty('age')", true);
1429 ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value");
1430}
1431
1432
1433THREADED_TEST(SwitchFromInterceptorToProperty) {
1434 v8::HandleScope scope(CcTest::isolate());
1435 Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
1436 Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
1437 child->Inherit(parent);
1438 AddInterceptor(child, InterceptorGetter, InterceptorSetter);
1439 LocalContext env;
1440 env->Global()
1441 ->Set(env.local(), v8_str("Child"),
1442 child->GetFunction(env.local()).ToLocalChecked())
1443 .FromJust();
1444 CompileRun(
1445 "var child = new Child;"
1446 "function setAge(i){ child.age = i; };"
1447 "for(var i = 0; i <= 10000; i++) setAge(i);");
1448 // All i < 10000 go to the interceptor.
1449 ExpectInt32("child.interceptor_age", 9999);
1450 // The last i goes to child's own property.
1451 ExpectInt32("child.age", 10000);
1452}
1453
1454
1455THREADED_TEST(SwitchFromPropertyToInterceptor) {
1456 v8::HandleScope scope(CcTest::isolate());
1457 Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
1458 Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
1459 child->Inherit(parent);
1460 AddInterceptor(child, InterceptorGetter, InterceptorSetter);
1461 LocalContext env;
1462 env->Global()
1463 ->Set(env.local(), v8_str("Child"),
1464 child->GetFunction(env.local()).ToLocalChecked())
1465 .FromJust();
1466 CompileRun(
1467 "var child = new Child;"
1468 "function setAge(i){ child.age = i; };"
1469 "for(var i = 20000; i >= 9999; i--) setAge(i);");
1470 // All i >= 10000 go to child's own property.
1471 ExpectInt32("child.age", 10000);
1472 // The last i goes to the interceptor.
1473 ExpectInt32("child.interceptor_age", 9999);
1474}
1475
1476
1477static bool interceptor_for_hidden_properties_called;
1478static void InterceptorForHiddenProperties(
1479 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
1480 interceptor_for_hidden_properties_called = true;
1481}
1482
1483
1484THREADED_TEST(HiddenPropertiesWithInterceptors) {
1485 LocalContext context;
1486 v8::Isolate* isolate = context->GetIsolate();
1487 v8::HandleScope scope(isolate);
1488
1489 interceptor_for_hidden_properties_called = false;
1490
1491 v8::Local<v8::Private> key =
1492 v8::Private::New(isolate, v8_str("api-test::hidden-key"));
1493
1494 // Associate an interceptor with an object and start setting hidden values.
1495 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
1496 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1497 instance_templ->SetHandler(
1498 v8::NamedPropertyHandlerConfiguration(InterceptorForHiddenProperties));
1499 Local<v8::Function> function =
1500 fun_templ->GetFunction(context.local()).ToLocalChecked();
1501 Local<v8::Object> obj =
1502 function->NewInstance(context.local()).ToLocalChecked();
1503 CHECK(obj->SetPrivate(context.local(), key, v8::Integer::New(isolate, 2302))
1504 .FromJust());
1505 CHECK_EQ(2302, obj->GetPrivate(context.local(), key)
1506 .ToLocalChecked()
1507 ->Int32Value(context.local())
1508 .FromJust());
1509 CHECK(!interceptor_for_hidden_properties_called);
1510}
1511
1512
1513static void XPropertyGetter(Local<Name> property,
1514 const v8::PropertyCallbackInfo<v8::Value>& info) {
1515 ApiTestFuzzer::Fuzz();
1516 CHECK(info.Data()->IsUndefined());
1517 info.GetReturnValue().Set(property);
1518}
1519
1520
1521THREADED_TEST(NamedInterceptorPropertyRead) {
1522 v8::Isolate* isolate = CcTest::isolate();
1523 v8::HandleScope scope(isolate);
1524 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1525 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
1526 LocalContext context;
1527 context->Global()
1528 ->Set(context.local(), v8_str("obj"),
1529 templ->NewInstance(context.local()).ToLocalChecked())
1530 .FromJust();
1531 Local<Script> script = v8_compile("obj.x");
1532 for (int i = 0; i < 10; i++) {
1533 Local<Value> result = script->Run(context.local()).ToLocalChecked();
1534 CHECK(result->Equals(context.local(), v8_str("x")).FromJust());
1535 }
1536}
1537
1538
1539THREADED_TEST(NamedInterceptorDictionaryIC) {
1540 v8::Isolate* isolate = CcTest::isolate();
1541 v8::HandleScope scope(isolate);
1542 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1543 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
1544 LocalContext context;
1545 // Create an object with a named interceptor.
1546 context->Global()
1547 ->Set(context.local(), v8_str("interceptor_obj"),
1548 templ->NewInstance(context.local()).ToLocalChecked())
1549 .FromJust();
1550 Local<Script> script = v8_compile("interceptor_obj.x");
1551 for (int i = 0; i < 10; i++) {
1552 Local<Value> result = script->Run(context.local()).ToLocalChecked();
1553 CHECK(result->Equals(context.local(), v8_str("x")).FromJust());
1554 }
1555 // Create a slow case object and a function accessing a property in
1556 // that slow case object (with dictionary probing in generated
1557 // code). Then force object with a named interceptor into slow-case,
1558 // pass it to the function, and check that the interceptor is called
1559 // instead of accessing the local property.
1560 Local<Value> result = CompileRun(
1561 "function get_x(o) { return o.x; };"
1562 "var obj = { x : 42, y : 0 };"
1563 "delete obj.y;"
1564 "for (var i = 0; i < 10; i++) get_x(obj);"
1565 "interceptor_obj.x = 42;"
1566 "interceptor_obj.y = 10;"
1567 "delete interceptor_obj.y;"
1568 "get_x(interceptor_obj)");
1569 CHECK(result->Equals(context.local(), v8_str("x")).FromJust());
1570}
1571
1572
1573THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
1574 v8::Isolate* isolate = CcTest::isolate();
1575 v8::HandleScope scope(isolate);
1576 v8::Local<Context> context1 = Context::New(isolate);
1577
1578 context1->Enter();
1579 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1580 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
1581 // Create an object with a named interceptor.
1582 v8::Local<v8::Object> object = templ->NewInstance(context1).ToLocalChecked();
1583 context1->Global()
1584 ->Set(context1, v8_str("interceptor_obj"), object)
1585 .FromJust();
1586
1587 // Force the object into the slow case.
1588 CompileRun(
1589 "interceptor_obj.y = 0;"
1590 "delete interceptor_obj.y;");
1591 context1->Exit();
1592
1593 {
1594 // Introduce the object into a different context.
1595 // Repeat named loads to exercise ICs.
1596 LocalContext context2;
1597 context2->Global()
1598 ->Set(context2.local(), v8_str("interceptor_obj"), object)
1599 .FromJust();
1600 Local<Value> result = CompileRun(
1601 "function get_x(o) { return o.x; }"
1602 "interceptor_obj.x = 42;"
1603 "for (var i=0; i != 10; i++) {"
1604 " get_x(interceptor_obj);"
1605 "}"
1606 "get_x(interceptor_obj)");
1607 // Check that the interceptor was actually invoked.
1608 CHECK(result->Equals(context2.local(), v8_str("x")).FromJust());
1609 }
1610
1611 // Return to the original context and force some object to the slow case
1612 // to cause the NormalizedMapCache to verify.
1613 context1->Enter();
1614 CompileRun("var obj = { x : 0 }; delete obj.x;");
1615 context1->Exit();
1616}
1617
1618
1619static void SetXOnPrototypeGetter(
1620 Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
1621 // Set x on the prototype object and do not handle the get request.
1622 v8::Local<v8::Value> proto = info.Holder()->GetPrototype();
1623 proto.As<v8::Object>()
1624 ->Set(info.GetIsolate()->GetCurrentContext(), v8_str("x"),
1625 v8::Integer::New(info.GetIsolate(), 23))
1626 .FromJust();
1627}
1628
1629
1630// This is a regression test for http://crbug.com/20104. Map
1631// transitions should not interfere with post interceptor lookup.
1632THREADED_TEST(NamedInterceptorMapTransitionRead) {
1633 v8::Isolate* isolate = CcTest::isolate();
1634 v8::HandleScope scope(isolate);
1635 Local<v8::FunctionTemplate> function_template =
1636 v8::FunctionTemplate::New(isolate);
1637 Local<v8::ObjectTemplate> instance_template =
1638 function_template->InstanceTemplate();
1639 instance_template->SetHandler(
1640 v8::NamedPropertyHandlerConfiguration(SetXOnPrototypeGetter));
1641 LocalContext context;
1642 context->Global()
1643 ->Set(context.local(), v8_str("F"),
1644 function_template->GetFunction(context.local()).ToLocalChecked())
1645 .FromJust();
1646 // Create an instance of F and introduce a map transition for x.
1647 CompileRun("var o = new F(); o.x = 23;");
1648 // Create an instance of F and invoke the getter. The result should be 23.
1649 Local<Value> result = CompileRun("o = new F(); o.x");
1650 CHECK_EQ(result->Int32Value(context.local()).FromJust(), 23);
1651}
1652
1653
1654static void IndexedPropertyGetter(
1655 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
1656 ApiTestFuzzer::Fuzz();
1657 if (index == 37) {
1658 info.GetReturnValue().Set(v8_num(625));
1659 }
1660}
1661
1662
1663static void IndexedPropertySetter(
1664 uint32_t index, Local<Value> value,
1665 const v8::PropertyCallbackInfo<v8::Value>& info) {
1666 ApiTestFuzzer::Fuzz();
1667 if (index == 39) {
1668 info.GetReturnValue().Set(value);
1669 }
1670}
1671
1672
1673THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
1674 v8::Isolate* isolate = CcTest::isolate();
1675 v8::HandleScope scope(isolate);
1676 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1677 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
1678 IndexedPropertyGetter, IndexedPropertySetter));
1679 LocalContext context;
1680 context->Global()
1681 ->Set(context.local(), v8_str("obj"),
1682 templ->NewInstance(context.local()).ToLocalChecked())
1683 .FromJust();
1684 Local<Script> getter_script =
1685 v8_compile("obj.__defineGetter__(\"3\", function(){return 5;});obj[3];");
1686 Local<Script> setter_script = v8_compile(
1687 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
1688 "obj[17] = 23;"
1689 "obj.foo;");
1690 Local<Script> interceptor_setter_script = v8_compile(
1691 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
1692 "obj[39] = 47;"
1693 "obj.foo;"); // This setter should not run, due to the interceptor.
1694 Local<Script> interceptor_getter_script = v8_compile("obj[37];");
1695 Local<Value> result = getter_script->Run(context.local()).ToLocalChecked();
1696 CHECK(v8_num(5)->Equals(context.local(), result).FromJust());
1697 result = setter_script->Run(context.local()).ToLocalChecked();
1698 CHECK(v8_num(23)->Equals(context.local(), result).FromJust());
1699 result = interceptor_setter_script->Run(context.local()).ToLocalChecked();
1700 CHECK(v8_num(23)->Equals(context.local(), result).FromJust());
1701 result = interceptor_getter_script->Run(context.local()).ToLocalChecked();
1702 CHECK(v8_num(625)->Equals(context.local(), result).FromJust());
1703}
1704
1705
1706static void UnboxedDoubleIndexedPropertyGetter(
1707 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
1708 ApiTestFuzzer::Fuzz();
1709 if (index < 25) {
1710 info.GetReturnValue().Set(v8_num(index));
1711 }
1712}
1713
1714
1715static void UnboxedDoubleIndexedPropertySetter(
1716 uint32_t index, Local<Value> value,
1717 const v8::PropertyCallbackInfo<v8::Value>& info) {
1718 ApiTestFuzzer::Fuzz();
1719 if (index < 25) {
1720 info.GetReturnValue().Set(v8_num(index));
1721 }
1722}
1723
1724
1725void UnboxedDoubleIndexedPropertyEnumerator(
1726 const v8::PropertyCallbackInfo<v8::Array>& info) {
1727 // Force the list of returned keys to be stored in a FastDoubleArray.
1728 Local<Script> indexed_property_names_script = v8_compile(
1729 "keys = new Array(); keys[125000] = 1;"
1730 "for(i = 0; i < 80000; i++) { keys[i] = i; };"
1731 "keys.length = 25; keys;");
1732 Local<Value> result =
1733 indexed_property_names_script->Run(info.GetIsolate()->GetCurrentContext())
1734 .ToLocalChecked();
1735 info.GetReturnValue().Set(Local<v8::Array>::Cast(result));
1736}
1737
1738
1739// Make sure that the the interceptor code in the runtime properly handles
1740// merging property name lists for double-array-backed arrays.
1741THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) {
1742 v8::Isolate* isolate = CcTest::isolate();
1743 v8::HandleScope scope(isolate);
1744 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1745 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
1746 UnboxedDoubleIndexedPropertyGetter, UnboxedDoubleIndexedPropertySetter, 0,
1747 0, UnboxedDoubleIndexedPropertyEnumerator));
1748 LocalContext context;
1749 context->Global()
1750 ->Set(context.local(), v8_str("obj"),
1751 templ->NewInstance(context.local()).ToLocalChecked())
1752 .FromJust();
1753 // When obj is created, force it to be Stored in a FastDoubleArray.
1754 Local<Script> create_unboxed_double_script = v8_compile(
1755 "obj[125000] = 1; for(i = 0; i < 80000; i+=2) { obj[i] = i; } "
1756 "key_count = 0; "
1757 "for (x in obj) {key_count++;};"
1758 "obj;");
1759 Local<Value> result =
1760 create_unboxed_double_script->Run(context.local()).ToLocalChecked();
1761 CHECK(result->ToObject(context.local())
1762 .ToLocalChecked()
1763 ->HasRealIndexedProperty(context.local(), 2000)
1764 .FromJust());
1765 Local<Script> key_count_check = v8_compile("key_count;");
1766 result = key_count_check->Run(context.local()).ToLocalChecked();
1767 CHECK(v8_num(40013)->Equals(context.local(), result).FromJust());
1768}
1769
1770
1771void SloppyArgsIndexedPropertyEnumerator(
1772 const v8::PropertyCallbackInfo<v8::Array>& info) {
1773 // Force the list of returned keys to be stored in a Arguments object.
1774 Local<Script> indexed_property_names_script = v8_compile(
1775 "function f(w,x) {"
1776 " return arguments;"
1777 "}"
1778 "keys = f(0, 1, 2, 3);"
1779 "keys;");
1780 Local<Object> result = Local<Object>::Cast(
1781 indexed_property_names_script->Run(info.GetIsolate()->GetCurrentContext())
1782 .ToLocalChecked());
1783 // Have to populate the handle manually, as it's not Cast-able.
1784 i::Handle<i::JSReceiver> o =
1785 v8::Utils::OpenHandle<Object, i::JSReceiver>(result);
1786 i::Handle<i::JSArray> array(reinterpret_cast<i::JSArray*>(*o));
1787 info.GetReturnValue().Set(v8::Utils::ToLocal(array));
1788}
1789
1790
1791static void SloppyIndexedPropertyGetter(
1792 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
1793 ApiTestFuzzer::Fuzz();
1794 if (index < 4) {
1795 info.GetReturnValue().Set(v8_num(index));
1796 }
1797}
1798
1799
1800// Make sure that the the interceptor code in the runtime properly handles
1801// merging property name lists for non-string arguments arrays.
1802THREADED_TEST(IndexedInterceptorSloppyArgsWithIndexedAccessor) {
1803 v8::Isolate* isolate = CcTest::isolate();
1804 v8::HandleScope scope(isolate);
1805 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1806 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
1807 SloppyIndexedPropertyGetter, 0, 0, 0,
1808 SloppyArgsIndexedPropertyEnumerator));
1809 LocalContext context;
1810 context->Global()
1811 ->Set(context.local(), v8_str("obj"),
1812 templ->NewInstance(context.local()).ToLocalChecked())
1813 .FromJust();
1814 Local<Script> create_args_script = v8_compile(
1815 "var key_count = 0;"
1816 "for (x in obj) {key_count++;} key_count;");
1817 Local<Value> result =
1818 create_args_script->Run(context.local()).ToLocalChecked();
1819 CHECK(v8_num(4)->Equals(context.local(), result).FromJust());
1820}
1821
1822
1823static void IdentityIndexedPropertyGetter(
1824 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
1825 info.GetReturnValue().Set(index);
1826}
1827
1828
1829THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
1830 v8::Isolate* isolate = CcTest::isolate();
1831 v8::HandleScope scope(isolate);
1832 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1833 templ->SetHandler(
1834 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
1835
1836 LocalContext context;
1837 context->Global()
1838 ->Set(context.local(), v8_str("obj"),
1839 templ->NewInstance(context.local()).ToLocalChecked())
1840 .FromJust();
1841
1842 // Check fast object case.
1843 const char* fast_case_code =
1844 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
1845 ExpectString(fast_case_code, "0");
1846
1847 // Check slow case.
1848 const char* slow_case_code =
1849 "obj.x = 1; delete obj.x;"
1850 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
1851 ExpectString(slow_case_code, "1");
1852}
1853
1854
1855THREADED_TEST(IndexedInterceptorWithNoSetter) {
1856 v8::Isolate* isolate = CcTest::isolate();
1857 v8::HandleScope scope(isolate);
1858 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1859 templ->SetHandler(
1860 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
1861
1862 LocalContext context;
1863 context->Global()
1864 ->Set(context.local(), v8_str("obj"),
1865 templ->NewInstance(context.local()).ToLocalChecked())
1866 .FromJust();
1867
1868 const char* code =
1869 "try {"
1870 " obj[0] = 239;"
1871 " for (var i = 0; i < 100; i++) {"
1872 " var v = obj[0];"
1873 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
1874 " }"
1875 " 'PASSED'"
1876 "} catch(e) {"
1877 " e"
1878 "}";
1879 ExpectString(code, "PASSED");
1880}
1881
1882
1883static bool AccessAlwaysBlocked(Local<v8::Context> accessing_context,
1884 Local<v8::Object> accessed_object) {
1885 return false;
1886}
1887
1888
1889THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
1890 v8::Isolate* isolate = CcTest::isolate();
1891 v8::HandleScope scope(isolate);
1892 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1893 templ->SetHandler(
1894 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
1895
1896 templ->SetAccessCheckCallback(AccessAlwaysBlocked);
1897
1898 LocalContext context;
1899 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
1900 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
1901
1902 const char* code =
1903 "var result = 'PASSED';"
1904 "for (var i = 0; i < 100; i++) {"
1905 " try {"
1906 " var v = obj[0];"
1907 " result = 'Wrong value ' + v + ' at iteration ' + i;"
1908 " break;"
1909 " } catch (e) {"
1910 " /* pass */"
1911 " }"
1912 "}"
1913 "result";
1914 ExpectString(code, "PASSED");
1915}
1916
1917
1918THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
1919 v8::Isolate* isolate = CcTest::isolate();
1920 v8::HandleScope scope(isolate);
1921 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1922 templ->SetHandler(
1923 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
1924
1925 LocalContext context;
1926 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
1927 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
1928
1929 const char* code =
1930 "try {"
1931 " for (var i = 0; i < 100; i++) {"
1932 " var v = obj[i];"
1933 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
1934 " }"
1935 " 'PASSED'"
1936 "} catch(e) {"
1937 " e"
1938 "}";
1939 ExpectString(code, "PASSED");
1940}
1941
1942
1943THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
1944 v8::Isolate* isolate = CcTest::isolate();
1945 v8::HandleScope scope(isolate);
1946 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1947 templ->SetHandler(
1948 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
1949
1950 LocalContext context;
1951 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
1952 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
1953
1954 const char* code =
1955 "try {"
1956 " for (var i = 0; i < 100; i++) {"
1957 " var expected = i;"
1958 " var key = i;"
1959 " if (i == 25) {"
1960 " key = -1;"
1961 " expected = undefined;"
1962 " }"
1963 " if (i == 50) {"
1964 " /* probe minimal Smi number on 32-bit platforms */"
1965 " key = -(1 << 30);"
1966 " expected = undefined;"
1967 " }"
1968 " if (i == 75) {"
1969 " /* probe minimal Smi number on 64-bit platforms */"
1970 " key = 1 << 31;"
1971 " expected = undefined;"
1972 " }"
1973 " var v = obj[key];"
1974 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
1975 " }"
1976 " 'PASSED'"
1977 "} catch(e) {"
1978 " e"
1979 "}";
1980 ExpectString(code, "PASSED");
1981}
1982
1983
1984THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
1985 v8::Isolate* isolate = CcTest::isolate();
1986 v8::HandleScope scope(isolate);
1987 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
1988 templ->SetHandler(
1989 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
1990
1991 LocalContext context;
1992 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
1993 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
1994
1995 const char* code =
1996 "try {"
1997 " for (var i = 0; i < 100; i++) {"
1998 " var expected = i;"
1999 " var key = i;"
2000 " if (i == 50) {"
2001 " key = 'foobar';"
2002 " expected = undefined;"
2003 " }"
2004 " var v = obj[key];"
2005 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
2006 " }"
2007 " 'PASSED'"
2008 "} catch(e) {"
2009 " e"
2010 "}";
2011 ExpectString(code, "PASSED");
2012}
2013
2014
2015THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
2016 v8::Isolate* isolate = CcTest::isolate();
2017 v8::HandleScope scope(isolate);
2018 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
2019 templ->SetHandler(
2020 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
2021
2022 LocalContext context;
2023 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
2024 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
2025
2026 const char* code =
2027 "var original = obj;"
2028 "try {"
2029 " for (var i = 0; i < 100; i++) {"
2030 " var expected = i;"
2031 " if (i == 50) {"
2032 " obj = {50: 'foobar'};"
2033 " expected = 'foobar';"
2034 " }"
2035 " var v = obj[i];"
2036 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
2037 " if (i == 50) obj = original;"
2038 " }"
2039 " 'PASSED'"
2040 "} catch(e) {"
2041 " e"
2042 "}";
2043 ExpectString(code, "PASSED");
2044}
2045
2046
2047THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
2048 v8::Isolate* isolate = CcTest::isolate();
2049 v8::HandleScope scope(isolate);
2050 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
2051 templ->SetHandler(
2052 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
2053
2054 LocalContext context;
2055 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
2056 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
2057
2058 const char* code =
2059 "var original = obj;"
2060 "try {"
2061 " for (var i = 0; i < 100; i++) {"
2062 " var expected = i;"
2063 " if (i == 5) {"
2064 " obj = 239;"
2065 " expected = undefined;"
2066 " }"
2067 " var v = obj[i];"
2068 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
2069 " if (i == 5) obj = original;"
2070 " }"
2071 " 'PASSED'"
2072 "} catch(e) {"
2073 " e"
2074 "}";
2075 ExpectString(code, "PASSED");
2076}
2077
2078
2079THREADED_TEST(IndexedInterceptorOnProto) {
2080 v8::Isolate* isolate = CcTest::isolate();
2081 v8::HandleScope scope(isolate);
2082 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
2083 templ->SetHandler(
2084 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
2085
2086 LocalContext context;
2087 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
2088 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
2089
2090 const char* code =
2091 "var o = {__proto__: obj};"
2092 "try {"
2093 " for (var i = 0; i < 100; i++) {"
2094 " var v = o[i];"
2095 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
2096 " }"
2097 " 'PASSED'"
2098 "} catch(e) {"
2099 " e"
2100 "}";
2101 ExpectString(code, "PASSED");
2102}
2103
2104
2105static void NoBlockGetterX(Local<Name> name,
2106 const v8::PropertyCallbackInfo<v8::Value>&) {}
2107
2108
2109static void NoBlockGetterI(uint32_t index,
2110 const v8::PropertyCallbackInfo<v8::Value>&) {}
2111
2112
2113static void PDeleter(Local<Name> name,
2114 const v8::PropertyCallbackInfo<v8::Boolean>& info) {
2115 if (!name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("foo"))
2116 .FromJust()) {
2117 return; // not intercepted
2118 }
2119
2120 info.GetReturnValue().Set(false); // intercepted, don't delete the property
2121}
2122
2123
2124static void IDeleter(uint32_t index,
2125 const v8::PropertyCallbackInfo<v8::Boolean>& info) {
2126 if (index != 2) {
2127 return; // not intercepted
2128 }
2129
2130 info.GetReturnValue().Set(false); // intercepted, don't delete the property
2131}
2132
2133
2134THREADED_TEST(Deleter) {
2135 v8::Isolate* isolate = CcTest::isolate();
2136 v8::HandleScope scope(isolate);
2137 v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
2138 obj->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX, NULL,
2139 NULL, PDeleter, NULL));
2140 obj->SetHandler(v8::IndexedPropertyHandlerConfiguration(
2141 NoBlockGetterI, NULL, NULL, IDeleter, NULL));
2142 LocalContext context;
2143 context->Global()
2144 ->Set(context.local(), v8_str("k"),
2145 obj->NewInstance(context.local()).ToLocalChecked())
2146 .FromJust();
2147 CompileRun(
2148 "k.foo = 'foo';"
2149 "k.bar = 'bar';"
2150 "k[2] = 2;"
2151 "k[4] = 4;");
2152 CHECK(v8_compile("delete k.foo")
2153 ->Run(context.local())
2154 .ToLocalChecked()
2155 ->IsFalse());
2156 CHECK(v8_compile("delete k.bar")
2157 ->Run(context.local())
2158 .ToLocalChecked()
2159 ->IsTrue());
2160
2161 CHECK(v8_compile("k.foo")
2162 ->Run(context.local())
2163 .ToLocalChecked()
2164 ->Equals(context.local(), v8_str("foo"))
2165 .FromJust());
2166 CHECK(v8_compile("k.bar")
2167 ->Run(context.local())
2168 .ToLocalChecked()
2169 ->IsUndefined());
2170
2171 CHECK(v8_compile("delete k[2]")
2172 ->Run(context.local())
2173 .ToLocalChecked()
2174 ->IsFalse());
2175 CHECK(v8_compile("delete k[4]")
2176 ->Run(context.local())
2177 .ToLocalChecked()
2178 ->IsTrue());
2179
2180 CHECK(v8_compile("k[2]")
2181 ->Run(context.local())
2182 .ToLocalChecked()
2183 ->Equals(context.local(), v8_num(2))
2184 .FromJust());
2185 CHECK(
2186 v8_compile("k[4]")->Run(context.local()).ToLocalChecked()->IsUndefined());
2187}
2188
2189
2190static void GetK(Local<Name> name,
2191 const v8::PropertyCallbackInfo<v8::Value>& info) {
2192 ApiTestFuzzer::Fuzz();
2193 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
2194 if (name->Equals(context, v8_str("foo")).FromJust() ||
2195 name->Equals(context, v8_str("bar")).FromJust() ||
2196 name->Equals(context, v8_str("baz")).FromJust()) {
2197 info.GetReturnValue().SetUndefined();
2198 }
2199}
2200
2201
2202static void IndexedGetK(uint32_t index,
2203 const v8::PropertyCallbackInfo<v8::Value>& info) {
2204 ApiTestFuzzer::Fuzz();
2205 if (index == 0 || index == 1) info.GetReturnValue().SetUndefined();
2206}
2207
2208
2209static void NamedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) {
2210 ApiTestFuzzer::Fuzz();
2211 v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 3);
2212 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
2213 result->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_str("foo"))
2214 .FromJust();
2215 result->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_str("bar"))
2216 .FromJust();
2217 result->Set(context, v8::Integer::New(info.GetIsolate(), 2), v8_str("baz"))
2218 .FromJust();
2219 info.GetReturnValue().Set(result);
2220}
2221
2222
2223static void IndexedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) {
2224 ApiTestFuzzer::Fuzz();
2225 v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 2);
2226 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
2227 result->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_str("0"))
2228 .FromJust();
2229 result->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_str("1"))
2230 .FromJust();
2231 info.GetReturnValue().Set(result);
2232}
2233
2234
2235THREADED_TEST(Enumerators) {
2236 v8::Isolate* isolate = CcTest::isolate();
2237 v8::HandleScope scope(isolate);
2238 v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
2239 obj->SetHandler(
2240 v8::NamedPropertyHandlerConfiguration(GetK, NULL, NULL, NULL, NamedEnum));
2241 obj->SetHandler(v8::IndexedPropertyHandlerConfiguration(
2242 IndexedGetK, NULL, NULL, NULL, IndexedEnum));
2243 LocalContext context;
2244 context->Global()
2245 ->Set(context.local(), v8_str("k"),
2246 obj->NewInstance(context.local()).ToLocalChecked())
2247 .FromJust();
2248 v8::Local<v8::Array> result =
2249 v8::Local<v8::Array>::Cast(CompileRun("k[10] = 0;"
2250 "k.a = 0;"
2251 "k[5] = 0;"
2252 "k.b = 0;"
2253 "k[4294967294] = 0;"
2254 "k.c = 0;"
2255 "k[4294967295] = 0;"
2256 "k.d = 0;"
2257 "k[140000] = 0;"
2258 "k.e = 0;"
2259 "k[30000000000] = 0;"
2260 "k.f = 0;"
2261 "var result = [];"
2262 "for (var prop in k) {"
2263 " result.push(prop);"
2264 "}"
2265 "result"));
2266 // Check that we get all the property names returned including the
2267 // ones from the enumerators in the right order: indexed properties
2268 // in numerical order, indexed interceptor properties, named
2269 // properties in insertion order, named interceptor properties.
2270 // This order is not mandated by the spec, so this test is just
2271 // documenting our behavior.
2272 CHECK_EQ(17u, result->Length());
2273 // Indexed properties + indexed interceptor properties in numerical order.
2274 CHECK(v8_str("0")
2275 ->Equals(context.local(),
2276 result->Get(context.local(), v8::Integer::New(isolate, 0))
2277 .ToLocalChecked())
2278 .FromJust());
2279 CHECK(v8_str("1")
2280 ->Equals(context.local(),
2281 result->Get(context.local(), v8::Integer::New(isolate, 1))
2282 .ToLocalChecked())
2283 .FromJust());
2284 CHECK(v8_str("5")
2285 ->Equals(context.local(),
2286 result->Get(context.local(), v8::Integer::New(isolate, 2))
2287 .ToLocalChecked())
2288 .FromJust());
2289 CHECK(v8_str("10")
2290 ->Equals(context.local(),
2291 result->Get(context.local(), v8::Integer::New(isolate, 3))
2292 .ToLocalChecked())
2293 .FromJust());
2294 CHECK(v8_str("140000")
2295 ->Equals(context.local(),
2296 result->Get(context.local(), v8::Integer::New(isolate, 4))
2297 .ToLocalChecked())
2298 .FromJust());
2299 CHECK(v8_str("4294967294")
2300 ->Equals(context.local(),
2301 result->Get(context.local(), v8::Integer::New(isolate, 5))
2302 .ToLocalChecked())
2303 .FromJust());
2304 // Named properties in insertion order.
2305 CHECK(v8_str("a")
2306 ->Equals(context.local(),
2307 result->Get(context.local(), v8::Integer::New(isolate, 6))
2308 .ToLocalChecked())
2309 .FromJust());
2310 CHECK(v8_str("b")
2311 ->Equals(context.local(),
2312 result->Get(context.local(), v8::Integer::New(isolate, 7))
2313 .ToLocalChecked())
2314 .FromJust());
2315 CHECK(v8_str("c")
2316 ->Equals(context.local(),
2317 result->Get(context.local(), v8::Integer::New(isolate, 8))
2318 .ToLocalChecked())
2319 .FromJust());
2320 CHECK(v8_str("4294967295")
2321 ->Equals(context.local(),
2322 result->Get(context.local(), v8::Integer::New(isolate, 9))
2323 .ToLocalChecked())
2324 .FromJust());
2325 CHECK(v8_str("d")
2326 ->Equals(context.local(),
2327 result->Get(context.local(), v8::Integer::New(isolate, 10))
2328 .ToLocalChecked())
2329 .FromJust());
2330 CHECK(v8_str("e")
2331 ->Equals(context.local(),
2332 result->Get(context.local(), v8::Integer::New(isolate, 11))
2333 .ToLocalChecked())
2334 .FromJust());
2335 CHECK(v8_str("30000000000")
2336 ->Equals(context.local(),
2337 result->Get(context.local(), v8::Integer::New(isolate, 12))
2338 .ToLocalChecked())
2339 .FromJust());
2340 CHECK(v8_str("f")
2341 ->Equals(context.local(),
2342 result->Get(context.local(), v8::Integer::New(isolate, 13))
2343 .ToLocalChecked())
2344 .FromJust());
2345 // Named interceptor properties.
2346 CHECK(v8_str("foo")
2347 ->Equals(context.local(),
2348 result->Get(context.local(), v8::Integer::New(isolate, 14))
2349 .ToLocalChecked())
2350 .FromJust());
2351 CHECK(v8_str("bar")
2352 ->Equals(context.local(),
2353 result->Get(context.local(), v8::Integer::New(isolate, 15))
2354 .ToLocalChecked())
2355 .FromJust());
2356 CHECK(v8_str("baz")
2357 ->Equals(context.local(),
2358 result->Get(context.local(), v8::Integer::New(isolate, 16))
2359 .ToLocalChecked())
2360 .FromJust());
2361}
2362
2363
2364v8::Local<Value> call_ic_function;
2365v8::Local<Value> call_ic_function2;
2366v8::Local<Value> call_ic_function3;
2367
2368static void InterceptorCallICGetter(
2369 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
2370 ApiTestFuzzer::Fuzz();
2371 CHECK(v8_str("x")
2372 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
2373 .FromJust());
2374 info.GetReturnValue().Set(call_ic_function);
2375}
2376
2377
2378// This test should hit the call IC for the interceptor case.
2379THREADED_TEST(InterceptorCallIC) {
2380 v8::Isolate* isolate = CcTest::isolate();
2381 v8::HandleScope scope(isolate);
2382 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2383 templ->SetHandler(
2384 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter));
2385 LocalContext context;
2386 context->Global()
2387 ->Set(context.local(), v8_str("o"),
2388 templ->NewInstance(context.local()).ToLocalChecked())
2389 .FromJust();
2390 call_ic_function = v8_compile("function f(x) { return x + 1; }; f")
2391 ->Run(context.local())
2392 .ToLocalChecked();
2393 v8::Local<Value> value = CompileRun(
2394 "var result = 0;"
2395 "for (var i = 0; i < 1000; i++) {"
2396 " result = o.x(41);"
2397 "}");
2398 CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
2399}
2400
2401
2402// This test checks that if interceptor doesn't provide
2403// a value, we can fetch regular value.
2404THREADED_TEST(InterceptorCallICSeesOthers) {
2405 v8::Isolate* isolate = CcTest::isolate();
2406 v8::HandleScope scope(isolate);
2407 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2408 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2409 LocalContext context;
2410 context->Global()
2411 ->Set(context.local(), v8_str("o"),
2412 templ->NewInstance(context.local()).ToLocalChecked())
2413 .FromJust();
2414 v8::Local<Value> value = CompileRun(
2415 "o.x = function f(x) { return x + 1; };"
2416 "var result = 0;"
2417 "for (var i = 0; i < 7; i++) {"
2418 " result = o.x(41);"
2419 "}");
2420 CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
2421}
2422
2423
2424static v8::Local<Value> call_ic_function4;
2425static void InterceptorCallICGetter4(
2426 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
2427 ApiTestFuzzer::Fuzz();
2428 CHECK(v8_str("x")
2429 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
2430 .FromJust());
2431 info.GetReturnValue().Set(call_ic_function4);
2432}
2433
2434
2435// This test checks that if interceptor provides a function,
2436// even if we cached shadowed variant, interceptor's function
2437// is invoked
2438THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
2439 v8::Isolate* isolate = CcTest::isolate();
2440 v8::HandleScope scope(isolate);
2441 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2442 templ->SetHandler(
2443 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter4));
2444 LocalContext context;
2445 context->Global()
2446 ->Set(context.local(), v8_str("o"),
2447 templ->NewInstance(context.local()).ToLocalChecked())
2448 .FromJust();
2449 call_ic_function4 = v8_compile("function f(x) { return x - 1; }; f")
2450 ->Run(context.local())
2451 .ToLocalChecked();
2452 v8::Local<Value> value = CompileRun(
2453 "Object.getPrototypeOf(o).x = function(x) { return x + 1; };"
2454 "var result = 0;"
2455 "for (var i = 0; i < 1000; i++) {"
2456 " result = o.x(42);"
2457 "}");
2458 CHECK_EQ(41, value->Int32Value(context.local()).FromJust());
2459}
2460
2461
2462// Test the case when we stored cacheable lookup into
2463// a stub, but it got invalidated later on
2464THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
2465 v8::Isolate* isolate = CcTest::isolate();
2466 v8::HandleScope scope(isolate);
2467 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2468 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2469 LocalContext context;
2470 context->Global()
2471 ->Set(context.local(), v8_str("o"),
2472 templ->NewInstance(context.local()).ToLocalChecked())
2473 .FromJust();
2474 v8::Local<Value> value = CompileRun(
2475 "proto1 = new Object();"
2476 "proto2 = new Object();"
2477 "o.__proto__ = proto1;"
2478 "proto1.__proto__ = proto2;"
2479 "proto2.y = function(x) { return x + 1; };"
2480 // Invoke it many times to compile a stub
2481 "for (var i = 0; i < 7; i++) {"
2482 " o.y(42);"
2483 "}"
2484 "proto1.y = function(x) { return x - 1; };"
2485 "var result = 0;"
2486 "for (var i = 0; i < 7; i++) {"
2487 " result += o.y(42);"
2488 "}");
2489 CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust());
2490}
2491
2492
2493// This test checks that if interceptor doesn't provide a function,
2494// cached constant function is used
2495THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
2496 v8::Isolate* isolate = CcTest::isolate();
2497 v8::HandleScope scope(isolate);
2498 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2499 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2500 LocalContext context;
2501 context->Global()
2502 ->Set(context.local(), v8_str("o"),
2503 templ->NewInstance(context.local()).ToLocalChecked())
2504 .FromJust();
2505 v8::Local<Value> value = CompileRun(
2506 "function inc(x) { return x + 1; };"
2507 "inc(1);"
2508 "o.x = inc;"
2509 "var result = 0;"
2510 "for (var i = 0; i < 1000; i++) {"
2511 " result = o.x(42);"
2512 "}");
2513 CHECK_EQ(43, value->Int32Value(context.local()).FromJust());
2514}
2515
2516
2517static v8::Local<Value> call_ic_function5;
2518static void InterceptorCallICGetter5(
2519 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
2520 ApiTestFuzzer::Fuzz();
2521 if (v8_str("x")
2522 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
2523 .FromJust())
2524 info.GetReturnValue().Set(call_ic_function5);
2525}
2526
2527
2528// This test checks that if interceptor provides a function,
2529// even if we cached constant function, interceptor's function
2530// is invoked
2531THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
2532 v8::Isolate* isolate = CcTest::isolate();
2533 v8::HandleScope scope(isolate);
2534 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2535 templ->SetHandler(
2536 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter5));
2537 LocalContext context;
2538 context->Global()
2539 ->Set(context.local(), v8_str("o"),
2540 templ->NewInstance(context.local()).ToLocalChecked())
2541 .FromJust();
2542 call_ic_function5 = v8_compile("function f(x) { return x - 1; }; f")
2543 ->Run(context.local())
2544 .ToLocalChecked();
2545 v8::Local<Value> value = CompileRun(
2546 "function inc(x) { return x + 1; };"
2547 "inc(1);"
2548 "o.x = inc;"
2549 "var result = 0;"
2550 "for (var i = 0; i < 1000; i++) {"
2551 " result = o.x(42);"
2552 "}");
2553 CHECK_EQ(41, value->Int32Value(context.local()).FromJust());
2554}
2555
2556
2557static v8::Local<Value> call_ic_function6;
2558static void InterceptorCallICGetter6(
2559 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
2560 ApiTestFuzzer::Fuzz();
2561 if (v8_str("x")
2562 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
2563 .FromJust())
2564 info.GetReturnValue().Set(call_ic_function6);
2565}
2566
2567
2568// Same test as above, except the code is wrapped in a function
2569// to test the optimized compiler.
2570THREADED_TEST(InterceptorCallICConstantFunctionNotNeededWrapped) {
2571 i::FLAG_allow_natives_syntax = true;
2572 v8::Isolate* isolate = CcTest::isolate();
2573 v8::HandleScope scope(isolate);
2574 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2575 templ->SetHandler(
2576 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter6));
2577 LocalContext context;
2578 context->Global()
2579 ->Set(context.local(), v8_str("o"),
2580 templ->NewInstance(context.local()).ToLocalChecked())
2581 .FromJust();
2582 call_ic_function6 = v8_compile("function f(x) { return x - 1; }; f")
2583 ->Run(context.local())
2584 .ToLocalChecked();
2585 v8::Local<Value> value = CompileRun(
2586 "function inc(x) { return x + 1; };"
2587 "inc(1);"
2588 "o.x = inc;"
2589 "function test() {"
2590 " var result = 0;"
2591 " for (var i = 0; i < 1000; i++) {"
2592 " result = o.x(42);"
2593 " }"
2594 " return result;"
2595 "};"
2596 "test();"
2597 "test();"
2598 "test();"
2599 "%OptimizeFunctionOnNextCall(test);"
2600 "test()");
2601 CHECK_EQ(41, value->Int32Value(context.local()).FromJust());
2602}
2603
2604
2605// Test the case when we stored constant function into
2606// a stub, but it got invalidated later on
2607THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
2608 v8::Isolate* isolate = CcTest::isolate();
2609 v8::HandleScope scope(isolate);
2610 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2611 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2612 LocalContext context;
2613 context->Global()
2614 ->Set(context.local(), v8_str("o"),
2615 templ->NewInstance(context.local()).ToLocalChecked())
2616 .FromJust();
2617 v8::Local<Value> value = CompileRun(
2618 "function inc(x) { return x + 1; };"
2619 "inc(1);"
2620 "proto1 = new Object();"
2621 "proto2 = new Object();"
2622 "o.__proto__ = proto1;"
2623 "proto1.__proto__ = proto2;"
2624 "proto2.y = inc;"
2625 // Invoke it many times to compile a stub
2626 "for (var i = 0; i < 7; i++) {"
2627 " o.y(42);"
2628 "}"
2629 "proto1.y = function(x) { return x - 1; };"
2630 "var result = 0;"
2631 "for (var i = 0; i < 7; i++) {"
2632 " result += o.y(42);"
2633 "}");
2634 CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust());
2635}
2636
2637
2638// Test the case when we stored constant function into
2639// a stub, but it got invalidated later on due to override on
2640// global object which is between interceptor and constant function' holders.
2641THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
2642 v8::Isolate* isolate = CcTest::isolate();
2643 v8::HandleScope scope(isolate);
2644 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2645 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2646 LocalContext context;
2647 context->Global()
2648 ->Set(context.local(), v8_str("o"),
2649 templ->NewInstance(context.local()).ToLocalChecked())
2650 .FromJust();
2651 v8::Local<Value> value = CompileRun(
2652 "function inc(x) { return x + 1; };"
2653 "inc(1);"
2654 "o.__proto__ = this;"
2655 "this.__proto__.y = inc;"
2656 // Invoke it many times to compile a stub
2657 "for (var i = 0; i < 7; i++) {"
2658 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
2659 "}"
2660 "this.y = function(x) { return x - 1; };"
2661 "var result = 0;"
2662 "for (var i = 0; i < 7; i++) {"
2663 " result += o.y(42);"
2664 "}");
2665 CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust());
2666}
2667
2668
2669// Test the case when actual function to call sits on global object.
2670THREADED_TEST(InterceptorCallICCachedFromGlobal) {
2671 v8::Isolate* isolate = CcTest::isolate();
2672 v8::HandleScope scope(isolate);
2673 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
2674 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2675
2676 LocalContext context;
2677 context->Global()
2678 ->Set(context.local(), v8_str("o"),
2679 templ_o->NewInstance(context.local()).ToLocalChecked())
2680 .FromJust();
2681
2682 v8::Local<Value> value = CompileRun(
2683 "try {"
2684 " o.__proto__ = this;"
2685 " for (var i = 0; i < 10; i++) {"
2686 " var v = o.parseFloat('239');"
2687 " if (v != 239) throw v;"
2688 // Now it should be ICed and keep a reference to parseFloat.
2689 " }"
2690 " var result = 0;"
2691 " for (var i = 0; i < 10; i++) {"
2692 " result += o.parseFloat('239');"
2693 " }"
2694 " result"
2695 "} catch(e) {"
2696 " e"
2697 "};");
2698 CHECK_EQ(239 * 10, value->Int32Value(context.local()).FromJust());
2699}
2700
2701
2702v8::Local<Value> keyed_call_ic_function;
2703
2704static void InterceptorKeyedCallICGetter(
2705 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
2706 ApiTestFuzzer::Fuzz();
2707 if (v8_str("x")
2708 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
2709 .FromJust()) {
2710 info.GetReturnValue().Set(keyed_call_ic_function);
2711 }
2712}
2713
2714
2715// Test the case when we stored cacheable lookup into
2716// a stub, but the function name changed (to another cacheable function).
2717THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
2718 v8::Isolate* isolate = CcTest::isolate();
2719 v8::HandleScope scope(isolate);
2720 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2721 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2722 LocalContext context;
2723 context->Global()
2724 ->Set(context.local(), v8_str("o"),
2725 templ->NewInstance(context.local()).ToLocalChecked())
2726 .FromJust();
2727 CompileRun(
2728 "proto = new Object();"
2729 "proto.y = function(x) { return x + 1; };"
2730 "proto.z = function(x) { return x - 1; };"
2731 "o.__proto__ = proto;"
2732 "var result = 0;"
2733 "var method = 'y';"
2734 "for (var i = 0; i < 10; i++) {"
2735 " if (i == 5) { method = 'z'; };"
2736 " result += o[method](41);"
2737 "}");
2738 CHECK_EQ(42 * 5 + 40 * 5, context->Global()
2739 ->Get(context.local(), v8_str("result"))
2740 .ToLocalChecked()
2741 ->Int32Value(context.local())
2742 .FromJust());
2743}
2744
2745
2746// Test the case when we stored cacheable lookup into
2747// a stub, but the function name changed (and the new function is present
2748// both before and after the interceptor in the prototype chain).
2749THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
2750 v8::Isolate* isolate = CcTest::isolate();
2751 v8::HandleScope scope(isolate);
2752 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2753 templ->SetHandler(
2754 v8::NamedPropertyHandlerConfiguration(InterceptorKeyedCallICGetter));
2755 LocalContext context;
2756 context->Global()
2757 ->Set(context.local(), v8_str("proto1"),
2758 templ->NewInstance(context.local()).ToLocalChecked())
2759 .FromJust();
2760 keyed_call_ic_function = v8_compile("function f(x) { return x - 1; }; f")
2761 ->Run(context.local())
2762 .ToLocalChecked();
2763 CompileRun(
2764 "o = new Object();"
2765 "proto2 = new Object();"
2766 "o.y = function(x) { return x + 1; };"
2767 "proto2.y = function(x) { return x + 2; };"
2768 "o.__proto__ = proto1;"
2769 "proto1.__proto__ = proto2;"
2770 "var result = 0;"
2771 "var method = 'x';"
2772 "for (var i = 0; i < 10; i++) {"
2773 " if (i == 5) { method = 'y'; };"
2774 " result += o[method](41);"
2775 "}");
2776 CHECK_EQ(42 * 5 + 40 * 5, context->Global()
2777 ->Get(context.local(), v8_str("result"))
2778 .ToLocalChecked()
2779 ->Int32Value(context.local())
2780 .FromJust());
2781}
2782
2783
2784// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
2785// on the global object.
2786THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
2787 v8::Isolate* isolate = CcTest::isolate();
2788 v8::HandleScope scope(isolate);
2789 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2790 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2791 LocalContext context;
2792 context->Global()
2793 ->Set(context.local(), v8_str("o"),
2794 templ->NewInstance(context.local()).ToLocalChecked())
2795 .FromJust();
2796 CompileRun(
2797 "function inc(x) { return x + 1; };"
2798 "inc(1);"
2799 "function dec(x) { return x - 1; };"
2800 "dec(1);"
2801 "o.__proto__ = this;"
2802 "this.__proto__.x = inc;"
2803 "this.__proto__.y = dec;"
2804 "var result = 0;"
2805 "var method = 'x';"
2806 "for (var i = 0; i < 10; i++) {"
2807 " if (i == 5) { method = 'y'; };"
2808 " result += o[method](41);"
2809 "}");
2810 CHECK_EQ(42 * 5 + 40 * 5, context->Global()
2811 ->Get(context.local(), v8_str("result"))
2812 .ToLocalChecked()
2813 ->Int32Value(context.local())
2814 .FromJust());
2815}
2816
2817
2818// Test the case when actual function to call sits on global object.
2819THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
2820 v8::Isolate* isolate = CcTest::isolate();
2821 v8::HandleScope scope(isolate);
2822 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
2823 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2824 LocalContext context;
2825 context->Global()
2826 ->Set(context.local(), v8_str("o"),
2827 templ_o->NewInstance(context.local()).ToLocalChecked())
2828 .FromJust();
2829
2830 CompileRun(
2831 "function len(x) { return x.length; };"
2832 "o.__proto__ = this;"
2833 "var m = 'parseFloat';"
2834 "var result = 0;"
2835 "for (var i = 0; i < 10; i++) {"
2836 " if (i == 5) {"
2837 " m = 'len';"
2838 " saved_result = result;"
2839 " };"
2840 " result = o[m]('239');"
2841 "}");
2842 CHECK_EQ(3, context->Global()
2843 ->Get(context.local(), v8_str("result"))
2844 .ToLocalChecked()
2845 ->Int32Value(context.local())
2846 .FromJust());
2847 CHECK_EQ(239, context->Global()
2848 ->Get(context.local(), v8_str("saved_result"))
2849 .ToLocalChecked()
2850 ->Int32Value(context.local())
2851 .FromJust());
2852}
2853
2854
2855// Test the map transition before the interceptor.
2856THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
2857 v8::Isolate* isolate = CcTest::isolate();
2858 v8::HandleScope scope(isolate);
2859 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
2860 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2861 LocalContext context;
2862 context->Global()
2863 ->Set(context.local(), v8_str("proto"),
2864 templ_o->NewInstance(context.local()).ToLocalChecked())
2865 .FromJust();
2866
2867 CompileRun(
2868 "var o = new Object();"
2869 "o.__proto__ = proto;"
2870 "o.method = function(x) { return x + 1; };"
2871 "var m = 'method';"
2872 "var result = 0;"
2873 "for (var i = 0; i < 10; i++) {"
2874 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
2875 " result += o[m](41);"
2876 "}");
2877 CHECK_EQ(42 * 5 + 40 * 5, context->Global()
2878 ->Get(context.local(), v8_str("result"))
2879 .ToLocalChecked()
2880 ->Int32Value(context.local())
2881 .FromJust());
2882}
2883
2884
2885// Test the map transition after the interceptor.
2886THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
2887 v8::Isolate* isolate = CcTest::isolate();
2888 v8::HandleScope scope(isolate);
2889 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
2890 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
2891 LocalContext context;
2892 context->Global()
2893 ->Set(context.local(), v8_str("o"),
2894 templ_o->NewInstance(context.local()).ToLocalChecked())
2895 .FromJust();
2896
2897 CompileRun(
2898 "var proto = new Object();"
2899 "o.__proto__ = proto;"
2900 "proto.method = function(x) { return x + 1; };"
2901 "var m = 'method';"
2902 "var result = 0;"
2903 "for (var i = 0; i < 10; i++) {"
2904 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
2905 " result += o[m](41);"
2906 "}");
2907 CHECK_EQ(42 * 5 + 40 * 5, context->Global()
2908 ->Get(context.local(), v8_str("result"))
2909 .ToLocalChecked()
2910 ->Int32Value(context.local())
2911 .FromJust());
2912}
2913
2914
2915static int interceptor_call_count = 0;
2916
2917static void InterceptorICRefErrorGetter(
2918 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
2919 ApiTestFuzzer::Fuzz();
2920 if (!is_bootstrapping &&
2921 v8_str("x")
2922 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
2923 .FromJust() &&
2924 interceptor_call_count++ < 20) {
2925 info.GetReturnValue().Set(call_ic_function2);
2926 }
2927}
2928
2929
2930// This test should hit load and call ICs for the interceptor case.
2931// Once in a while, the interceptor will reply that a property was not
2932// found in which case we should get a reference error.
2933THREADED_TEST(InterceptorICReferenceErrors) {
2934 v8::Isolate* isolate = CcTest::isolate();
2935 v8::HandleScope scope(isolate);
2936 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2937 templ->SetHandler(
2938 v8::NamedPropertyHandlerConfiguration(InterceptorICRefErrorGetter));
2939 is_bootstrapping = true;
2940 LocalContext context(0, templ, v8::Local<Value>());
2941 is_bootstrapping = false;
2942 call_ic_function2 = v8_compile("function h(x) { return x; }; h")
2943 ->Run(context.local())
2944 .ToLocalChecked();
2945 v8::Local<Value> value = CompileRun(
2946 "function f() {"
2947 " for (var i = 0; i < 1000; i++) {"
2948 " try { x; } catch(e) { return true; }"
2949 " }"
2950 " return false;"
2951 "};"
2952 "f();");
2953 CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
2954 interceptor_call_count = 0;
2955 value = CompileRun(
2956 "function g() {"
2957 " for (var i = 0; i < 1000; i++) {"
2958 " try { x(42); } catch(e) { return true; }"
2959 " }"
2960 " return false;"
2961 "};"
2962 "g();");
2963 CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
2964}
2965
2966
2967static int interceptor_ic_exception_get_count = 0;
2968
2969static void InterceptorICExceptionGetter(
2970 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
2971 ApiTestFuzzer::Fuzz();
2972 if (is_bootstrapping) return;
2973 if (v8_str("x")
2974 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
2975 .FromJust() &&
2976 ++interceptor_ic_exception_get_count < 20) {
2977 info.GetReturnValue().Set(call_ic_function3);
2978 }
2979 if (interceptor_ic_exception_get_count == 20) {
2980 info.GetIsolate()->ThrowException(v8_num(42));
2981 return;
2982 }
2983}
2984
2985
2986// Test interceptor load/call IC where the interceptor throws an
2987// exception once in a while.
2988THREADED_TEST(InterceptorICGetterExceptions) {
2989 interceptor_ic_exception_get_count = 0;
2990 v8::Isolate* isolate = CcTest::isolate();
2991 v8::HandleScope scope(isolate);
2992 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
2993 templ->SetHandler(
2994 v8::NamedPropertyHandlerConfiguration(InterceptorICExceptionGetter));
2995 is_bootstrapping = true;
2996 LocalContext context(0, templ, v8::Local<Value>());
2997 is_bootstrapping = false;
2998 call_ic_function3 = v8_compile("function h(x) { return x; }; h")
2999 ->Run(context.local())
3000 .ToLocalChecked();
3001 v8::Local<Value> value = CompileRun(
3002 "function f() {"
3003 " for (var i = 0; i < 100; i++) {"
3004 " try { x; } catch(e) { return true; }"
3005 " }"
3006 " return false;"
3007 "};"
3008 "f();");
3009 CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
3010 interceptor_ic_exception_get_count = 0;
3011 value = CompileRun(
3012 "function f() {"
3013 " for (var i = 0; i < 100; i++) {"
3014 " try { x(42); } catch(e) { return true; }"
3015 " }"
3016 " return false;"
3017 "};"
3018 "f();");
3019 CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
3020}
3021
3022
3023static int interceptor_ic_exception_set_count = 0;
3024
3025static void InterceptorICExceptionSetter(
3026 Local<Name> key, Local<Value> value,
3027 const v8::PropertyCallbackInfo<v8::Value>& info) {
3028 ApiTestFuzzer::Fuzz();
3029 if (++interceptor_ic_exception_set_count > 20) {
3030 info.GetIsolate()->ThrowException(v8_num(42));
3031 }
3032}
3033
3034
3035// Test interceptor store IC where the interceptor throws an exception
3036// once in a while.
3037THREADED_TEST(InterceptorICSetterExceptions) {
3038 interceptor_ic_exception_set_count = 0;
3039 v8::Isolate* isolate = CcTest::isolate();
3040 v8::HandleScope scope(isolate);
3041 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3042 templ->SetHandler(
3043 v8::NamedPropertyHandlerConfiguration(0, InterceptorICExceptionSetter));
3044 LocalContext context(0, templ, v8::Local<Value>());
3045 v8::Local<Value> value = CompileRun(
3046 "function f() {"
3047 " for (var i = 0; i < 100; i++) {"
3048 " try { x = 42; } catch(e) { return true; }"
3049 " }"
3050 " return false;"
3051 "};"
3052 "f();");
3053 CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
3054}
3055
3056
3057// Test that we ignore null interceptors.
3058THREADED_TEST(NullNamedInterceptor) {
3059 v8::Isolate* isolate = CcTest::isolate();
3060 v8::HandleScope scope(isolate);
3061 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3062 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
3063 static_cast<v8::GenericNamedPropertyGetterCallback>(0)));
3064 LocalContext context;
3065 templ->Set(CcTest::isolate(), "x", v8_num(42));
3066 v8::Local<v8::Object> obj =
3067 templ->NewInstance(context.local()).ToLocalChecked();
3068 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
3069 v8::Local<Value> value = CompileRun("obj.x");
3070 CHECK(value->IsInt32());
3071 CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
3072}
3073
3074
3075// Test that we ignore null interceptors.
3076THREADED_TEST(NullIndexedInterceptor) {
3077 v8::Isolate* isolate = CcTest::isolate();
3078 v8::HandleScope scope(isolate);
3079 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3080 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
3081 static_cast<v8::IndexedPropertyGetterCallback>(0)));
3082 LocalContext context;
3083 templ->Set(CcTest::isolate(), "42", v8_num(42));
3084 v8::Local<v8::Object> obj =
3085 templ->NewInstance(context.local()).ToLocalChecked();
3086 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
3087 v8::Local<Value> value = CompileRun("obj[42]");
3088 CHECK(value->IsInt32());
3089 CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
3090}
3091
3092
3093THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
3094 v8::Isolate* isolate = CcTest::isolate();
3095 v8::HandleScope scope(isolate);
3096 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
3097 templ->InstanceTemplate()->SetHandler(
3098 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
3099 LocalContext env;
3100 env->Global()
3101 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
3102 .ToLocalChecked()
3103 ->NewInstance(env.local())
3104 .ToLocalChecked())
3105 .FromJust();
3106 ExpectTrue("obj.x === 42");
3107 ExpectTrue("!obj.propertyIsEnumerable('x')");
3108}
3109
3110
3111THREADED_TEST(Regress256330) {
3112 i::FLAG_allow_natives_syntax = true;
3113 LocalContext context;
3114 v8::HandleScope scope(context->GetIsolate());
3115 Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
3116 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
3117 context->Global()
3118 ->Set(context.local(), v8_str("Bug"),
3119 templ->GetFunction(context.local()).ToLocalChecked())
3120 .FromJust();
3121 CompileRun(
3122 "\"use strict\"; var o = new Bug;"
3123 "function f(o) { o.x = 10; };"
3124 "f(o); f(o); f(o);"
3125 "%OptimizeFunctionOnNextCall(f);"
3126 "f(o);");
3127 ExpectBoolean("%GetOptimizationStatus(f) != 2", true);
3128}
3129
3130
3131THREADED_TEST(CrankshaftInterceptorSetter) {
3132 i::FLAG_allow_natives_syntax = true;
3133 v8::HandleScope scope(CcTest::isolate());
3134 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
3135 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
3136 LocalContext env;
3137 env->Global()
3138 ->Set(env.local(), v8_str("Obj"),
3139 templ->GetFunction(env.local()).ToLocalChecked())
3140 .FromJust();
3141 CompileRun(
3142 "var obj = new Obj;"
3143 // Initialize fields to avoid transitions later.
3144 "obj.age = 0;"
3145 "obj.accessor_age = 42;"
3146 "function setter(i) { this.accessor_age = i; };"
3147 "function getter() { return this.accessor_age; };"
3148 "function setAge(i) { obj.age = i; };"
3149 "Object.defineProperty(obj, 'age', { get:getter, set:setter });"
3150 "setAge(1);"
3151 "setAge(2);"
3152 "setAge(3);"
3153 "%OptimizeFunctionOnNextCall(setAge);"
3154 "setAge(4);");
3155 // All stores went through the interceptor.
3156 ExpectInt32("obj.interceptor_age", 4);
3157 ExpectInt32("obj.accessor_age", 42);
3158}
3159
3160
3161THREADED_TEST(CrankshaftInterceptorGetter) {
3162 i::FLAG_allow_natives_syntax = true;
3163 v8::HandleScope scope(CcTest::isolate());
3164 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
3165 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
3166 LocalContext env;
3167 env->Global()
3168 ->Set(env.local(), v8_str("Obj"),
3169 templ->GetFunction(env.local()).ToLocalChecked())
3170 .FromJust();
3171 CompileRun(
3172 "var obj = new Obj;"
3173 // Initialize fields to avoid transitions later.
3174 "obj.age = 1;"
3175 "obj.accessor_age = 42;"
3176 "function getter() { return this.accessor_age; };"
3177 "function getAge() { return obj.interceptor_age; };"
3178 "Object.defineProperty(obj, 'interceptor_age', { get:getter });"
3179 "getAge();"
3180 "getAge();"
3181 "getAge();"
3182 "%OptimizeFunctionOnNextCall(getAge);");
3183 // Access through interceptor.
3184 ExpectInt32("getAge()", 1);
3185}
3186
3187
3188THREADED_TEST(CrankshaftInterceptorFieldRead) {
3189 i::FLAG_allow_natives_syntax = true;
3190 v8::HandleScope scope(CcTest::isolate());
3191 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
3192 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
3193 LocalContext env;
3194 env->Global()
3195 ->Set(env.local(), v8_str("Obj"),
3196 templ->GetFunction(env.local()).ToLocalChecked())
3197 .FromJust();
3198 CompileRun(
3199 "var obj = new Obj;"
3200 "obj.__proto__.interceptor_age = 42;"
3201 "obj.age = 100;"
3202 "function getAge() { return obj.interceptor_age; };");
3203 ExpectInt32("getAge();", 100);
3204 ExpectInt32("getAge();", 100);
3205 ExpectInt32("getAge();", 100);
3206 CompileRun("%OptimizeFunctionOnNextCall(getAge);");
3207 // Access through interceptor.
3208 ExpectInt32("getAge();", 100);
3209}
3210
3211
3212THREADED_TEST(CrankshaftInterceptorFieldWrite) {
3213 i::FLAG_allow_natives_syntax = true;
3214 v8::HandleScope scope(CcTest::isolate());
3215 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
3216 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
3217 LocalContext env;
3218 env->Global()
3219 ->Set(env.local(), v8_str("Obj"),
3220 templ->GetFunction(env.local()).ToLocalChecked())
3221 .FromJust();
3222 CompileRun(
3223 "var obj = new Obj;"
3224 "obj.age = 100000;"
3225 "function setAge(i) { obj.age = i };"
3226 "setAge(100);"
3227 "setAge(101);"
3228 "setAge(102);"
3229 "%OptimizeFunctionOnNextCall(setAge);"
3230 "setAge(103);");
3231 ExpectInt32("obj.age", 100000);
3232 ExpectInt32("obj.interceptor_age", 103);
3233}
3234
3235
3236THREADED_TEST(Regress149912) {
3237 LocalContext context;
3238 v8::HandleScope scope(context->GetIsolate());
3239 Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
3240 AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
3241 context->Global()
3242 ->Set(context.local(), v8_str("Bug"),
3243 templ->GetFunction(context.local()).ToLocalChecked())
3244 .FromJust();
3245 CompileRun("Number.prototype.__proto__ = new Bug; var x = 0; x.foo();");
3246}
3247
3248
3249THREADED_TEST(Regress125988) {
3250 v8::HandleScope scope(CcTest::isolate());
3251 Local<FunctionTemplate> intercept = FunctionTemplate::New(CcTest::isolate());
3252 AddInterceptor(intercept, EmptyInterceptorGetter, EmptyInterceptorSetter);
3253 LocalContext env;
3254 env->Global()
3255 ->Set(env.local(), v8_str("Intercept"),
3256 intercept->GetFunction(env.local()).ToLocalChecked())
3257 .FromJust();
3258 CompileRun(
3259 "var a = new Object();"
3260 "var b = new Intercept();"
3261 "var c = new Object();"
3262 "c.__proto__ = b;"
3263 "b.__proto__ = a;"
3264 "a.x = 23;"
3265 "for (var i = 0; i < 3; i++) c.x;");
3266 ExpectBoolean("c.hasOwnProperty('x')", false);
3267 ExpectInt32("c.x", 23);
3268 CompileRun(
3269 "a.y = 42;"
3270 "for (var i = 0; i < 3; i++) c.x;");
3271 ExpectBoolean("c.hasOwnProperty('x')", false);
3272 ExpectInt32("c.x", 23);
3273 ExpectBoolean("c.hasOwnProperty('y')", false);
3274 ExpectInt32("c.y", 42);
3275}
3276
3277
3278static void IndexedPropertyEnumerator(
3279 const v8::PropertyCallbackInfo<v8::Array>& info) {
3280 v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 1);
3281 result->Set(info.GetIsolate()->GetCurrentContext(), 0,
3282 v8::Integer::New(info.GetIsolate(), 7))
3283 .FromJust();
3284 info.GetReturnValue().Set(result);
3285}
3286
3287
3288static void NamedPropertyEnumerator(
3289 const v8::PropertyCallbackInfo<v8::Array>& info) {
3290 v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 2);
3291 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
3292 result->Set(context, 0, v8_str("x")).FromJust();
3293 result->Set(context, 1, v8::Symbol::GetIterator(info.GetIsolate()))
3294 .FromJust();
3295 info.GetReturnValue().Set(result);
3296}
3297
3298
3299THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
3300 v8::Isolate* isolate = CcTest::isolate();
3301 v8::HandleScope handle_scope(isolate);
3302 v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
3303
3304 obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7));
3305 obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42));
3306 obj_template->SetHandler(v8::IndexedPropertyHandlerConfiguration(
3307 NULL, NULL, NULL, NULL, IndexedPropertyEnumerator));
3308 obj_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
3309 NULL, NULL, NULL, NULL, NamedPropertyEnumerator));
3310
3311 LocalContext context;
3312 v8::Local<v8::Object> global = context->Global();
3313 global->Set(context.local(), v8_str("object"),
3314 obj_template->NewInstance(context.local()).ToLocalChecked())
3315 .FromJust();
3316
3317 v8::Local<v8::Value> result =
3318 CompileRun("Object.getOwnPropertyNames(object)");
3319 CHECK(result->IsArray());
3320 v8::Local<v8::Array> result_array = v8::Local<v8::Array>::Cast(result);
3321 CHECK_EQ(2u, result_array->Length());
3322 CHECK(result_array->Get(context.local(), 0).ToLocalChecked()->IsString());
3323 CHECK(result_array->Get(context.local(), 1).ToLocalChecked()->IsString());
3324 CHECK(v8_str("7")
3325 ->Equals(context.local(),
3326 result_array->Get(context.local(), 0).ToLocalChecked())
3327 .FromJust());
3328 CHECK(v8_str("x")
3329 ->Equals(context.local(),
3330 result_array->Get(context.local(), 1).ToLocalChecked())
3331 .FromJust());
3332
3333 result = CompileRun("var ret = []; for (var k in object) ret.push(k); ret");
3334 CHECK(result->IsArray());
3335 result_array = v8::Local<v8::Array>::Cast(result);
3336 CHECK_EQ(2u, result_array->Length());
3337 CHECK(result_array->Get(context.local(), 0).ToLocalChecked()->IsString());
3338 CHECK(result_array->Get(context.local(), 1).ToLocalChecked()->IsString());
3339 CHECK(v8_str("7")
3340 ->Equals(context.local(),
3341 result_array->Get(context.local(), 0).ToLocalChecked())
3342 .FromJust());
3343 CHECK(v8_str("x")
3344 ->Equals(context.local(),
3345 result_array->Get(context.local(), 1).ToLocalChecked())
3346 .FromJust());
3347
3348 result = CompileRun("Object.getOwnPropertySymbols(object)");
3349 CHECK(result->IsArray());
3350 result_array = v8::Local<v8::Array>::Cast(result);
3351 CHECK_EQ(1u, result_array->Length());
3352 CHECK(result_array->Get(context.local(), 0)
3353 .ToLocalChecked()
3354 ->Equals(context.local(), v8::Symbol::GetIterator(isolate))
3355 .FromJust());
3356}
3357
3358
3359static void IndexedPropertyEnumeratorException(
3360 const v8::PropertyCallbackInfo<v8::Array>& info) {
3361 info.GetIsolate()->ThrowException(v8_num(42));
3362}
3363
3364
3365THREADED_TEST(GetOwnPropertyNamesWithIndexedInterceptorExceptions_regress4026) {
3366 v8::Isolate* isolate = CcTest::isolate();
3367 v8::HandleScope handle_scope(isolate);
3368 v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
3369
3370 obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7));
3371 obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42));
3372 // First just try a failing indexed interceptor.
3373 obj_template->SetHandler(v8::IndexedPropertyHandlerConfiguration(
3374 NULL, NULL, NULL, NULL, IndexedPropertyEnumeratorException));
3375
3376 LocalContext context;
3377 v8::Local<v8::Object> global = context->Global();
3378 global->Set(context.local(), v8_str("object"),
3379 obj_template->NewInstance(context.local()).ToLocalChecked())
3380 .FromJust();
3381 v8::Local<v8::Value> result = CompileRun(
3382 "var result = []; "
3383 "try { "
3384 " for (var k in object) result .push(k);"
3385 "} catch (e) {"
3386 " result = e"
3387 "}"
3388 "result ");
3389 CHECK(!result->IsArray());
3390 CHECK(v8_num(42)->Equals(context.local(), result).FromJust());
3391
3392 result = CompileRun(
3393 "var result = [];"
3394 "try { "
3395 " result = Object.keys(object);"
3396 "} catch (e) {"
3397 " result = e;"
3398 "}"
3399 "result");
3400 CHECK(!result->IsArray());
3401 CHECK(v8_num(42)->Equals(context.local(), result).FromJust());
3402}
3403
3404
3405static void NamedPropertyEnumeratorException(
3406 const v8::PropertyCallbackInfo<v8::Array>& info) {
3407 info.GetIsolate()->ThrowException(v8_num(43));
3408}
3409
3410
3411THREADED_TEST(GetOwnPropertyNamesWithNamedInterceptorExceptions_regress4026) {
3412 v8::Isolate* isolate = CcTest::isolate();
3413 v8::HandleScope handle_scope(isolate);
3414 v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
3415
3416 obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7));
3417 obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42));
3418 // First just try a failing indexed interceptor.
3419 obj_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
3420 NULL, NULL, NULL, NULL, NamedPropertyEnumeratorException));
3421
3422 LocalContext context;
3423 v8::Local<v8::Object> global = context->Global();
3424 global->Set(context.local(), v8_str("object"),
3425 obj_template->NewInstance(context.local()).ToLocalChecked())
3426 .FromJust();
3427
3428 v8::Local<v8::Value> result = CompileRun(
3429 "var result = []; "
3430 "try { "
3431 " for (var k in object) result.push(k);"
3432 "} catch (e) {"
3433 " result = e"
3434 "}"
3435 "result");
3436 CHECK(!result->IsArray());
3437 CHECK(v8_num(43)->Equals(context.local(), result).FromJust());
3438
3439 result = CompileRun(
3440 "var result = [];"
3441 "try { "
3442 " result = Object.keys(object);"
3443 "} catch (e) {"
3444 " result = e;"
3445 "}"
3446 "result");
3447 CHECK(!result->IsArray());
3448 CHECK(v8_num(43)->Equals(context.local(), result).FromJust());
3449}
3450
3451namespace {
3452
3453template <typename T>
3454Local<Object> BuildWrappedObject(v8::Isolate* isolate, T* data) {
3455 auto templ = v8::ObjectTemplate::New(isolate);
3456 templ->SetInternalFieldCount(1);
3457 auto instance =
3458 templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
3459 instance->SetAlignedPointerInInternalField(0, data);
3460 return instance;
3461}
3462
3463
3464template <typename T>
3465T* GetWrappedObject(Local<Value> data) {
3466 return reinterpret_cast<T*>(
3467 Object::Cast(*data)->GetAlignedPointerFromInternalField(0));
3468}
3469
3470
3471struct AccessCheckData {
3472 int count;
3473 bool result;
3474};
3475
3476AccessCheckData* g_access_check_data = nullptr;
3477
3478
3479bool SimpleAccessChecker(Local<v8::Context> accessing_context,
3480 Local<v8::Object> access_object) {
3481 g_access_check_data->count++;
3482 return g_access_check_data->result;
3483}
3484
3485
3486struct ShouldInterceptData {
3487 int value;
3488 bool should_intercept;
3489};
3490
3491
3492void ShouldNamedInterceptor(Local<Name> name,
3493 const v8::PropertyCallbackInfo<Value>& info) {
3494 ApiTestFuzzer::Fuzz();
3495 CheckReturnValue(info, FUNCTION_ADDR(ShouldNamedInterceptor));
3496 auto data = GetWrappedObject<ShouldInterceptData>(info.Data());
3497 if (!data->should_intercept) return;
3498 info.GetReturnValue().Set(v8_num(data->value));
3499}
3500
3501
3502void ShouldIndexedInterceptor(uint32_t,
3503 const v8::PropertyCallbackInfo<Value>& info) {
3504 ApiTestFuzzer::Fuzz();
3505 CheckReturnValue(info, FUNCTION_ADDR(ShouldIndexedInterceptor));
3506 auto data = GetWrappedObject<ShouldInterceptData>(info.Data());
3507 if (!data->should_intercept) return;
3508 info.GetReturnValue().Set(v8_num(data->value));
3509}
3510
3511} // namespace
3512
3513
3514TEST(NamedAllCanReadInterceptor) {
3515 auto isolate = CcTest::isolate();
3516 v8::HandleScope handle_scope(isolate);
3517 LocalContext context;
3518
3519 AccessCheckData access_check_data;
3520 access_check_data.result = true;
3521 access_check_data.count = 0;
3522
3523 g_access_check_data = &access_check_data;
3524
3525 ShouldInterceptData intercept_data_0;
3526 intercept_data_0.value = 239;
3527 intercept_data_0.should_intercept = true;
3528
3529 ShouldInterceptData intercept_data_1;
3530 intercept_data_1.value = 165;
3531 intercept_data_1.should_intercept = false;
3532
3533 auto intercepted_0 = v8::ObjectTemplate::New(isolate);
3534 {
3535 v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
3536 conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
3537 conf.data =
3538 BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_0);
3539 intercepted_0->SetHandler(conf);
3540 }
3541
3542 auto intercepted_1 = v8::ObjectTemplate::New(isolate);
3543 {
3544 v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
3545 conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
3546 conf.data =
3547 BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_1);
3548 intercepted_1->SetHandler(conf);
3549 }
3550
3551 auto checked = v8::ObjectTemplate::New(isolate);
3552 checked->SetAccessCheckCallback(SimpleAccessChecker);
3553
3554 context->Global()
3555 ->Set(context.local(), v8_str("intercepted_0"),
3556 intercepted_0->NewInstance(context.local()).ToLocalChecked())
3557 .FromJust();
3558 context->Global()
3559 ->Set(context.local(), v8_str("intercepted_1"),
3560 intercepted_1->NewInstance(context.local()).ToLocalChecked())
3561 .FromJust();
3562 auto checked_instance =
3563 checked->NewInstance(context.local()).ToLocalChecked();
3564 checked_instance->Set(context.local(), v8_str("whatever"), v8_num(17))
3565 .FromJust();
3566 context->Global()
3567 ->Set(context.local(), v8_str("checked"), checked_instance)
3568 .FromJust();
3569 CompileRun(
3570 "checked.__proto__ = intercepted_1;"
3571 "intercepted_1.__proto__ = intercepted_0;");
3572
3573 CHECK_EQ(3, access_check_data.count);
3574
3575 ExpectInt32("checked.whatever", 17);
3576 CHECK(!CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')")
3577 ->IsUndefined());
3578 CHECK_EQ(5, access_check_data.count);
3579
3580 access_check_data.result = false;
3581 ExpectInt32("checked.whatever", intercept_data_0.value);
3582 {
3583 v8::TryCatch try_catch(isolate);
3584 CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')");
3585 CHECK(try_catch.HasCaught());
3586 }
3587 CHECK_EQ(7, access_check_data.count);
3588
3589 intercept_data_1.should_intercept = true;
3590 ExpectInt32("checked.whatever", intercept_data_1.value);
3591 {
3592 v8::TryCatch try_catch(isolate);
3593 CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')");
3594 CHECK(try_catch.HasCaught());
3595 }
3596 CHECK_EQ(9, access_check_data.count);
3597 g_access_check_data = nullptr;
3598}
3599
3600
3601TEST(IndexedAllCanReadInterceptor) {
3602 auto isolate = CcTest::isolate();
3603 v8::HandleScope handle_scope(isolate);
3604 LocalContext context;
3605
3606 AccessCheckData access_check_data;
3607 access_check_data.result = true;
3608 access_check_data.count = 0;
3609
3610 g_access_check_data = &access_check_data;
3611
3612 ShouldInterceptData intercept_data_0;
3613 intercept_data_0.value = 239;
3614 intercept_data_0.should_intercept = true;
3615
3616 ShouldInterceptData intercept_data_1;
3617 intercept_data_1.value = 165;
3618 intercept_data_1.should_intercept = false;
3619
3620 auto intercepted_0 = v8::ObjectTemplate::New(isolate);
3621 {
3622 v8::IndexedPropertyHandlerConfiguration conf(ShouldIndexedInterceptor);
3623 conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
3624 conf.data =
3625 BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_0);
3626 intercepted_0->SetHandler(conf);
3627 }
3628
3629 auto intercepted_1 = v8::ObjectTemplate::New(isolate);
3630 {
3631 v8::IndexedPropertyHandlerConfiguration conf(ShouldIndexedInterceptor);
3632 conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
3633 conf.data =
3634 BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_1);
3635 intercepted_1->SetHandler(conf);
3636 }
3637
3638 auto checked = v8::ObjectTemplate::New(isolate);
3639 checked->SetAccessCheckCallback(SimpleAccessChecker);
3640
3641 context->Global()
3642 ->Set(context.local(), v8_str("intercepted_0"),
3643 intercepted_0->NewInstance(context.local()).ToLocalChecked())
3644 .FromJust();
3645 context->Global()
3646 ->Set(context.local(), v8_str("intercepted_1"),
3647 intercepted_1->NewInstance(context.local()).ToLocalChecked())
3648 .FromJust();
3649 auto checked_instance =
3650 checked->NewInstance(context.local()).ToLocalChecked();
3651 context->Global()
3652 ->Set(context.local(), v8_str("checked"), checked_instance)
3653 .FromJust();
3654 checked_instance->Set(context.local(), 15, v8_num(17)).FromJust();
3655 CompileRun(
3656 "checked.__proto__ = intercepted_1;"
3657 "intercepted_1.__proto__ = intercepted_0;");
3658
3659 CHECK_EQ(3, access_check_data.count);
3660
3661 access_check_data.result = true;
3662 ExpectInt32("checked[15]", 17);
3663 CHECK(!CompileRun("Object.getOwnPropertyDescriptor(checked, '15')")
3664 ->IsUndefined());
3665 CHECK_EQ(5, access_check_data.count);
3666
3667 access_check_data.result = false;
3668 ExpectInt32("checked[15]", intercept_data_0.value);
3669 {
3670 v8::TryCatch try_catch(isolate);
3671 CompileRun("Object.getOwnPropertyDescriptor(checked, '15')");
3672 CHECK(try_catch.HasCaught());
3673 }
3674 CHECK_EQ(7, access_check_data.count);
3675
3676 intercept_data_1.should_intercept = true;
3677 ExpectInt32("checked[15]", intercept_data_1.value);
3678 {
3679 v8::TryCatch try_catch(isolate);
3680 CompileRun("Object.getOwnPropertyDescriptor(checked, '15')");
3681 CHECK(try_catch.HasCaught());
3682 }
3683 CHECK_EQ(9, access_check_data.count);
3684
3685 g_access_check_data = nullptr;
3686}
3687
3688
3689THREADED_TEST(NonMaskingInterceptorOwnProperty) {
3690 auto isolate = CcTest::isolate();
3691 v8::HandleScope handle_scope(isolate);
3692 LocalContext context;
3693
3694 ShouldInterceptData intercept_data;
3695 intercept_data.value = 239;
3696 intercept_data.should_intercept = true;
3697
3698 auto interceptor_templ = v8::ObjectTemplate::New(isolate);
3699 v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
3700 conf.flags = v8::PropertyHandlerFlags::kNonMasking;
3701 conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data);
3702 interceptor_templ->SetHandler(conf);
3703
3704 auto interceptor =
3705 interceptor_templ->NewInstance(context.local()).ToLocalChecked();
3706 context->Global()
3707 ->Set(context.local(), v8_str("obj"), interceptor)
3708 .FromJust();
3709
3710 ExpectInt32("obj.whatever", 239);
3711
3712 CompileRun("obj.whatever = 4;");
3713 ExpectInt32("obj.whatever", 4);
3714
3715 CompileRun("delete obj.whatever;");
3716 ExpectInt32("obj.whatever", 239);
3717}
3718
3719
3720THREADED_TEST(NonMaskingInterceptorPrototypeProperty) {
3721 auto isolate = CcTest::isolate();
3722 v8::HandleScope handle_scope(isolate);
3723 LocalContext context;
3724
3725 ShouldInterceptData intercept_data;
3726 intercept_data.value = 239;
3727 intercept_data.should_intercept = true;
3728
3729 auto interceptor_templ = v8::ObjectTemplate::New(isolate);
3730 v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
3731 conf.flags = v8::PropertyHandlerFlags::kNonMasking;
3732 conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data);
3733 interceptor_templ->SetHandler(conf);
3734
3735 auto interceptor =
3736 interceptor_templ->NewInstance(context.local()).ToLocalChecked();
3737 context->Global()
3738 ->Set(context.local(), v8_str("obj"), interceptor)
3739 .FromJust();
3740
3741 ExpectInt32("obj.whatever", 239);
3742
3743 CompileRun("obj.__proto__ = {'whatever': 4};");
3744 ExpectInt32("obj.whatever", 4);
3745
3746 CompileRun("delete obj.__proto__.whatever;");
3747 ExpectInt32("obj.whatever", 239);
3748}
3749
3750
3751THREADED_TEST(NonMaskingInterceptorPrototypePropertyIC) {
3752 auto isolate = CcTest::isolate();
3753 v8::HandleScope handle_scope(isolate);
3754 LocalContext context;
3755
3756 ShouldInterceptData intercept_data;
3757 intercept_data.value = 239;
3758 intercept_data.should_intercept = true;
3759
3760 auto interceptor_templ = v8::ObjectTemplate::New(isolate);
3761 v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
3762 conf.flags = v8::PropertyHandlerFlags::kNonMasking;
3763 conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data);
3764 interceptor_templ->SetHandler(conf);
3765
3766 auto interceptor =
3767 interceptor_templ->NewInstance(context.local()).ToLocalChecked();
3768 context->Global()
3769 ->Set(context.local(), v8_str("obj"), interceptor)
3770 .FromJust();
3771
3772 CompileRun(
3773 "outer = {};"
3774 "outer.__proto__ = obj;"
3775 "function f(obj) {"
3776 " var x;"
3777 " for (var i = 0; i < 4; i++) {"
3778 " x = obj.whatever;"
3779 " }"
3780 " return x;"
3781 "}");
3782
3783 // Receiver == holder.
3784 CompileRun("obj.__proto__ = null;");
3785 ExpectInt32("f(obj)", 239);
3786 ExpectInt32("f(outer)", 239);
3787
3788 // Receiver != holder.
3789 CompileRun("Object.setPrototypeOf(obj, {});");
3790 ExpectInt32("f(obj)", 239);
3791 ExpectInt32("f(outer)", 239);
3792
3793 // Masked value on prototype.
3794 CompileRun("obj.__proto__.whatever = 4;");
3795 CompileRun("obj.__proto__.__proto__ = { 'whatever' : 5 };");
3796 ExpectInt32("f(obj)", 4);
3797 ExpectInt32("f(outer)", 4);
3798
3799 // Masked value on prototype prototype.
3800 CompileRun("delete obj.__proto__.whatever;");
3801 ExpectInt32("f(obj)", 5);
3802 ExpectInt32("f(outer)", 5);
3803
3804 // Reset.
3805 CompileRun("delete obj.__proto__.__proto__.whatever;");
3806 ExpectInt32("f(obj)", 239);
3807 ExpectInt32("f(outer)", 239);
3808
3809 // Masked value on self.
3810 CompileRun("obj.whatever = 4;");
3811 ExpectInt32("f(obj)", 4);
3812 ExpectInt32("f(outer)", 4);
3813
3814 // Reset.
3815 CompileRun("delete obj.whatever;");
3816 ExpectInt32("f(obj)", 239);
3817 ExpectInt32("f(outer)", 239);
3818
3819 CompileRun("outer.whatever = 4;");
3820 ExpectInt32("f(obj)", 239);
3821 ExpectInt32("f(outer)", 4);
3822}
3823
3824
3825namespace {
3826
3827void DatabaseGetter(Local<Name> name,
3828 const v8::PropertyCallbackInfo<Value>& info) {
3829 ApiTestFuzzer::Fuzz();
3830 auto context = info.GetIsolate()->GetCurrentContext();
3831 Local<v8::Object> db = info.Holder()
3832 ->GetRealNamedProperty(context, v8_str("db"))
3833 .ToLocalChecked()
3834 .As<v8::Object>();
3835 if (!db->Has(context, name).FromJust()) return;
3836 info.GetReturnValue().Set(db->Get(context, name).ToLocalChecked());
3837}
3838
3839
3840void DatabaseSetter(Local<Name> name, Local<Value> value,
3841 const v8::PropertyCallbackInfo<Value>& info) {
3842 ApiTestFuzzer::Fuzz();
3843 auto context = info.GetIsolate()->GetCurrentContext();
3844 if (name->Equals(context, v8_str("db")).FromJust()) return;
3845 Local<v8::Object> db = info.Holder()
3846 ->GetRealNamedProperty(context, v8_str("db"))
3847 .ToLocalChecked()
3848 .As<v8::Object>();
3849 db->Set(context, name, value).FromJust();
3850 info.GetReturnValue().Set(value);
3851}
3852
3853} // namespace
3854
3855
3856THREADED_TEST(NonMaskingInterceptorGlobalEvalRegression) {
3857 auto isolate = CcTest::isolate();
3858 v8::HandleScope handle_scope(isolate);
3859 LocalContext context;
3860
3861 auto interceptor_templ = v8::ObjectTemplate::New(isolate);
3862 v8::NamedPropertyHandlerConfiguration conf(DatabaseGetter, DatabaseSetter);
3863 conf.flags = v8::PropertyHandlerFlags::kNonMasking;
3864 interceptor_templ->SetHandler(conf);
3865
3866 context->Global()
3867 ->Set(context.local(), v8_str("intercepted_1"),
3868 interceptor_templ->NewInstance(context.local()).ToLocalChecked())
3869 .FromJust();
3870 context->Global()
3871 ->Set(context.local(), v8_str("intercepted_2"),
3872 interceptor_templ->NewInstance(context.local()).ToLocalChecked())
3873 .FromJust();
3874
3875 // Init dbs.
3876 CompileRun(
3877 "intercepted_1.db = {};"
3878 "intercepted_2.db = {};");
3879
3880 ExpectInt32(
3881 "var obj = intercepted_1;"
3882 "obj.x = 4;"
3883 "eval('obj.x');"
3884 "eval('obj.x');"
3885 "eval('obj.x');"
3886 "obj = intercepted_2;"
3887 "obj.x = 9;"
3888 "eval('obj.x');",
3889 9);
3890}